Skip to content

Commit 4a48898

Browse files
committed
std: add localtime/gmtime support.
1 parent 7244463 commit 4a48898

File tree

3 files changed

+304
-0
lines changed

3 files changed

+304
-0
lines changed

Diff for: src/libstd/time.rs

+177
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,29 @@
1+
import libc::{c_char, c_int, c_long, size_t, time_t};
2+
import io::{reader, reader_util};
3+
import result::{result, ok, err, extensions};
4+
5+
export
6+
timespec,
7+
get_time,
8+
precise_time_ns,
9+
precise_time_s,
10+
tm,
11+
empty_tm,
12+
now,
13+
at,
14+
now_utc,
15+
at_utc;
16+
117
#[abi = "cdecl"]
218
native mod rustrt {
319
fn get_time(&sec: i64, &nsec: i32);
420
fn precise_time_ns(&ns: u64);
21+
22+
// FIXME: The i64 values can be passed by-val when #2064 is fixed.
23+
fn rust_gmtime(&&sec: i64, &&nsec: i32, &&result: tm);
24+
fn rust_localtime(&&sec: i64, &&nsec: i32, &result: tm);
25+
fn rust_timegm(&&tm: tm, &sec: i64);
26+
fn rust_mktime(&&tm: tm, &sec: i64);
527
}
628

729
#[doc = "A record specifying a time value in seconds and microseconds."]
@@ -36,6 +58,87 @@ fn precise_time_s() -> float {
3658
ret (precise_time_ns() as float) / 1000000000.;
3759
}
3860

61+
type tm = {
62+
tm_sec: i32, // seconds after the minute [0-60]
63+
tm_min: i32, // minutes after the hour [0-59]
64+
tm_hour: i32, // hours after midnight [0-23]
65+
tm_mday: i32, // days of the month [1-31]
66+
tm_mon: i32, // months since January [0-11]
67+
tm_year: i32, // years since 1900
68+
tm_wday: i32, // days since Sunday [0-6]
69+
tm_yday: i32, // days since January 1 [0-365]
70+
tm_isdst: i32, // Daylight Savings Time flag
71+
tm_gmtoff: i32, // offset from UTC in seconds
72+
tm_zone: str, // timezone abbreviation
73+
tm_nsec: i32, // nanoseconds
74+
};
75+
76+
fn empty_tm() -> tm {
77+
{
78+
tm_sec: 0_i32,
79+
tm_min: 0_i32,
80+
tm_hour: 0_i32,
81+
tm_mday: 0_i32,
82+
tm_mon: 0_i32,
83+
tm_year: 0_i32,
84+
tm_wday: 0_i32,
85+
tm_yday: 0_i32,
86+
tm_isdst: 0_i32,
87+
tm_gmtoff: 0_i32,
88+
tm_zone: "",
89+
tm_nsec: 0_i32,
90+
}
91+
}
92+
93+
#[doc = "Returns the specified time in UTC"]
94+
fn at_utc(clock: timespec) -> tm {
95+
let mut {sec, nsec} = clock;
96+
let mut tm = empty_tm();
97+
rustrt::rust_gmtime(sec, nsec, tm);
98+
tm
99+
}
100+
101+
#[doc = "Returns the current time in UTC"]
102+
fn now_utc() -> tm {
103+
at_utc(get_time())
104+
}
105+
106+
#[doc = "Returns the specified time in the local timezone"]
107+
fn at(clock: timespec) -> tm {
108+
let mut {sec, nsec} = clock;
109+
let mut tm = empty_tm();
110+
rustrt::rust_localtime(sec, nsec, tm);
111+
tm
112+
}
113+
114+
#[doc = "Returns the current time in the local timezone"]
115+
fn now() -> tm {
116+
at(get_time())
117+
}
118+
119+
impl tm for tm {
120+
#[doc = "Convert time to the seconds from January 1, 1970"]
121+
fn to_timespec() -> timespec {
122+
let mut sec = 0i64;
123+
if self.tm_gmtoff == 0_i32 {
124+
rustrt::rust_timegm(self, sec);
125+
} else {
126+
rustrt::rust_mktime(self, sec);
127+
}
128+
{ sec: sec, nsec: self.tm_nsec }
129+
}
130+
131+
#[doc = "Convert time to the local timezone"]
132+
fn to_local() -> tm {
133+
at(self.to_timespec())
134+
}
135+
136+
#[doc = "Convert time to the UTC"]
137+
fn to_utc() -> tm {
138+
at_utc(self.to_timespec())
139+
}
140+
}
141+
39142
#[cfg(test)]
40143
mod tests {
41144
import task;
@@ -81,4 +184,78 @@ mod tests {
81184
log(debug, "ns2=" + u64::str(ns2) + " ns");
82185
assert ns2 >= ns1;
83186
}
187+
188+
#[test]
189+
fn test_at_utc() {
190+
os::setenv("TZ", "America/Los_Angeles");
191+
192+
let time = { sec: 1234567890_i64, nsec: 54321_i32 };
193+
let utc = at_utc(time);
194+
195+
assert utc.tm_sec == 30_i32;
196+
assert utc.tm_min == 31_i32;
197+
assert utc.tm_hour == 23_i32;
198+
assert utc.tm_mday == 13_i32;
199+
assert utc.tm_mon == 1_i32;
200+
assert utc.tm_year == 109_i32;
201+
assert utc.tm_wday == 5_i32;
202+
assert utc.tm_yday == 43_i32;
203+
assert utc.tm_isdst == 0_i32;
204+
assert utc.tm_gmtoff == 0_i32;
205+
assert utc.tm_zone == "UTC";
206+
assert utc.tm_nsec == 54321_i32;
207+
}
208+
209+
#[test]
210+
fn test_at() {
211+
os::setenv("TZ", "America/Los_Angeles");
212+
213+
let time = { sec: 1234567890_i64, nsec: 54321_i32 };
214+
let local = at(time);
215+
216+
assert local.tm_sec == 30_i32;
217+
assert local.tm_min == 31_i32;
218+
assert local.tm_hour == 15_i32;
219+
assert local.tm_mday == 13_i32;
220+
assert local.tm_mon == 1_i32;
221+
assert local.tm_year == 109_i32;
222+
assert local.tm_wday == 5_i32;
223+
assert local.tm_yday == 43_i32;
224+
assert local.tm_isdst == 0_i32;
225+
assert local.tm_gmtoff == -28800_i32;
226+
227+
// FIXME: We should probably standardize on the timezone
228+
// abbreviation.
229+
let zone = local.tm_zone;
230+
assert zone == "PST" || zone == "Pacific Standard Time";
231+
232+
assert local.tm_nsec == 54321_i32;
233+
}
234+
235+
#[test]
236+
fn test_to_timespec() {
237+
os::setenv("TZ", "America/Los_Angeles");
238+
239+
let time = { sec: 1234567890_i64, nsec: 54321_i32 };
240+
let utc = at_utc(time);
241+
242+
assert utc.to_timespec() == time;
243+
assert utc.to_local().to_timespec() == time;
244+
}
245+
246+
#[test]
247+
fn test_conversions() {
248+
os::setenv("TZ", "America/Los_Angeles");
249+
250+
let time = { sec: 1234567890_i64, nsec: 54321_i32 };
251+
let utc = at_utc(time);
252+
let local = at(time);
253+
254+
assert local.to_local() == local;
255+
assert local.to_utc() == utc;
256+
assert local.to_utc().to_local() == local;
257+
assert utc.to_utc() == utc;
258+
assert utc.to_local() == local;
259+
assert utc.to_local().to_utc() == utc;
260+
}
84261
}

Diff for: src/rt/rust_builtin.cpp

+123
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
#include "rust_abi.h"
99
#include "rust_port.h"
1010

11+
#include <time.h>
12+
1113
#ifdef __APPLE__
1214
#include <crt_externs.h>
1315
#endif
@@ -448,6 +450,127 @@ precise_time_ns(uint64_t *ns) {
448450
*ns = t.time_ns();
449451
}
450452

453+
struct rust_tm {
454+
int32_t tm_sec;
455+
int32_t tm_min;
456+
int32_t tm_hour;
457+
int32_t tm_mday;
458+
int32_t tm_mon;
459+
int32_t tm_year;
460+
int32_t tm_wday;
461+
int32_t tm_yday;
462+
int32_t tm_isdst;
463+
int32_t tm_gmtoff;
464+
rust_str *tm_zone;
465+
int32_t tm_nsec;
466+
};
467+
468+
void rust_tm_to_tm(rust_tm* in_tm, tm* out_tm) {
469+
memset(out_tm, 0, sizeof(tm));
470+
out_tm->tm_sec = in_tm->tm_sec;
471+
out_tm->tm_min = in_tm->tm_min;
472+
out_tm->tm_hour = in_tm->tm_hour;
473+
out_tm->tm_mday = in_tm->tm_mday;
474+
out_tm->tm_mon = in_tm->tm_mon;
475+
out_tm->tm_year = in_tm->tm_year;
476+
out_tm->tm_wday = in_tm->tm_wday;
477+
out_tm->tm_yday = in_tm->tm_yday;
478+
out_tm->tm_isdst = in_tm->tm_isdst;
479+
}
480+
481+
void tm_to_rust_tm(tm* in_tm, rust_tm* out_tm, int32_t gmtoff,
482+
const char *zone, int32_t nsec) {
483+
out_tm->tm_sec = in_tm->tm_sec;
484+
out_tm->tm_min = in_tm->tm_min;
485+
out_tm->tm_hour = in_tm->tm_hour;
486+
out_tm->tm_mday = in_tm->tm_mday;
487+
out_tm->tm_mon = in_tm->tm_mon;
488+
out_tm->tm_year = in_tm->tm_year;
489+
out_tm->tm_wday = in_tm->tm_wday;
490+
out_tm->tm_yday = in_tm->tm_yday;
491+
out_tm->tm_isdst = in_tm->tm_isdst;
492+
out_tm->tm_gmtoff = gmtoff;
493+
out_tm->tm_nsec = nsec;
494+
495+
if (zone != NULL) {
496+
size_t size = strlen(zone);
497+
str_reserve_shared(&out_tm->tm_zone, size);
498+
memcpy(out_tm->tm_zone->data, zone, size);
499+
out_tm->tm_zone->fill = size + 1;
500+
out_tm->tm_zone->data[size] = '\0';
501+
}
502+
}
503+
504+
#if defined(__WIN32__)
505+
#define TZSET() _tzset()
506+
#if defined(_MSC_VER) && (_MSC_VER >= 1400)
507+
#define GMTIME(clock, result) gmtime_s((result), (clock))
508+
#define LOCALTIME(clock, result) localtime_s((result), (clock))
509+
#define TIMEGM(result) _mkgmtime64(result)
510+
#else
511+
struct tm* GMTIME(const time_t *clock, tm *result) {
512+
struct tm* t = gmtime(clock);
513+
if (t == NULL || result == NULL) { return NULL; }
514+
*result = *t;
515+
return result;
516+
}
517+
struct tm* LOCALTIME(const time_t *clock, tm *result) {
518+
struct tm* t = localtime(clock);
519+
if (t == NULL || result == NULL) { return NULL; }
520+
*result = *t;
521+
return result;
522+
}
523+
#define TIMEGM(result) mktime((result)) - _timezone
524+
#endif
525+
#else
526+
#define TZSET() tzset()
527+
#define GMTIME(clock, result) gmtime_r((clock), (result))
528+
#define LOCALTIME(clock, result) localtime_r((clock), (result))
529+
#define TIMEGM(result) timegm(result)
530+
#endif
531+
532+
extern "C" CDECL void
533+
rust_gmtime(int64_t *sec, int32_t *nsec, rust_tm *timeptr) {
534+
tm tm;
535+
time_t s = *sec;
536+
GMTIME(&s, &tm);
537+
538+
tm_to_rust_tm(&tm, timeptr, 0, "UTC", *nsec);
539+
}
540+
541+
extern "C" CDECL void
542+
rust_localtime(int64_t *sec, int32_t *nsec, rust_tm *timeptr) {
543+
tm tm;
544+
TZSET();
545+
time_t s = *sec;
546+
LOCALTIME(&s, &tm);
547+
548+
#if defined(__WIN32__)
549+
int32_t gmtoff = -timezone;
550+
char zone[64];
551+
strftime(zone, sizeof(zone), "%Z", &tm);
552+
#else
553+
int32_t gmtoff = tm.tm_gmtoff;
554+
const char *zone = tm.tm_zone;
555+
#endif
556+
557+
tm_to_rust_tm(&tm, timeptr, gmtoff, zone, *nsec);
558+
}
559+
560+
extern "C" CDECL void
561+
rust_timegm(rust_tm* timeptr, int64_t *out) {
562+
tm t;
563+
rust_tm_to_tm(timeptr, &t);
564+
*out = TIMEGM(&t);
565+
}
566+
567+
extern "C" CDECL void
568+
rust_mktime(rust_tm* timeptr, int64_t *out) {
569+
tm t;
570+
rust_tm_to_tm(timeptr, &t);
571+
*out = mktime(&t);
572+
}
573+
451574
extern "C" CDECL rust_sched_id
452575
rust_get_sched_id() {
453576
rust_task *task = rust_get_current_task();

Diff for: src/rt/rustrt.def.in

+4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ debug_abi_2
1212
get_port_id
1313
get_task_id
1414
get_time
15+
rust_gmtime
16+
rust_localtime
17+
rust_timegm
18+
rust_mktime
1519
last_os_error
1620
new_port
1721
new_task

0 commit comments

Comments
 (0)