]>
Commit | Line | Data |
---|---|---|
ff7c6d11 XL |
1 | #![allow(bad_style)] |
2 | ||
3 | pub use self::inner::*; | |
4 | ||
8faf50e0 XL |
5 | #[cfg(any( |
6 | all(target_arch = "wasm32", not(target_os = "emscripten")), | |
60c5eb7d | 7 | target_env = "sgx" |
8faf50e0 XL |
8 | ))] |
9 | mod common { | |
ff7c6d11 XL |
10 | use Tm; |
11 | ||
8faf50e0 | 12 | pub fn time_to_tm(ts: i64, tm: &mut Tm) { |
ff7c6d11 XL |
13 | let leapyear = |year| -> bool { |
14 | year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) | |
15 | }; | |
16 | ||
17 | static _ytab: [[i64; 12]; 2] = [ | |
18 | [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ], | |
19 | [ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ] | |
20 | ]; | |
21 | ||
22 | let mut year = 1970; | |
23 | ||
24 | let dayclock = ts % 86400; | |
25 | let mut dayno = ts / 86400; | |
26 | ||
27 | tm.tm_sec = (dayclock % 60) as i32; | |
28 | tm.tm_min = ((dayclock % 3600) / 60) as i32; | |
29 | tm.tm_hour = (dayclock / 3600) as i32; | |
30 | tm.tm_wday = ((dayno + 4) % 7) as i32; | |
31 | loop { | |
32 | let yearsize = if leapyear(year) { | |
33 | 366 | |
34 | } else { | |
35 | 365 | |
36 | }; | |
37 | if dayno >= yearsize { | |
38 | dayno -= yearsize; | |
39 | year += 1; | |
40 | } else { | |
41 | break; | |
42 | } | |
43 | } | |
44 | tm.tm_year = (year - 1900) as i32; | |
45 | tm.tm_yday = dayno as i32; | |
46 | let mut mon = 0; | |
47 | while dayno >= _ytab[if leapyear(year) { 1 } else { 0 }][mon] { | |
48 | dayno -= _ytab[if leapyear(year) { 1 } else { 0 }][mon]; | |
49 | mon += 1; | |
50 | } | |
51 | tm.tm_mon = mon as i32; | |
52 | tm.tm_mday = dayno as i32 + 1; | |
53 | tm.tm_isdst = 0; | |
54 | } | |
55 | ||
8faf50e0 | 56 | pub fn tm_to_time(tm: &Tm) -> i64 { |
ff7c6d11 XL |
57 | let mut y = tm.tm_year as i64 + 1900; |
58 | let mut m = tm.tm_mon as i64 + 1; | |
59 | if m <= 2 { | |
60 | y -= 1; | |
61 | m += 12; | |
62 | } | |
63 | let d = tm.tm_mday as i64; | |
64 | let h = tm.tm_hour as i64; | |
65 | let mi = tm.tm_min as i64; | |
66 | let s = tm.tm_sec as i64; | |
67 | (365*y + y/4 - y/100 + y/400 + 3*(m+1)/5 + 30*m + d - 719561) | |
68 | * 86400 + 3600 * h + 60 * mi + s | |
69 | } | |
8faf50e0 XL |
70 | } |
71 | ||
6a06907d | 72 | #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] |
8faf50e0 XL |
73 | mod inner { |
74 | use std::ops::{Add, Sub}; | |
75 | use Tm; | |
76 | use Duration; | |
77 | use super::common::{time_to_tm, tm_to_time}; | |
78 | ||
79 | #[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] | |
80 | pub struct SteadyTime; | |
81 | ||
82 | pub fn time_to_utc_tm(sec: i64, tm: &mut Tm) { | |
83 | time_to_tm(sec, tm); | |
84 | } | |
85 | ||
86 | pub fn time_to_local_tm(sec: i64, tm: &mut Tm) { | |
87 | // FIXME: Add timezone logic | |
88 | time_to_tm(sec, tm); | |
89 | } | |
90 | ||
91 | pub fn utc_tm_to_time(tm: &Tm) -> i64 { | |
92 | tm_to_time(tm) | |
93 | } | |
94 | ||
95 | pub fn local_tm_to_time(tm: &Tm) -> i64 { | |
96 | // FIXME: Add timezone logic | |
97 | tm_to_time(tm) | |
98 | } | |
99 | ||
100 | pub fn get_time() -> (i64, i32) { | |
101 | unimplemented!() | |
102 | } | |
103 | ||
104 | pub fn get_precise_ns() -> u64 { | |
105 | unimplemented!() | |
106 | } | |
107 | ||
108 | impl SteadyTime { | |
109 | pub fn now() -> SteadyTime { | |
110 | unimplemented!() | |
111 | } | |
112 | } | |
113 | ||
114 | impl Sub for SteadyTime { | |
115 | type Output = Duration; | |
116 | fn sub(self, _other: SteadyTime) -> Duration { | |
117 | unimplemented!() | |
118 | } | |
119 | } | |
120 | ||
121 | impl Sub<Duration> for SteadyTime { | |
122 | type Output = SteadyTime; | |
123 | fn sub(self, _other: Duration) -> SteadyTime { | |
6a06907d | 124 | unimplemented!() |
8faf50e0 XL |
125 | } |
126 | } | |
127 | ||
128 | impl Add<Duration> for SteadyTime { | |
129 | type Output = SteadyTime; | |
130 | fn add(self, _other: Duration) -> SteadyTime { | |
131 | unimplemented!() | |
132 | } | |
133 | } | |
134 | } | |
135 | ||
60c5eb7d XL |
136 | #[cfg(target_env = "sgx")] |
137 | mod inner { | |
138 | use std::ops::{Add, Sub}; | |
139 | use Tm; | |
140 | use Duration; | |
141 | use super::common::{time_to_tm, tm_to_time}; | |
142 | use std::time::SystemTime; | |
143 | ||
144 | /// The number of nanoseconds in seconds. | |
145 | const NANOS_PER_SEC: u64 = 1_000_000_000; | |
146 | ||
147 | #[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] | |
148 | pub struct SteadyTime { | |
149 | t: Duration | |
150 | } | |
151 | ||
152 | pub fn time_to_utc_tm(sec: i64, tm: &mut Tm) { | |
153 | time_to_tm(sec, tm); | |
154 | } | |
155 | ||
156 | pub fn time_to_local_tm(sec: i64, tm: &mut Tm) { | |
157 | // FIXME: Add timezone logic | |
158 | time_to_tm(sec, tm); | |
159 | } | |
160 | ||
161 | pub fn utc_tm_to_time(tm: &Tm) -> i64 { | |
162 | tm_to_time(tm) | |
163 | } | |
164 | ||
165 | pub fn local_tm_to_time(tm: &Tm) -> i64 { | |
166 | // FIXME: Add timezone logic | |
167 | tm_to_time(tm) | |
168 | } | |
169 | ||
170 | pub fn get_time() -> (i64, i32) { | |
171 | SteadyTime::now().t.raw() | |
172 | } | |
173 | ||
174 | pub fn get_precise_ns() -> u64 { | |
f035d41b XL |
175 | // This unwrap is safe because current time is well ahead of UNIX_EPOCH, unless system |
176 | // clock is adjusted backward. | |
60c5eb7d XL |
177 | let std_duration = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); |
178 | std_duration.as_secs() * NANOS_PER_SEC + std_duration.subsec_nanos() as u64 | |
179 | } | |
180 | ||
181 | impl SteadyTime { | |
182 | pub fn now() -> SteadyTime { | |
f035d41b XL |
183 | // This unwrap is safe because current time is well ahead of UNIX_EPOCH, unless system |
184 | // clock is adjusted backward. | |
60c5eb7d XL |
185 | let std_duration = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); |
186 | // This unwrap is safe because duration is well within the limits of i64. | |
187 | let duration = Duration::from_std(std_duration).unwrap(); | |
188 | SteadyTime { t: duration } | |
189 | } | |
190 | } | |
191 | ||
192 | impl Sub for SteadyTime { | |
193 | type Output = Duration; | |
194 | fn sub(self, other: SteadyTime) -> Duration { | |
195 | self.t - other.t | |
196 | } | |
197 | } | |
198 | ||
199 | impl Sub<Duration> for SteadyTime { | |
200 | type Output = SteadyTime; | |
201 | fn sub(self, other: Duration) -> SteadyTime { | |
202 | SteadyTime { t: self.t - other } | |
203 | } | |
204 | } | |
205 | ||
206 | impl Add<Duration> for SteadyTime { | |
207 | type Output = SteadyTime; | |
208 | fn add(self, other: Duration) -> SteadyTime { | |
209 | SteadyTime { t: self.t + other } | |
210 | } | |
211 | } | |
212 | } | |
213 | ||
ff7c6d11 XL |
214 | #[cfg(unix)] |
215 | mod inner { | |
216 | use libc::{self, time_t}; | |
217 | use std::mem; | |
218 | use std::io; | |
219 | use Tm; | |
220 | ||
221 | #[cfg(any(target_os = "macos", target_os = "ios"))] | |
222 | pub use self::mac::*; | |
223 | #[cfg(all(not(target_os = "macos"), not(target_os = "ios")))] | |
224 | pub use self::unix::*; | |
225 | ||
f035d41b | 226 | #[cfg(any(target_os = "solaris", target_os = "illumos"))] |
ff7c6d11 XL |
227 | extern { |
228 | static timezone: time_t; | |
229 | static altzone: time_t; | |
230 | } | |
231 | ||
232 | fn rust_tm_to_tm(rust_tm: &Tm, tm: &mut libc::tm) { | |
233 | tm.tm_sec = rust_tm.tm_sec; | |
234 | tm.tm_min = rust_tm.tm_min; | |
235 | tm.tm_hour = rust_tm.tm_hour; | |
236 | tm.tm_mday = rust_tm.tm_mday; | |
237 | tm.tm_mon = rust_tm.tm_mon; | |
238 | tm.tm_year = rust_tm.tm_year; | |
239 | tm.tm_wday = rust_tm.tm_wday; | |
240 | tm.tm_yday = rust_tm.tm_yday; | |
241 | tm.tm_isdst = rust_tm.tm_isdst; | |
242 | } | |
243 | ||
244 | fn tm_to_rust_tm(tm: &libc::tm, utcoff: i32, rust_tm: &mut Tm) { | |
245 | rust_tm.tm_sec = tm.tm_sec; | |
246 | rust_tm.tm_min = tm.tm_min; | |
247 | rust_tm.tm_hour = tm.tm_hour; | |
248 | rust_tm.tm_mday = tm.tm_mday; | |
249 | rust_tm.tm_mon = tm.tm_mon; | |
250 | rust_tm.tm_year = tm.tm_year; | |
251 | rust_tm.tm_wday = tm.tm_wday; | |
252 | rust_tm.tm_yday = tm.tm_yday; | |
253 | rust_tm.tm_isdst = tm.tm_isdst; | |
254 | rust_tm.tm_utcoff = utcoff; | |
255 | } | |
256 | ||
f035d41b | 257 | #[cfg(any(target_os = "nacl", target_os = "solaris", target_os = "illumos"))] |
ff7c6d11 XL |
258 | unsafe fn timegm(tm: *mut libc::tm) -> time_t { |
259 | use std::env::{set_var, var_os, remove_var}; | |
260 | extern { | |
261 | fn tzset(); | |
262 | } | |
263 | ||
264 | let ret; | |
265 | ||
266 | let current_tz = var_os("TZ"); | |
267 | set_var("TZ", "UTC"); | |
268 | tzset(); | |
269 | ||
270 | ret = libc::mktime(tm); | |
271 | ||
272 | if let Some(tz) = current_tz { | |
273 | set_var("TZ", tz); | |
274 | } else { | |
275 | remove_var("TZ"); | |
276 | } | |
277 | tzset(); | |
278 | ||
279 | ret | |
280 | } | |
281 | ||
282 | pub fn time_to_utc_tm(sec: i64, tm: &mut Tm) { | |
283 | unsafe { | |
284 | let sec = sec as time_t; | |
285 | let mut out = mem::zeroed(); | |
286 | if libc::gmtime_r(&sec, &mut out).is_null() { | |
287 | panic!("gmtime_r failed: {}", io::Error::last_os_error()); | |
288 | } | |
289 | tm_to_rust_tm(&out, 0, tm); | |
290 | } | |
291 | } | |
292 | ||
293 | pub fn time_to_local_tm(sec: i64, tm: &mut Tm) { | |
294 | unsafe { | |
295 | let sec = sec as time_t; | |
296 | let mut out = mem::zeroed(); | |
297 | if libc::localtime_r(&sec, &mut out).is_null() { | |
298 | panic!("localtime_r failed: {}", io::Error::last_os_error()); | |
299 | } | |
f035d41b | 300 | #[cfg(any(target_os = "solaris", target_os = "illumos"))] |
ff7c6d11 XL |
301 | let gmtoff = { |
302 | ::tzset(); | |
303 | // < 0 means we don't know; assume we're not in DST. | |
304 | if out.tm_isdst == 0 { | |
305 | // timezone is seconds west of UTC, tm_gmtoff is seconds east | |
306 | -timezone | |
307 | } else if out.tm_isdst > 0 { | |
308 | -altzone | |
309 | } else { | |
310 | -timezone | |
311 | } | |
312 | }; | |
f035d41b | 313 | #[cfg(not(any(target_os = "solaris", target_os = "illumos")))] |
ff7c6d11 XL |
314 | let gmtoff = out.tm_gmtoff; |
315 | tm_to_rust_tm(&out, gmtoff as i32, tm); | |
316 | } | |
317 | } | |
318 | ||
319 | pub fn utc_tm_to_time(rust_tm: &Tm) -> i64 { | |
320 | #[cfg(all(target_os = "android", target_pointer_width = "32"))] | |
321 | use libc::timegm64 as timegm; | |
f035d41b XL |
322 | #[cfg(not(any( |
323 | all(target_os = "android", target_pointer_width = "32"), | |
324 | target_os = "nacl", | |
325 | target_os = "solaris", | |
326 | target_os = "illumos" | |
327 | )))] | |
ff7c6d11 XL |
328 | use libc::timegm; |
329 | ||
330 | let mut tm = unsafe { mem::zeroed() }; | |
331 | rust_tm_to_tm(rust_tm, &mut tm); | |
332 | unsafe { timegm(&mut tm) as i64 } | |
333 | } | |
334 | ||
335 | pub fn local_tm_to_time(rust_tm: &Tm) -> i64 { | |
336 | let mut tm = unsafe { mem::zeroed() }; | |
337 | rust_tm_to_tm(rust_tm, &mut tm); | |
338 | unsafe { libc::mktime(&mut tm) as i64 } | |
339 | } | |
340 | ||
341 | #[cfg(any(target_os = "macos", target_os = "ios"))] | |
342 | mod mac { | |
f035d41b | 343 | #[allow(deprecated)] |
ff7c6d11 | 344 | use libc::{self, timeval, mach_timebase_info}; |
f035d41b | 345 | #[allow(deprecated)] |
ff7c6d11 XL |
346 | use std::sync::{Once, ONCE_INIT}; |
347 | use std::ops::{Add, Sub}; | |
348 | use Duration; | |
349 | ||
f035d41b | 350 | #[allow(deprecated)] |
ff7c6d11 XL |
351 | fn info() -> &'static mach_timebase_info { |
352 | static mut INFO: mach_timebase_info = mach_timebase_info { | |
353 | numer: 0, | |
354 | denom: 0, | |
355 | }; | |
356 | static ONCE: Once = ONCE_INIT; | |
357 | ||
358 | unsafe { | |
359 | ONCE.call_once(|| { | |
360 | mach_timebase_info(&mut INFO); | |
361 | }); | |
362 | &INFO | |
363 | } | |
364 | } | |
365 | ||
366 | pub fn get_time() -> (i64, i32) { | |
367 | use std::ptr; | |
368 | let mut tv = timeval { tv_sec: 0, tv_usec: 0 }; | |
369 | unsafe { libc::gettimeofday(&mut tv, ptr::null_mut()); } | |
370 | (tv.tv_sec as i64, tv.tv_usec * 1000) | |
371 | } | |
372 | ||
f035d41b | 373 | #[allow(deprecated)] |
8faf50e0 | 374 | #[inline] |
ff7c6d11 XL |
375 | pub fn get_precise_ns() -> u64 { |
376 | unsafe { | |
377 | let time = libc::mach_absolute_time(); | |
378 | let info = info(); | |
379 | time * info.numer as u64 / info.denom as u64 | |
380 | } | |
381 | } | |
382 | ||
383 | #[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Debug)] | |
384 | pub struct SteadyTime { t: u64 } | |
385 | ||
386 | impl SteadyTime { | |
387 | pub fn now() -> SteadyTime { | |
388 | SteadyTime { t: get_precise_ns() } | |
389 | } | |
390 | } | |
391 | impl Sub for SteadyTime { | |
392 | type Output = Duration; | |
393 | fn sub(self, other: SteadyTime) -> Duration { | |
394 | Duration::nanoseconds(self.t as i64 - other.t as i64) | |
395 | } | |
396 | } | |
397 | impl Sub<Duration> for SteadyTime { | |
398 | type Output = SteadyTime; | |
399 | fn sub(self, other: Duration) -> SteadyTime { | |
400 | self + -other | |
401 | } | |
402 | } | |
403 | impl Add<Duration> for SteadyTime { | |
404 | type Output = SteadyTime; | |
405 | fn add(self, other: Duration) -> SteadyTime { | |
406 | let delta = other.num_nanoseconds().unwrap(); | |
407 | SteadyTime { | |
408 | t: (self.t as i64 + delta) as u64 | |
409 | } | |
410 | } | |
411 | } | |
412 | } | |
413 | ||
414 | #[cfg(test)] | |
415 | pub struct TzReset; | |
416 | ||
417 | #[cfg(test)] | |
418 | pub fn set_los_angeles_time_zone() -> TzReset { | |
419 | use std::env; | |
420 | env::set_var("TZ", "America/Los_Angeles"); | |
421 | ::tzset(); | |
422 | TzReset | |
423 | } | |
424 | ||
425 | #[cfg(test)] | |
426 | pub fn set_london_with_dst_time_zone() -> TzReset { | |
427 | use std::env; | |
428 | env::set_var("TZ", "Europe/London"); | |
429 | ::tzset(); | |
430 | TzReset | |
431 | } | |
432 | ||
433 | #[cfg(all(not(target_os = "macos"), not(target_os = "ios")))] | |
434 | mod unix { | |
435 | use std::fmt; | |
436 | use std::cmp::Ordering; | |
437 | use std::ops::{Add, Sub}; | |
438 | use libc; | |
439 | ||
440 | use Duration; | |
441 | ||
442 | pub fn get_time() -> (i64, i32) { | |
443 | let mut tv = libc::timespec { tv_sec: 0, tv_nsec: 0 }; | |
444 | unsafe { libc::clock_gettime(libc::CLOCK_REALTIME, &mut tv); } | |
445 | (tv.tv_sec as i64, tv.tv_nsec as i32) | |
446 | } | |
447 | ||
448 | pub fn get_precise_ns() -> u64 { | |
449 | let mut ts = libc::timespec { tv_sec: 0, tv_nsec: 0 }; | |
450 | unsafe { | |
451 | libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut ts); | |
452 | } | |
453 | (ts.tv_sec as u64) * 1000000000 + (ts.tv_nsec as u64) | |
454 | } | |
455 | ||
456 | #[derive(Copy)] | |
457 | pub struct SteadyTime { | |
458 | t: libc::timespec, | |
459 | } | |
460 | ||
461 | impl fmt::Debug for SteadyTime { | |
462 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | |
463 | write!(fmt, "SteadyTime {{ tv_sec: {:?}, tv_nsec: {:?} }}", | |
464 | self.t.tv_sec, self.t.tv_nsec) | |
465 | } | |
466 | } | |
467 | ||
468 | impl Clone for SteadyTime { | |
469 | fn clone(&self) -> SteadyTime { | |
470 | SteadyTime { t: self.t } | |
471 | } | |
472 | } | |
473 | ||
474 | impl SteadyTime { | |
475 | pub fn now() -> SteadyTime { | |
476 | let mut t = SteadyTime { | |
477 | t: libc::timespec { | |
478 | tv_sec: 0, | |
479 | tv_nsec: 0, | |
480 | } | |
481 | }; | |
482 | unsafe { | |
483 | assert_eq!(0, libc::clock_gettime(libc::CLOCK_MONOTONIC, | |
484 | &mut t.t)); | |
485 | } | |
486 | t | |
487 | } | |
488 | } | |
489 | ||
490 | impl Sub for SteadyTime { | |
491 | type Output = Duration; | |
492 | fn sub(self, other: SteadyTime) -> Duration { | |
493 | if self.t.tv_nsec >= other.t.tv_nsec { | |
494 | Duration::seconds(self.t.tv_sec as i64 - other.t.tv_sec as i64) + | |
495 | Duration::nanoseconds(self.t.tv_nsec as i64 - other.t.tv_nsec as i64) | |
496 | } else { | |
497 | Duration::seconds(self.t.tv_sec as i64 - 1 - other.t.tv_sec as i64) + | |
498 | Duration::nanoseconds(self.t.tv_nsec as i64 + ::NSEC_PER_SEC as i64 - | |
499 | other.t.tv_nsec as i64) | |
500 | } | |
501 | } | |
502 | } | |
503 | ||
504 | impl Sub<Duration> for SteadyTime { | |
505 | type Output = SteadyTime; | |
506 | fn sub(self, other: Duration) -> SteadyTime { | |
507 | self + -other | |
508 | } | |
509 | } | |
510 | ||
511 | impl Add<Duration> for SteadyTime { | |
512 | type Output = SteadyTime; | |
513 | fn add(mut self, other: Duration) -> SteadyTime { | |
514 | let seconds = other.num_seconds(); | |
515 | let nanoseconds = other - Duration::seconds(seconds); | |
516 | let nanoseconds = nanoseconds.num_nanoseconds().unwrap(); | |
8faf50e0 XL |
517 | |
518 | #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))] | |
519 | type nsec = i64; | |
520 | #[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))] | |
521 | type nsec = libc::c_long; | |
522 | ||
ff7c6d11 | 523 | self.t.tv_sec += seconds as libc::time_t; |
8faf50e0 XL |
524 | self.t.tv_nsec += nanoseconds as nsec; |
525 | if self.t.tv_nsec >= ::NSEC_PER_SEC as nsec { | |
526 | self.t.tv_nsec -= ::NSEC_PER_SEC as nsec; | |
ff7c6d11 XL |
527 | self.t.tv_sec += 1; |
528 | } else if self.t.tv_nsec < 0 { | |
529 | self.t.tv_sec -= 1; | |
8faf50e0 | 530 | self.t.tv_nsec += ::NSEC_PER_SEC as nsec; |
ff7c6d11 XL |
531 | } |
532 | self | |
533 | } | |
534 | } | |
535 | ||
536 | impl PartialOrd for SteadyTime { | |
537 | fn partial_cmp(&self, other: &SteadyTime) -> Option<Ordering> { | |
538 | Some(self.cmp(other)) | |
539 | } | |
540 | } | |
541 | ||
542 | impl Ord for SteadyTime { | |
543 | fn cmp(&self, other: &SteadyTime) -> Ordering { | |
544 | match self.t.tv_sec.cmp(&other.t.tv_sec) { | |
545 | Ordering::Equal => self.t.tv_nsec.cmp(&other.t.tv_nsec), | |
546 | ord => ord | |
547 | } | |
548 | } | |
549 | } | |
550 | ||
551 | impl PartialEq for SteadyTime { | |
552 | fn eq(&self, other: &SteadyTime) -> bool { | |
553 | self.t.tv_sec == other.t.tv_sec && | |
554 | self.t.tv_nsec == other.t.tv_nsec | |
555 | } | |
556 | } | |
557 | ||
558 | impl Eq for SteadyTime {} | |
559 | ||
560 | } | |
561 | } | |
562 | ||
563 | #[cfg(windows)] | |
564 | #[allow(non_snake_case)] | |
565 | mod inner { | |
566 | use std::io; | |
567 | use std::mem; | |
f035d41b | 568 | #[allow(deprecated)] |
ff7c6d11 XL |
569 | use std::sync::{Once, ONCE_INIT}; |
570 | use std::ops::{Add, Sub}; | |
571 | use {Tm, Duration}; | |
572 | ||
573 | use winapi::um::winnt::*; | |
574 | use winapi::shared::minwindef::*; | |
575 | use winapi::um::minwinbase::SYSTEMTIME; | |
576 | use winapi::um::profileapi::*; | |
577 | use winapi::um::timezoneapi::*; | |
578 | use winapi::um::sysinfoapi::GetSystemTimeAsFileTime; | |
579 | ||
580 | fn frequency() -> i64 { | |
581 | static mut FREQUENCY: i64 = 0; | |
f035d41b | 582 | #[allow(deprecated)] |
ff7c6d11 XL |
583 | static ONCE: Once = ONCE_INIT; |
584 | ||
585 | unsafe { | |
586 | ONCE.call_once(|| { | |
587 | let mut l = i64_to_large_integer(0); | |
588 | QueryPerformanceFrequency(&mut l); | |
589 | FREQUENCY = large_integer_to_i64(l); | |
590 | }); | |
591 | FREQUENCY | |
592 | } | |
593 | } | |
594 | ||
595 | fn i64_to_large_integer(i: i64) -> LARGE_INTEGER { | |
596 | unsafe { | |
597 | let mut large_integer: LARGE_INTEGER = mem::zeroed(); | |
598 | *large_integer.QuadPart_mut() = i; | |
599 | large_integer | |
600 | } | |
601 | } | |
602 | ||
603 | fn large_integer_to_i64(l: LARGE_INTEGER) -> i64 { | |
604 | unsafe { | |
605 | *l.QuadPart() | |
606 | } | |
607 | } | |
608 | ||
609 | const HECTONANOSECS_IN_SEC: i64 = 10_000_000; | |
610 | const HECTONANOSEC_TO_UNIX_EPOCH: i64 = 11_644_473_600 * HECTONANOSECS_IN_SEC; | |
611 | ||
612 | fn time_to_file_time(sec: i64) -> FILETIME { | |
613 | let t = (((sec * HECTONANOSECS_IN_SEC) + HECTONANOSEC_TO_UNIX_EPOCH)) as u64; | |
614 | FILETIME { | |
615 | dwLowDateTime: t as DWORD, | |
616 | dwHighDateTime: (t >> 32) as DWORD | |
617 | } | |
618 | } | |
619 | ||
620 | fn file_time_as_u64(ft: &FILETIME) -> u64 { | |
621 | ((ft.dwHighDateTime as u64) << 32) | (ft.dwLowDateTime as u64) | |
622 | } | |
623 | ||
624 | fn file_time_to_nsec(ft: &FILETIME) -> i32 { | |
625 | let t = file_time_as_u64(ft) as i64; | |
626 | ((t % HECTONANOSECS_IN_SEC) * 100) as i32 | |
627 | } | |
628 | ||
629 | fn file_time_to_unix_seconds(ft: &FILETIME) -> i64 { | |
630 | let t = file_time_as_u64(ft) as i64; | |
631 | ((t - HECTONANOSEC_TO_UNIX_EPOCH) / HECTONANOSECS_IN_SEC) as i64 | |
632 | } | |
633 | ||
634 | fn system_time_to_file_time(sys: &SYSTEMTIME) -> FILETIME { | |
635 | unsafe { | |
636 | let mut ft = mem::zeroed(); | |
637 | SystemTimeToFileTime(sys, &mut ft); | |
638 | ft | |
639 | } | |
640 | } | |
641 | ||
642 | fn tm_to_system_time(tm: &Tm) -> SYSTEMTIME { | |
643 | let mut sys: SYSTEMTIME = unsafe { mem::zeroed() }; | |
644 | sys.wSecond = tm.tm_sec as WORD; | |
645 | sys.wMinute = tm.tm_min as WORD; | |
646 | sys.wHour = tm.tm_hour as WORD; | |
647 | sys.wDay = tm.tm_mday as WORD; | |
648 | sys.wDayOfWeek = tm.tm_wday as WORD; | |
649 | sys.wMonth = (tm.tm_mon + 1) as WORD; | |
650 | sys.wYear = (tm.tm_year + 1900) as WORD; | |
651 | sys | |
652 | } | |
653 | ||
654 | fn system_time_to_tm(sys: &SYSTEMTIME, tm: &mut Tm) { | |
655 | tm.tm_sec = sys.wSecond as i32; | |
656 | tm.tm_min = sys.wMinute as i32; | |
657 | tm.tm_hour = sys.wHour as i32; | |
658 | tm.tm_mday = sys.wDay as i32; | |
659 | tm.tm_wday = sys.wDayOfWeek as i32; | |
660 | tm.tm_mon = (sys.wMonth - 1) as i32; | |
661 | tm.tm_year = (sys.wYear - 1900) as i32; | |
662 | tm.tm_yday = yday(tm.tm_year, tm.tm_mon + 1, tm.tm_mday); | |
663 | ||
664 | fn yday(year: i32, month: i32, day: i32) -> i32 { | |
665 | let leap = if month > 2 { | |
666 | if year % 4 == 0 { 1 } else { 2 } | |
667 | } else { | |
668 | 0 | |
669 | }; | |
670 | let july = if month > 7 { 1 } else { 0 }; | |
671 | ||
672 | (month - 1) * 30 + month / 2 + (day - 1) - leap + july | |
673 | } | |
674 | } | |
675 | ||
676 | macro_rules! call { | |
677 | ($name:ident($($arg:expr),*)) => { | |
678 | if $name($($arg),*) == 0 { | |
679 | panic!(concat!(stringify!($name), " failed with: {}"), | |
680 | io::Error::last_os_error()); | |
681 | } | |
682 | } | |
683 | } | |
684 | ||
685 | pub fn time_to_utc_tm(sec: i64, tm: &mut Tm) { | |
686 | let mut out = unsafe { mem::zeroed() }; | |
687 | let ft = time_to_file_time(sec); | |
688 | unsafe { | |
689 | call!(FileTimeToSystemTime(&ft, &mut out)); | |
690 | } | |
691 | system_time_to_tm(&out, tm); | |
692 | tm.tm_utcoff = 0; | |
693 | } | |
694 | ||
695 | pub fn time_to_local_tm(sec: i64, tm: &mut Tm) { | |
696 | let ft = time_to_file_time(sec); | |
697 | unsafe { | |
698 | let mut utc = mem::zeroed(); | |
699 | let mut local = mem::zeroed(); | |
700 | call!(FileTimeToSystemTime(&ft, &mut utc)); | |
701 | call!(SystemTimeToTzSpecificLocalTime(0 as *const _, | |
702 | &mut utc, &mut local)); | |
703 | system_time_to_tm(&local, tm); | |
704 | ||
705 | let local = system_time_to_file_time(&local); | |
706 | let local_sec = file_time_to_unix_seconds(&local); | |
707 | ||
708 | let mut tz = mem::zeroed(); | |
709 | GetTimeZoneInformation(&mut tz); | |
710 | ||
711 | // SystemTimeToTzSpecificLocalTime already applied the biases so | |
712 | // check if it non standard | |
713 | tm.tm_utcoff = (local_sec - sec) as i32; | |
714 | tm.tm_isdst = if tm.tm_utcoff == -60 * (tz.Bias + tz.StandardBias) { | |
715 | 0 | |
716 | } else { | |
717 | 1 | |
718 | }; | |
719 | } | |
720 | } | |
721 | ||
722 | pub fn utc_tm_to_time(tm: &Tm) -> i64 { | |
723 | unsafe { | |
724 | let mut ft = mem::zeroed(); | |
725 | let sys_time = tm_to_system_time(tm); | |
726 | call!(SystemTimeToFileTime(&sys_time, &mut ft)); | |
727 | file_time_to_unix_seconds(&ft) | |
728 | } | |
729 | } | |
730 | ||
731 | pub fn local_tm_to_time(tm: &Tm) -> i64 { | |
732 | unsafe { | |
733 | let mut ft = mem::zeroed(); | |
734 | let mut utc = mem::zeroed(); | |
735 | let mut sys_time = tm_to_system_time(tm); | |
736 | call!(TzSpecificLocalTimeToSystemTime(0 as *mut _, | |
737 | &mut sys_time, &mut utc)); | |
738 | call!(SystemTimeToFileTime(&utc, &mut ft)); | |
739 | file_time_to_unix_seconds(&ft) | |
740 | } | |
741 | } | |
742 | ||
743 | pub fn get_time() -> (i64, i32) { | |
744 | unsafe { | |
745 | let mut ft = mem::zeroed(); | |
746 | GetSystemTimeAsFileTime(&mut ft); | |
747 | (file_time_to_unix_seconds(&ft), file_time_to_nsec(&ft)) | |
748 | } | |
749 | } | |
750 | ||
751 | pub fn get_precise_ns() -> u64 { | |
752 | let mut ticks = i64_to_large_integer(0); | |
753 | unsafe { | |
754 | assert!(QueryPerformanceCounter(&mut ticks) == 1); | |
755 | } | |
756 | mul_div_i64(large_integer_to_i64(ticks), 1000000000, frequency()) as u64 | |
757 | ||
758 | } | |
759 | ||
760 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] | |
761 | pub struct SteadyTime { | |
762 | t: i64, | |
763 | } | |
764 | ||
765 | impl SteadyTime { | |
766 | pub fn now() -> SteadyTime { | |
767 | let mut l = i64_to_large_integer(0); | |
768 | unsafe { QueryPerformanceCounter(&mut l); } | |
769 | SteadyTime { t : large_integer_to_i64(l) } | |
770 | } | |
771 | } | |
772 | ||
773 | impl Sub for SteadyTime { | |
774 | type Output = Duration; | |
775 | fn sub(self, other: SteadyTime) -> Duration { | |
776 | let diff = self.t as i64 - other.t as i64; | |
777 | Duration::nanoseconds(mul_div_i64(diff, 1000000000, | |
778 | frequency())) | |
779 | } | |
780 | } | |
781 | ||
782 | impl Sub<Duration> for SteadyTime { | |
783 | type Output = SteadyTime; | |
784 | fn sub(self, other: Duration) -> SteadyTime { | |
785 | self + -other | |
786 | } | |
787 | } | |
788 | ||
789 | impl Add<Duration> for SteadyTime { | |
790 | type Output = SteadyTime; | |
791 | fn add(mut self, other: Duration) -> SteadyTime { | |
792 | self.t += (other.num_microseconds().unwrap() * frequency() / | |
793 | 1_000_000) as i64; | |
794 | self | |
795 | } | |
796 | } | |
797 | ||
798 | #[cfg(test)] | |
799 | pub struct TzReset { | |
800 | old: TIME_ZONE_INFORMATION, | |
801 | } | |
802 | ||
803 | #[cfg(test)] | |
804 | impl Drop for TzReset { | |
805 | fn drop(&mut self) { | |
806 | unsafe { | |
807 | call!(SetTimeZoneInformation(&self.old)); | |
808 | } | |
809 | } | |
810 | } | |
811 | ||
812 | #[cfg(test)] | |
813 | pub fn set_los_angeles_time_zone() -> TzReset { | |
814 | acquire_privileges(); | |
815 | ||
816 | unsafe { | |
817 | let mut tz = mem::zeroed::<TIME_ZONE_INFORMATION>(); | |
818 | GetTimeZoneInformation(&mut tz); | |
819 | let ret = TzReset { old: tz }; | |
820 | tz.Bias = 60 * 8; | |
821 | call!(SetTimeZoneInformation(&tz)); | |
822 | return ret | |
823 | } | |
824 | } | |
825 | ||
826 | #[cfg(test)] | |
827 | pub fn set_london_with_dst_time_zone() -> TzReset { | |
828 | acquire_privileges(); | |
829 | ||
830 | unsafe { | |
831 | let mut tz = mem::zeroed::<TIME_ZONE_INFORMATION>(); | |
832 | GetTimeZoneInformation(&mut tz); | |
833 | let ret = TzReset { old: tz }; | |
834 | // Since date set precisely this is 2015's dates | |
835 | tz.Bias = 0; | |
836 | tz.DaylightBias = -60; | |
837 | tz.DaylightDate.wYear = 0; | |
838 | tz.DaylightDate.wMonth = 3; | |
839 | tz.DaylightDate.wDayOfWeek = 0; | |
840 | tz.DaylightDate.wDay = 5; | |
841 | tz.DaylightDate.wHour = 2; | |
842 | tz.StandardBias = 0; | |
843 | tz.StandardDate.wYear = 0; | |
844 | tz.StandardDate.wMonth = 10; | |
845 | tz.StandardDate.wDayOfWeek = 0; | |
846 | tz.StandardDate.wDay = 5; | |
847 | tz.StandardDate.wHour = 2; | |
848 | call!(SetTimeZoneInformation(&tz)); | |
849 | return ret | |
850 | } | |
851 | } | |
852 | ||
853 | // Ensures that this process has the necessary privileges to set a new time | |
854 | // zone, and this is all transcribed from: | |
855 | // https://msdn.microsoft.com/en-us/library/windows/desktop/ms724944%28v=vs.85%29.aspx | |
856 | #[cfg(test)] | |
857 | fn acquire_privileges() { | |
ff7c6d11 XL |
858 | use winapi::um::processthreadsapi::*; |
859 | use winapi::um::winbase::LookupPrivilegeValueA; | |
860 | const SE_PRIVILEGE_ENABLED: DWORD = 2; | |
f035d41b | 861 | #[allow(deprecated)] |
ff7c6d11 XL |
862 | static INIT: Once = ONCE_INIT; |
863 | ||
864 | // TODO: FIXME | |
865 | extern "system" { | |
866 | fn AdjustTokenPrivileges( | |
867 | TokenHandle: HANDLE, DisableAllPrivileges: BOOL, NewState: PTOKEN_PRIVILEGES, | |
868 | BufferLength: DWORD, PreviousState: PTOKEN_PRIVILEGES, ReturnLength: PDWORD, | |
869 | ) -> BOOL; | |
870 | } | |
871 | ||
ff7c6d11 XL |
872 | INIT.call_once(|| unsafe { |
873 | let mut hToken = 0 as *mut _; | |
874 | call!(OpenProcessToken(GetCurrentProcess(), | |
875 | TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, | |
876 | &mut hToken)); | |
877 | ||
f035d41b XL |
878 | let mut tkp = mem::zeroed::<TOKEN_PRIVILEGES>(); |
879 | assert_eq!(tkp.Privileges.len(), 1); | |
ff7c6d11 XL |
880 | let c = ::std::ffi::CString::new("SeTimeZonePrivilege").unwrap(); |
881 | call!(LookupPrivilegeValueA(0 as *const _, c.as_ptr(), | |
f035d41b XL |
882 | &mut tkp.Privileges[0].Luid)); |
883 | tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; | |
884 | tkp.PrivilegeCount = 1; | |
885 | call!(AdjustTokenPrivileges(hToken, FALSE, &mut tkp, 0, | |
ff7c6d11 XL |
886 | 0 as *mut _, 0 as *mut _)); |
887 | }); | |
888 | } | |
889 | ||
890 | ||
891 | ||
892 | // Computes (value*numer)/denom without overflow, as long as both | |
893 | // (numer*denom) and the overall result fit into i64 (which is the case | |
894 | // for our time conversions). | |
895 | fn mul_div_i64(value: i64, numer: i64, denom: i64) -> i64 { | |
896 | let q = value / denom; | |
897 | let r = value % denom; | |
898 | // Decompose value as (value/denom*denom + value%denom), | |
899 | // substitute into (value*numer)/denom and simplify. | |
900 | // r < denom, so (denom*numer) is the upper bound of (r*numer) | |
901 | q * numer + r * numer / denom | |
902 | } | |
903 | ||
904 | #[test] | |
905 | fn test_muldiv() { | |
906 | assert_eq!(mul_div_i64( 1_000_000_000_001, 1_000_000_000, 1_000_000), | |
907 | 1_000_000_000_001_000); | |
908 | assert_eq!(mul_div_i64(-1_000_000_000_001, 1_000_000_000, 1_000_000), | |
909 | -1_000_000_000_001_000); | |
910 | assert_eq!(mul_div_i64(-1_000_000_000_001,-1_000_000_000, 1_000_000), | |
911 | 1_000_000_000_001_000); | |
912 | assert_eq!(mul_div_i64( 1_000_000_000_001, 1_000_000_000,-1_000_000), | |
913 | -1_000_000_000_001_000); | |
914 | assert_eq!(mul_div_i64( 1_000_000_000_001,-1_000_000_000,-1_000_000), | |
915 | 1_000_000_000_001_000); | |
916 | } | |
917 | } |