1use oldtime;
7
8use {Datelike, Timelike};
9use naive::{NaiveDate, NaiveTime, NaiveDateTime};
10use {Date, DateTime};
11use super::{TimeZone, LocalResult};
12use super::fixed::FixedOffset;
13
14fn tm_to_datetime(mut tm: oldtime::Tm) -> DateTime<Local> {
17 if tm.tm_sec >= 60 {
18 tm.tm_nsec += (tm.tm_sec - 59) * 1_000_000_000;
19 tm.tm_sec = 59;
20 }
21
22 #[cfg(not(windows))]
23 fn tm_to_naive_date(tm: &oldtime::Tm) -> NaiveDate {
24 NaiveDate::from_yo(tm.tm_year + 1900, tm.tm_yday as u32 + 1)
26 }
27
28 #[cfg(windows)]
29 fn tm_to_naive_date(tm: &oldtime::Tm) -> NaiveDate {
30 NaiveDate::from_ymd(tm.tm_year + 1900, tm.tm_mon as u32 + 1, tm.tm_mday as u32)
32 }
33
34 let date = tm_to_naive_date(&tm);
35 let time = NaiveTime::from_hms_nano(tm.tm_hour as u32, tm.tm_min as u32,
36 tm.tm_sec as u32, tm.tm_nsec as u32);
37 let offset = FixedOffset::east(tm.tm_utcoff);
38 DateTime::from_utc(date.and_time(time) - offset, offset)
39}
40
41fn datetime_to_timespec(d: &NaiveDateTime, local: bool) -> oldtime::Timespec {
43 let tm_utcoff = if local {1} else {0};
47
48 let tm = oldtime::Tm {
49 tm_sec: d.second() as i32,
50 tm_min: d.minute() as i32,
51 tm_hour: d.hour() as i32,
52 tm_mday: d.day() as i32,
53 tm_mon: d.month0() as i32, tm_year: d.year() - 1900, tm_wday: 0, tm_yday: 0, tm_isdst: -1,
58 tm_utcoff: tm_utcoff,
59 tm_nsec: 0,
61 };
62
63 tm.to_timespec()
64}
65
66#[derive(Copy, Clone, Debug)]
81pub struct Local;
82
83impl Local {
84 pub fn today() -> Date<Local> {
86 Local::now().date()
87 }
88
89 #[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))]
91 pub fn now() -> DateTime<Local> {
92 tm_to_datetime(oldtime::now())
93 }
94
95 #[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))]
97 pub fn now() -> DateTime<Local> {
98 use super::Utc;
99 let now: DateTime<Utc> = super::Utc::now();
100
101 let offset = FixedOffset::west((js_sys::Date::new_0().get_timezone_offset() as i32) * 60);
103 DateTime::from_utc(now.naive_utc(), offset)
104 }
105}
106
107impl TimeZone for Local {
108 type Offset = FixedOffset;
109
110 fn from_offset(_offset: &FixedOffset) -> Local { Local }
111
112 fn offset_from_local_date(&self, local: &NaiveDate) -> LocalResult<FixedOffset> {
114 self.from_local_date(local).map(|date| *date.offset())
115 }
116
117 fn offset_from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<FixedOffset> {
118 self.from_local_datetime(local).map(|datetime| *datetime.offset())
119 }
120
121 fn offset_from_utc_date(&self, utc: &NaiveDate) -> FixedOffset {
122 *self.from_utc_date(utc).offset()
123 }
124
125 fn offset_from_utc_datetime(&self, utc: &NaiveDateTime) -> FixedOffset {
126 *self.from_utc_datetime(utc).offset()
127 }
128
129 fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<Local>> {
131 let midnight = self.from_local_datetime(&local.and_hms(0, 0, 0));
135 midnight.map(|datetime| Date::from_utc(*local, *datetime.offset()))
136 }
137
138 fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<Local>> {
139 let timespec = datetime_to_timespec(local, true);
140
141 let mut tm = oldtime::at(timespec);
143 assert_eq!(tm.tm_nsec, 0);
144 tm.tm_nsec = local.nanosecond() as i32;
145
146 LocalResult::Single(tm_to_datetime(tm))
147 }
148
149 fn from_utc_date(&self, utc: &NaiveDate) -> Date<Local> {
150 let midnight = self.from_utc_datetime(&utc.and_hms(0, 0, 0));
151 Date::from_utc(*utc, *midnight.offset())
152 }
153
154 fn from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime<Local> {
155 let timespec = datetime_to_timespec(utc, false);
156
157 let mut tm = oldtime::at(timespec);
159 assert_eq!(tm.tm_nsec, 0);
160 tm.tm_nsec = utc.nanosecond() as i32;
161
162 tm_to_datetime(tm)
163 }
164}
165
166#[cfg(test)]
167mod tests {
168 use Datelike;
169 use offset::TimeZone;
170 use super::Local;
171
172 #[test]
173 fn test_local_date_sanity_check() { assert_eq!(Local.ymd(2999, 12, 28).day(), 28);
175 }
176
177 #[test]
178 fn test_leap_second() { let today = Local::today();
180
181 let dt = today.and_hms_milli(1, 2, 59, 1000);
182 let timestr = dt.time().to_string();
183 assert!(timestr == "01:02:60" || timestr == "01:03:00",
186 "unexpected timestr {:?}", timestr);
187
188 let dt = today.and_hms_milli(1, 2, 3, 1234);
189 let timestr = dt.time().to_string();
190 assert!(timestr == "01:02:03.234" || timestr == "01:02:04.234",
191 "unexpected timestr {:?}", timestr);
192 }
193}