]> git.madduck.net Git - etc/awesome.git/blob - luatz/strftime.lua

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

doc/index.md: Fix internal link
[etc/awesome.git] / luatz / strftime.lua
1 local strformat = string.format
2 local floor = math.floor
3 local function idiv(n, d)
4         return floor(n / d)
5 end
6
7 local c_locale = {
8         abday = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
9         day = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
10         abmon = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
11         mon = {"January", "February", "March", "April", "May", "June",
12                 "July", "August", "September", "October", "November", "December"};
13         am_pm = {"AM", "PM"};
14 }
15
16 --- ISO-8601 week logic
17 -- ISO 8601 weekday as number with Monday as 1 (1-7)
18 local function iso_8601_weekday(wday)
19         if wday == 1 then
20                 return 7
21         else
22                 return wday - 1
23         end
24 end
25 local iso_8601_week do
26         -- Years that have 53 weeks according to ISO-8601
27         local long_years = {}
28         for _, v in ipairs {
29                   4,   9,  15,  20,  26,  32,  37,  43,  48,  54,  60,  65,  71,  76,  82,
30                  88,  93,  99, 105, 111, 116, 122, 128, 133, 139, 144, 150, 156, 161, 167,
31                 172, 178, 184, 189, 195, 201, 207, 212, 218, 224, 229, 235, 240, 246, 252,
32                 257, 263, 268, 274, 280, 285, 291, 296, 303, 308, 314, 320, 325, 331, 336,
33                 342, 348, 353, 359, 364, 370, 376, 381, 387, 392, 398
34         } do
35                 long_years[v] = true
36         end
37         local function is_long_year(year)
38                 return long_years[year % 400]
39         end
40         function iso_8601_week(self)
41                 local wday = iso_8601_weekday(self.wday)
42                 local n = self.yday - wday
43                 local year = self.year
44                 if n < -3 then
45                         year = year - 1
46                         if is_long_year(year) then
47                                 return year, 53, wday
48                         else
49                                 return year, 52, wday
50                         end
51                 elseif n >= 361 and not is_long_year(year) then
52                         return year + 1, 1, wday
53                 else
54                         return year, idiv(n + 10, 7), wday
55                 end
56         end
57 end
58
59 --- Specifiers
60 local t = {}
61 function t:a(locale)
62         return "%s", locale.abday[self.wday]
63 end
64 function t:A(locale)
65         return "%s", locale.day[self.wday]
66 end
67 function t:b(locale)
68         return "%s", locale.abmon[self.month]
69 end
70 function t:B(locale)
71         return "%s", locale.mon[self.month]
72 end
73 function t:c(locale)
74         return "%.3s %.3s%3d %.2d:%.2d:%.2d %d",
75                 locale.abday[self.wday], locale.abmon[self.month],
76                 self.day, self.hour, self.min, self.sec, self.year
77 end
78 -- Century
79 function t:C()
80         return "%02d", idiv(self.year, 100)
81 end
82 function t:d()
83         return "%02d", self.day
84 end
85 -- Short MM/DD/YY date, equivalent to %m/%d/%y
86 function t:D()
87         return "%02d/%02d/%02d", self.month, self.day, self.year % 100
88 end
89 function t:e()
90         return "%2d", self.day
91 end
92 -- Short YYYY-MM-DD date, equivalent to %Y-%m-%d
93 function t:F()
94         return "%d-%02d-%02d", self.year, self.month, self.day
95 end
96 -- Week-based year, last two digits (00-99)
97 function t:g()
98         return "%02d", iso_8601_week(self) % 100
99 end
100 -- Week-based year
101 function t:G()
102         return "%d", iso_8601_week(self)
103 end
104 t.h = t.b
105 function t:H()
106         return "%02d", self.hour
107 end
108 function t:I()
109         return "%02d", (self.hour-1) % 12 + 1
110 end
111 function t:j()
112         return "%03d", self.yday
113 end
114 function t:m()
115         return "%02d", self.month
116 end
117 function t:M()
118         return "%02d", self.min
119 end
120 -- New-line character ('\n')
121 function t:n() -- luacheck: ignore 212
122         return "\n"
123 end
124 function t:p(locale)
125         return self.hour < 12 and locale.am_pm[1] or locale.am_pm[2]
126 end
127 -- TODO: should respect locale
128 function t:r(locale)
129         return "%02d:%02d:%02d %s",
130                 (self.hour-1) % 12 + 1, self.min, self.sec,
131                 self.hour < 12 and locale.am_pm[1] or locale.am_pm[2]
132 end
133 -- 24-hour HH:MM time, equivalent to %H:%M
134 function t:R()
135         return "%02d:%02d", self.hour, self.min
136 end
137 function t:s()
138         return "%d", self:timestamp()
139 end
140 function t:S()
141         return "%02d", self.sec
142 end
143 -- Horizontal-tab character ('\t')
144 function t:t() -- luacheck: ignore 212
145         return "\t"
146 end
147 -- ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S
148 function t:T()
149         return "%02d:%02d:%02d", self.hour, self.min, self.sec
150 end
151 function t:u()
152         return "%d", iso_8601_weekday(self.wday)
153 end
154 -- Week number with the first Sunday as the first day of week one (00-53)
155 function t:U()
156         return "%02d", idiv(self.yday - self.wday + 7, 7)
157 end
158 -- ISO 8601 week number (00-53)
159 function t:V()
160         return "%02d", select(2, iso_8601_week(self))
161 end
162 -- Weekday as a decimal number with Sunday as 0 (0-6)
163 function t:w()
164         return "%d", self.wday - 1
165 end
166 -- Week number with the first Monday as the first day of week one (00-53)
167 function t:W()
168         return "%02d", idiv(self.yday - iso_8601_weekday(self.wday) + 7, 7)
169 end
170 -- TODO make t.x and t.X respect locale
171 t.x = t.D
172 t.X = t.T
173 function t:y()
174         return "%02d", self.year % 100
175 end
176 function t:Y()
177         return "%d", self.year
178 end
179 -- TODO timezones
180 function t:z() -- luacheck: ignore 212
181         return "+0000"
182 end
183 function t:Z() -- luacheck: ignore 212
184         return "GMT"
185 end
186 -- A literal '%' character.
187 t["%"] = function(self) -- luacheck: ignore 212
188         return "%%"
189 end
190
191 local function strftime(format_string, timetable)
192         return (string.gsub(format_string, "%%([EO]?)(.)", function(locale_modifier, specifier)
193                 local func = t[specifier]
194                 if func then
195                         return strformat(func(timetable, c_locale))
196                 else
197                         error("invalid conversation specifier '%"..locale_modifier..specifier.."'", 3)
198                 end
199         end))
200 end
201
202 local function asctime(timetable)
203         -- Equivalent to the format string "%c\n"
204         return strformat(t.c(timetable, c_locale)) .. "\n"
205 end
206
207 return {
208         strftime = strftime;
209         asctime = asctime;
210 }