Skip to content

Commit bb10e42

Browse files
committed
time: add TimeSpec
1 parent c4a38ec commit bb10e42

File tree

1 file changed

+215
-2
lines changed

1 file changed

+215
-2
lines changed

src/sys/time.rs

+215-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::{fmt, ops};
2-
use libc::{time_t, suseconds_t, timeval};
2+
use libc::{time_t, c_long, suseconds_t, timeval, timespec};
33

4+
const NANOS_PER_SEC: i64 = 1_000_000_000;
45
const MICROS_PER_SEC: i64 = 1_000_000;
56
const SECS_PER_MINUTE: i64 = 60;
67
const SECS_PER_HOUR: i64 = 3600;
@@ -183,6 +184,193 @@ impl PartialEq for TimeVal {
183184

184185
impl Eq for TimeVal { }
185186

187+
#[derive(Clone, Copy)]
188+
pub struct TimeSpec {
189+
spec: timespec,
190+
}
191+
192+
impl AsRef<timespec> for TimeSpec {
193+
fn as_ref(&self) -> &timespec {
194+
&self.spec
195+
}
196+
}
197+
198+
impl TimeSpec {
199+
#[inline]
200+
pub fn zero() -> TimeSpec {
201+
TimeSpec::nanoseconds(0)
202+
}
203+
204+
#[inline]
205+
pub fn hours(hours: i64) -> TimeSpec {
206+
let secs = hours.checked_mul(SECS_PER_HOUR)
207+
.expect("TimeSpec::hours ouf of bounds");
208+
209+
TimeSpec::seconds(secs)
210+
}
211+
212+
#[inline]
213+
pub fn minutes(minutes: i64) -> TimeSpec {
214+
let secs = minutes.checked_mul(SECS_PER_MINUTE)
215+
.expect("TimeSpec::minutes out of bounds");
216+
217+
TimeSpec::seconds(secs)
218+
}
219+
220+
#[inline]
221+
pub fn seconds(seconds: i64) -> TimeSpec {
222+
assert!(seconds >= time_t::min_value() && seconds <= time_t::max_value(), "TimeSpec out of bounds; seconds={}", seconds);
223+
TimeSpec { spec: timespec { tv_sec: seconds as time_t, tv_nsec: 0 } }
224+
}
225+
226+
#[inline]
227+
pub fn milliseconds(milliseconds: i64) -> TimeSpec {
228+
let nanoseconds = milliseconds.checked_mul(1_000_000)
229+
.expect("TimeSpec::milliseconds out of bounds");
230+
231+
TimeSpec::nanoseconds(nanoseconds)
232+
}
233+
234+
#[inline]
235+
pub fn microseconds(microseconds: i64) -> TimeSpec {
236+
let nanoseconds = microseconds.checked_mul(1_000)
237+
.expect("TimeSpec::microseconds out of bounds");
238+
239+
TimeSpec::nanoseconds(nanoseconds)
240+
}
241+
242+
243+
/// Makes a new `TimeSpec` with given number of nanoseconds.
244+
#[inline]
245+
pub fn nanoseconds(nanoseconds: i64) -> TimeSpec {
246+
let (secs, nanos) = div_mod_floor_64(nanoseconds, NANOS_PER_SEC);
247+
assert!(secs >= time_t::min_value() && secs <= time_t::max_value(), "TimeSpec out of bounds; seconds={}", secs);
248+
TimeSpec { spec: timespec { tv_sec: secs as time_t, tv_nsec: nanos as c_long } }
249+
}
250+
251+
pub fn num_hours(&self) -> i64 {
252+
self.num_seconds() / 3600
253+
}
254+
255+
pub fn num_minutes(&self) -> i64 {
256+
self.num_seconds() / 60
257+
}
258+
259+
pub fn num_seconds(&self) -> i64 {
260+
if self.spec.tv_sec < 0 && self.spec.tv_nsec > 0 {
261+
(self.spec.tv_sec + 1) as i64
262+
} else {
263+
self.spec.tv_sec as i64
264+
}
265+
}
266+
267+
pub fn num_milliseconds(&self) -> i64 {
268+
self.num_nanoseconds() / 1_000_000
269+
}
270+
271+
pub fn num_microseconds(&self) -> i64 {
272+
self.num_nanoseconds() / 1_000
273+
}
274+
275+
pub fn num_nanoseconds(&self) -> i64 {
276+
let secs = self.num_seconds() * NANOS_PER_SEC;
277+
let nsec = self.nanos_mod_sec();
278+
secs + nsec as i64
279+
}
280+
281+
fn nanos_mod_sec(&self) -> suseconds_t {
282+
if self.spec.tv_sec < 0 && self.spec.tv_nsec > 0 {
283+
self.spec.tv_nsec - NANOS_PER_SEC as c_long
284+
} else {
285+
self.spec.tv_nsec
286+
}
287+
}
288+
}
289+
290+
impl ops::Neg for TimeSpec {
291+
type Output = TimeSpec;
292+
293+
fn neg(self) -> TimeSpec {
294+
TimeSpec::nanoseconds(-self.num_nanoseconds())
295+
}
296+
}
297+
298+
impl ops::Add for TimeSpec {
299+
type Output = TimeSpec;
300+
301+
fn add(self, rhs: TimeSpec) -> TimeSpec {
302+
TimeSpec::nanoseconds(
303+
self.num_nanoseconds() + rhs.num_nanoseconds())
304+
}
305+
}
306+
307+
impl ops::Sub for TimeSpec {
308+
type Output = TimeSpec;
309+
310+
fn sub(self, rhs: TimeSpec) -> TimeSpec {
311+
TimeSpec::nanoseconds(
312+
self.num_nanoseconds() - rhs.num_nanoseconds())
313+
}
314+
}
315+
316+
impl ops::Mul<i32> for TimeSpec {
317+
type Output = TimeSpec;
318+
319+
fn mul(self, rhs: i32) -> TimeSpec {
320+
let nsec = self.num_nanoseconds().checked_mul(rhs as i64)
321+
.expect("TimeSpec multiply out of bounds");
322+
323+
TimeSpec::nanoseconds(nsec)
324+
}
325+
}
326+
327+
impl ops::Div<i32> for TimeSpec {
328+
type Output = TimeSpec;
329+
330+
fn div(self, rhs: i32) -> TimeSpec {
331+
let nsec = self.num_nanoseconds() / rhs as i64;
332+
TimeSpec::nanoseconds(nsec)
333+
}
334+
}
335+
336+
impl fmt::Display for TimeSpec {
337+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
338+
let (abs, sign) = if self.spec.tv_sec < 0 {
339+
(-*self, "-")
340+
} else {
341+
(*self, "")
342+
};
343+
344+
let sec = abs.spec.tv_sec;
345+
346+
try!(write!(f, "{}", sign));
347+
348+
if abs.spec.tv_nsec == 0 {
349+
if abs.spec.tv_sec == 1 {
350+
try!(write!(f, "{} second", sec));
351+
} else {
352+
try!(write!(f, "{} seconds", sec));
353+
}
354+
} else if abs.spec.tv_nsec % 1_000_000 == 0 {
355+
try!(write!(f, "{}.{:03} seconds", sec, abs.spec.tv_nsec / 1_000_000));
356+
} else if abs.spec.tv_nsec % 1_000 == 0 {
357+
try!(write!(f, "{}.{:06} seconds", sec, abs.spec.tv_nsec / 1_000));
358+
} else {
359+
try!(write!(f, "{}.{:09} seconds", sec, abs.spec.tv_nsec));
360+
}
361+
362+
Ok(())
363+
}
364+
}
365+
366+
impl PartialEq for TimeSpec {
367+
fn eq(&self, rhs: &TimeSpec) -> bool {
368+
self.spec.tv_sec == rhs.spec.tv_sec && self.spec.tv_nsec == rhs.spec.tv_nsec
369+
}
370+
}
371+
372+
impl Eq for TimeSpec { }
373+
186374
#[inline]
187375
fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) {
188376
(div_floor_64(this, other), mod_floor_64(this, other))
@@ -213,7 +401,7 @@ fn div_rem_64(this: i64, other: i64) -> (i64, i64) {
213401

214402
#[cfg(test)]
215403
mod test {
216-
use super::TimeVal;
404+
use super::{TimeVal, TimeSpec};
217405

218406
#[test]
219407
pub fn test_time_val() {
@@ -238,4 +426,29 @@ mod test {
238426
assert_eq!(TimeVal::microseconds(42).to_string(), "0.000042 seconds");
239427
assert_eq!(TimeVal::seconds(-86401).to_string(), "-86401 seconds");
240428
}
429+
430+
#[test]
431+
pub fn test_time_spec() {
432+
assert!(TimeSpec::seconds(1) != TimeSpec::zero());
433+
assert!(TimeSpec::seconds(1) + TimeSpec::seconds(2) == TimeSpec::seconds(3));
434+
assert!(TimeSpec::minutes(3) + TimeSpec::seconds(2) == TimeSpec::seconds(182));
435+
}
436+
437+
#[test]
438+
pub fn test_time_spec_neg() {
439+
let a = TimeSpec::seconds(1) + TimeSpec::nanoseconds(123);
440+
let b = TimeSpec::seconds(-1) + TimeSpec::nanoseconds(-123);
441+
442+
assert!(a == -b);
443+
}
444+
445+
#[test]
446+
pub fn test_time_spec_fmt() {
447+
assert_eq!(TimeSpec::zero().to_string(), "0 seconds");
448+
assert_eq!(TimeSpec::seconds(42).to_string(), "42 seconds");
449+
assert_eq!(TimeSpec::milliseconds(42).to_string(), "0.042 seconds");
450+
assert_eq!(TimeSpec::microseconds(42).to_string(), "0.000042 seconds");
451+
assert_eq!(TimeSpec::nanoseconds(42).to_string(), "0.000000042 seconds");
452+
assert_eq!(TimeSpec::seconds(-86401).to_string(), "-86401 seconds");
453+
}
241454
}

0 commit comments

Comments
 (0)