Skip to content

Commit ff9305c

Browse files
committed
std: add a pure rust strftime formatter.
1 parent 4a48898 commit ff9305c

File tree

1 file changed

+306
-0
lines changed

1 file changed

+306
-0
lines changed

Diff for: src/libstd/time.rs

+306
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,175 @@ fn now() -> tm {
116116
at(get_time())
117117
}
118118

119+
fn strftime(format: str, tm: tm) -> str {
120+
fn parse_type(ch: char, tm: tm) -> str {
121+
//FIXME: Implement missing types.
122+
alt check ch {
123+
'A' {
124+
alt check tm.tm_wday as int {
125+
0 { "Sunday" }
126+
1 { "Monday" }
127+
2 { "Tuesday" }
128+
3 { "Wednesday" }
129+
4 { "Thursday" }
130+
5 { "Friday" }
131+
6 { "Saturday" }
132+
}
133+
}
134+
'a' {
135+
alt check tm.tm_wday as int {
136+
0 { "Sun" }
137+
1 { "Mon" }
138+
2 { "Tue" }
139+
3 { "Wed" }
140+
4 { "Thu" }
141+
5 { "Fri" }
142+
6 { "Sat" }
143+
}
144+
}
145+
'B' {
146+
alt check tm.tm_mon as int {
147+
0 { "January" }
148+
1 { "February" }
149+
2 { "March" }
150+
3 { "April" }
151+
4 { "May" }
152+
5 { "June" }
153+
6 { "July" }
154+
7 { "August" }
155+
8 { "September" }
156+
9 { "October" }
157+
10 { "November" }
158+
11 { "December" }
159+
}
160+
}
161+
'b' | 'h' {
162+
alt check tm.tm_mon as int {
163+
0 { "Jan" }
164+
1 { "Feb" }
165+
2 { "Mar" }
166+
3 { "Apr" }
167+
4 { "May" }
168+
5 { "Jun" }
169+
6 { "Jul" }
170+
7 { "Aug" }
171+
8 { "Sep" }
172+
9 { "Oct" }
173+
10 { "Nov" }
174+
11 { "Dec" }
175+
}
176+
}
177+
'C' { #fmt("%02d", (tm.tm_year as int + 1900) / 100) }
178+
'c' {
179+
#fmt("%s %s %s %s %s",
180+
parse_type('a', tm),
181+
parse_type('b', tm),
182+
parse_type('e', tm),
183+
parse_type('T', tm),
184+
parse_type('Y', tm))
185+
}
186+
'D' | 'x' {
187+
#fmt("%s/%s/%s",
188+
parse_type('m', tm),
189+
parse_type('d', tm),
190+
parse_type('y', tm))
191+
}
192+
'd' { #fmt("%02d", tm.tm_mday as int) }
193+
'e' { #fmt("%2d", tm.tm_mday as int) }
194+
'F' {
195+
#fmt("%s-%s-%s",
196+
parse_type('Y', tm),
197+
parse_type('m', tm),
198+
parse_type('d', tm))
199+
}
200+
//'G' {}
201+
//'g' {}
202+
'H' { #fmt("%02d", tm.tm_hour as int) }
203+
'I' {
204+
let mut h = tm.tm_hour as int;
205+
if h == 0 { h = 12 }
206+
if h > 12 { h -= 12 }
207+
#fmt("%02d", h)
208+
}
209+
'j' { #fmt("%03d", tm.tm_yday as int + 1) }
210+
'k' { #fmt("%2d", tm.tm_hour as int) }
211+
'l' {
212+
let mut h = tm.tm_hour as int;
213+
if h == 0 { h = 12 }
214+
if h > 12 { h -= 12 }
215+
#fmt("%2d", h)
216+
}
217+
'M' { #fmt("%02d", tm.tm_min as int) }
218+
'm' { #fmt("%02d", tm.tm_mon as int + 1) }
219+
'n' { "\n" }
220+
'P' { if tm.tm_hour as int < 12 { "am" } else { "pm" } }
221+
'p' { if tm.tm_hour as int < 12 { "AM" } else { "PM" } }
222+
'R' {
223+
#fmt("%s:%s",
224+
parse_type('H', tm),
225+
parse_type('M', tm))
226+
}
227+
'r' {
228+
#fmt("%s:%s:%s %s",
229+
parse_type('I', tm),
230+
parse_type('M', tm),
231+
parse_type('S', tm),
232+
parse_type('p', tm))
233+
}
234+
'S' { #fmt("%02d", tm.tm_sec as int) }
235+
's' { #fmt("%d", tm.to_timespec().sec as int) }
236+
'T' | 'X' {
237+
#fmt("%s:%s:%s",
238+
parse_type('H', tm),
239+
parse_type('M', tm),
240+
parse_type('S', tm))
241+
}
242+
't' { "\t" }
243+
//'U' {}
244+
'u' {
245+
let i = tm.tm_wday as int;
246+
int::str(if i == 0 { 7 } else { i })
247+
}
248+
//'V' {}
249+
'v' {
250+
#fmt("%s-%s-%s",
251+
parse_type('e', tm),
252+
parse_type('b', tm),
253+
parse_type('Y', tm))
254+
}
255+
//'W' {}
256+
'w' { int::str(tm.tm_wday as int) }
257+
//'X' {}
258+
//'x' {}
259+
'Y' { int::str(tm.tm_year as int + 1900) }
260+
'y' { #fmt("%02d", (tm.tm_year as int + 1900) % 100) }
261+
'Z' { tm.tm_zone }
262+
'z' {
263+
let sign = if tm.tm_gmtoff > 0_i32 { '+' } else { '-' };
264+
let mut m = i32::abs(tm.tm_gmtoff) / 60_i32;
265+
let h = m / 60_i32;
266+
m -= h * 60_i32;
267+
#fmt("%c%02d%02d", sign, h as int, m as int)
268+
}
269+
//'+' {}
270+
'%' { "%" }
271+
}
272+
}
273+
274+
let mut buf = "";
275+
276+
io::with_str_reader(format) { |rdr|
277+
while !rdr.eof() {
278+
alt rdr.read_char() {
279+
'%' { buf += parse_type(rdr.read_char(), tm); }
280+
ch { str::push_char(buf, ch); }
281+
}
282+
}
283+
}
284+
285+
buf
286+
}
287+
119288
impl tm for tm {
120289
#[doc = "Convert time to the seconds from January 1, 1970"]
121290
fn to_timespec() -> timespec {
@@ -137,6 +306,58 @@ impl tm for tm {
137306
fn to_utc() -> tm {
138307
at_utc(self.to_timespec())
139308
}
309+
310+
#[doc = "
311+
Return a string of the current time in the form
312+
\"Thu Jan 1 00:00:00 1970\".
313+
"]
314+
fn ctime() -> str { self.strftime("%c") }
315+
316+
#[doc = "Formats the time according to the format string."]
317+
fn strftime(format: str) -> str { strftime(format, self) }
318+
319+
#[doc = "
320+
Returns a time string formatted according to RFC 822.
321+
322+
local: \"Thu, 22 Mar 2012 07:53:18 PST\"
323+
utc: \"Thu, 22 Mar 2012 14:53:18 UTC\"
324+
"]
325+
fn rfc822() -> str {
326+
if self.tm_gmtoff == 0_i32 {
327+
self.strftime("%a, %d %b %Y %T GMT")
328+
} else {
329+
self.strftime("%a, %d %b %Y %T %Z")
330+
}
331+
}
332+
333+
#[doc = "
334+
Returns a time string formatted according to RFC 822 with Zulu time.
335+
336+
local: \"Thu, 22 Mar 2012 07:53:18 -0700\"
337+
utc: \"Thu, 22 Mar 2012 14:53:18 -0000\"
338+
"]
339+
fn rfc822z() -> str {
340+
self.strftime("%a, %d %b %Y %T %z")
341+
}
342+
343+
#[doc = "
344+
Returns a time string formatted according to ISO 8601.
345+
346+
local: \"2012-02-22T07:53:18-07:00\"
347+
utc: \"2012-02-22T14:53:18Z\"
348+
"]
349+
fn rfc3339() -> str {
350+
if self.tm_gmtoff == 0_i32 {
351+
self.strftime("%Y-%m-%dT%H:%M:%SZ")
352+
} else {
353+
let s = self.strftime("%Y-%m-%dT%H:%M:%S");
354+
let sign = if self.tm_gmtoff > 0_i32 { '+' } else { '-' };
355+
let mut m = i32::abs(self.tm_gmtoff) / 60_i32;
356+
let h = m / 60_i32;
357+
m -= h * 60_i32;
358+
s + #fmt("%c%02d:%02d", sign, h as int, m as int)
359+
}
360+
}
140361
}
141362

142363
#[cfg(test)]
@@ -258,4 +479,89 @@ mod tests {
258479
assert utc.to_local() == local;
259480
assert utc.to_local().to_utc() == utc;
260481
}
482+
483+
#[test]
484+
fn test_ctime() {
485+
os::setenv("TZ", "America/Los_Angeles");
486+
487+
let time = { sec: 1234567890_i64, nsec: 54321_i32 };
488+
let utc = at_utc(time);
489+
let local = at(time);
490+
491+
assert utc.ctime() == "Fri Feb 13 23:31:30 2009";
492+
assert local.ctime() == "Fri Feb 13 15:31:30 2009";
493+
}
494+
495+
#[test]
496+
fn test_strftime() {
497+
os::setenv("TZ", "America/Los_Angeles");
498+
499+
let time = { sec: 1234567890_i64, nsec: 54321_i32 };
500+
let utc = at_utc(time);
501+
let local = at(time);
502+
503+
assert local.strftime("") == "";
504+
assert local.strftime("%A") == "Friday";
505+
assert local.strftime("%a") == "Fri";
506+
assert local.strftime("%B") == "February";
507+
assert local.strftime("%b") == "Feb";
508+
assert local.strftime("%C") == "20";
509+
assert local.strftime("%c") == "Fri Feb 13 15:31:30 2009";
510+
assert local.strftime("%D") == "02/13/09";
511+
assert local.strftime("%d") == "13";
512+
assert local.strftime("%e") == "13";
513+
assert local.strftime("%F") == "2009-02-13";
514+
// assert local.strftime("%G") == "2009";
515+
// assert local.strftime("%g") == "09";
516+
assert local.strftime("%H") == "15";
517+
assert local.strftime("%I") == "03";
518+
assert local.strftime("%j") == "044";
519+
assert local.strftime("%k") == "15";
520+
assert local.strftime("%l") == " 3";
521+
assert local.strftime("%M") == "31";
522+
assert local.strftime("%m") == "02";
523+
assert local.strftime("%n") == "\n";
524+
assert local.strftime("%P") == "pm";
525+
assert local.strftime("%p") == "PM";
526+
assert local.strftime("%R") == "15:31";
527+
assert local.strftime("%r") == "03:31:30 PM";
528+
assert local.strftime("%S") == "30";
529+
assert local.strftime("%s") == "1234567890";
530+
assert local.strftime("%T") == "15:31:30";
531+
assert local.strftime("%t") == "\t";
532+
// assert local.strftime("%U") == "06";
533+
assert local.strftime("%u") == "5";
534+
// assert local.strftime("%V") == "07";
535+
assert local.strftime("%v") == "13-Feb-2009";
536+
// assert local.strftime("%W") == "06";
537+
assert local.strftime("%w") == "5";
538+
// handle "%X"
539+
// handle "%x"
540+
assert local.strftime("%Y") == "2009";
541+
assert local.strftime("%y") == "09";
542+
543+
// FIXME: We should probably standardize on the timezone
544+
// abbreviation.
545+
let zone = local.strftime("%Z");
546+
assert zone == "PST" || zone == "Pacific Standard Time";
547+
548+
assert local.strftime("%z") == "-0800";
549+
assert local.strftime("%%") == "%";
550+
551+
// FIXME: We should probably standardize on the timezone
552+
// abbreviation.
553+
let rfc822 = local.rfc822();
554+
let prefix = "Fri, 13 Feb 2009 15:31:30 ";
555+
assert rfc822 == prefix + "PST" ||
556+
rfc822 == prefix + "Pacific Standard Time";
557+
558+
assert local.ctime() == "Fri Feb 13 15:31:30 2009";
559+
assert local.rfc822z() == "Fri, 13 Feb 2009 15:31:30 -0800";
560+
assert local.rfc3339() == "2009-02-13T15:31:30-08:00";
561+
562+
assert utc.ctime() == "Fri Feb 13 23:31:30 2009";
563+
assert utc.rfc822() == "Fri, 13 Feb 2009 23:31:30 GMT";
564+
assert utc.rfc822z() == "Fri, 13 Feb 2009 23:31:30 -0000";
565+
assert utc.rfc3339() == "2009-02-13T23:31:30Z";
566+
}
261567
}

0 commit comments

Comments
 (0)