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