#[cfg(any(feature = "alloc", feature = "std", test))]
use core::borrow::Borrow;
use core::ops::{Add, AddAssign, Sub, SubAssign};
use core::{fmt, str};
use num_integer::div_mod_floor;
#[cfg(feature = "rkyv")]
use rkyv::{Archive, Deserialize, Serialize};
#[cfg(any(feature = "alloc", feature = "std", test))]
use crate::format::DelayedFormat;
use crate::format::{parse, write_hundreds, ParseError, ParseResult, Parsed, StrftimeItems};
use crate::format::{Fixed, Item, Numeric, Pad};
use crate::oldtime::Duration as OldDuration;
use crate::Timelike;
#[cfg(feature = "rustc-serialize")]
mod rustc_serialize;
#[cfg(feature = "serde")]
mod serde;
#[cfg(test)]
mod tests;
#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Clone)]
#[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))]
pub struct NaiveTime {
secs: u32,
frac: u32,
}
#[cfg(feature = "arbitrary")]
impl arbitrary::Arbitrary<'_> for NaiveTime {
fn arbitrary(u: &mut arbitrary::Unstructured) -> arbitrary::Result<NaiveTime> {
let secs = u.int_in_range(0..=86_399)?;
let nano = u.int_in_range(0..=1_999_999_999)?;
let time = NaiveTime::from_num_seconds_from_midnight_opt(secs, nano)
.expect("Could not generate a valid chrono::NaiveTime. It looks like implementation of Arbitrary for NaiveTime is erroneous.");
Ok(time)
}
}
impl NaiveTime {
#[deprecated(since = "0.4.23", note = "use `from_hms_opt()` instead")]
#[inline]
pub fn from_hms(hour: u32, min: u32, sec: u32) -> NaiveTime {
NaiveTime::from_hms_opt(hour, min, sec).expect("invalid time")
}
#[inline]
pub fn from_hms_opt(hour: u32, min: u32, sec: u32) -> Option<NaiveTime> {
NaiveTime::from_hms_nano_opt(hour, min, sec, 0)
}
#[deprecated(since = "0.4.23", note = "use `from_hms_milli_opt()` instead")]
#[inline]
pub fn from_hms_milli(hour: u32, min: u32, sec: u32, milli: u32) -> NaiveTime {
NaiveTime::from_hms_milli_opt(hour, min, sec, milli).expect("invalid time")
}
#[inline]
pub fn from_hms_milli_opt(hour: u32, min: u32, sec: u32, milli: u32) -> Option<NaiveTime> {
milli
.checked_mul(1_000_000)
.and_then(|nano| NaiveTime::from_hms_nano_opt(hour, min, sec, nano))
}
#[deprecated(since = "0.4.23", note = "use `from_hms_micro_opt()` instead")]
#[inline]
pub fn from_hms_micro(hour: u32, min: u32, sec: u32, micro: u32) -> NaiveTime {
NaiveTime::from_hms_micro_opt(hour, min, sec, micro).expect("invalid time")
}
#[inline]
pub fn from_hms_micro_opt(hour: u32, min: u32, sec: u32, micro: u32) -> Option<NaiveTime> {
micro.checked_mul(1_000).and_then(|nano| NaiveTime::from_hms_nano_opt(hour, min, sec, nano))
}
#[deprecated(since = "0.4.23", note = "use `from_hms_nano_opt()` instead")]
#[inline]
pub fn from_hms_nano(hour: u32, min: u32, sec: u32, nano: u32) -> NaiveTime {
NaiveTime::from_hms_nano_opt(hour, min, sec, nano).expect("invalid time")
}
#[inline]
pub fn from_hms_nano_opt(hour: u32, min: u32, sec: u32, nano: u32) -> Option<NaiveTime> {
if hour >= 24 || min >= 60 || sec >= 60 || nano >= 2_000_000_000 {
return None;
}
let secs = hour * 3600 + min * 60 + sec;
Some(NaiveTime { secs, frac: nano })
}
#[deprecated(since = "0.4.23", note = "use `from_num_seconds_from_midnight_opt()` instead")]
#[inline]
pub fn from_num_seconds_from_midnight(secs: u32, nano: u32) -> NaiveTime {
NaiveTime::from_num_seconds_from_midnight_opt(secs, nano).expect("invalid time")
}
#[inline]
pub fn from_num_seconds_from_midnight_opt(secs: u32, nano: u32) -> Option<NaiveTime> {
if secs >= 86_400 || nano >= 2_000_000_000 {
return None;
}
Some(NaiveTime { secs, frac: nano })
}
pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult<NaiveTime> {
let mut parsed = Parsed::new();
parse(&mut parsed, s, StrftimeItems::new(fmt))?;
parsed.to_naive_time()
}
pub fn overflowing_add_signed(&self, mut rhs: OldDuration) -> (NaiveTime, i64) {
let mut secs = self.secs;
let mut frac = self.frac;
if frac >= 1_000_000_000 {
let rfrac = 2_000_000_000 - frac;
if rhs >= OldDuration::nanoseconds(i64::from(rfrac)) {
rhs = rhs - OldDuration::nanoseconds(i64::from(rfrac));
secs += 1;
frac = 0;
} else if rhs < OldDuration::nanoseconds(-i64::from(frac)) {
rhs = rhs + OldDuration::nanoseconds(i64::from(frac));
frac = 0;
} else {
frac = (i64::from(frac) + rhs.num_nanoseconds().unwrap()) as u32;
debug_assert!(frac < 2_000_000_000);
return (NaiveTime { secs, frac }, 0);
}
}
debug_assert!(secs <= 86_400);
debug_assert!(frac < 1_000_000_000);
let rhssecs = rhs.num_seconds();
let rhsfrac = (rhs - OldDuration::seconds(rhssecs)).num_nanoseconds().unwrap();
debug_assert_eq!(OldDuration::seconds(rhssecs) + OldDuration::nanoseconds(rhsfrac), rhs);
let rhssecsinday = rhssecs % 86_400;
let mut morerhssecs = rhssecs - rhssecsinday;
let rhssecs = rhssecsinday as i32;
let rhsfrac = rhsfrac as i32;
debug_assert!(-86_400 < rhssecs && rhssecs < 86_400);
debug_assert_eq!(morerhssecs % 86_400, 0);
debug_assert!(-1_000_000_000 < rhsfrac && rhsfrac < 1_000_000_000);
let mut secs = secs as i32 + rhssecs;
let mut frac = frac as i32 + rhsfrac;
debug_assert!(-86_400 < secs && secs < 2 * 86_400);
debug_assert!(-1_000_000_000 < frac && frac < 2_000_000_000);
if frac < 0 {
frac += 1_000_000_000;
secs -= 1;
} else if frac >= 1_000_000_000 {
frac -= 1_000_000_000;
secs += 1;
}
debug_assert!((-86_400..2 * 86_400).contains(&secs));
debug_assert!((0..1_000_000_000).contains(&frac));
if secs < 0 {
secs += 86_400;
morerhssecs -= 86_400;
} else if secs >= 86_400 {
secs -= 86_400;
morerhssecs += 86_400;
}
debug_assert!((0..86_400).contains(&secs));
(NaiveTime { secs: secs as u32, frac: frac as u32 }, morerhssecs)
}
#[inline]
pub fn overflowing_sub_signed(&self, rhs: OldDuration) -> (NaiveTime, i64) {
let (time, rhs) = self.overflowing_add_signed(-rhs);
(time, -rhs) }
pub fn signed_duration_since(self, rhs: NaiveTime) -> OldDuration {
use core::cmp::Ordering;
let secs = i64::from(self.secs) - i64::from(rhs.secs);
let frac = i64::from(self.frac) - i64::from(rhs.frac);
let adjust = match self.secs.cmp(&rhs.secs) {
Ordering::Greater => i64::from(rhs.frac >= 1_000_000_000),
Ordering::Equal => 0,
Ordering::Less => {
if self.frac >= 1_000_000_000 {
-1
} else {
0
}
}
};
OldDuration::seconds(secs + adjust) + OldDuration::nanoseconds(frac)
}
#[cfg(any(feature = "alloc", feature = "std", test))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))]
#[inline]
pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I>
where
I: Iterator<Item = B> + Clone,
B: Borrow<Item<'a>>,
{
DelayedFormat::new(None, Some(*self), items)
}
#[cfg(any(feature = "alloc", feature = "std", test))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))]
#[inline]
pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> {
self.format_with_items(StrftimeItems::new(fmt))
}
fn hms(&self) -> (u32, u32, u32) {
let (mins, sec) = div_mod_floor(self.secs, 60);
let (hour, min) = div_mod_floor(mins, 60);
(hour, min, sec)
}
pub const MIN: Self = Self { secs: 0, frac: 0 };
pub(super) const MAX: Self = Self { secs: 23 * 3600 + 59 * 60 + 59, frac: 999_999_999 };
}
impl Timelike for NaiveTime {
#[inline]
fn hour(&self) -> u32 {
self.hms().0
}
#[inline]
fn minute(&self) -> u32 {
self.hms().1
}
#[inline]
fn second(&self) -> u32 {
self.hms().2
}
#[inline]
fn nanosecond(&self) -> u32 {
self.frac
}
#[inline]
fn with_hour(&self, hour: u32) -> Option<NaiveTime> {
if hour >= 24 {
return None;
}
let secs = hour * 3600 + self.secs % 3600;
Some(NaiveTime { secs, ..*self })
}
#[inline]
fn with_minute(&self, min: u32) -> Option<NaiveTime> {
if min >= 60 {
return None;
}
let secs = self.secs / 3600 * 3600 + min * 60 + self.secs % 60;
Some(NaiveTime { secs, ..*self })
}
#[inline]
fn with_second(&self, sec: u32) -> Option<NaiveTime> {
if sec >= 60 {
return None;
}
let secs = self.secs / 60 * 60 + sec;
Some(NaiveTime { secs, ..*self })
}
#[inline]
fn with_nanosecond(&self, nano: u32) -> Option<NaiveTime> {
if nano >= 2_000_000_000 {
return None;
}
Some(NaiveTime { frac: nano, ..*self })
}
#[inline]
fn num_seconds_from_midnight(&self) -> u32 {
self.secs }
}
impl Add<OldDuration> for NaiveTime {
type Output = NaiveTime;
#[inline]
fn add(self, rhs: OldDuration) -> NaiveTime {
self.overflowing_add_signed(rhs).0
}
}
impl AddAssign<OldDuration> for NaiveTime {
#[inline]
fn add_assign(&mut self, rhs: OldDuration) {
*self = self.add(rhs);
}
}
impl Sub<OldDuration> for NaiveTime {
type Output = NaiveTime;
#[inline]
fn sub(self, rhs: OldDuration) -> NaiveTime {
self.overflowing_sub_signed(rhs).0
}
}
impl SubAssign<OldDuration> for NaiveTime {
#[inline]
fn sub_assign(&mut self, rhs: OldDuration) {
*self = self.sub(rhs);
}
}
impl Sub<NaiveTime> for NaiveTime {
type Output = OldDuration;
#[inline]
fn sub(self, rhs: NaiveTime) -> OldDuration {
self.signed_duration_since(rhs)
}
}
impl fmt::Debug for NaiveTime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let (hour, min, sec) = self.hms();
let (sec, nano) = if self.frac >= 1_000_000_000 {
(sec + 1, self.frac - 1_000_000_000)
} else {
(sec, self.frac)
};
use core::fmt::Write;
write_hundreds(f, hour as u8)?;
f.write_char(':')?;
write_hundreds(f, min as u8)?;
f.write_char(':')?;
write_hundreds(f, sec as u8)?;
if nano == 0 {
Ok(())
} else if nano % 1_000_000 == 0 {
write!(f, ".{:03}", nano / 1_000_000)
} else if nano % 1_000 == 0 {
write!(f, ".{:06}", nano / 1_000)
} else {
write!(f, ".{:09}", nano)
}
}
}
impl fmt::Display for NaiveTime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
impl str::FromStr for NaiveTime {
type Err = ParseError;
fn from_str(s: &str) -> ParseResult<NaiveTime> {
const ITEMS: &[Item<'static>] = &[
Item::Numeric(Numeric::Hour, Pad::Zero),
Item::Space(""),
Item::Literal(":"),
Item::Numeric(Numeric::Minute, Pad::Zero),
Item::Space(""),
Item::Literal(":"),
Item::Numeric(Numeric::Second, Pad::Zero),
Item::Fixed(Fixed::Nanosecond),
Item::Space(""),
];
let mut parsed = Parsed::new();
parse(&mut parsed, s, ITEMS.iter())?;
parsed.to_naive_time()
}
}
impl Default for NaiveTime {
fn default() -> Self {
NaiveTime::from_hms_opt(0, 0, 0).unwrap()
}
}
#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))]
fn test_encodable_json<F, E>(to_string: F)
where
F: Fn(&NaiveTime) -> Result<String, E>,
E: ::std::fmt::Debug,
{
assert_eq!(
to_string(&NaiveTime::from_hms_opt(0, 0, 0).unwrap()).ok(),
Some(r#""00:00:00""#.into())
);
assert_eq!(
to_string(&NaiveTime::from_hms_milli_opt(0, 0, 0, 950).unwrap()).ok(),
Some(r#""00:00:00.950""#.into())
);
assert_eq!(
to_string(&NaiveTime::from_hms_milli_opt(0, 0, 59, 1_000).unwrap()).ok(),
Some(r#""00:00:60""#.into())
);
assert_eq!(
to_string(&NaiveTime::from_hms_opt(0, 1, 2).unwrap()).ok(),
Some(r#""00:01:02""#.into())
);
assert_eq!(
to_string(&NaiveTime::from_hms_nano_opt(3, 5, 7, 98765432).unwrap()).ok(),
Some(r#""03:05:07.098765432""#.into())
);
assert_eq!(
to_string(&NaiveTime::from_hms_opt(7, 8, 9).unwrap()).ok(),
Some(r#""07:08:09""#.into())
);
assert_eq!(
to_string(&NaiveTime::from_hms_micro_opt(12, 34, 56, 789).unwrap()).ok(),
Some(r#""12:34:56.000789""#.into())
);
assert_eq!(
to_string(&NaiveTime::from_hms_nano_opt(23, 59, 59, 1_999_999_999).unwrap()).ok(),
Some(r#""23:59:60.999999999""#.into())
);
}
#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))]
fn test_decodable_json<F, E>(from_str: F)
where
F: Fn(&str) -> Result<NaiveTime, E>,
E: ::std::fmt::Debug,
{
assert_eq!(from_str(r#""00:00:00""#).ok(), Some(NaiveTime::from_hms_opt(0, 0, 0).unwrap()));
assert_eq!(from_str(r#""0:0:0""#).ok(), Some(NaiveTime::from_hms_opt(0, 0, 0).unwrap()));
assert_eq!(
from_str(r#""00:00:00.950""#).ok(),
Some(NaiveTime::from_hms_milli_opt(0, 0, 0, 950).unwrap())
);
assert_eq!(
from_str(r#""0:0:0.95""#).ok(),
Some(NaiveTime::from_hms_milli_opt(0, 0, 0, 950).unwrap())
);
assert_eq!(
from_str(r#""00:00:60""#).ok(),
Some(NaiveTime::from_hms_milli_opt(0, 0, 59, 1_000).unwrap())
);
assert_eq!(from_str(r#""00:01:02""#).ok(), Some(NaiveTime::from_hms_opt(0, 1, 2).unwrap()));
assert_eq!(
from_str(r#""03:05:07.098765432""#).ok(),
Some(NaiveTime::from_hms_nano_opt(3, 5, 7, 98765432).unwrap())
);
assert_eq!(from_str(r#""07:08:09""#).ok(), Some(NaiveTime::from_hms_opt(7, 8, 9).unwrap()));
assert_eq!(
from_str(r#""12:34:56.000789""#).ok(),
Some(NaiveTime::from_hms_micro_opt(12, 34, 56, 789).unwrap())
);
assert_eq!(
from_str(r#""23:59:60.999999999""#).ok(),
Some(NaiveTime::from_hms_nano_opt(23, 59, 59, 1_999_999_999).unwrap())
);
assert_eq!(
from_str(r#""23:59:60.9999999999997""#).ok(), Some(NaiveTime::from_hms_nano_opt(23, 59, 59, 1_999_999_999).unwrap())
);
assert!(from_str(r#""""#).is_err());
assert!(from_str(r#""000000""#).is_err());
assert!(from_str(r#""00:00:61""#).is_err());
assert!(from_str(r#""00:60:00""#).is_err());
assert!(from_str(r#""24:00:00""#).is_err());
assert!(from_str(r#""23:59:59,1""#).is_err());
assert!(from_str(r#""012:34:56""#).is_err());
assert!(from_str(r#""hh:mm:ss""#).is_err());
assert!(from_str(r#"0"#).is_err());
assert!(from_str(r#"86399"#).is_err());
assert!(from_str(r#"{}"#).is_err());
assert!(from_str(r#"{"secs":0,"frac":0}"#).is_err());
assert!(from_str(r#"null"#).is_err());
}