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