RPM build fix (reverted CI changes which will need to be un-reverted or made conditional) and vendor Rust dependencies to make builds much faster in any CI system.
This commit is contained in:
480
zeroidc/vendor/chrono/src/date.rs
vendored
Normal file
480
zeroidc/vendor/chrono/src/date.rs
vendored
Normal file
@@ -0,0 +1,480 @@
|
||||
// This is a part of Chrono.
|
||||
// See README.md and LICENSE.txt for details.
|
||||
|
||||
//! ISO 8601 calendar date with time zone.
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
use core::borrow::Borrow;
|
||||
use core::cmp::Ordering;
|
||||
use core::ops::{Add, Sub};
|
||||
use core::{fmt, hash};
|
||||
use oldtime::Duration as OldDuration;
|
||||
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
use format::Locale;
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
use format::{DelayedFormat, Item, StrftimeItems};
|
||||
use naive::{self, IsoWeek, NaiveDate, NaiveTime};
|
||||
use offset::{TimeZone, Utc};
|
||||
use DateTime;
|
||||
use {Datelike, Weekday};
|
||||
|
||||
/// ISO 8601 calendar date with time zone.
|
||||
///
|
||||
/// This type should be considered ambiguous at best,
|
||||
/// due to the inherent lack of precision required for the time zone resolution.
|
||||
/// For serialization and deserialization uses, it is best to use `NaiveDate` instead.
|
||||
/// There are some guarantees on the usage of `Date<Tz>`:
|
||||
///
|
||||
/// - If properly constructed via `TimeZone::ymd` and others without an error,
|
||||
/// the corresponding local date should exist for at least a moment.
|
||||
/// (It may still have a gap from the offset changes.)
|
||||
///
|
||||
/// - The `TimeZone` is free to assign *any* `Offset` to the local date,
|
||||
/// as long as that offset did occur in given day.
|
||||
/// For example, if `2015-03-08T01:59-08:00` is followed by `2015-03-08T03:00-07:00`,
|
||||
/// it may produce either `2015-03-08-08:00` or `2015-03-08-07:00`
|
||||
/// but *not* `2015-03-08+00:00` and others.
|
||||
///
|
||||
/// - Once constructed as a full `DateTime`,
|
||||
/// `DateTime::date` and other associated methods should return those for the original `Date`.
|
||||
/// For example, if `dt = tz.ymd(y,m,d).hms(h,n,s)` were valid, `dt.date() == tz.ymd(y,m,d)`.
|
||||
///
|
||||
/// - The date is timezone-agnostic up to one day (i.e. practically always),
|
||||
/// so the local date and UTC date should be equal for most cases
|
||||
/// even though the raw calculation between `NaiveDate` and `Duration` may not.
|
||||
#[derive(Clone)]
|
||||
pub struct Date<Tz: TimeZone> {
|
||||
date: NaiveDate,
|
||||
offset: Tz::Offset,
|
||||
}
|
||||
|
||||
/// The minimum possible `Date`.
|
||||
pub const MIN_DATE: Date<Utc> = Date { date: naive::MIN_DATE, offset: Utc };
|
||||
/// The maximum possible `Date`.
|
||||
pub const MAX_DATE: Date<Utc> = Date { date: naive::MAX_DATE, offset: Utc };
|
||||
|
||||
impl<Tz: TimeZone> Date<Tz> {
|
||||
/// Makes a new `Date` with given *UTC* date and offset.
|
||||
/// The local date should be constructed via the `TimeZone` trait.
|
||||
//
|
||||
// note: this constructor is purposely not named to `new` to discourage the direct usage.
|
||||
#[inline]
|
||||
pub fn from_utc(date: NaiveDate, offset: Tz::Offset) -> Date<Tz> {
|
||||
Date { date: date, offset: offset }
|
||||
}
|
||||
|
||||
/// Makes a new `DateTime` from the current date and given `NaiveTime`.
|
||||
/// The offset in the current date is preserved.
|
||||
///
|
||||
/// Panics on invalid datetime.
|
||||
#[inline]
|
||||
pub fn and_time(&self, time: NaiveTime) -> Option<DateTime<Tz>> {
|
||||
let localdt = self.naive_local().and_time(time);
|
||||
self.timezone().from_local_datetime(&localdt).single()
|
||||
}
|
||||
|
||||
/// Makes a new `DateTime` from the current date, hour, minute and second.
|
||||
/// The offset in the current date is preserved.
|
||||
///
|
||||
/// Panics on invalid hour, minute and/or second.
|
||||
#[inline]
|
||||
pub fn and_hms(&self, hour: u32, min: u32, sec: u32) -> DateTime<Tz> {
|
||||
self.and_hms_opt(hour, min, sec).expect("invalid time")
|
||||
}
|
||||
|
||||
/// Makes a new `DateTime` from the current date, hour, minute and second.
|
||||
/// The offset in the current date is preserved.
|
||||
///
|
||||
/// Returns `None` on invalid hour, minute and/or second.
|
||||
#[inline]
|
||||
pub fn and_hms_opt(&self, hour: u32, min: u32, sec: u32) -> Option<DateTime<Tz>> {
|
||||
NaiveTime::from_hms_opt(hour, min, sec).and_then(|time| self.and_time(time))
|
||||
}
|
||||
|
||||
/// Makes a new `DateTime` from the current date, hour, minute, second and millisecond.
|
||||
/// The millisecond part can exceed 1,000 in order to represent the leap second.
|
||||
/// The offset in the current date is preserved.
|
||||
///
|
||||
/// Panics on invalid hour, minute, second and/or millisecond.
|
||||
#[inline]
|
||||
pub fn and_hms_milli(&self, hour: u32, min: u32, sec: u32, milli: u32) -> DateTime<Tz> {
|
||||
self.and_hms_milli_opt(hour, min, sec, milli).expect("invalid time")
|
||||
}
|
||||
|
||||
/// Makes a new `DateTime` from the current date, hour, minute, second and millisecond.
|
||||
/// The millisecond part can exceed 1,000 in order to represent the leap second.
|
||||
/// The offset in the current date is preserved.
|
||||
///
|
||||
/// Returns `None` on invalid hour, minute, second and/or millisecond.
|
||||
#[inline]
|
||||
pub fn and_hms_milli_opt(
|
||||
&self,
|
||||
hour: u32,
|
||||
min: u32,
|
||||
sec: u32,
|
||||
milli: u32,
|
||||
) -> Option<DateTime<Tz>> {
|
||||
NaiveTime::from_hms_milli_opt(hour, min, sec, milli).and_then(|time| self.and_time(time))
|
||||
}
|
||||
|
||||
/// Makes a new `DateTime` from the current date, hour, minute, second and microsecond.
|
||||
/// The microsecond part can exceed 1,000,000 in order to represent the leap second.
|
||||
/// The offset in the current date is preserved.
|
||||
///
|
||||
/// Panics on invalid hour, minute, second and/or microsecond.
|
||||
#[inline]
|
||||
pub fn and_hms_micro(&self, hour: u32, min: u32, sec: u32, micro: u32) -> DateTime<Tz> {
|
||||
self.and_hms_micro_opt(hour, min, sec, micro).expect("invalid time")
|
||||
}
|
||||
|
||||
/// Makes a new `DateTime` from the current date, hour, minute, second and microsecond.
|
||||
/// The microsecond part can exceed 1,000,000 in order to represent the leap second.
|
||||
/// The offset in the current date is preserved.
|
||||
///
|
||||
/// Returns `None` on invalid hour, minute, second and/or microsecond.
|
||||
#[inline]
|
||||
pub fn and_hms_micro_opt(
|
||||
&self,
|
||||
hour: u32,
|
||||
min: u32,
|
||||
sec: u32,
|
||||
micro: u32,
|
||||
) -> Option<DateTime<Tz>> {
|
||||
NaiveTime::from_hms_micro_opt(hour, min, sec, micro).and_then(|time| self.and_time(time))
|
||||
}
|
||||
|
||||
/// Makes a new `DateTime` from the current date, hour, minute, second and nanosecond.
|
||||
/// The nanosecond part can exceed 1,000,000,000 in order to represent the leap second.
|
||||
/// The offset in the current date is preserved.
|
||||
///
|
||||
/// Panics on invalid hour, minute, second and/or nanosecond.
|
||||
#[inline]
|
||||
pub fn and_hms_nano(&self, hour: u32, min: u32, sec: u32, nano: u32) -> DateTime<Tz> {
|
||||
self.and_hms_nano_opt(hour, min, sec, nano).expect("invalid time")
|
||||
}
|
||||
|
||||
/// Makes a new `DateTime` from the current date, hour, minute, second and nanosecond.
|
||||
/// The nanosecond part can exceed 1,000,000,000 in order to represent the leap second.
|
||||
/// The offset in the current date is preserved.
|
||||
///
|
||||
/// Returns `None` on invalid hour, minute, second and/or nanosecond.
|
||||
#[inline]
|
||||
pub fn and_hms_nano_opt(
|
||||
&self,
|
||||
hour: u32,
|
||||
min: u32,
|
||||
sec: u32,
|
||||
nano: u32,
|
||||
) -> Option<DateTime<Tz>> {
|
||||
NaiveTime::from_hms_nano_opt(hour, min, sec, nano).and_then(|time| self.and_time(time))
|
||||
}
|
||||
|
||||
/// Makes a new `Date` for the next date.
|
||||
///
|
||||
/// Panics when `self` is the last representable date.
|
||||
#[inline]
|
||||
pub fn succ(&self) -> Date<Tz> {
|
||||
self.succ_opt().expect("out of bound")
|
||||
}
|
||||
|
||||
/// Makes a new `Date` for the next date.
|
||||
///
|
||||
/// Returns `None` when `self` is the last representable date.
|
||||
#[inline]
|
||||
pub fn succ_opt(&self) -> Option<Date<Tz>> {
|
||||
self.date.succ_opt().map(|date| Date::from_utc(date, self.offset.clone()))
|
||||
}
|
||||
|
||||
/// Makes a new `Date` for the prior date.
|
||||
///
|
||||
/// Panics when `self` is the first representable date.
|
||||
#[inline]
|
||||
pub fn pred(&self) -> Date<Tz> {
|
||||
self.pred_opt().expect("out of bound")
|
||||
}
|
||||
|
||||
/// Makes a new `Date` for the prior date.
|
||||
///
|
||||
/// Returns `None` when `self` is the first representable date.
|
||||
#[inline]
|
||||
pub fn pred_opt(&self) -> Option<Date<Tz>> {
|
||||
self.date.pred_opt().map(|date| Date::from_utc(date, self.offset.clone()))
|
||||
}
|
||||
|
||||
/// Retrieves an associated offset from UTC.
|
||||
#[inline]
|
||||
pub fn offset(&self) -> &Tz::Offset {
|
||||
&self.offset
|
||||
}
|
||||
|
||||
/// Retrieves an associated time zone.
|
||||
#[inline]
|
||||
pub fn timezone(&self) -> Tz {
|
||||
TimeZone::from_offset(&self.offset)
|
||||
}
|
||||
|
||||
/// Changes the associated time zone.
|
||||
/// This does not change the actual `Date` (but will change the string representation).
|
||||
#[inline]
|
||||
pub fn with_timezone<Tz2: TimeZone>(&self, tz: &Tz2) -> Date<Tz2> {
|
||||
tz.from_utc_date(&self.date)
|
||||
}
|
||||
|
||||
/// Adds given `Duration` to the current date.
|
||||
///
|
||||
/// Returns `None` when it will result in overflow.
|
||||
#[inline]
|
||||
pub fn checked_add_signed(self, rhs: OldDuration) -> Option<Date<Tz>> {
|
||||
let date = try_opt!(self.date.checked_add_signed(rhs));
|
||||
Some(Date { date: date, offset: self.offset })
|
||||
}
|
||||
|
||||
/// Subtracts given `Duration` from the current date.
|
||||
///
|
||||
/// Returns `None` when it will result in overflow.
|
||||
#[inline]
|
||||
pub fn checked_sub_signed(self, rhs: OldDuration) -> Option<Date<Tz>> {
|
||||
let date = try_opt!(self.date.checked_sub_signed(rhs));
|
||||
Some(Date { date: date, offset: self.offset })
|
||||
}
|
||||
|
||||
/// Subtracts another `Date` from the current date.
|
||||
/// Returns a `Duration` of integral numbers.
|
||||
///
|
||||
/// This does not overflow or underflow at all,
|
||||
/// as all possible output fits in the range of `Duration`.
|
||||
#[inline]
|
||||
pub fn signed_duration_since<Tz2: TimeZone>(self, rhs: Date<Tz2>) -> OldDuration {
|
||||
self.date.signed_duration_since(rhs.date)
|
||||
}
|
||||
|
||||
/// Returns a view to the naive UTC date.
|
||||
#[inline]
|
||||
pub fn naive_utc(&self) -> NaiveDate {
|
||||
self.date
|
||||
}
|
||||
|
||||
/// Returns a view to the naive local date.
|
||||
///
|
||||
/// This is technically the same as [`naive_utc`](#method.naive_utc)
|
||||
/// because the offset is restricted to never exceed one day,
|
||||
/// but provided for the consistency.
|
||||
#[inline]
|
||||
pub fn naive_local(&self) -> NaiveDate {
|
||||
self.date
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps the local date to other date with given conversion function.
|
||||
fn map_local<Tz: TimeZone, F>(d: &Date<Tz>, mut f: F) -> Option<Date<Tz>>
|
||||
where
|
||||
F: FnMut(NaiveDate) -> Option<NaiveDate>,
|
||||
{
|
||||
f(d.naive_local()).and_then(|date| d.timezone().from_local_date(&date).single())
|
||||
}
|
||||
|
||||
impl<Tz: TimeZone> Date<Tz>
|
||||
where
|
||||
Tz::Offset: fmt::Display,
|
||||
{
|
||||
/// Formats the date with the specified formatting items.
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
#[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_with_offset(Some(self.naive_local()), None, &self.offset, items)
|
||||
}
|
||||
|
||||
/// Formats the date with the specified format string.
|
||||
/// See the [`format::strftime` module](./format/strftime/index.html)
|
||||
/// on the supported escape sequences.
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
#[inline]
|
||||
pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> {
|
||||
self.format_with_items(StrftimeItems::new(fmt))
|
||||
}
|
||||
|
||||
/// Formats the date with the specified formatting items and locale.
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
#[inline]
|
||||
pub fn format_localized_with_items<'a, I, B>(
|
||||
&self,
|
||||
items: I,
|
||||
locale: Locale,
|
||||
) -> DelayedFormat<I>
|
||||
where
|
||||
I: Iterator<Item = B> + Clone,
|
||||
B: Borrow<Item<'a>>,
|
||||
{
|
||||
DelayedFormat::new_with_offset_and_locale(
|
||||
Some(self.naive_local()),
|
||||
None,
|
||||
&self.offset,
|
||||
items,
|
||||
locale,
|
||||
)
|
||||
}
|
||||
|
||||
/// Formats the date with the specified format string and locale.
|
||||
/// See the [`format::strftime` module](./format/strftime/index.html)
|
||||
/// on the supported escape sequences.
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
#[inline]
|
||||
pub fn format_localized<'a>(
|
||||
&self,
|
||||
fmt: &'a str,
|
||||
locale: Locale,
|
||||
) -> DelayedFormat<StrftimeItems<'a>> {
|
||||
self.format_localized_with_items(StrftimeItems::new_with_locale(fmt, locale), locale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Tz: TimeZone> Datelike for Date<Tz> {
|
||||
#[inline]
|
||||
fn year(&self) -> i32 {
|
||||
self.naive_local().year()
|
||||
}
|
||||
#[inline]
|
||||
fn month(&self) -> u32 {
|
||||
self.naive_local().month()
|
||||
}
|
||||
#[inline]
|
||||
fn month0(&self) -> u32 {
|
||||
self.naive_local().month0()
|
||||
}
|
||||
#[inline]
|
||||
fn day(&self) -> u32 {
|
||||
self.naive_local().day()
|
||||
}
|
||||
#[inline]
|
||||
fn day0(&self) -> u32 {
|
||||
self.naive_local().day0()
|
||||
}
|
||||
#[inline]
|
||||
fn ordinal(&self) -> u32 {
|
||||
self.naive_local().ordinal()
|
||||
}
|
||||
#[inline]
|
||||
fn ordinal0(&self) -> u32 {
|
||||
self.naive_local().ordinal0()
|
||||
}
|
||||
#[inline]
|
||||
fn weekday(&self) -> Weekday {
|
||||
self.naive_local().weekday()
|
||||
}
|
||||
#[inline]
|
||||
fn iso_week(&self) -> IsoWeek {
|
||||
self.naive_local().iso_week()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_year(&self, year: i32) -> Option<Date<Tz>> {
|
||||
map_local(self, |date| date.with_year(year))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_month(&self, month: u32) -> Option<Date<Tz>> {
|
||||
map_local(self, |date| date.with_month(month))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_month0(&self, month0: u32) -> Option<Date<Tz>> {
|
||||
map_local(self, |date| date.with_month0(month0))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_day(&self, day: u32) -> Option<Date<Tz>> {
|
||||
map_local(self, |date| date.with_day(day))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_day0(&self, day0: u32) -> Option<Date<Tz>> {
|
||||
map_local(self, |date| date.with_day0(day0))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_ordinal(&self, ordinal: u32) -> Option<Date<Tz>> {
|
||||
map_local(self, |date| date.with_ordinal(ordinal))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_ordinal0(&self, ordinal0: u32) -> Option<Date<Tz>> {
|
||||
map_local(self, |date| date.with_ordinal0(ordinal0))
|
||||
}
|
||||
}
|
||||
|
||||
// we need them as automatic impls cannot handle associated types
|
||||
impl<Tz: TimeZone> Copy for Date<Tz> where <Tz as TimeZone>::Offset: Copy {}
|
||||
unsafe impl<Tz: TimeZone> Send for Date<Tz> where <Tz as TimeZone>::Offset: Send {}
|
||||
|
||||
impl<Tz: TimeZone, Tz2: TimeZone> PartialEq<Date<Tz2>> for Date<Tz> {
|
||||
fn eq(&self, other: &Date<Tz2>) -> bool {
|
||||
self.date == other.date
|
||||
}
|
||||
}
|
||||
|
||||
impl<Tz: TimeZone> Eq for Date<Tz> {}
|
||||
|
||||
impl<Tz: TimeZone> PartialOrd for Date<Tz> {
|
||||
fn partial_cmp(&self, other: &Date<Tz>) -> Option<Ordering> {
|
||||
self.date.partial_cmp(&other.date)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Tz: TimeZone> Ord for Date<Tz> {
|
||||
fn cmp(&self, other: &Date<Tz>) -> Ordering {
|
||||
self.date.cmp(&other.date)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Tz: TimeZone> hash::Hash for Date<Tz> {
|
||||
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||
self.date.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Tz: TimeZone> Add<OldDuration> for Date<Tz> {
|
||||
type Output = Date<Tz>;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: OldDuration) -> Date<Tz> {
|
||||
self.checked_add_signed(rhs).expect("`Date + Duration` overflowed")
|
||||
}
|
||||
}
|
||||
|
||||
impl<Tz: TimeZone> Sub<OldDuration> for Date<Tz> {
|
||||
type Output = Date<Tz>;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: OldDuration) -> Date<Tz> {
|
||||
self.checked_sub_signed(rhs).expect("`Date - Duration` overflowed")
|
||||
}
|
||||
}
|
||||
|
||||
impl<Tz: TimeZone> Sub<Date<Tz>> for Date<Tz> {
|
||||
type Output = OldDuration;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: Date<Tz>) -> OldDuration {
|
||||
self.signed_duration_since(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Tz: TimeZone> fmt::Debug for Date<Tz> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}{:?}", self.naive_local(), self.offset)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Tz: TimeZone> fmt::Display for Date<Tz>
|
||||
where
|
||||
Tz::Offset: fmt::Display,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}{}", self.naive_local(), self.offset)
|
||||
}
|
||||
}
|
||||
2589
zeroidc/vendor/chrono/src/datetime.rs
vendored
Normal file
2589
zeroidc/vendor/chrono/src/datetime.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
41
zeroidc/vendor/chrono/src/div.rs
vendored
Normal file
41
zeroidc/vendor/chrono/src/div.rs
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
// This is a part of Chrono.
|
||||
// Portions Copyright 2013-2014 The Rust Project Developers.
|
||||
// See README.md and LICENSE.txt for details.
|
||||
|
||||
//! Integer division utilities. (Shamelessly copied from [num](https://github.com/rust-lang/num/))
|
||||
|
||||
// Algorithm from [Daan Leijen. _Division and Modulus for Computer Scientists_,
|
||||
// December 2001](http://research.microsoft.com/pubs/151917/divmodnote-letter.pdf)
|
||||
|
||||
pub use num_integer::{div_floor, div_mod_floor, div_rem, mod_floor};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{div_mod_floor, mod_floor};
|
||||
|
||||
#[test]
|
||||
fn test_mod_floor() {
|
||||
assert_eq!(mod_floor(8, 3), 2);
|
||||
assert_eq!(mod_floor(8, -3), -1);
|
||||
assert_eq!(mod_floor(-8, 3), 1);
|
||||
assert_eq!(mod_floor(-8, -3), -2);
|
||||
|
||||
assert_eq!(mod_floor(1, 2), 1);
|
||||
assert_eq!(mod_floor(1, -2), -1);
|
||||
assert_eq!(mod_floor(-1, 2), 1);
|
||||
assert_eq!(mod_floor(-1, -2), -1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_div_mod_floor() {
|
||||
assert_eq!(div_mod_floor(8, 3), (2, 2));
|
||||
assert_eq!(div_mod_floor(8, -3), (-3, -1));
|
||||
assert_eq!(div_mod_floor(-8, 3), (-3, 1));
|
||||
assert_eq!(div_mod_floor(-8, -3), (2, -2));
|
||||
|
||||
assert_eq!(div_mod_floor(1, 2), (0, 1));
|
||||
assert_eq!(div_mod_floor(1, -2), (-1, -1));
|
||||
assert_eq!(div_mod_floor(-1, 2), (-1, 1));
|
||||
assert_eq!(div_mod_floor(-1, -2), (0, -1));
|
||||
}
|
||||
}
|
||||
33
zeroidc/vendor/chrono/src/format/locales.rs
vendored
Normal file
33
zeroidc/vendor/chrono/src/format/locales.rs
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
use pure_rust_locales::{locale_match, Locale};
|
||||
|
||||
pub(crate) fn short_months(locale: Locale) -> &'static [&'static str] {
|
||||
locale_match!(locale => LC_TIME::ABMON)
|
||||
}
|
||||
|
||||
pub(crate) fn long_months(locale: Locale) -> &'static [&'static str] {
|
||||
locale_match!(locale => LC_TIME::MON)
|
||||
}
|
||||
|
||||
pub(crate) fn short_weekdays(locale: Locale) -> &'static [&'static str] {
|
||||
locale_match!(locale => LC_TIME::ABDAY)
|
||||
}
|
||||
|
||||
pub(crate) fn long_weekdays(locale: Locale) -> &'static [&'static str] {
|
||||
locale_match!(locale => LC_TIME::DAY)
|
||||
}
|
||||
|
||||
pub(crate) fn am_pm(locale: Locale) -> &'static [&'static str] {
|
||||
locale_match!(locale => LC_TIME::AM_PM)
|
||||
}
|
||||
|
||||
pub(crate) fn d_fmt(locale: Locale) -> &'static str {
|
||||
locale_match!(locale => LC_TIME::D_FMT)
|
||||
}
|
||||
|
||||
pub(crate) fn d_t_fmt(locale: Locale) -> &'static str {
|
||||
locale_match!(locale => LC_TIME::D_T_FMT)
|
||||
}
|
||||
|
||||
pub(crate) fn t_fmt(locale: Locale) -> &'static str {
|
||||
locale_match!(locale => LC_TIME::T_FMT)
|
||||
}
|
||||
938
zeroidc/vendor/chrono/src/format/mod.rs
vendored
Normal file
938
zeroidc/vendor/chrono/src/format/mod.rs
vendored
Normal file
@@ -0,0 +1,938 @@
|
||||
// This is a part of Chrono.
|
||||
// See README.md and LICENSE.txt for details.
|
||||
|
||||
//! Formatting (and parsing) utilities for date and time.
|
||||
//!
|
||||
//! This module provides the common types and routines to implement,
|
||||
//! for example, [`DateTime::format`](../struct.DateTime.html#method.format) or
|
||||
//! [`DateTime::parse_from_str`](../struct.DateTime.html#method.parse_from_str) methods.
|
||||
//! For most cases you should use these high-level interfaces.
|
||||
//!
|
||||
//! Internally the formatting and parsing shares the same abstract **formatting items**,
|
||||
//! which are just an [`Iterator`](https://doc.rust-lang.org/std/iter/trait.Iterator.html) of
|
||||
//! the [`Item`](./enum.Item.html) type.
|
||||
//! They are generated from more readable **format strings**;
|
||||
//! currently Chrono supports [one built-in syntax closely resembling
|
||||
//! C's `strftime` format](./strftime/index.html).
|
||||
|
||||
#![allow(ellipsis_inclusive_range_patterns)]
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::boxed::Box;
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::string::{String, ToString};
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
use core::borrow::Borrow;
|
||||
use core::fmt;
|
||||
use core::str::FromStr;
|
||||
#[cfg(any(feature = "std", test))]
|
||||
use std::error::Error;
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
use naive::{NaiveDate, NaiveTime};
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
use offset::{FixedOffset, Offset};
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
use {Datelike, Timelike};
|
||||
use {Month, ParseMonthError, ParseWeekdayError, Weekday};
|
||||
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
pub(crate) mod locales;
|
||||
|
||||
pub use self::parse::parse;
|
||||
pub use self::parsed::Parsed;
|
||||
pub use self::strftime::StrftimeItems;
|
||||
/// L10n locales.
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
pub use pure_rust_locales::Locale;
|
||||
|
||||
#[cfg(not(feature = "unstable-locales"))]
|
||||
#[derive(Debug)]
|
||||
struct Locale;
|
||||
|
||||
/// An uninhabited type used for `InternalNumeric` and `InternalFixed` below.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
enum Void {}
|
||||
|
||||
/// Padding characters for numeric items.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub enum Pad {
|
||||
/// No padding.
|
||||
None,
|
||||
/// Zero (`0`) padding.
|
||||
Zero,
|
||||
/// Space padding.
|
||||
Space,
|
||||
}
|
||||
|
||||
/// Numeric item types.
|
||||
/// They have associated formatting width (FW) and parsing width (PW).
|
||||
///
|
||||
/// The **formatting width** is the minimal width to be formatted.
|
||||
/// If the number is too short, and the padding is not [`Pad::None`](./enum.Pad.html#variant.None),
|
||||
/// then it is left-padded.
|
||||
/// If the number is too long or (in some cases) negative, it is printed as is.
|
||||
///
|
||||
/// The **parsing width** is the maximal width to be scanned.
|
||||
/// The parser only tries to consume from one to given number of digits (greedily).
|
||||
/// It also trims the preceding whitespace if any.
|
||||
/// It cannot parse the negative number, so some date and time cannot be formatted then
|
||||
/// parsed with the same formatting items.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum Numeric {
|
||||
/// Full Gregorian year (FW=4, PW=∞).
|
||||
/// May accept years before 1 BCE or after 9999 CE, given an initial sign.
|
||||
Year,
|
||||
/// Gregorian year divided by 100 (century number; FW=PW=2). Implies the non-negative year.
|
||||
YearDiv100,
|
||||
/// Gregorian year modulo 100 (FW=PW=2). Cannot be negative.
|
||||
YearMod100,
|
||||
/// Year in the ISO week date (FW=4, PW=∞).
|
||||
/// May accept years before 1 BCE or after 9999 CE, given an initial sign.
|
||||
IsoYear,
|
||||
/// Year in the ISO week date, divided by 100 (FW=PW=2). Implies the non-negative year.
|
||||
IsoYearDiv100,
|
||||
/// Year in the ISO week date, modulo 100 (FW=PW=2). Cannot be negative.
|
||||
IsoYearMod100,
|
||||
/// Month (FW=PW=2).
|
||||
Month,
|
||||
/// Day of the month (FW=PW=2).
|
||||
Day,
|
||||
/// Week number, where the week 1 starts at the first Sunday of January (FW=PW=2).
|
||||
WeekFromSun,
|
||||
/// Week number, where the week 1 starts at the first Monday of January (FW=PW=2).
|
||||
WeekFromMon,
|
||||
/// Week number in the ISO week date (FW=PW=2).
|
||||
IsoWeek,
|
||||
/// Day of the week, where Sunday = 0 and Saturday = 6 (FW=PW=1).
|
||||
NumDaysFromSun,
|
||||
/// Day of the week, where Monday = 1 and Sunday = 7 (FW=PW=1).
|
||||
WeekdayFromMon,
|
||||
/// Day of the year (FW=PW=3).
|
||||
Ordinal,
|
||||
/// Hour number in the 24-hour clocks (FW=PW=2).
|
||||
Hour,
|
||||
/// Hour number in the 12-hour clocks (FW=PW=2).
|
||||
Hour12,
|
||||
/// The number of minutes since the last whole hour (FW=PW=2).
|
||||
Minute,
|
||||
/// The number of seconds since the last whole minute (FW=PW=2).
|
||||
Second,
|
||||
/// The number of nanoseconds since the last whole second (FW=PW=9).
|
||||
/// Note that this is *not* left-aligned;
|
||||
/// see also [`Fixed::Nanosecond`](./enum.Fixed.html#variant.Nanosecond).
|
||||
Nanosecond,
|
||||
/// The number of non-leap seconds since the midnight UTC on January 1, 1970 (FW=1, PW=∞).
|
||||
/// For formatting, it assumes UTC upon the absence of time zone offset.
|
||||
Timestamp,
|
||||
|
||||
/// Internal uses only.
|
||||
///
|
||||
/// This item exists so that one can add additional internal-only formatting
|
||||
/// without breaking major compatibility (as enum variants cannot be selectively private).
|
||||
Internal(InternalNumeric),
|
||||
}
|
||||
|
||||
/// An opaque type representing numeric item types for internal uses only.
|
||||
pub struct InternalNumeric {
|
||||
_dummy: Void,
|
||||
}
|
||||
|
||||
impl Clone for InternalNumeric {
|
||||
fn clone(&self) -> Self {
|
||||
match self._dummy {}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for InternalNumeric {
|
||||
fn eq(&self, _other: &InternalNumeric) -> bool {
|
||||
match self._dummy {}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for InternalNumeric {}
|
||||
|
||||
impl fmt::Debug for InternalNumeric {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "<InternalNumeric>")
|
||||
}
|
||||
}
|
||||
|
||||
/// Fixed-format item types.
|
||||
///
|
||||
/// They have their own rules of formatting and parsing.
|
||||
/// Otherwise noted, they print in the specified cases but parse case-insensitively.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum Fixed {
|
||||
/// Abbreviated month names.
|
||||
///
|
||||
/// Prints a three-letter-long name in the title case, reads the same name in any case.
|
||||
ShortMonthName,
|
||||
/// Full month names.
|
||||
///
|
||||
/// Prints a full name in the title case, reads either a short or full name in any case.
|
||||
LongMonthName,
|
||||
/// Abbreviated day of the week names.
|
||||
///
|
||||
/// Prints a three-letter-long name in the title case, reads the same name in any case.
|
||||
ShortWeekdayName,
|
||||
/// Full day of the week names.
|
||||
///
|
||||
/// Prints a full name in the title case, reads either a short or full name in any case.
|
||||
LongWeekdayName,
|
||||
/// AM/PM.
|
||||
///
|
||||
/// Prints in lower case, reads in any case.
|
||||
LowerAmPm,
|
||||
/// AM/PM.
|
||||
///
|
||||
/// Prints in upper case, reads in any case.
|
||||
UpperAmPm,
|
||||
/// An optional dot plus one or more digits for left-aligned nanoseconds.
|
||||
/// May print nothing, 3, 6 or 9 digits according to the available accuracy.
|
||||
/// See also [`Numeric::Nanosecond`](./enum.Numeric.html#variant.Nanosecond).
|
||||
Nanosecond,
|
||||
/// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 3.
|
||||
Nanosecond3,
|
||||
/// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 6.
|
||||
Nanosecond6,
|
||||
/// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 9.
|
||||
Nanosecond9,
|
||||
/// Timezone name.
|
||||
///
|
||||
/// It does not support parsing, its use in the parser is an immediate failure.
|
||||
TimezoneName,
|
||||
/// Offset from the local time to UTC (`+09:00` or `-04:00` or `+00:00`).
|
||||
///
|
||||
/// In the parser, the colon can be omitted and/or surrounded with any amount of whitespace.
|
||||
/// The offset is limited from `-24:00` to `+24:00`,
|
||||
/// which is the same as [`FixedOffset`](../offset/struct.FixedOffset.html)'s range.
|
||||
TimezoneOffsetColon,
|
||||
/// Offset from the local time to UTC (`+09:00` or `-04:00` or `Z`).
|
||||
///
|
||||
/// In the parser, the colon can be omitted and/or surrounded with any amount of whitespace,
|
||||
/// and `Z` can be either in upper case or in lower case.
|
||||
/// The offset is limited from `-24:00` to `+24:00`,
|
||||
/// which is the same as [`FixedOffset`](../offset/struct.FixedOffset.html)'s range.
|
||||
TimezoneOffsetColonZ,
|
||||
/// Same as [`TimezoneOffsetColon`](#variant.TimezoneOffsetColon) but prints no colon.
|
||||
/// Parsing allows an optional colon.
|
||||
TimezoneOffset,
|
||||
/// Same as [`TimezoneOffsetColonZ`](#variant.TimezoneOffsetColonZ) but prints no colon.
|
||||
/// Parsing allows an optional colon.
|
||||
TimezoneOffsetZ,
|
||||
/// RFC 2822 date and time syntax. Commonly used for email and MIME date and time.
|
||||
RFC2822,
|
||||
/// RFC 3339 & ISO 8601 date and time syntax.
|
||||
RFC3339,
|
||||
|
||||
/// Internal uses only.
|
||||
///
|
||||
/// This item exists so that one can add additional internal-only formatting
|
||||
/// without breaking major compatibility (as enum variants cannot be selectively private).
|
||||
Internal(InternalFixed),
|
||||
}
|
||||
|
||||
/// An opaque type representing fixed-format item types for internal uses only.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct InternalFixed {
|
||||
val: InternalInternal,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
enum InternalInternal {
|
||||
/// Same as [`TimezoneOffsetColonZ`](#variant.TimezoneOffsetColonZ), but
|
||||
/// allows missing minutes (per [ISO 8601][iso8601]).
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If you try to use this for printing.
|
||||
///
|
||||
/// [iso8601]: https://en.wikipedia.org/wiki/ISO_8601#Time_offsets_from_UTC
|
||||
TimezoneOffsetPermissive,
|
||||
/// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 3 and there is no leading dot.
|
||||
Nanosecond3NoDot,
|
||||
/// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 6 and there is no leading dot.
|
||||
Nanosecond6NoDot,
|
||||
/// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 9 and there is no leading dot.
|
||||
Nanosecond9NoDot,
|
||||
}
|
||||
|
||||
/// A single formatting item. This is used for both formatting and parsing.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum Item<'a> {
|
||||
/// A literally printed and parsed text.
|
||||
Literal(&'a str),
|
||||
/// Same as `Literal` but with the string owned by the item.
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
OwnedLiteral(Box<str>),
|
||||
/// Whitespace. Prints literally but reads zero or more whitespace.
|
||||
Space(&'a str),
|
||||
/// Same as `Space` but with the string owned by the item.
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
OwnedSpace(Box<str>),
|
||||
/// Numeric item. Can be optionally padded to the maximal length (if any) when formatting;
|
||||
/// the parser simply ignores any padded whitespace and zeroes.
|
||||
Numeric(Numeric, Pad),
|
||||
/// Fixed-format item.
|
||||
Fixed(Fixed),
|
||||
/// Issues a formatting error. Used to signal an invalid format string.
|
||||
Error,
|
||||
}
|
||||
|
||||
macro_rules! lit {
|
||||
($x:expr) => {
|
||||
Item::Literal($x)
|
||||
};
|
||||
}
|
||||
macro_rules! sp {
|
||||
($x:expr) => {
|
||||
Item::Space($x)
|
||||
};
|
||||
}
|
||||
macro_rules! num {
|
||||
($x:ident) => {
|
||||
Item::Numeric(Numeric::$x, Pad::None)
|
||||
};
|
||||
}
|
||||
macro_rules! num0 {
|
||||
($x:ident) => {
|
||||
Item::Numeric(Numeric::$x, Pad::Zero)
|
||||
};
|
||||
}
|
||||
macro_rules! nums {
|
||||
($x:ident) => {
|
||||
Item::Numeric(Numeric::$x, Pad::Space)
|
||||
};
|
||||
}
|
||||
macro_rules! fix {
|
||||
($x:ident) => {
|
||||
Item::Fixed(Fixed::$x)
|
||||
};
|
||||
}
|
||||
macro_rules! internal_fix {
|
||||
($x:ident) => {
|
||||
Item::Fixed(Fixed::Internal(InternalFixed { val: InternalInternal::$x }))
|
||||
};
|
||||
}
|
||||
|
||||
/// An error from the `parse` function.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||
pub struct ParseError(ParseErrorKind);
|
||||
|
||||
/// The category of parse error
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||
enum ParseErrorKind {
|
||||
/// Given field is out of permitted range.
|
||||
OutOfRange,
|
||||
|
||||
/// There is no possible date and time value with given set of fields.
|
||||
///
|
||||
/// This does not include the out-of-range conditions, which are trivially invalid.
|
||||
/// It includes the case that there are one or more fields that are inconsistent to each other.
|
||||
Impossible,
|
||||
|
||||
/// Given set of fields is not enough to make a requested date and time value.
|
||||
///
|
||||
/// Note that there *may* be a case that given fields constrain the possible values so much
|
||||
/// that there is a unique possible value. Chrono only tries to be correct for
|
||||
/// most useful sets of fields however, as such constraint solving can be expensive.
|
||||
NotEnough,
|
||||
|
||||
/// The input string has some invalid character sequence for given formatting items.
|
||||
Invalid,
|
||||
|
||||
/// The input string has been prematurely ended.
|
||||
TooShort,
|
||||
|
||||
/// All formatting items have been read but there is a remaining input.
|
||||
TooLong,
|
||||
|
||||
/// There was an error on the formatting string, or there were non-supported formating items.
|
||||
BadFormat,
|
||||
}
|
||||
|
||||
/// Same as `Result<T, ParseError>`.
|
||||
pub type ParseResult<T> = Result<T, ParseError>;
|
||||
|
||||
impl fmt::Display for ParseError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.0 {
|
||||
ParseErrorKind::OutOfRange => write!(f, "input is out of range"),
|
||||
ParseErrorKind::Impossible => write!(f, "no possible date and time matching input"),
|
||||
ParseErrorKind::NotEnough => write!(f, "input is not enough for unique date and time"),
|
||||
ParseErrorKind::Invalid => write!(f, "input contains invalid characters"),
|
||||
ParseErrorKind::TooShort => write!(f, "premature end of input"),
|
||||
ParseErrorKind::TooLong => write!(f, "trailing input"),
|
||||
ParseErrorKind::BadFormat => write!(f, "bad or unsupported format string"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
impl Error for ParseError {
|
||||
#[allow(deprecated)]
|
||||
fn description(&self) -> &str {
|
||||
"parser error, see to_string() for details"
|
||||
}
|
||||
}
|
||||
|
||||
// to be used in this module and submodules
|
||||
const OUT_OF_RANGE: ParseError = ParseError(ParseErrorKind::OutOfRange);
|
||||
const IMPOSSIBLE: ParseError = ParseError(ParseErrorKind::Impossible);
|
||||
const NOT_ENOUGH: ParseError = ParseError(ParseErrorKind::NotEnough);
|
||||
const INVALID: ParseError = ParseError(ParseErrorKind::Invalid);
|
||||
const TOO_SHORT: ParseError = ParseError(ParseErrorKind::TooShort);
|
||||
const TOO_LONG: ParseError = ParseError(ParseErrorKind::TooLong);
|
||||
const BAD_FORMAT: ParseError = ParseError(ParseErrorKind::BadFormat);
|
||||
|
||||
/// Formats single formatting item
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
pub fn format_item<'a>(
|
||||
w: &mut fmt::Formatter,
|
||||
date: Option<&NaiveDate>,
|
||||
time: Option<&NaiveTime>,
|
||||
off: Option<&(String, FixedOffset)>,
|
||||
item: &Item<'a>,
|
||||
) -> fmt::Result {
|
||||
let mut result = String::new();
|
||||
format_inner(&mut result, date, time, off, item, None)?;
|
||||
w.pad(&result)
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
fn format_inner<'a>(
|
||||
result: &mut String,
|
||||
date: Option<&NaiveDate>,
|
||||
time: Option<&NaiveTime>,
|
||||
off: Option<&(String, FixedOffset)>,
|
||||
item: &Item<'a>,
|
||||
_locale: Option<Locale>,
|
||||
) -> fmt::Result {
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
let (short_months, long_months, short_weekdays, long_weekdays, am_pm, am_pm_lowercase) = {
|
||||
let locale = _locale.unwrap_or(Locale::POSIX);
|
||||
let am_pm = locales::am_pm(locale);
|
||||
(
|
||||
locales::short_months(locale),
|
||||
locales::long_months(locale),
|
||||
locales::short_weekdays(locale),
|
||||
locales::long_weekdays(locale),
|
||||
am_pm,
|
||||
&[am_pm[0].to_lowercase(), am_pm[1].to_lowercase()],
|
||||
)
|
||||
};
|
||||
#[cfg(not(feature = "unstable-locales"))]
|
||||
let (short_months, long_months, short_weekdays, long_weekdays, am_pm, am_pm_lowercase) = {
|
||||
(
|
||||
&["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
|
||||
&[
|
||||
"January",
|
||||
"February",
|
||||
"March",
|
||||
"April",
|
||||
"May",
|
||||
"June",
|
||||
"July",
|
||||
"August",
|
||||
"September",
|
||||
"October",
|
||||
"November",
|
||||
"December",
|
||||
],
|
||||
&["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
|
||||
&["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
|
||||
&["AM", "PM"],
|
||||
&["am", "pm"],
|
||||
)
|
||||
};
|
||||
|
||||
use core::fmt::Write;
|
||||
use div::{div_floor, mod_floor};
|
||||
|
||||
match *item {
|
||||
Item::Literal(s) | Item::Space(s) => result.push_str(s),
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
Item::OwnedLiteral(ref s) | Item::OwnedSpace(ref s) => result.push_str(s),
|
||||
|
||||
Item::Numeric(ref spec, ref pad) => {
|
||||
use self::Numeric::*;
|
||||
|
||||
let week_from_sun = |d: &NaiveDate| {
|
||||
(d.ordinal() as i32 - d.weekday().num_days_from_sunday() as i32 + 7) / 7
|
||||
};
|
||||
let week_from_mon = |d: &NaiveDate| {
|
||||
(d.ordinal() as i32 - d.weekday().num_days_from_monday() as i32 + 7) / 7
|
||||
};
|
||||
|
||||
let (width, v) = match *spec {
|
||||
Year => (4, date.map(|d| i64::from(d.year()))),
|
||||
YearDiv100 => (2, date.map(|d| div_floor(i64::from(d.year()), 100))),
|
||||
YearMod100 => (2, date.map(|d| mod_floor(i64::from(d.year()), 100))),
|
||||
IsoYear => (4, date.map(|d| i64::from(d.iso_week().year()))),
|
||||
IsoYearDiv100 => (2, date.map(|d| div_floor(i64::from(d.iso_week().year()), 100))),
|
||||
IsoYearMod100 => (2, date.map(|d| mod_floor(i64::from(d.iso_week().year()), 100))),
|
||||
Month => (2, date.map(|d| i64::from(d.month()))),
|
||||
Day => (2, date.map(|d| i64::from(d.day()))),
|
||||
WeekFromSun => (2, date.map(|d| i64::from(week_from_sun(d)))),
|
||||
WeekFromMon => (2, date.map(|d| i64::from(week_from_mon(d)))),
|
||||
IsoWeek => (2, date.map(|d| i64::from(d.iso_week().week()))),
|
||||
NumDaysFromSun => (1, date.map(|d| i64::from(d.weekday().num_days_from_sunday()))),
|
||||
WeekdayFromMon => (1, date.map(|d| i64::from(d.weekday().number_from_monday()))),
|
||||
Ordinal => (3, date.map(|d| i64::from(d.ordinal()))),
|
||||
Hour => (2, time.map(|t| i64::from(t.hour()))),
|
||||
Hour12 => (2, time.map(|t| i64::from(t.hour12().1))),
|
||||
Minute => (2, time.map(|t| i64::from(t.minute()))),
|
||||
Second => (2, time.map(|t| i64::from(t.second() + t.nanosecond() / 1_000_000_000))),
|
||||
Nanosecond => (9, time.map(|t| i64::from(t.nanosecond() % 1_000_000_000))),
|
||||
Timestamp => (
|
||||
1,
|
||||
match (date, time, off) {
|
||||
(Some(d), Some(t), None) => Some(d.and_time(*t).timestamp()),
|
||||
(Some(d), Some(t), Some(&(_, off))) => {
|
||||
Some((d.and_time(*t) - off).timestamp())
|
||||
}
|
||||
(_, _, _) => None,
|
||||
},
|
||||
),
|
||||
|
||||
// for the future expansion
|
||||
Internal(ref int) => match int._dummy {},
|
||||
};
|
||||
|
||||
if let Some(v) = v {
|
||||
if (spec == &Year || spec == &IsoYear) && !(0 <= v && v < 10_000) {
|
||||
// non-four-digit years require an explicit sign as per ISO 8601
|
||||
match *pad {
|
||||
Pad::None => write!(result, "{:+}", v),
|
||||
Pad::Zero => write!(result, "{:+01$}", v, width + 1),
|
||||
Pad::Space => write!(result, "{:+1$}", v, width + 1),
|
||||
}
|
||||
} else {
|
||||
match *pad {
|
||||
Pad::None => write!(result, "{}", v),
|
||||
Pad::Zero => write!(result, "{:01$}", v, width),
|
||||
Pad::Space => write!(result, "{:1$}", v, width),
|
||||
}
|
||||
}?
|
||||
} else {
|
||||
return Err(fmt::Error); // insufficient arguments for given format
|
||||
}
|
||||
}
|
||||
|
||||
Item::Fixed(ref spec) => {
|
||||
use self::Fixed::*;
|
||||
|
||||
/// Prints an offset from UTC in the format of `+HHMM` or `+HH:MM`.
|
||||
/// `Z` instead of `+00[:]00` is allowed when `allow_zulu` is true.
|
||||
fn write_local_minus_utc(
|
||||
result: &mut String,
|
||||
off: FixedOffset,
|
||||
allow_zulu: bool,
|
||||
use_colon: bool,
|
||||
) -> fmt::Result {
|
||||
let off = off.local_minus_utc();
|
||||
if !allow_zulu || off != 0 {
|
||||
let (sign, off) = if off < 0 { ('-', -off) } else { ('+', off) };
|
||||
if use_colon {
|
||||
write!(result, "{}{:02}:{:02}", sign, off / 3600, off / 60 % 60)
|
||||
} else {
|
||||
write!(result, "{}{:02}{:02}", sign, off / 3600, off / 60 % 60)
|
||||
}
|
||||
} else {
|
||||
result.push_str("Z");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
let ret =
|
||||
match *spec {
|
||||
ShortMonthName => date.map(|d| {
|
||||
result.push_str(short_months[d.month0() as usize]);
|
||||
Ok(())
|
||||
}),
|
||||
LongMonthName => date.map(|d| {
|
||||
result.push_str(long_months[d.month0() as usize]);
|
||||
Ok(())
|
||||
}),
|
||||
ShortWeekdayName => date.map(|d| {
|
||||
result
|
||||
.push_str(short_weekdays[d.weekday().num_days_from_sunday() as usize]);
|
||||
Ok(())
|
||||
}),
|
||||
LongWeekdayName => date.map(|d| {
|
||||
result.push_str(long_weekdays[d.weekday().num_days_from_sunday() as usize]);
|
||||
Ok(())
|
||||
}),
|
||||
LowerAmPm => time.map(|t| {
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(useless_asref))]
|
||||
{
|
||||
result.push_str(if t.hour12().0 {
|
||||
am_pm_lowercase[1].as_ref()
|
||||
} else {
|
||||
am_pm_lowercase[0].as_ref()
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}),
|
||||
UpperAmPm => time.map(|t| {
|
||||
result.push_str(if t.hour12().0 { am_pm[1] } else { am_pm[0] });
|
||||
Ok(())
|
||||
}),
|
||||
Nanosecond => time.map(|t| {
|
||||
let nano = t.nanosecond() % 1_000_000_000;
|
||||
if nano == 0 {
|
||||
Ok(())
|
||||
} else if nano % 1_000_000 == 0 {
|
||||
write!(result, ".{:03}", nano / 1_000_000)
|
||||
} else if nano % 1_000 == 0 {
|
||||
write!(result, ".{:06}", nano / 1_000)
|
||||
} else {
|
||||
write!(result, ".{:09}", nano)
|
||||
}
|
||||
}),
|
||||
Nanosecond3 => time.map(|t| {
|
||||
let nano = t.nanosecond() % 1_000_000_000;
|
||||
write!(result, ".{:03}", nano / 1_000_000)
|
||||
}),
|
||||
Nanosecond6 => time.map(|t| {
|
||||
let nano = t.nanosecond() % 1_000_000_000;
|
||||
write!(result, ".{:06}", nano / 1_000)
|
||||
}),
|
||||
Nanosecond9 => time.map(|t| {
|
||||
let nano = t.nanosecond() % 1_000_000_000;
|
||||
write!(result, ".{:09}", nano)
|
||||
}),
|
||||
Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot }) => time
|
||||
.map(|t| {
|
||||
let nano = t.nanosecond() % 1_000_000_000;
|
||||
write!(result, "{:03}", nano / 1_000_000)
|
||||
}),
|
||||
Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot }) => time
|
||||
.map(|t| {
|
||||
let nano = t.nanosecond() % 1_000_000_000;
|
||||
write!(result, "{:06}", nano / 1_000)
|
||||
}),
|
||||
Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot }) => time
|
||||
.map(|t| {
|
||||
let nano = t.nanosecond() % 1_000_000_000;
|
||||
write!(result, "{:09}", nano)
|
||||
}),
|
||||
TimezoneName => off.map(|&(ref name, _)| {
|
||||
result.push_str(name);
|
||||
Ok(())
|
||||
}),
|
||||
TimezoneOffsetColon => {
|
||||
off.map(|&(_, off)| write_local_minus_utc(result, off, false, true))
|
||||
}
|
||||
TimezoneOffsetColonZ => {
|
||||
off.map(|&(_, off)| write_local_minus_utc(result, off, true, true))
|
||||
}
|
||||
TimezoneOffset => {
|
||||
off.map(|&(_, off)| write_local_minus_utc(result, off, false, false))
|
||||
}
|
||||
TimezoneOffsetZ => {
|
||||
off.map(|&(_, off)| write_local_minus_utc(result, off, true, false))
|
||||
}
|
||||
Internal(InternalFixed { val: InternalInternal::TimezoneOffsetPermissive }) => {
|
||||
panic!("Do not try to write %#z it is undefined")
|
||||
}
|
||||
RFC2822 =>
|
||||
// same as `%a, %d %b %Y %H:%M:%S %z`
|
||||
{
|
||||
if let (Some(d), Some(t), Some(&(_, off))) = (date, time, off) {
|
||||
let sec = t.second() + t.nanosecond() / 1_000_000_000;
|
||||
write!(
|
||||
result,
|
||||
"{}, {:02} {} {:04} {:02}:{:02}:{:02} ",
|
||||
short_weekdays[d.weekday().num_days_from_sunday() as usize],
|
||||
d.day(),
|
||||
short_months[d.month0() as usize],
|
||||
d.year(),
|
||||
t.hour(),
|
||||
t.minute(),
|
||||
sec
|
||||
)?;
|
||||
Some(write_local_minus_utc(result, off, false, false))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
RFC3339 =>
|
||||
// same as `%Y-%m-%dT%H:%M:%S%.f%:z`
|
||||
{
|
||||
if let (Some(d), Some(t), Some(&(_, off))) = (date, time, off) {
|
||||
// reuse `Debug` impls which already print ISO 8601 format.
|
||||
// this is faster in this way.
|
||||
write!(result, "{:?}T{:?}", d, t)?;
|
||||
Some(write_local_minus_utc(result, off, false, true))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match ret {
|
||||
Some(ret) => ret?,
|
||||
None => return Err(fmt::Error), // insufficient arguments for given format
|
||||
}
|
||||
}
|
||||
|
||||
Item::Error => return Err(fmt::Error),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Tries to format given arguments with given formatting items.
|
||||
/// Internally used by `DelayedFormat`.
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
pub fn format<'a, I, B>(
|
||||
w: &mut fmt::Formatter,
|
||||
date: Option<&NaiveDate>,
|
||||
time: Option<&NaiveTime>,
|
||||
off: Option<&(String, FixedOffset)>,
|
||||
items: I,
|
||||
) -> fmt::Result
|
||||
where
|
||||
I: Iterator<Item = B> + Clone,
|
||||
B: Borrow<Item<'a>>,
|
||||
{
|
||||
let mut result = String::new();
|
||||
for item in items {
|
||||
format_inner(&mut result, date, time, off, item.borrow(), None)?;
|
||||
}
|
||||
w.pad(&result)
|
||||
}
|
||||
|
||||
mod parsed;
|
||||
|
||||
// due to the size of parsing routines, they are in separate modules.
|
||||
mod parse;
|
||||
mod scan;
|
||||
|
||||
pub mod strftime;
|
||||
|
||||
/// A *temporary* object which can be used as an argument to `format!` or others.
|
||||
/// This is normally constructed via `format` methods of each date and time type.
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
#[derive(Debug)]
|
||||
pub struct DelayedFormat<I> {
|
||||
/// The date view, if any.
|
||||
date: Option<NaiveDate>,
|
||||
/// The time view, if any.
|
||||
time: Option<NaiveTime>,
|
||||
/// The name and local-to-UTC difference for the offset (timezone), if any.
|
||||
off: Option<(String, FixedOffset)>,
|
||||
/// An iterator returning formatting items.
|
||||
items: I,
|
||||
/// Locale used for text.
|
||||
locale: Option<Locale>,
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
impl<'a, I: Iterator<Item = B> + Clone, B: Borrow<Item<'a>>> DelayedFormat<I> {
|
||||
/// Makes a new `DelayedFormat` value out of local date and time.
|
||||
pub fn new(date: Option<NaiveDate>, time: Option<NaiveTime>, items: I) -> DelayedFormat<I> {
|
||||
DelayedFormat { date: date, time: time, off: None, items: items, locale: None }
|
||||
}
|
||||
|
||||
/// Makes a new `DelayedFormat` value out of local date and time and UTC offset.
|
||||
pub fn new_with_offset<Off>(
|
||||
date: Option<NaiveDate>,
|
||||
time: Option<NaiveTime>,
|
||||
offset: &Off,
|
||||
items: I,
|
||||
) -> DelayedFormat<I>
|
||||
where
|
||||
Off: Offset + fmt::Display,
|
||||
{
|
||||
let name_and_diff = (offset.to_string(), offset.fix());
|
||||
DelayedFormat {
|
||||
date: date,
|
||||
time: time,
|
||||
off: Some(name_and_diff),
|
||||
items: items,
|
||||
locale: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a new `DelayedFormat` value out of local date and time and locale.
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
pub fn new_with_locale(
|
||||
date: Option<NaiveDate>,
|
||||
time: Option<NaiveTime>,
|
||||
items: I,
|
||||
locale: Locale,
|
||||
) -> DelayedFormat<I> {
|
||||
DelayedFormat { date: date, time: time, off: None, items: items, locale: Some(locale) }
|
||||
}
|
||||
|
||||
/// Makes a new `DelayedFormat` value out of local date and time, UTC offset and locale.
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
pub fn new_with_offset_and_locale<Off>(
|
||||
date: Option<NaiveDate>,
|
||||
time: Option<NaiveTime>,
|
||||
offset: &Off,
|
||||
items: I,
|
||||
locale: Locale,
|
||||
) -> DelayedFormat<I>
|
||||
where
|
||||
Off: Offset + fmt::Display,
|
||||
{
|
||||
let name_and_diff = (offset.to_string(), offset.fix());
|
||||
DelayedFormat {
|
||||
date: date,
|
||||
time: time,
|
||||
off: Some(name_and_diff),
|
||||
items: items,
|
||||
locale: Some(locale),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
impl<'a, I: Iterator<Item = B> + Clone, B: Borrow<Item<'a>>> fmt::Display for DelayedFormat<I> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
{
|
||||
if let Some(locale) = self.locale {
|
||||
return format_localized(
|
||||
f,
|
||||
self.date.as_ref(),
|
||||
self.time.as_ref(),
|
||||
self.off.as_ref(),
|
||||
self.items.clone(),
|
||||
locale,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
format(f, self.date.as_ref(), self.time.as_ref(), self.off.as_ref(), self.items.clone())
|
||||
}
|
||||
}
|
||||
|
||||
// this implementation is here only because we need some private code from `scan`
|
||||
|
||||
/// Parsing a `str` into a `Weekday` uses the format [`%W`](./format/strftime/index.html).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::Weekday;
|
||||
///
|
||||
/// assert_eq!("Sunday".parse::<Weekday>(), Ok(Weekday::Sun));
|
||||
/// assert!("any day".parse::<Weekday>().is_err());
|
||||
/// ~~~~
|
||||
///
|
||||
/// The parsing is case-insensitive.
|
||||
///
|
||||
/// ~~~~
|
||||
/// # use chrono::Weekday;
|
||||
/// assert_eq!("mON".parse::<Weekday>(), Ok(Weekday::Mon));
|
||||
/// ~~~~
|
||||
///
|
||||
/// Only the shortest form (e.g. `sun`) and the longest form (e.g. `sunday`) is accepted.
|
||||
///
|
||||
/// ~~~~
|
||||
/// # use chrono::Weekday;
|
||||
/// assert!("thurs".parse::<Weekday>().is_err());
|
||||
/// ~~~~
|
||||
impl FromStr for Weekday {
|
||||
type Err = ParseWeekdayError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if let Ok(("", w)) = scan::short_or_long_weekday(s) {
|
||||
Ok(w)
|
||||
} else {
|
||||
Err(ParseWeekdayError { _dummy: () })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Formats single formatting item
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
pub fn format_item_localized<'a>(
|
||||
w: &mut fmt::Formatter,
|
||||
date: Option<&NaiveDate>,
|
||||
time: Option<&NaiveTime>,
|
||||
off: Option<&(String, FixedOffset)>,
|
||||
item: &Item<'a>,
|
||||
locale: Locale,
|
||||
) -> fmt::Result {
|
||||
let mut result = String::new();
|
||||
format_inner(&mut result, date, time, off, item, Some(locale))?;
|
||||
w.pad(&result)
|
||||
}
|
||||
|
||||
/// Tries to format given arguments with given formatting items.
|
||||
/// Internally used by `DelayedFormat`.
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
pub fn format_localized<'a, I, B>(
|
||||
w: &mut fmt::Formatter,
|
||||
date: Option<&NaiveDate>,
|
||||
time: Option<&NaiveTime>,
|
||||
off: Option<&(String, FixedOffset)>,
|
||||
items: I,
|
||||
locale: Locale,
|
||||
) -> fmt::Result
|
||||
where
|
||||
I: Iterator<Item = B> + Clone,
|
||||
B: Borrow<Item<'a>>,
|
||||
{
|
||||
let mut result = String::new();
|
||||
for item in items {
|
||||
format_inner(&mut result, date, time, off, item.borrow(), Some(locale))?;
|
||||
}
|
||||
w.pad(&result)
|
||||
}
|
||||
|
||||
/// Parsing a `str` into a `Month` uses the format [`%W`](./format/strftime/index.html).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::Month;
|
||||
///
|
||||
/// assert_eq!("January".parse::<Month>(), Ok(Month::January));
|
||||
/// assert!("any day".parse::<Month>().is_err());
|
||||
/// ~~~~
|
||||
///
|
||||
/// The parsing is case-insensitive.
|
||||
///
|
||||
/// ~~~~
|
||||
/// # use chrono::Month;
|
||||
/// assert_eq!("fEbruARy".parse::<Month>(), Ok(Month::February));
|
||||
/// ~~~~
|
||||
///
|
||||
/// Only the shortest form (e.g. `jan`) and the longest form (e.g. `january`) is accepted.
|
||||
///
|
||||
/// ~~~~
|
||||
/// # use chrono::Month;
|
||||
/// assert!("septem".parse::<Month>().is_err());
|
||||
/// assert!("Augustin".parse::<Month>().is_err());
|
||||
/// ~~~~
|
||||
impl FromStr for Month {
|
||||
type Err = ParseMonthError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if let Ok(("", w)) = scan::short_or_long_month0(s) {
|
||||
match w {
|
||||
0 => Ok(Month::January),
|
||||
1 => Ok(Month::February),
|
||||
2 => Ok(Month::March),
|
||||
3 => Ok(Month::April),
|
||||
4 => Ok(Month::May),
|
||||
5 => Ok(Month::June),
|
||||
6 => Ok(Month::July),
|
||||
7 => Ok(Month::August),
|
||||
8 => Ok(Month::September),
|
||||
9 => Ok(Month::October),
|
||||
10 => Ok(Month::November),
|
||||
11 => Ok(Month::December),
|
||||
_ => Err(ParseMonthError { _dummy: () }),
|
||||
}
|
||||
} else {
|
||||
Err(ParseMonthError { _dummy: () })
|
||||
}
|
||||
}
|
||||
}
|
||||
934
zeroidc/vendor/chrono/src/format/parse.rs
vendored
Normal file
934
zeroidc/vendor/chrono/src/format/parse.rs
vendored
Normal file
@@ -0,0 +1,934 @@
|
||||
// This is a part of Chrono.
|
||||
// Portions copyright (c) 2015, John Nagle.
|
||||
// See README.md and LICENSE.txt for details.
|
||||
|
||||
//! Date and time parsing routines.
|
||||
|
||||
#![allow(deprecated)]
|
||||
|
||||
use core::borrow::Borrow;
|
||||
use core::str;
|
||||
use core::usize;
|
||||
|
||||
use super::scan;
|
||||
use super::{Fixed, InternalFixed, InternalInternal, Item, Numeric, Pad, Parsed};
|
||||
use super::{ParseError, ParseErrorKind, ParseResult};
|
||||
use super::{BAD_FORMAT, INVALID, NOT_ENOUGH, OUT_OF_RANGE, TOO_LONG, TOO_SHORT};
|
||||
use {DateTime, FixedOffset, Weekday};
|
||||
|
||||
fn set_weekday_with_num_days_from_sunday(p: &mut Parsed, v: i64) -> ParseResult<()> {
|
||||
p.set_weekday(match v {
|
||||
0 => Weekday::Sun,
|
||||
1 => Weekday::Mon,
|
||||
2 => Weekday::Tue,
|
||||
3 => Weekday::Wed,
|
||||
4 => Weekday::Thu,
|
||||
5 => Weekday::Fri,
|
||||
6 => Weekday::Sat,
|
||||
_ => return Err(OUT_OF_RANGE),
|
||||
})
|
||||
}
|
||||
|
||||
fn set_weekday_with_number_from_monday(p: &mut Parsed, v: i64) -> ParseResult<()> {
|
||||
p.set_weekday(match v {
|
||||
1 => Weekday::Mon,
|
||||
2 => Weekday::Tue,
|
||||
3 => Weekday::Wed,
|
||||
4 => Weekday::Thu,
|
||||
5 => Weekday::Fri,
|
||||
6 => Weekday::Sat,
|
||||
7 => Weekday::Sun,
|
||||
_ => return Err(OUT_OF_RANGE),
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_rfc2822<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a str, ())> {
|
||||
macro_rules! try_consume {
|
||||
($e:expr) => {{
|
||||
let (s_, v) = $e?;
|
||||
s = s_;
|
||||
v
|
||||
}};
|
||||
}
|
||||
|
||||
// an adapted RFC 2822 syntax from Section 3.3 and 4.3:
|
||||
//
|
||||
// date-time = [ day-of-week "," ] date 1*S time *S
|
||||
// day-of-week = *S day-name *S
|
||||
// day-name = "Mon" / "Tue" / "Wed" / "Thu" / "Fri" / "Sat" / "Sun"
|
||||
// date = day month year
|
||||
// day = *S 1*2DIGIT *S
|
||||
// month = 1*S month-name 1*S
|
||||
// month-name = "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" /
|
||||
// "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec"
|
||||
// year = *S 2*DIGIT *S
|
||||
// time = time-of-day 1*S zone
|
||||
// time-of-day = hour ":" minute [ ":" second ]
|
||||
// hour = *S 2DIGIT *S
|
||||
// minute = *S 2DIGIT *S
|
||||
// second = *S 2DIGIT *S
|
||||
// zone = ( "+" / "-" ) 4DIGIT /
|
||||
// "UT" / "GMT" / ; same as +0000
|
||||
// "EST" / "CST" / "MST" / "PST" / ; same as -0500 to -0800
|
||||
// "EDT" / "CDT" / "MDT" / "PDT" / ; same as -0400 to -0700
|
||||
// 1*(%d65-90 / %d97-122) ; same as -0000
|
||||
//
|
||||
// some notes:
|
||||
//
|
||||
// - quoted characters can be in any mixture of lower and upper cases.
|
||||
//
|
||||
// - we do not recognize a folding white space (FWS) or comment (CFWS).
|
||||
// for our purposes, instead, we accept any sequence of Unicode
|
||||
// white space characters (denoted here to `S`). any actual RFC 2822
|
||||
// parser is expected to parse FWS and/or CFWS themselves and replace
|
||||
// it with a single SP (`%x20`); this is legitimate.
|
||||
//
|
||||
// - two-digit year < 50 should be interpreted by adding 2000.
|
||||
// two-digit year >= 50 or three-digit year should be interpreted
|
||||
// by adding 1900. note that four-or-more-digit years less than 1000
|
||||
// are *never* affected by this rule.
|
||||
//
|
||||
// - mismatching day-of-week is always an error, which is consistent to
|
||||
// Chrono's own rules.
|
||||
//
|
||||
// - zones can range from `-9959` to `+9959`, but `FixedOffset` does not
|
||||
// support offsets larger than 24 hours. this is not *that* problematic
|
||||
// since we do not directly go to a `DateTime` so one can recover
|
||||
// the offset information from `Parsed` anyway.
|
||||
|
||||
s = s.trim_left();
|
||||
|
||||
if let Ok((s_, weekday)) = scan::short_weekday(s) {
|
||||
if !s_.starts_with(',') {
|
||||
return Err(INVALID);
|
||||
}
|
||||
s = &s_[1..];
|
||||
parsed.set_weekday(weekday)?;
|
||||
}
|
||||
|
||||
s = s.trim_left();
|
||||
parsed.set_day(try_consume!(scan::number(s, 1, 2)))?;
|
||||
s = scan::space(s)?; // mandatory
|
||||
parsed.set_month(1 + i64::from(try_consume!(scan::short_month0(s))))?;
|
||||
s = scan::space(s)?; // mandatory
|
||||
|
||||
// distinguish two- and three-digit years from four-digit years
|
||||
let prevlen = s.len();
|
||||
let mut year = try_consume!(scan::number(s, 2, usize::MAX));
|
||||
let yearlen = prevlen - s.len();
|
||||
match (yearlen, year) {
|
||||
(2, 0...49) => {
|
||||
year += 2000;
|
||||
} // 47 -> 2047, 05 -> 2005
|
||||
(2, 50...99) => {
|
||||
year += 1900;
|
||||
} // 79 -> 1979
|
||||
(3, _) => {
|
||||
year += 1900;
|
||||
} // 112 -> 2012, 009 -> 1909
|
||||
(_, _) => {} // 1987 -> 1987, 0654 -> 0654
|
||||
}
|
||||
parsed.set_year(year)?;
|
||||
|
||||
s = scan::space(s)?; // mandatory
|
||||
parsed.set_hour(try_consume!(scan::number(s, 2, 2)))?;
|
||||
s = scan::char(s.trim_left(), b':')?.trim_left(); // *S ":" *S
|
||||
parsed.set_minute(try_consume!(scan::number(s, 2, 2)))?;
|
||||
if let Ok(s_) = scan::char(s.trim_left(), b':') {
|
||||
// [ ":" *S 2DIGIT ]
|
||||
parsed.set_second(try_consume!(scan::number(s_, 2, 2)))?;
|
||||
}
|
||||
|
||||
s = scan::space(s)?; // mandatory
|
||||
if let Some(offset) = try_consume!(scan::timezone_offset_2822(s)) {
|
||||
// only set the offset when it is definitely known (i.e. not `-0000`)
|
||||
parsed.set_offset(i64::from(offset))?;
|
||||
}
|
||||
|
||||
Ok((s, ()))
|
||||
}
|
||||
|
||||
fn parse_rfc3339<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a str, ())> {
|
||||
macro_rules! try_consume {
|
||||
($e:expr) => {{
|
||||
let (s_, v) = $e?;
|
||||
s = s_;
|
||||
v
|
||||
}};
|
||||
}
|
||||
|
||||
// an adapted RFC 3339 syntax from Section 5.6:
|
||||
//
|
||||
// date-fullyear = 4DIGIT
|
||||
// date-month = 2DIGIT ; 01-12
|
||||
// date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on month/year
|
||||
// time-hour = 2DIGIT ; 00-23
|
||||
// time-minute = 2DIGIT ; 00-59
|
||||
// time-second = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second rules
|
||||
// time-secfrac = "." 1*DIGIT
|
||||
// time-numoffset = ("+" / "-") time-hour ":" time-minute
|
||||
// time-offset = "Z" / time-numoffset
|
||||
// partial-time = time-hour ":" time-minute ":" time-second [time-secfrac]
|
||||
// full-date = date-fullyear "-" date-month "-" date-mday
|
||||
// full-time = partial-time time-offset
|
||||
// date-time = full-date "T" full-time
|
||||
//
|
||||
// some notes:
|
||||
//
|
||||
// - quoted characters can be in any mixture of lower and upper cases.
|
||||
//
|
||||
// - it may accept any number of fractional digits for seconds.
|
||||
// for Chrono, this means that we should skip digits past first 9 digits.
|
||||
//
|
||||
// - unlike RFC 2822, the valid offset ranges from -23:59 to +23:59.
|
||||
// note that this restriction is unique to RFC 3339 and not ISO 8601.
|
||||
// since this is not a typical Chrono behavior, we check it earlier.
|
||||
|
||||
parsed.set_year(try_consume!(scan::number(s, 4, 4)))?;
|
||||
s = scan::char(s, b'-')?;
|
||||
parsed.set_month(try_consume!(scan::number(s, 2, 2)))?;
|
||||
s = scan::char(s, b'-')?;
|
||||
parsed.set_day(try_consume!(scan::number(s, 2, 2)))?;
|
||||
|
||||
s = match s.as_bytes().first() {
|
||||
Some(&b't') | Some(&b'T') => &s[1..],
|
||||
Some(_) => return Err(INVALID),
|
||||
None => return Err(TOO_SHORT),
|
||||
};
|
||||
|
||||
parsed.set_hour(try_consume!(scan::number(s, 2, 2)))?;
|
||||
s = scan::char(s, b':')?;
|
||||
parsed.set_minute(try_consume!(scan::number(s, 2, 2)))?;
|
||||
s = scan::char(s, b':')?;
|
||||
parsed.set_second(try_consume!(scan::number(s, 2, 2)))?;
|
||||
if s.starts_with('.') {
|
||||
let nanosecond = try_consume!(scan::nanosecond(&s[1..]));
|
||||
parsed.set_nanosecond(nanosecond)?;
|
||||
}
|
||||
|
||||
let offset = try_consume!(scan::timezone_offset_zulu(s, |s| scan::char(s, b':')));
|
||||
if offset <= -86_400 || offset >= 86_400 {
|
||||
return Err(OUT_OF_RANGE);
|
||||
}
|
||||
parsed.set_offset(i64::from(offset))?;
|
||||
|
||||
Ok((s, ()))
|
||||
}
|
||||
|
||||
/// Tries to parse given string into `parsed` with given formatting items.
|
||||
/// Returns `Ok` when the entire string has been parsed (otherwise `parsed` should not be used).
|
||||
/// There should be no trailing string after parsing;
|
||||
/// use a stray [`Item::Space`](./enum.Item.html#variant.Space) to trim whitespaces.
|
||||
///
|
||||
/// This particular date and time parser is:
|
||||
///
|
||||
/// - Greedy. It will consume the longest possible prefix.
|
||||
/// For example, `April` is always consumed entirely when the long month name is requested;
|
||||
/// it equally accepts `Apr`, but prefers the longer prefix in this case.
|
||||
///
|
||||
/// - Padding-agnostic (for numeric items).
|
||||
/// The [`Pad`](./enum.Pad.html) field is completely ignored,
|
||||
/// so one can prepend any number of whitespace then any number of zeroes before numbers.
|
||||
///
|
||||
/// - (Still) obeying the intrinsic parsing width. This allows, for example, parsing `HHMMSS`.
|
||||
pub fn parse<'a, I, B>(parsed: &mut Parsed, s: &str, items: I) -> ParseResult<()>
|
||||
where
|
||||
I: Iterator<Item = B>,
|
||||
B: Borrow<Item<'a>>,
|
||||
{
|
||||
parse_internal(parsed, s, items).map(|_| ()).map_err(|(_s, e)| e)
|
||||
}
|
||||
|
||||
fn parse_internal<'a, 'b, I, B>(
|
||||
parsed: &mut Parsed,
|
||||
mut s: &'b str,
|
||||
items: I,
|
||||
) -> Result<&'b str, (&'b str, ParseError)>
|
||||
where
|
||||
I: Iterator<Item = B>,
|
||||
B: Borrow<Item<'a>>,
|
||||
{
|
||||
macro_rules! try_consume {
|
||||
($e:expr) => {{
|
||||
match $e {
|
||||
Ok((s_, v)) => {
|
||||
s = s_;
|
||||
v
|
||||
}
|
||||
Err(e) => return Err((s, e)),
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
for item in items {
|
||||
match *item.borrow() {
|
||||
Item::Literal(prefix) => {
|
||||
if s.len() < prefix.len() {
|
||||
return Err((s, TOO_SHORT));
|
||||
}
|
||||
if !s.starts_with(prefix) {
|
||||
return Err((s, INVALID));
|
||||
}
|
||||
s = &s[prefix.len()..];
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
Item::OwnedLiteral(ref prefix) => {
|
||||
if s.len() < prefix.len() {
|
||||
return Err((s, TOO_SHORT));
|
||||
}
|
||||
if !s.starts_with(&prefix[..]) {
|
||||
return Err((s, INVALID));
|
||||
}
|
||||
s = &s[prefix.len()..];
|
||||
}
|
||||
|
||||
Item::Space(_) => {
|
||||
s = s.trim_left();
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
Item::OwnedSpace(_) => {
|
||||
s = s.trim_left();
|
||||
}
|
||||
|
||||
Item::Numeric(ref spec, ref _pad) => {
|
||||
use super::Numeric::*;
|
||||
type Setter = fn(&mut Parsed, i64) -> ParseResult<()>;
|
||||
|
||||
let (width, signed, set): (usize, bool, Setter) = match *spec {
|
||||
Year => (4, true, Parsed::set_year),
|
||||
YearDiv100 => (2, false, Parsed::set_year_div_100),
|
||||
YearMod100 => (2, false, Parsed::set_year_mod_100),
|
||||
IsoYear => (4, true, Parsed::set_isoyear),
|
||||
IsoYearDiv100 => (2, false, Parsed::set_isoyear_div_100),
|
||||
IsoYearMod100 => (2, false, Parsed::set_isoyear_mod_100),
|
||||
Month => (2, false, Parsed::set_month),
|
||||
Day => (2, false, Parsed::set_day),
|
||||
WeekFromSun => (2, false, Parsed::set_week_from_sun),
|
||||
WeekFromMon => (2, false, Parsed::set_week_from_mon),
|
||||
IsoWeek => (2, false, Parsed::set_isoweek),
|
||||
NumDaysFromSun => (1, false, set_weekday_with_num_days_from_sunday),
|
||||
WeekdayFromMon => (1, false, set_weekday_with_number_from_monday),
|
||||
Ordinal => (3, false, Parsed::set_ordinal),
|
||||
Hour => (2, false, Parsed::set_hour),
|
||||
Hour12 => (2, false, Parsed::set_hour12),
|
||||
Minute => (2, false, Parsed::set_minute),
|
||||
Second => (2, false, Parsed::set_second),
|
||||
Nanosecond => (9, false, Parsed::set_nanosecond),
|
||||
Timestamp => (usize::MAX, false, Parsed::set_timestamp),
|
||||
|
||||
// for the future expansion
|
||||
Internal(ref int) => match int._dummy {},
|
||||
};
|
||||
|
||||
s = s.trim_left();
|
||||
let v = if signed {
|
||||
if s.starts_with('-') {
|
||||
let v = try_consume!(scan::number(&s[1..], 1, usize::MAX));
|
||||
0i64.checked_sub(v).ok_or((s, OUT_OF_RANGE))?
|
||||
} else if s.starts_with('+') {
|
||||
try_consume!(scan::number(&s[1..], 1, usize::MAX))
|
||||
} else {
|
||||
// if there is no explicit sign, we respect the original `width`
|
||||
try_consume!(scan::number(s, 1, width))
|
||||
}
|
||||
} else {
|
||||
try_consume!(scan::number(s, 1, width))
|
||||
};
|
||||
set(parsed, v).map_err(|e| (s, e))?;
|
||||
}
|
||||
|
||||
Item::Fixed(ref spec) => {
|
||||
use super::Fixed::*;
|
||||
|
||||
match spec {
|
||||
&ShortMonthName => {
|
||||
let month0 = try_consume!(scan::short_month0(s));
|
||||
parsed.set_month(i64::from(month0) + 1).map_err(|e| (s, e))?;
|
||||
}
|
||||
|
||||
&LongMonthName => {
|
||||
let month0 = try_consume!(scan::short_or_long_month0(s));
|
||||
parsed.set_month(i64::from(month0) + 1).map_err(|e| (s, e))?;
|
||||
}
|
||||
|
||||
&ShortWeekdayName => {
|
||||
let weekday = try_consume!(scan::short_weekday(s));
|
||||
parsed.set_weekday(weekday).map_err(|e| (s, e))?;
|
||||
}
|
||||
|
||||
&LongWeekdayName => {
|
||||
let weekday = try_consume!(scan::short_or_long_weekday(s));
|
||||
parsed.set_weekday(weekday).map_err(|e| (s, e))?;
|
||||
}
|
||||
|
||||
&LowerAmPm | &UpperAmPm => {
|
||||
if s.len() < 2 {
|
||||
return Err((s, TOO_SHORT));
|
||||
}
|
||||
let ampm = match (s.as_bytes()[0] | 32, s.as_bytes()[1] | 32) {
|
||||
(b'a', b'm') => false,
|
||||
(b'p', b'm') => true,
|
||||
_ => return Err((s, INVALID)),
|
||||
};
|
||||
parsed.set_ampm(ampm).map_err(|e| (s, e))?;
|
||||
s = &s[2..];
|
||||
}
|
||||
|
||||
&Nanosecond | &Nanosecond3 | &Nanosecond6 | &Nanosecond9 => {
|
||||
if s.starts_with('.') {
|
||||
let nano = try_consume!(scan::nanosecond(&s[1..]));
|
||||
parsed.set_nanosecond(nano).map_err(|e| (s, e))?;
|
||||
}
|
||||
}
|
||||
|
||||
&Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot }) => {
|
||||
if s.len() < 3 {
|
||||
return Err((s, TOO_SHORT));
|
||||
}
|
||||
let nano = try_consume!(scan::nanosecond_fixed(s, 3));
|
||||
parsed.set_nanosecond(nano).map_err(|e| (s, e))?;
|
||||
}
|
||||
|
||||
&Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot }) => {
|
||||
if s.len() < 6 {
|
||||
return Err((s, TOO_SHORT));
|
||||
}
|
||||
let nano = try_consume!(scan::nanosecond_fixed(s, 6));
|
||||
parsed.set_nanosecond(nano).map_err(|e| (s, e))?;
|
||||
}
|
||||
|
||||
&Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot }) => {
|
||||
if s.len() < 9 {
|
||||
return Err((s, TOO_SHORT));
|
||||
}
|
||||
let nano = try_consume!(scan::nanosecond_fixed(s, 9));
|
||||
parsed.set_nanosecond(nano).map_err(|e| (s, e))?;
|
||||
}
|
||||
|
||||
&TimezoneName => {
|
||||
try_consume!(scan::timezone_name_skip(s));
|
||||
}
|
||||
|
||||
&TimezoneOffsetColon | &TimezoneOffset => {
|
||||
let offset = try_consume!(scan::timezone_offset(
|
||||
s.trim_left(),
|
||||
scan::colon_or_space
|
||||
));
|
||||
parsed.set_offset(i64::from(offset)).map_err(|e| (s, e))?;
|
||||
}
|
||||
|
||||
&TimezoneOffsetColonZ | &TimezoneOffsetZ => {
|
||||
let offset = try_consume!(scan::timezone_offset_zulu(
|
||||
s.trim_left(),
|
||||
scan::colon_or_space
|
||||
));
|
||||
parsed.set_offset(i64::from(offset)).map_err(|e| (s, e))?;
|
||||
}
|
||||
&Internal(InternalFixed {
|
||||
val: InternalInternal::TimezoneOffsetPermissive,
|
||||
}) => {
|
||||
let offset = try_consume!(scan::timezone_offset_permissive(
|
||||
s.trim_left(),
|
||||
scan::colon_or_space
|
||||
));
|
||||
parsed.set_offset(i64::from(offset)).map_err(|e| (s, e))?;
|
||||
}
|
||||
|
||||
&RFC2822 => try_consume!(parse_rfc2822(parsed, s)),
|
||||
&RFC3339 => try_consume!(parse_rfc3339(parsed, s)),
|
||||
}
|
||||
}
|
||||
|
||||
Item::Error => {
|
||||
return Err((s, BAD_FORMAT));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if there are trailling chars, it is an error
|
||||
if !s.is_empty() {
|
||||
Err((s, TOO_LONG))
|
||||
} else {
|
||||
Ok(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl str::FromStr for DateTime<FixedOffset> {
|
||||
type Err = ParseError;
|
||||
|
||||
fn from_str(s: &str) -> ParseResult<DateTime<FixedOffset>> {
|
||||
const DATE_ITEMS: &'static [Item<'static>] = &[
|
||||
Item::Numeric(Numeric::Year, Pad::Zero),
|
||||
Item::Space(""),
|
||||
Item::Literal("-"),
|
||||
Item::Numeric(Numeric::Month, Pad::Zero),
|
||||
Item::Space(""),
|
||||
Item::Literal("-"),
|
||||
Item::Numeric(Numeric::Day, Pad::Zero),
|
||||
];
|
||||
const TIME_ITEMS: &'static [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(""),
|
||||
Item::Fixed(Fixed::TimezoneOffsetZ),
|
||||
Item::Space(""),
|
||||
];
|
||||
|
||||
let mut parsed = Parsed::new();
|
||||
match parse_internal(&mut parsed, s, DATE_ITEMS.iter()) {
|
||||
Err((remainder, e)) if e.0 == ParseErrorKind::TooLong => {
|
||||
if remainder.starts_with('T') || remainder.starts_with(' ') {
|
||||
parse(&mut parsed, &remainder[1..], TIME_ITEMS.iter())?;
|
||||
} else {
|
||||
Err(INVALID)?;
|
||||
}
|
||||
}
|
||||
Err((_s, e)) => Err(e)?,
|
||||
Ok(_) => Err(NOT_ENOUGH)?,
|
||||
};
|
||||
parsed.to_datetime()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn test_parse() {
|
||||
use super::IMPOSSIBLE;
|
||||
use super::*;
|
||||
|
||||
// workaround for Rust issue #22255
|
||||
fn parse_all(s: &str, items: &[Item]) -> ParseResult<Parsed> {
|
||||
let mut parsed = Parsed::new();
|
||||
parse(&mut parsed, s, items.iter())?;
|
||||
Ok(parsed)
|
||||
}
|
||||
|
||||
macro_rules! check {
|
||||
($fmt:expr, $items:expr; $err:tt) => (
|
||||
assert_eq!(parse_all($fmt, &$items), Err($err))
|
||||
);
|
||||
($fmt:expr, $items:expr; $($k:ident: $v:expr),*) => (#[allow(unused_mut)] {
|
||||
let mut expected = Parsed::new();
|
||||
$(expected.$k = Some($v);)*
|
||||
assert_eq!(parse_all($fmt, &$items), Ok(expected))
|
||||
});
|
||||
}
|
||||
|
||||
// empty string
|
||||
check!("", []; );
|
||||
check!(" ", []; TOO_LONG);
|
||||
check!("a", []; TOO_LONG);
|
||||
|
||||
// whitespaces
|
||||
check!("", [sp!("")]; );
|
||||
check!(" ", [sp!("")]; );
|
||||
check!("\t", [sp!("")]; );
|
||||
check!(" \n\r \n", [sp!("")]; );
|
||||
check!("a", [sp!("")]; TOO_LONG);
|
||||
|
||||
// literal
|
||||
check!("", [lit!("a")]; TOO_SHORT);
|
||||
check!(" ", [lit!("a")]; INVALID);
|
||||
check!("a", [lit!("a")]; );
|
||||
check!("aa", [lit!("a")]; TOO_LONG);
|
||||
check!("A", [lit!("a")]; INVALID);
|
||||
check!("xy", [lit!("xy")]; );
|
||||
check!("xy", [lit!("x"), lit!("y")]; );
|
||||
check!("x y", [lit!("x"), lit!("y")]; INVALID);
|
||||
check!("xy", [lit!("x"), sp!(""), lit!("y")]; );
|
||||
check!("x y", [lit!("x"), sp!(""), lit!("y")]; );
|
||||
|
||||
// numeric
|
||||
check!("1987", [num!(Year)]; year: 1987);
|
||||
check!("1987 ", [num!(Year)]; TOO_LONG);
|
||||
check!("0x12", [num!(Year)]; TOO_LONG); // `0` is parsed
|
||||
check!("x123", [num!(Year)]; INVALID);
|
||||
check!("2015", [num!(Year)]; year: 2015);
|
||||
check!("0000", [num!(Year)]; year: 0);
|
||||
check!("9999", [num!(Year)]; year: 9999);
|
||||
check!(" \t987", [num!(Year)]; year: 987);
|
||||
check!("5", [num!(Year)]; year: 5);
|
||||
check!("5\0", [num!(Year)]; TOO_LONG);
|
||||
check!("\05", [num!(Year)]; INVALID);
|
||||
check!("", [num!(Year)]; TOO_SHORT);
|
||||
check!("12345", [num!(Year), lit!("5")]; year: 1234);
|
||||
check!("12345", [nums!(Year), lit!("5")]; year: 1234);
|
||||
check!("12345", [num0!(Year), lit!("5")]; year: 1234);
|
||||
check!("12341234", [num!(Year), num!(Year)]; year: 1234);
|
||||
check!("1234 1234", [num!(Year), num!(Year)]; year: 1234);
|
||||
check!("1234 1235", [num!(Year), num!(Year)]; IMPOSSIBLE);
|
||||
check!("1234 1234", [num!(Year), lit!("x"), num!(Year)]; INVALID);
|
||||
check!("1234x1234", [num!(Year), lit!("x"), num!(Year)]; year: 1234);
|
||||
check!("1234xx1234", [num!(Year), lit!("x"), num!(Year)]; INVALID);
|
||||
check!("1234 x 1234", [num!(Year), lit!("x"), num!(Year)]; INVALID);
|
||||
|
||||
// signed numeric
|
||||
check!("-42", [num!(Year)]; year: -42);
|
||||
check!("+42", [num!(Year)]; year: 42);
|
||||
check!("-0042", [num!(Year)]; year: -42);
|
||||
check!("+0042", [num!(Year)]; year: 42);
|
||||
check!("-42195", [num!(Year)]; year: -42195);
|
||||
check!("+42195", [num!(Year)]; year: 42195);
|
||||
check!(" -42195", [num!(Year)]; year: -42195);
|
||||
check!(" +42195", [num!(Year)]; year: 42195);
|
||||
check!(" - 42", [num!(Year)]; INVALID);
|
||||
check!(" + 42", [num!(Year)]; INVALID);
|
||||
check!("-", [num!(Year)]; TOO_SHORT);
|
||||
check!("+", [num!(Year)]; TOO_SHORT);
|
||||
|
||||
// unsigned numeric
|
||||
check!("345", [num!(Ordinal)]; ordinal: 345);
|
||||
check!("+345", [num!(Ordinal)]; INVALID);
|
||||
check!("-345", [num!(Ordinal)]; INVALID);
|
||||
check!(" 345", [num!(Ordinal)]; ordinal: 345);
|
||||
check!(" +345", [num!(Ordinal)]; INVALID);
|
||||
check!(" -345", [num!(Ordinal)]; INVALID);
|
||||
|
||||
// various numeric fields
|
||||
check!("1234 5678",
|
||||
[num!(Year), num!(IsoYear)];
|
||||
year: 1234, isoyear: 5678);
|
||||
check!("12 34 56 78",
|
||||
[num!(YearDiv100), num!(YearMod100), num!(IsoYearDiv100), num!(IsoYearMod100)];
|
||||
year_div_100: 12, year_mod_100: 34, isoyear_div_100: 56, isoyear_mod_100: 78);
|
||||
check!("1 2 3 4 5 6",
|
||||
[num!(Month), num!(Day), num!(WeekFromSun), num!(WeekFromMon), num!(IsoWeek),
|
||||
num!(NumDaysFromSun)];
|
||||
month: 1, day: 2, week_from_sun: 3, week_from_mon: 4, isoweek: 5, weekday: Weekday::Sat);
|
||||
check!("7 89 01",
|
||||
[num!(WeekdayFromMon), num!(Ordinal), num!(Hour12)];
|
||||
weekday: Weekday::Sun, ordinal: 89, hour_mod_12: 1);
|
||||
check!("23 45 6 78901234 567890123",
|
||||
[num!(Hour), num!(Minute), num!(Second), num!(Nanosecond), num!(Timestamp)];
|
||||
hour_div_12: 1, hour_mod_12: 11, minute: 45, second: 6, nanosecond: 78_901_234,
|
||||
timestamp: 567_890_123);
|
||||
|
||||
// fixed: month and weekday names
|
||||
check!("apr", [fix!(ShortMonthName)]; month: 4);
|
||||
check!("Apr", [fix!(ShortMonthName)]; month: 4);
|
||||
check!("APR", [fix!(ShortMonthName)]; month: 4);
|
||||
check!("ApR", [fix!(ShortMonthName)]; month: 4);
|
||||
check!("April", [fix!(ShortMonthName)]; TOO_LONG); // `Apr` is parsed
|
||||
check!("A", [fix!(ShortMonthName)]; TOO_SHORT);
|
||||
check!("Sol", [fix!(ShortMonthName)]; INVALID);
|
||||
check!("Apr", [fix!(LongMonthName)]; month: 4);
|
||||
check!("Apri", [fix!(LongMonthName)]; TOO_LONG); // `Apr` is parsed
|
||||
check!("April", [fix!(LongMonthName)]; month: 4);
|
||||
check!("Aprill", [fix!(LongMonthName)]; TOO_LONG);
|
||||
check!("Aprill", [fix!(LongMonthName), lit!("l")]; month: 4);
|
||||
check!("Aprl", [fix!(LongMonthName), lit!("l")]; month: 4);
|
||||
check!("April", [fix!(LongMonthName), lit!("il")]; TOO_SHORT); // do not backtrack
|
||||
check!("thu", [fix!(ShortWeekdayName)]; weekday: Weekday::Thu);
|
||||
check!("Thu", [fix!(ShortWeekdayName)]; weekday: Weekday::Thu);
|
||||
check!("THU", [fix!(ShortWeekdayName)]; weekday: Weekday::Thu);
|
||||
check!("tHu", [fix!(ShortWeekdayName)]; weekday: Weekday::Thu);
|
||||
check!("Thursday", [fix!(ShortWeekdayName)]; TOO_LONG); // `Thu` is parsed
|
||||
check!("T", [fix!(ShortWeekdayName)]; TOO_SHORT);
|
||||
check!("The", [fix!(ShortWeekdayName)]; INVALID);
|
||||
check!("Nop", [fix!(ShortWeekdayName)]; INVALID);
|
||||
check!("Thu", [fix!(LongWeekdayName)]; weekday: Weekday::Thu);
|
||||
check!("Thur", [fix!(LongWeekdayName)]; TOO_LONG); // `Thu` is parsed
|
||||
check!("Thurs", [fix!(LongWeekdayName)]; TOO_LONG); // ditto
|
||||
check!("Thursday", [fix!(LongWeekdayName)]; weekday: Weekday::Thu);
|
||||
check!("Thursdays", [fix!(LongWeekdayName)]; TOO_LONG);
|
||||
check!("Thursdays", [fix!(LongWeekdayName), lit!("s")]; weekday: Weekday::Thu);
|
||||
check!("Thus", [fix!(LongWeekdayName), lit!("s")]; weekday: Weekday::Thu);
|
||||
check!("Thursday", [fix!(LongWeekdayName), lit!("rsday")]; TOO_SHORT); // do not backtrack
|
||||
|
||||
// fixed: am/pm
|
||||
check!("am", [fix!(LowerAmPm)]; hour_div_12: 0);
|
||||
check!("pm", [fix!(LowerAmPm)]; hour_div_12: 1);
|
||||
check!("AM", [fix!(LowerAmPm)]; hour_div_12: 0);
|
||||
check!("PM", [fix!(LowerAmPm)]; hour_div_12: 1);
|
||||
check!("am", [fix!(UpperAmPm)]; hour_div_12: 0);
|
||||
check!("pm", [fix!(UpperAmPm)]; hour_div_12: 1);
|
||||
check!("AM", [fix!(UpperAmPm)]; hour_div_12: 0);
|
||||
check!("PM", [fix!(UpperAmPm)]; hour_div_12: 1);
|
||||
check!("Am", [fix!(LowerAmPm)]; hour_div_12: 0);
|
||||
check!(" Am", [fix!(LowerAmPm)]; INVALID);
|
||||
check!("ame", [fix!(LowerAmPm)]; TOO_LONG); // `am` is parsed
|
||||
check!("a", [fix!(LowerAmPm)]; TOO_SHORT);
|
||||
check!("p", [fix!(LowerAmPm)]; TOO_SHORT);
|
||||
check!("x", [fix!(LowerAmPm)]; TOO_SHORT);
|
||||
check!("xx", [fix!(LowerAmPm)]; INVALID);
|
||||
check!("", [fix!(LowerAmPm)]; TOO_SHORT);
|
||||
|
||||
// fixed: dot plus nanoseconds
|
||||
check!("", [fix!(Nanosecond)]; ); // no field set, but not an error
|
||||
check!("4", [fix!(Nanosecond)]; TOO_LONG); // never consumes `4`
|
||||
check!("4", [fix!(Nanosecond), num!(Second)]; second: 4);
|
||||
check!(".0", [fix!(Nanosecond)]; nanosecond: 0);
|
||||
check!(".4", [fix!(Nanosecond)]; nanosecond: 400_000_000);
|
||||
check!(".42", [fix!(Nanosecond)]; nanosecond: 420_000_000);
|
||||
check!(".421", [fix!(Nanosecond)]; nanosecond: 421_000_000);
|
||||
check!(".42195", [fix!(Nanosecond)]; nanosecond: 421_950_000);
|
||||
check!(".421950803", [fix!(Nanosecond)]; nanosecond: 421_950_803);
|
||||
check!(".421950803547", [fix!(Nanosecond)]; nanosecond: 421_950_803);
|
||||
check!(".000000003547", [fix!(Nanosecond)]; nanosecond: 3);
|
||||
check!(".000000000547", [fix!(Nanosecond)]; nanosecond: 0);
|
||||
check!(".", [fix!(Nanosecond)]; TOO_SHORT);
|
||||
check!(".4x", [fix!(Nanosecond)]; TOO_LONG);
|
||||
check!(". 4", [fix!(Nanosecond)]; INVALID);
|
||||
check!(" .4", [fix!(Nanosecond)]; TOO_LONG); // no automatic trimming
|
||||
|
||||
// fixed: nanoseconds without the dot
|
||||
check!("", [internal_fix!(Nanosecond3NoDot)]; TOO_SHORT);
|
||||
check!("0", [internal_fix!(Nanosecond3NoDot)]; TOO_SHORT);
|
||||
check!("4", [internal_fix!(Nanosecond3NoDot)]; TOO_SHORT);
|
||||
check!("42", [internal_fix!(Nanosecond3NoDot)]; TOO_SHORT);
|
||||
check!("421", [internal_fix!(Nanosecond3NoDot)]; nanosecond: 421_000_000);
|
||||
check!("42143", [internal_fix!(Nanosecond3NoDot), num!(Second)]; nanosecond: 421_000_000, second: 43);
|
||||
check!("42195", [internal_fix!(Nanosecond3NoDot)]; TOO_LONG);
|
||||
check!("4x", [internal_fix!(Nanosecond3NoDot)]; TOO_SHORT);
|
||||
check!(" 4", [internal_fix!(Nanosecond3NoDot)]; INVALID);
|
||||
check!(".421", [internal_fix!(Nanosecond3NoDot)]; INVALID);
|
||||
|
||||
check!("", [internal_fix!(Nanosecond6NoDot)]; TOO_SHORT);
|
||||
check!("0", [internal_fix!(Nanosecond6NoDot)]; TOO_SHORT);
|
||||
check!("42195", [internal_fix!(Nanosecond6NoDot)]; TOO_SHORT);
|
||||
check!("421950", [internal_fix!(Nanosecond6NoDot)]; nanosecond: 421_950_000);
|
||||
check!("000003", [internal_fix!(Nanosecond6NoDot)]; nanosecond: 3000);
|
||||
check!("000000", [internal_fix!(Nanosecond6NoDot)]; nanosecond: 0);
|
||||
check!("4x", [internal_fix!(Nanosecond6NoDot)]; TOO_SHORT);
|
||||
check!(" 4", [internal_fix!(Nanosecond6NoDot)]; INVALID);
|
||||
check!(".42100", [internal_fix!(Nanosecond6NoDot)]; INVALID);
|
||||
|
||||
check!("", [internal_fix!(Nanosecond9NoDot)]; TOO_SHORT);
|
||||
check!("42195", [internal_fix!(Nanosecond9NoDot)]; TOO_SHORT);
|
||||
check!("421950803", [internal_fix!(Nanosecond9NoDot)]; nanosecond: 421_950_803);
|
||||
check!("000000003", [internal_fix!(Nanosecond9NoDot)]; nanosecond: 3);
|
||||
check!("42195080354", [internal_fix!(Nanosecond9NoDot), num!(Second)]; nanosecond: 421_950_803, second: 54); // don't skip digits that come after the 9
|
||||
check!("421950803547", [internal_fix!(Nanosecond9NoDot)]; TOO_LONG);
|
||||
check!("000000000", [internal_fix!(Nanosecond9NoDot)]; nanosecond: 0);
|
||||
check!("00000000x", [internal_fix!(Nanosecond9NoDot)]; INVALID);
|
||||
check!(" 4", [internal_fix!(Nanosecond9NoDot)]; INVALID);
|
||||
check!(".42100000", [internal_fix!(Nanosecond9NoDot)]; INVALID);
|
||||
|
||||
// fixed: timezone offsets
|
||||
check!("+00:00", [fix!(TimezoneOffset)]; offset: 0);
|
||||
check!("-00:00", [fix!(TimezoneOffset)]; offset: 0);
|
||||
check!("+00:01", [fix!(TimezoneOffset)]; offset: 60);
|
||||
check!("-00:01", [fix!(TimezoneOffset)]; offset: -60);
|
||||
check!("+00:30", [fix!(TimezoneOffset)]; offset: 30 * 60);
|
||||
check!("-00:30", [fix!(TimezoneOffset)]; offset: -30 * 60);
|
||||
check!("+04:56", [fix!(TimezoneOffset)]; offset: 296 * 60);
|
||||
check!("-04:56", [fix!(TimezoneOffset)]; offset: -296 * 60);
|
||||
check!("+24:00", [fix!(TimezoneOffset)]; offset: 24 * 60 * 60);
|
||||
check!("-24:00", [fix!(TimezoneOffset)]; offset: -24 * 60 * 60);
|
||||
check!("+99:59", [fix!(TimezoneOffset)]; offset: (100 * 60 - 1) * 60);
|
||||
check!("-99:59", [fix!(TimezoneOffset)]; offset: -(100 * 60 - 1) * 60);
|
||||
check!("+00:59", [fix!(TimezoneOffset)]; offset: 59 * 60);
|
||||
check!("+00:60", [fix!(TimezoneOffset)]; OUT_OF_RANGE);
|
||||
check!("+00:99", [fix!(TimezoneOffset)]; OUT_OF_RANGE);
|
||||
check!("#12:34", [fix!(TimezoneOffset)]; INVALID);
|
||||
check!("12:34", [fix!(TimezoneOffset)]; INVALID);
|
||||
check!("+12:34 ", [fix!(TimezoneOffset)]; TOO_LONG);
|
||||
check!(" +12:34", [fix!(TimezoneOffset)]; offset: 754 * 60);
|
||||
check!("\t -12:34", [fix!(TimezoneOffset)]; offset: -754 * 60);
|
||||
check!("", [fix!(TimezoneOffset)]; TOO_SHORT);
|
||||
check!("+", [fix!(TimezoneOffset)]; TOO_SHORT);
|
||||
check!("+1", [fix!(TimezoneOffset)]; TOO_SHORT);
|
||||
check!("+12", [fix!(TimezoneOffset)]; TOO_SHORT);
|
||||
check!("+123", [fix!(TimezoneOffset)]; TOO_SHORT);
|
||||
check!("+1234", [fix!(TimezoneOffset)]; offset: 754 * 60);
|
||||
check!("+12345", [fix!(TimezoneOffset)]; TOO_LONG);
|
||||
check!("+12345", [fix!(TimezoneOffset), num!(Day)]; offset: 754 * 60, day: 5);
|
||||
check!("Z", [fix!(TimezoneOffset)]; INVALID);
|
||||
check!("z", [fix!(TimezoneOffset)]; INVALID);
|
||||
check!("Z", [fix!(TimezoneOffsetZ)]; offset: 0);
|
||||
check!("z", [fix!(TimezoneOffsetZ)]; offset: 0);
|
||||
check!("Y", [fix!(TimezoneOffsetZ)]; INVALID);
|
||||
check!("Zulu", [fix!(TimezoneOffsetZ), lit!("ulu")]; offset: 0);
|
||||
check!("zulu", [fix!(TimezoneOffsetZ), lit!("ulu")]; offset: 0);
|
||||
check!("+1234ulu", [fix!(TimezoneOffsetZ), lit!("ulu")]; offset: 754 * 60);
|
||||
check!("+12:34ulu", [fix!(TimezoneOffsetZ), lit!("ulu")]; offset: 754 * 60);
|
||||
check!("Z", [internal_fix!(TimezoneOffsetPermissive)]; offset: 0);
|
||||
check!("z", [internal_fix!(TimezoneOffsetPermissive)]; offset: 0);
|
||||
check!("+12:00", [internal_fix!(TimezoneOffsetPermissive)]; offset: 12 * 60 * 60);
|
||||
check!("+12", [internal_fix!(TimezoneOffsetPermissive)]; offset: 12 * 60 * 60);
|
||||
check!("CEST 5", [fix!(TimezoneName), lit!(" "), num!(Day)]; day: 5);
|
||||
|
||||
// some practical examples
|
||||
check!("2015-02-04T14:37:05+09:00",
|
||||
[num!(Year), lit!("-"), num!(Month), lit!("-"), num!(Day), lit!("T"),
|
||||
num!(Hour), lit!(":"), num!(Minute), lit!(":"), num!(Second), fix!(TimezoneOffset)];
|
||||
year: 2015, month: 2, day: 4, hour_div_12: 1, hour_mod_12: 2,
|
||||
minute: 37, second: 5, offset: 32400);
|
||||
check!("20150204143705567",
|
||||
[num!(Year), num!(Month), num!(Day),
|
||||
num!(Hour), num!(Minute), num!(Second), internal_fix!(Nanosecond3NoDot)];
|
||||
year: 2015, month: 2, day: 4, hour_div_12: 1, hour_mod_12: 2,
|
||||
minute: 37, second: 5, nanosecond: 567000000);
|
||||
check!("Mon, 10 Jun 2013 09:32:37 GMT",
|
||||
[fix!(ShortWeekdayName), lit!(","), sp!(" "), num!(Day), sp!(" "),
|
||||
fix!(ShortMonthName), sp!(" "), num!(Year), sp!(" "), num!(Hour), lit!(":"),
|
||||
num!(Minute), lit!(":"), num!(Second), sp!(" "), lit!("GMT")];
|
||||
year: 2013, month: 6, day: 10, weekday: Weekday::Mon,
|
||||
hour_div_12: 0, hour_mod_12: 9, minute: 32, second: 37);
|
||||
check!("Sun Aug 02 13:39:15 CEST 2020",
|
||||
[fix!(ShortWeekdayName), sp!(" "), fix!(ShortMonthName), sp!(" "),
|
||||
num!(Day), sp!(" "), num!(Hour), lit!(":"), num!(Minute), lit!(":"),
|
||||
num!(Second), sp!(" "), fix!(TimezoneName), sp!(" "), num!(Year)];
|
||||
year: 2020, month: 8, day: 2, weekday: Weekday::Sun,
|
||||
hour_div_12: 1, hour_mod_12: 1, minute: 39, second: 15);
|
||||
check!("20060102150405",
|
||||
[num!(Year), num!(Month), num!(Day), num!(Hour), num!(Minute), num!(Second)];
|
||||
year: 2006, month: 1, day: 2, hour_div_12: 1, hour_mod_12: 3, minute: 4, second: 5);
|
||||
check!("3:14PM",
|
||||
[num!(Hour12), lit!(":"), num!(Minute), fix!(LowerAmPm)];
|
||||
hour_div_12: 1, hour_mod_12: 3, minute: 14);
|
||||
check!("12345678901234.56789",
|
||||
[num!(Timestamp), lit!("."), num!(Nanosecond)];
|
||||
nanosecond: 56_789, timestamp: 12_345_678_901_234);
|
||||
check!("12345678901234.56789",
|
||||
[num!(Timestamp), fix!(Nanosecond)];
|
||||
nanosecond: 567_890_000, timestamp: 12_345_678_901_234);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn test_rfc2822() {
|
||||
use super::NOT_ENOUGH;
|
||||
use super::*;
|
||||
use offset::FixedOffset;
|
||||
use DateTime;
|
||||
|
||||
// Test data - (input, Ok(expected result after parse and format) or Err(error code))
|
||||
let testdates = [
|
||||
("Tue, 20 Jan 2015 17:35:20 -0800", Ok("Tue, 20 Jan 2015 17:35:20 -0800")), // normal case
|
||||
("Fri, 2 Jan 2015 17:35:20 -0800", Ok("Fri, 02 Jan 2015 17:35:20 -0800")), // folding whitespace
|
||||
("Fri, 02 Jan 2015 17:35:20 -0800", Ok("Fri, 02 Jan 2015 17:35:20 -0800")), // leading zero
|
||||
("20 Jan 2015 17:35:20 -0800", Ok("Tue, 20 Jan 2015 17:35:20 -0800")), // no day of week
|
||||
("20 JAN 2015 17:35:20 -0800", Ok("Tue, 20 Jan 2015 17:35:20 -0800")), // upper case month
|
||||
("Tue, 20 Jan 2015 17:35 -0800", Ok("Tue, 20 Jan 2015 17:35:00 -0800")), // no second
|
||||
("11 Sep 2001 09:45:00 EST", Ok("Tue, 11 Sep 2001 09:45:00 -0500")),
|
||||
("30 Feb 2015 17:35:20 -0800", Err(OUT_OF_RANGE)), // bad day of month
|
||||
("Tue, 20 Jan 2015", Err(TOO_SHORT)), // omitted fields
|
||||
("Tue, 20 Avr 2015 17:35:20 -0800", Err(INVALID)), // bad month name
|
||||
("Tue, 20 Jan 2015 25:35:20 -0800", Err(OUT_OF_RANGE)), // bad hour
|
||||
("Tue, 20 Jan 2015 7:35:20 -0800", Err(INVALID)), // bad # of digits in hour
|
||||
("Tue, 20 Jan 2015 17:65:20 -0800", Err(OUT_OF_RANGE)), // bad minute
|
||||
("Tue, 20 Jan 2015 17:35:90 -0800", Err(OUT_OF_RANGE)), // bad second
|
||||
("Tue, 20 Jan 2015 17:35:20 -0890", Err(OUT_OF_RANGE)), // bad offset
|
||||
("6 Jun 1944 04:00:00Z", Err(INVALID)), // bad offset (zulu not allowed)
|
||||
("Tue, 20 Jan 2015 17:35:20 HAS", Err(NOT_ENOUGH)), // bad named time zone
|
||||
];
|
||||
|
||||
fn rfc2822_to_datetime(date: &str) -> ParseResult<DateTime<FixedOffset>> {
|
||||
let mut parsed = Parsed::new();
|
||||
parse(&mut parsed, date, [Item::Fixed(Fixed::RFC2822)].iter())?;
|
||||
parsed.to_datetime()
|
||||
}
|
||||
|
||||
fn fmt_rfc2822_datetime(dt: DateTime<FixedOffset>) -> String {
|
||||
dt.format_with_items([Item::Fixed(Fixed::RFC2822)].iter()).to_string()
|
||||
}
|
||||
|
||||
// Test against test data above
|
||||
for &(date, checkdate) in testdates.iter() {
|
||||
let d = rfc2822_to_datetime(date); // parse a date
|
||||
let dt = match d {
|
||||
// did we get a value?
|
||||
Ok(dt) => Ok(fmt_rfc2822_datetime(dt)), // yes, go on
|
||||
Err(e) => Err(e), // otherwise keep an error for the comparison
|
||||
};
|
||||
if dt != checkdate.map(|s| s.to_string()) {
|
||||
// check for expected result
|
||||
panic!(
|
||||
"Date conversion failed for {}\nReceived: {:?}\nExpected: {:?}",
|
||||
date, dt, checkdate
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn parse_rfc850() {
|
||||
use {TimeZone, Utc};
|
||||
|
||||
static RFC850_FMT: &'static str = "%A, %d-%b-%y %T GMT";
|
||||
|
||||
let dt_str = "Sunday, 06-Nov-94 08:49:37 GMT";
|
||||
let dt = Utc.ymd(1994, 11, 6).and_hms(8, 49, 37);
|
||||
|
||||
// Check that the format is what we expect
|
||||
assert_eq!(dt.format(RFC850_FMT).to_string(), dt_str);
|
||||
|
||||
// Check that it parses correctly
|
||||
assert_eq!(Ok(dt), Utc.datetime_from_str("Sunday, 06-Nov-94 08:49:37 GMT", RFC850_FMT));
|
||||
|
||||
// Check that the rest of the weekdays parse correctly (this test originally failed because
|
||||
// Sunday parsed incorrectly).
|
||||
let testdates = [
|
||||
(Utc.ymd(1994, 11, 7).and_hms(8, 49, 37), "Monday, 07-Nov-94 08:49:37 GMT"),
|
||||
(Utc.ymd(1994, 11, 8).and_hms(8, 49, 37), "Tuesday, 08-Nov-94 08:49:37 GMT"),
|
||||
(Utc.ymd(1994, 11, 9).and_hms(8, 49, 37), "Wednesday, 09-Nov-94 08:49:37 GMT"),
|
||||
(Utc.ymd(1994, 11, 10).and_hms(8, 49, 37), "Thursday, 10-Nov-94 08:49:37 GMT"),
|
||||
(Utc.ymd(1994, 11, 11).and_hms(8, 49, 37), "Friday, 11-Nov-94 08:49:37 GMT"),
|
||||
(Utc.ymd(1994, 11, 12).and_hms(8, 49, 37), "Saturday, 12-Nov-94 08:49:37 GMT"),
|
||||
];
|
||||
|
||||
for val in &testdates {
|
||||
assert_eq!(Ok(val.0), Utc.datetime_from_str(val.1, RFC850_FMT));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn test_rfc3339() {
|
||||
use super::*;
|
||||
use offset::FixedOffset;
|
||||
use DateTime;
|
||||
|
||||
// Test data - (input, Ok(expected result after parse and format) or Err(error code))
|
||||
let testdates = [
|
||||
("2015-01-20T17:35:20-08:00", Ok("2015-01-20T17:35:20-08:00")), // normal case
|
||||
("1944-06-06T04:04:00Z", Ok("1944-06-06T04:04:00+00:00")), // D-day
|
||||
("2001-09-11T09:45:00-08:00", Ok("2001-09-11T09:45:00-08:00")),
|
||||
("2015-01-20T17:35:20.001-08:00", Ok("2015-01-20T17:35:20.001-08:00")),
|
||||
("2015-01-20T17:35:20.000031-08:00", Ok("2015-01-20T17:35:20.000031-08:00")),
|
||||
("2015-01-20T17:35:20.000000004-08:00", Ok("2015-01-20T17:35:20.000000004-08:00")),
|
||||
("2015-01-20T17:35:20.000000000452-08:00", Ok("2015-01-20T17:35:20-08:00")), // too small
|
||||
("2015-02-30T17:35:20-08:00", Err(OUT_OF_RANGE)), // bad day of month
|
||||
("2015-01-20T25:35:20-08:00", Err(OUT_OF_RANGE)), // bad hour
|
||||
("2015-01-20T17:65:20-08:00", Err(OUT_OF_RANGE)), // bad minute
|
||||
("2015-01-20T17:35:90-08:00", Err(OUT_OF_RANGE)), // bad second
|
||||
("2015-01-20T17:35:20-24:00", Err(OUT_OF_RANGE)), // bad offset
|
||||
];
|
||||
|
||||
fn rfc3339_to_datetime(date: &str) -> ParseResult<DateTime<FixedOffset>> {
|
||||
let mut parsed = Parsed::new();
|
||||
parse(&mut parsed, date, [Item::Fixed(Fixed::RFC3339)].iter())?;
|
||||
parsed.to_datetime()
|
||||
}
|
||||
|
||||
fn fmt_rfc3339_datetime(dt: DateTime<FixedOffset>) -> String {
|
||||
dt.format_with_items([Item::Fixed(Fixed::RFC3339)].iter()).to_string()
|
||||
}
|
||||
|
||||
// Test against test data above
|
||||
for &(date, checkdate) in testdates.iter() {
|
||||
let d = rfc3339_to_datetime(date); // parse a date
|
||||
let dt = match d {
|
||||
// did we get a value?
|
||||
Ok(dt) => Ok(fmt_rfc3339_datetime(dt)), // yes, go on
|
||||
Err(e) => Err(e), // otherwise keep an error for the comparison
|
||||
};
|
||||
if dt != checkdate.map(|s| s.to_string()) {
|
||||
// check for expected result
|
||||
panic!(
|
||||
"Date conversion failed for {}\nReceived: {:?}\nExpected: {:?}",
|
||||
date, dt, checkdate
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
1283
zeroidc/vendor/chrono/src/format/parsed.rs
vendored
Normal file
1283
zeroidc/vendor/chrono/src/format/parsed.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
350
zeroidc/vendor/chrono/src/format/scan.rs
vendored
Normal file
350
zeroidc/vendor/chrono/src/format/scan.rs
vendored
Normal file
@@ -0,0 +1,350 @@
|
||||
// This is a part of Chrono.
|
||||
// See README.md and LICENSE.txt for details.
|
||||
|
||||
/*!
|
||||
* Various scanning routines for the parser.
|
||||
*/
|
||||
|
||||
#![allow(deprecated)]
|
||||
|
||||
use super::{ParseResult, INVALID, OUT_OF_RANGE, TOO_SHORT};
|
||||
use Weekday;
|
||||
|
||||
/// Returns true when two slices are equal case-insensitively (in ASCII).
|
||||
/// Assumes that the `pattern` is already converted to lower case.
|
||||
fn equals(s: &str, pattern: &str) -> bool {
|
||||
let mut xs = s.as_bytes().iter().map(|&c| match c {
|
||||
b'A'...b'Z' => c + 32,
|
||||
_ => c,
|
||||
});
|
||||
let mut ys = pattern.as_bytes().iter().cloned();
|
||||
loop {
|
||||
match (xs.next(), ys.next()) {
|
||||
(None, None) => return true,
|
||||
(None, _) | (_, None) => return false,
|
||||
(Some(x), Some(y)) if x != y => return false,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to parse the non-negative number from `min` to `max` digits.
|
||||
///
|
||||
/// The absence of digits at all is an unconditional error.
|
||||
/// More than `max` digits are consumed up to the first `max` digits.
|
||||
/// Any number that does not fit in `i64` is an error.
|
||||
#[inline]
|
||||
pub fn number(s: &str, min: usize, max: usize) -> ParseResult<(&str, i64)> {
|
||||
assert!(min <= max);
|
||||
|
||||
// We are only interested in ascii numbers, so we can work with the `str` as bytes. We stop on
|
||||
// the first non-numeric byte, which may be another ascii character or beginning of multi-byte
|
||||
// UTF-8 character.
|
||||
let bytes = s.as_bytes();
|
||||
if bytes.len() < min {
|
||||
return Err(TOO_SHORT);
|
||||
}
|
||||
|
||||
let mut n = 0i64;
|
||||
for (i, c) in bytes.iter().take(max).cloned().enumerate() {
|
||||
// cloned() = copied()
|
||||
if c < b'0' || b'9' < c {
|
||||
if i < min {
|
||||
return Err(INVALID);
|
||||
} else {
|
||||
return Ok((&s[i..], n));
|
||||
}
|
||||
}
|
||||
|
||||
n = match n.checked_mul(10).and_then(|n| n.checked_add((c - b'0') as i64)) {
|
||||
Some(n) => n,
|
||||
None => return Err(OUT_OF_RANGE),
|
||||
};
|
||||
}
|
||||
|
||||
Ok((&s[::core::cmp::min(max, bytes.len())..], n))
|
||||
}
|
||||
|
||||
/// Tries to consume at least one digits as a fractional second.
|
||||
/// Returns the number of whole nanoseconds (0--999,999,999).
|
||||
pub fn nanosecond(s: &str) -> ParseResult<(&str, i64)> {
|
||||
// record the number of digits consumed for later scaling.
|
||||
let origlen = s.len();
|
||||
let (s, v) = number(s, 1, 9)?;
|
||||
let consumed = origlen - s.len();
|
||||
|
||||
// scale the number accordingly.
|
||||
static SCALE: [i64; 10] =
|
||||
[0, 100_000_000, 10_000_000, 1_000_000, 100_000, 10_000, 1_000, 100, 10, 1];
|
||||
let v = v.checked_mul(SCALE[consumed]).ok_or(OUT_OF_RANGE)?;
|
||||
|
||||
// if there are more than 9 digits, skip next digits.
|
||||
let s = s.trim_left_matches(|c: char| '0' <= c && c <= '9');
|
||||
|
||||
Ok((s, v))
|
||||
}
|
||||
|
||||
/// Tries to consume a fixed number of digits as a fractional second.
|
||||
/// Returns the number of whole nanoseconds (0--999,999,999).
|
||||
pub fn nanosecond_fixed(s: &str, digits: usize) -> ParseResult<(&str, i64)> {
|
||||
// record the number of digits consumed for later scaling.
|
||||
let (s, v) = number(s, digits, digits)?;
|
||||
|
||||
// scale the number accordingly.
|
||||
static SCALE: [i64; 10] =
|
||||
[0, 100_000_000, 10_000_000, 1_000_000, 100_000, 10_000, 1_000, 100, 10, 1];
|
||||
let v = v.checked_mul(SCALE[digits]).ok_or(OUT_OF_RANGE)?;
|
||||
|
||||
Ok((s, v))
|
||||
}
|
||||
|
||||
/// Tries to parse the month index (0 through 11) with the first three ASCII letters.
|
||||
pub fn short_month0(s: &str) -> ParseResult<(&str, u8)> {
|
||||
if s.len() < 3 {
|
||||
return Err(TOO_SHORT);
|
||||
}
|
||||
let buf = s.as_bytes();
|
||||
let month0 = match (buf[0] | 32, buf[1] | 32, buf[2] | 32) {
|
||||
(b'j', b'a', b'n') => 0,
|
||||
(b'f', b'e', b'b') => 1,
|
||||
(b'm', b'a', b'r') => 2,
|
||||
(b'a', b'p', b'r') => 3,
|
||||
(b'm', b'a', b'y') => 4,
|
||||
(b'j', b'u', b'n') => 5,
|
||||
(b'j', b'u', b'l') => 6,
|
||||
(b'a', b'u', b'g') => 7,
|
||||
(b's', b'e', b'p') => 8,
|
||||
(b'o', b'c', b't') => 9,
|
||||
(b'n', b'o', b'v') => 10,
|
||||
(b'd', b'e', b'c') => 11,
|
||||
_ => return Err(INVALID),
|
||||
};
|
||||
Ok((&s[3..], month0))
|
||||
}
|
||||
|
||||
/// Tries to parse the weekday with the first three ASCII letters.
|
||||
pub fn short_weekday(s: &str) -> ParseResult<(&str, Weekday)> {
|
||||
if s.len() < 3 {
|
||||
return Err(TOO_SHORT);
|
||||
}
|
||||
let buf = s.as_bytes();
|
||||
let weekday = match (buf[0] | 32, buf[1] | 32, buf[2] | 32) {
|
||||
(b'm', b'o', b'n') => Weekday::Mon,
|
||||
(b't', b'u', b'e') => Weekday::Tue,
|
||||
(b'w', b'e', b'd') => Weekday::Wed,
|
||||
(b't', b'h', b'u') => Weekday::Thu,
|
||||
(b'f', b'r', b'i') => Weekday::Fri,
|
||||
(b's', b'a', b't') => Weekday::Sat,
|
||||
(b's', b'u', b'n') => Weekday::Sun,
|
||||
_ => return Err(INVALID),
|
||||
};
|
||||
Ok((&s[3..], weekday))
|
||||
}
|
||||
|
||||
/// Tries to parse the month index (0 through 11) with short or long month names.
|
||||
/// It prefers long month names to short month names when both are possible.
|
||||
pub fn short_or_long_month0(s: &str) -> ParseResult<(&str, u8)> {
|
||||
// lowercased month names, minus first three chars
|
||||
static LONG_MONTH_SUFFIXES: [&'static str; 12] =
|
||||
["uary", "ruary", "ch", "il", "", "e", "y", "ust", "tember", "ober", "ember", "ember"];
|
||||
|
||||
let (mut s, month0) = short_month0(s)?;
|
||||
|
||||
// tries to consume the suffix if possible
|
||||
let suffix = LONG_MONTH_SUFFIXES[month0 as usize];
|
||||
if s.len() >= suffix.len() && equals(&s[..suffix.len()], suffix) {
|
||||
s = &s[suffix.len()..];
|
||||
}
|
||||
|
||||
Ok((s, month0))
|
||||
}
|
||||
|
||||
/// Tries to parse the weekday with short or long weekday names.
|
||||
/// It prefers long weekday names to short weekday names when both are possible.
|
||||
pub fn short_or_long_weekday(s: &str) -> ParseResult<(&str, Weekday)> {
|
||||
// lowercased weekday names, minus first three chars
|
||||
static LONG_WEEKDAY_SUFFIXES: [&'static str; 7] =
|
||||
["day", "sday", "nesday", "rsday", "day", "urday", "day"];
|
||||
|
||||
let (mut s, weekday) = short_weekday(s)?;
|
||||
|
||||
// tries to consume the suffix if possible
|
||||
let suffix = LONG_WEEKDAY_SUFFIXES[weekday.num_days_from_monday() as usize];
|
||||
if s.len() >= suffix.len() && equals(&s[..suffix.len()], suffix) {
|
||||
s = &s[suffix.len()..];
|
||||
}
|
||||
|
||||
Ok((s, weekday))
|
||||
}
|
||||
|
||||
/// Tries to consume exactly one given character.
|
||||
pub fn char(s: &str, c1: u8) -> ParseResult<&str> {
|
||||
match s.as_bytes().first() {
|
||||
Some(&c) if c == c1 => Ok(&s[1..]),
|
||||
Some(_) => Err(INVALID),
|
||||
None => Err(TOO_SHORT),
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to consume one or more whitespace.
|
||||
pub fn space(s: &str) -> ParseResult<&str> {
|
||||
let s_ = s.trim_left();
|
||||
if s_.len() < s.len() {
|
||||
Ok(s_)
|
||||
} else if s.is_empty() {
|
||||
Err(TOO_SHORT)
|
||||
} else {
|
||||
Err(INVALID)
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes any number (including zero) of colon or spaces.
|
||||
pub fn colon_or_space(s: &str) -> ParseResult<&str> {
|
||||
Ok(s.trim_left_matches(|c: char| c == ':' || c.is_whitespace()))
|
||||
}
|
||||
|
||||
/// Tries to parse `[-+]\d\d` continued by `\d\d`. Return an offset in seconds if possible.
|
||||
///
|
||||
/// The additional `colon` may be used to parse a mandatory or optional `:`
|
||||
/// between hours and minutes, and should return either a new suffix or `Err` when parsing fails.
|
||||
pub fn timezone_offset<F>(s: &str, consume_colon: F) -> ParseResult<(&str, i32)>
|
||||
where
|
||||
F: FnMut(&str) -> ParseResult<&str>,
|
||||
{
|
||||
timezone_offset_internal(s, consume_colon, false)
|
||||
}
|
||||
|
||||
fn timezone_offset_internal<F>(
|
||||
mut s: &str,
|
||||
mut consume_colon: F,
|
||||
allow_missing_minutes: bool,
|
||||
) -> ParseResult<(&str, i32)>
|
||||
where
|
||||
F: FnMut(&str) -> ParseResult<&str>,
|
||||
{
|
||||
fn digits(s: &str) -> ParseResult<(u8, u8)> {
|
||||
let b = s.as_bytes();
|
||||
if b.len() < 2 {
|
||||
Err(TOO_SHORT)
|
||||
} else {
|
||||
Ok((b[0], b[1]))
|
||||
}
|
||||
}
|
||||
let negative = match s.as_bytes().first() {
|
||||
Some(&b'+') => false,
|
||||
Some(&b'-') => true,
|
||||
Some(_) => return Err(INVALID),
|
||||
None => return Err(TOO_SHORT),
|
||||
};
|
||||
s = &s[1..];
|
||||
|
||||
// hours (00--99)
|
||||
let hours = match digits(s)? {
|
||||
(h1 @ b'0'...b'9', h2 @ b'0'...b'9') => i32::from((h1 - b'0') * 10 + (h2 - b'0')),
|
||||
_ => return Err(INVALID),
|
||||
};
|
||||
s = &s[2..];
|
||||
|
||||
// colons (and possibly other separators)
|
||||
s = consume_colon(s)?;
|
||||
|
||||
// minutes (00--59)
|
||||
// if the next two items are digits then we have to add minutes
|
||||
let minutes = if let Ok(ds) = digits(s) {
|
||||
match ds {
|
||||
(m1 @ b'0'...b'5', m2 @ b'0'...b'9') => i32::from((m1 - b'0') * 10 + (m2 - b'0')),
|
||||
(b'6'...b'9', b'0'...b'9') => return Err(OUT_OF_RANGE),
|
||||
_ => return Err(INVALID),
|
||||
}
|
||||
} else if allow_missing_minutes {
|
||||
0
|
||||
} else {
|
||||
return Err(TOO_SHORT);
|
||||
};
|
||||
s = match s.len() {
|
||||
len if len >= 2 => &s[2..],
|
||||
len if len == 0 => s,
|
||||
_ => return Err(TOO_SHORT),
|
||||
};
|
||||
|
||||
let seconds = hours * 3600 + minutes * 60;
|
||||
Ok((s, if negative { -seconds } else { seconds }))
|
||||
}
|
||||
|
||||
/// Same as `timezone_offset` but also allows for `z`/`Z` which is the same as `+00:00`.
|
||||
pub fn timezone_offset_zulu<F>(s: &str, colon: F) -> ParseResult<(&str, i32)>
|
||||
where
|
||||
F: FnMut(&str) -> ParseResult<&str>,
|
||||
{
|
||||
let bytes = s.as_bytes();
|
||||
match bytes.first() {
|
||||
Some(&b'z') | Some(&b'Z') => Ok((&s[1..], 0)),
|
||||
Some(&b'u') | Some(&b'U') => {
|
||||
if bytes.len() >= 3 {
|
||||
let (b, c) = (bytes[1], bytes[2]);
|
||||
match (b | 32, c | 32) {
|
||||
(b't', b'c') => Ok((&s[3..], 0)),
|
||||
_ => Err(INVALID),
|
||||
}
|
||||
} else {
|
||||
Err(INVALID)
|
||||
}
|
||||
}
|
||||
_ => timezone_offset(s, colon),
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as `timezone_offset` but also allows for `z`/`Z` which is the same as
|
||||
/// `+00:00`, and allows missing minutes entirely.
|
||||
pub fn timezone_offset_permissive<F>(s: &str, colon: F) -> ParseResult<(&str, i32)>
|
||||
where
|
||||
F: FnMut(&str) -> ParseResult<&str>,
|
||||
{
|
||||
match s.as_bytes().first() {
|
||||
Some(&b'z') | Some(&b'Z') => Ok((&s[1..], 0)),
|
||||
_ => timezone_offset_internal(s, colon, true),
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as `timezone_offset` but also allows for RFC 2822 legacy timezones.
|
||||
/// May return `None` which indicates an insufficient offset data (i.e. `-0000`).
|
||||
pub fn timezone_offset_2822(s: &str) -> ParseResult<(&str, Option<i32>)> {
|
||||
// tries to parse legacy time zone names
|
||||
let upto = s
|
||||
.as_bytes()
|
||||
.iter()
|
||||
.position(|&c| match c {
|
||||
b'a'...b'z' | b'A'...b'Z' => false,
|
||||
_ => true,
|
||||
})
|
||||
.unwrap_or_else(|| s.len());
|
||||
if upto > 0 {
|
||||
let name = &s[..upto];
|
||||
let s = &s[upto..];
|
||||
let offset_hours = |o| Ok((s, Some(o * 3600)));
|
||||
if equals(name, "gmt") || equals(name, "ut") {
|
||||
offset_hours(0)
|
||||
} else if equals(name, "edt") {
|
||||
offset_hours(-4)
|
||||
} else if equals(name, "est") || equals(name, "cdt") {
|
||||
offset_hours(-5)
|
||||
} else if equals(name, "cst") || equals(name, "mdt") {
|
||||
offset_hours(-6)
|
||||
} else if equals(name, "mst") || equals(name, "pdt") {
|
||||
offset_hours(-7)
|
||||
} else if equals(name, "pst") {
|
||||
offset_hours(-8)
|
||||
} else {
|
||||
Ok((s, None)) // recommended by RFC 2822: consume but treat it as -0000
|
||||
}
|
||||
} else {
|
||||
let (s_, offset) = timezone_offset(s, |s| Ok(s))?;
|
||||
Ok((s_, Some(offset)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to consume everyting until next whitespace-like symbol.
|
||||
/// Does not provide any offset information from the consumed data.
|
||||
pub fn timezone_name_skip(s: &str) -> ParseResult<(&str, ())> {
|
||||
Ok((s.trim_left_matches(|c: char| !c.is_whitespace()), ()))
|
||||
}
|
||||
649
zeroidc/vendor/chrono/src/format/strftime.rs
vendored
Normal file
649
zeroidc/vendor/chrono/src/format/strftime.rs
vendored
Normal file
@@ -0,0 +1,649 @@
|
||||
// This is a part of Chrono.
|
||||
// See README.md and LICENSE.txt for details.
|
||||
|
||||
/*!
|
||||
`strftime`/`strptime`-inspired date and time formatting syntax.
|
||||
|
||||
## Specifiers
|
||||
|
||||
The following specifiers are available both to formatting and parsing.
|
||||
|
||||
| Spec. | Example | Description |
|
||||
|-------|----------|----------------------------------------------------------------------------|
|
||||
| | | **DATE SPECIFIERS:** |
|
||||
| `%Y` | `2001` | The full proleptic Gregorian year, zero-padded to 4 digits. [^1] |
|
||||
| `%C` | `20` | The proleptic Gregorian year divided by 100, zero-padded to 2 digits. [^2] |
|
||||
| `%y` | `01` | The proleptic Gregorian year modulo 100, zero-padded to 2 digits. [^2] |
|
||||
| | | |
|
||||
| `%m` | `07` | Month number (01--12), zero-padded to 2 digits. |
|
||||
| `%b` | `Jul` | Abbreviated month name. Always 3 letters. |
|
||||
| `%B` | `July` | Full month name. Also accepts corresponding abbreviation in parsing. |
|
||||
| `%h` | `Jul` | Same as `%b`. |
|
||||
| | | |
|
||||
| `%d` | `08` | Day number (01--31), zero-padded to 2 digits. |
|
||||
| `%e` | ` 8` | Same as `%d` but space-padded. Same as `%_d`. |
|
||||
| | | |
|
||||
| `%a` | `Sun` | Abbreviated weekday name. Always 3 letters. |
|
||||
| `%A` | `Sunday` | Full weekday name. Also accepts corresponding abbreviation in parsing. |
|
||||
| `%w` | `0` | Sunday = 0, Monday = 1, ..., Saturday = 6. |
|
||||
| `%u` | `7` | Monday = 1, Tuesday = 2, ..., Sunday = 7. (ISO 8601) |
|
||||
| | | |
|
||||
| `%U` | `28` | Week number starting with Sunday (00--53), zero-padded to 2 digits. [^3] |
|
||||
| `%W` | `27` | Same as `%U`, but week 1 starts with the first Monday in that year instead.|
|
||||
| | | |
|
||||
| `%G` | `2001` | Same as `%Y` but uses the year number in ISO 8601 week date. [^4] |
|
||||
| `%g` | `01` | Same as `%y` but uses the year number in ISO 8601 week date. [^4] |
|
||||
| `%V` | `27` | Same as `%U` but uses the week number in ISO 8601 week date (01--53). [^4] |
|
||||
| | | |
|
||||
| `%j` | `189` | Day of the year (001--366), zero-padded to 3 digits. |
|
||||
| | | |
|
||||
| `%D` | `07/08/01` | Month-day-year format. Same as `%m/%d/%y`. |
|
||||
| `%x` | `07/08/01` | Locale's date representation (e.g., 12/31/99). |
|
||||
| `%F` | `2001-07-08` | Year-month-day format (ISO 8601). Same as `%Y-%m-%d`. |
|
||||
| `%v` | ` 8-Jul-2001` | Day-month-year format. Same as `%e-%b-%Y`. |
|
||||
| | | |
|
||||
| | | **TIME SPECIFIERS:** |
|
||||
| `%H` | `00` | Hour number (00--23), zero-padded to 2 digits. |
|
||||
| `%k` | ` 0` | Same as `%H` but space-padded. Same as `%_H`. |
|
||||
| `%I` | `12` | Hour number in 12-hour clocks (01--12), zero-padded to 2 digits. |
|
||||
| `%l` | `12` | Same as `%I` but space-padded. Same as `%_I`. |
|
||||
| | | |
|
||||
| `%P` | `am` | `am` or `pm` in 12-hour clocks. |
|
||||
| `%p` | `AM` | `AM` or `PM` in 12-hour clocks. |
|
||||
| | | |
|
||||
| `%M` | `34` | Minute number (00--59), zero-padded to 2 digits. |
|
||||
| `%S` | `60` | Second number (00--60), zero-padded to 2 digits. [^5] |
|
||||
| `%f` | `026490000` | The fractional seconds (in nanoseconds) since last whole second. [^8] |
|
||||
| `%.f` | `.026490`| Similar to `.%f` but left-aligned. These all consume the leading dot. [^8] |
|
||||
| `%.3f`| `.026` | Similar to `.%f` but left-aligned but fixed to a length of 3. [^8] |
|
||||
| `%.6f`| `.026490` | Similar to `.%f` but left-aligned but fixed to a length of 6. [^8] |
|
||||
| `%.9f`| `.026490000` | Similar to `.%f` but left-aligned but fixed to a length of 9. [^8] |
|
||||
| `%3f` | `026` | Similar to `%.3f` but without the leading dot. [^8] |
|
||||
| `%6f` | `026490` | Similar to `%.6f` but without the leading dot. [^8] |
|
||||
| `%9f` | `026490000` | Similar to `%.9f` but without the leading dot. [^8] |
|
||||
| | | |
|
||||
| `%R` | `00:34` | Hour-minute format. Same as `%H:%M`. |
|
||||
| `%T` | `00:34:60` | Hour-minute-second format. Same as `%H:%M:%S`. |
|
||||
| `%X` | `00:34:60` | Locale's time representation (e.g., 23:13:48). |
|
||||
| `%r` | `12:34:60 AM` | Hour-minute-second format in 12-hour clocks. Same as `%I:%M:%S %p`. |
|
||||
| | | |
|
||||
| | | **TIME ZONE SPECIFIERS:** |
|
||||
| `%Z` | `ACST` | Local time zone name. Skips all non-whitespace characters during parsing. [^9] |
|
||||
| `%z` | `+0930` | Offset from the local time to UTC (with UTC being `+0000`). |
|
||||
| `%:z` | `+09:30` | Same as `%z` but with a colon. |
|
||||
| `%#z` | `+09` | *Parsing only:* Same as `%z` but allows minutes to be missing or present. |
|
||||
| | | |
|
||||
| | | **DATE & TIME SPECIFIERS:** |
|
||||
|`%c`|`Sun Jul 8 00:34:60 2001`|Locale's date and time (e.g., Thu Mar 3 23:05:25 2005). |
|
||||
| `%+` | `2001-07-08T00:34:60.026490+09:30` | ISO 8601 / RFC 3339 date & time format. [^6] |
|
||||
| | | |
|
||||
| `%s` | `994518299` | UNIX timestamp, the number of seconds since 1970-01-01 00:00 UTC. [^7]|
|
||||
| | | |
|
||||
| | | **SPECIAL SPECIFIERS:** |
|
||||
| `%t` | | Literal tab (`\t`). |
|
||||
| `%n` | | Literal newline (`\n`). |
|
||||
| `%%` | | Literal percent sign. |
|
||||
|
||||
It is possible to override the default padding behavior of numeric specifiers `%?`.
|
||||
This is not allowed for other specifiers and will result in the `BAD_FORMAT` error.
|
||||
|
||||
Modifier | Description
|
||||
-------- | -----------
|
||||
`%-?` | Suppresses any padding including spaces and zeroes. (e.g. `%j` = `012`, `%-j` = `12`)
|
||||
`%_?` | Uses spaces as a padding. (e.g. `%j` = `012`, `%_j` = ` 12`)
|
||||
`%0?` | Uses zeroes as a padding. (e.g. `%e` = ` 9`, `%0e` = `09`)
|
||||
|
||||
Notes:
|
||||
|
||||
[^1]: `%Y`:
|
||||
Negative years are allowed in formatting but not in parsing.
|
||||
|
||||
[^2]: `%C`, `%y`:
|
||||
This is floor division, so 100 BCE (year number -99) will print `-1` and `99` respectively.
|
||||
|
||||
[^3]: `%U`:
|
||||
Week 1 starts with the first Sunday in that year.
|
||||
It is possible to have week 0 for days before the first Sunday.
|
||||
|
||||
[^4]: `%G`, `%g`, `%V`:
|
||||
Week 1 is the first week with at least 4 days in that year.
|
||||
Week 0 does not exist, so this should be used with `%G` or `%g`.
|
||||
|
||||
[^5]: `%S`:
|
||||
It accounts for leap seconds, so `60` is possible.
|
||||
|
||||
[^6]: `%+`: Same as `%Y-%m-%dT%H:%M:%S%.f%:z`, i.e. 0, 3, 6 or 9 fractional
|
||||
digits for seconds and colons in the time zone offset.
|
||||
<br>
|
||||
<br>
|
||||
The typical `strftime` implementations have different (and locale-dependent)
|
||||
formats for this specifier. While Chrono's format for `%+` is far more
|
||||
stable, it is best to avoid this specifier if you want to control the exact
|
||||
output.
|
||||
|
||||
[^7]: `%s`:
|
||||
This is not padded and can be negative.
|
||||
For the purpose of Chrono, it only accounts for non-leap seconds
|
||||
so it slightly differs from ISO C `strftime` behavior.
|
||||
|
||||
[^8]: `%f`, `%.f`, `%.3f`, `%.6f`, `%.9f`, `%3f`, `%6f`, `%9f`:
|
||||
<br>
|
||||
The default `%f` is right-aligned and always zero-padded to 9 digits
|
||||
for the compatibility with glibc and others,
|
||||
so it always counts the number of nanoseconds since the last whole second.
|
||||
E.g. 7ms after the last second will print `007000000`,
|
||||
and parsing `7000000` will yield the same.
|
||||
<br>
|
||||
<br>
|
||||
The variant `%.f` is left-aligned and print 0, 3, 6 or 9 fractional digits
|
||||
according to the precision.
|
||||
E.g. 70ms after the last second under `%.f` will print `.070` (note: not `.07`),
|
||||
and parsing `.07`, `.070000` etc. will yield the same.
|
||||
Note that they can print or read nothing if the fractional part is zero or
|
||||
the next character is not `.`.
|
||||
<br>
|
||||
<br>
|
||||
The variant `%.3f`, `%.6f` and `%.9f` are left-aligned and print 3, 6 or 9 fractional digits
|
||||
according to the number preceding `f`.
|
||||
E.g. 70ms after the last second under `%.3f` will print `.070` (note: not `.07`),
|
||||
and parsing `.07`, `.070000` etc. will yield the same.
|
||||
Note that they can read nothing if the fractional part is zero or
|
||||
the next character is not `.` however will print with the specified length.
|
||||
<br>
|
||||
<br>
|
||||
The variant `%3f`, `%6f` and `%9f` are left-aligned and print 3, 6 or 9 fractional digits
|
||||
according to the number preceding `f`, but without the leading dot.
|
||||
E.g. 70ms after the last second under `%3f` will print `070` (note: not `07`),
|
||||
and parsing `07`, `070000` etc. will yield the same.
|
||||
Note that they can read nothing if the fractional part is zero.
|
||||
|
||||
[^9]: `%Z`:
|
||||
Offset will not be populated from the parsed data, nor will it be validated.
|
||||
Timezone is completely ignored. Similar to the glibc `strptime` treatment of
|
||||
this format code.
|
||||
<br>
|
||||
<br>
|
||||
It is not possible to reliably convert from an abbreviation to an offset,
|
||||
for example CDT can mean either Central Daylight Time (North America) or
|
||||
China Daylight Time.
|
||||
*/
|
||||
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
use super::{locales, Locale};
|
||||
use super::{Fixed, InternalFixed, InternalInternal, Item, Numeric, Pad};
|
||||
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
type Fmt<'a> = Vec<Item<'a>>;
|
||||
#[cfg(not(feature = "unstable-locales"))]
|
||||
type Fmt<'a> = &'static [Item<'static>];
|
||||
|
||||
static D_FMT: &'static [Item<'static>] =
|
||||
&[num0!(Month), lit!("/"), num0!(Day), lit!("/"), num0!(YearMod100)];
|
||||
static D_T_FMT: &'static [Item<'static>] = &[
|
||||
fix!(ShortWeekdayName),
|
||||
sp!(" "),
|
||||
fix!(ShortMonthName),
|
||||
sp!(" "),
|
||||
nums!(Day),
|
||||
sp!(" "),
|
||||
num0!(Hour),
|
||||
lit!(":"),
|
||||
num0!(Minute),
|
||||
lit!(":"),
|
||||
num0!(Second),
|
||||
sp!(" "),
|
||||
num0!(Year),
|
||||
];
|
||||
static T_FMT: &'static [Item<'static>] =
|
||||
&[num0!(Hour), lit!(":"), num0!(Minute), lit!(":"), num0!(Second)];
|
||||
|
||||
/// Parsing iterator for `strftime`-like format strings.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct StrftimeItems<'a> {
|
||||
/// Remaining portion of the string.
|
||||
remainder: &'a str,
|
||||
/// If the current specifier is composed of multiple formatting items (e.g. `%+`),
|
||||
/// parser refers to the statically reconstructed slice of them.
|
||||
/// If `recons` is not empty they have to be returned earlier than the `remainder`.
|
||||
recons: Fmt<'a>,
|
||||
/// Date format
|
||||
d_fmt: Fmt<'a>,
|
||||
/// Date and time format
|
||||
d_t_fmt: Fmt<'a>,
|
||||
/// Time format
|
||||
t_fmt: Fmt<'a>,
|
||||
}
|
||||
|
||||
impl<'a> StrftimeItems<'a> {
|
||||
/// Creates a new parsing iterator from the `strftime`-like format string.
|
||||
pub fn new(s: &'a str) -> StrftimeItems<'a> {
|
||||
Self::with_remainer(s)
|
||||
}
|
||||
|
||||
/// Creates a new parsing iterator from the `strftime`-like format string.
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
pub fn new_with_locale(s: &'a str, locale: Locale) -> StrftimeItems<'a> {
|
||||
let d_fmt = StrftimeItems::new(locales::d_fmt(locale)).collect();
|
||||
let d_t_fmt = StrftimeItems::new(locales::d_t_fmt(locale)).collect();
|
||||
let t_fmt = StrftimeItems::new(locales::t_fmt(locale)).collect();
|
||||
|
||||
StrftimeItems {
|
||||
remainder: s,
|
||||
recons: Vec::new(),
|
||||
d_fmt: d_fmt,
|
||||
d_t_fmt: d_t_fmt,
|
||||
t_fmt: t_fmt,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "unstable-locales"))]
|
||||
fn with_remainer(s: &'a str) -> StrftimeItems<'a> {
|
||||
static FMT_NONE: &'static [Item<'static>; 0] = &[];
|
||||
|
||||
StrftimeItems {
|
||||
remainder: s,
|
||||
recons: FMT_NONE,
|
||||
d_fmt: D_FMT,
|
||||
d_t_fmt: D_T_FMT,
|
||||
t_fmt: T_FMT,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
fn with_remainer(s: &'a str) -> StrftimeItems<'a> {
|
||||
StrftimeItems {
|
||||
remainder: s,
|
||||
recons: Vec::new(),
|
||||
d_fmt: D_FMT.to_vec(),
|
||||
d_t_fmt: D_T_FMT.to_vec(),
|
||||
t_fmt: T_FMT.to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const HAVE_ALTERNATES: &'static str = "z";
|
||||
|
||||
impl<'a> Iterator for StrftimeItems<'a> {
|
||||
type Item = Item<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Item<'a>> {
|
||||
// we have some reconstructed items to return
|
||||
if !self.recons.is_empty() {
|
||||
let item;
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
{
|
||||
item = self.recons.remove(0);
|
||||
}
|
||||
#[cfg(not(feature = "unstable-locales"))]
|
||||
{
|
||||
item = self.recons[0].clone();
|
||||
self.recons = &self.recons[1..];
|
||||
}
|
||||
return Some(item);
|
||||
}
|
||||
|
||||
match self.remainder.chars().next() {
|
||||
// we are done
|
||||
None => None,
|
||||
|
||||
// the next item is a specifier
|
||||
Some('%') => {
|
||||
self.remainder = &self.remainder[1..];
|
||||
|
||||
macro_rules! next {
|
||||
() => {
|
||||
match self.remainder.chars().next() {
|
||||
Some(x) => {
|
||||
self.remainder = &self.remainder[x.len_utf8()..];
|
||||
x
|
||||
}
|
||||
None => return Some(Item::Error), // premature end of string
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let spec = next!();
|
||||
let pad_override = match spec {
|
||||
'-' => Some(Pad::None),
|
||||
'0' => Some(Pad::Zero),
|
||||
'_' => Some(Pad::Space),
|
||||
_ => None,
|
||||
};
|
||||
let is_alternate = spec == '#';
|
||||
let spec = if pad_override.is_some() || is_alternate { next!() } else { spec };
|
||||
if is_alternate && !HAVE_ALTERNATES.contains(spec) {
|
||||
return Some(Item::Error);
|
||||
}
|
||||
|
||||
macro_rules! recons {
|
||||
[$head:expr, $($tail:expr),+ $(,)*] => ({
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
{
|
||||
self.recons.clear();
|
||||
$(self.recons.push($tail);)+
|
||||
}
|
||||
#[cfg(not(feature = "unstable-locales"))]
|
||||
{
|
||||
const RECONS: &'static [Item<'static>] = &[$($tail),+];
|
||||
self.recons = RECONS;
|
||||
}
|
||||
$head
|
||||
})
|
||||
}
|
||||
|
||||
macro_rules! recons_from_slice {
|
||||
($slice:expr) => {{
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
{
|
||||
self.recons.clear();
|
||||
self.recons.extend_from_slice(&$slice[1..]);
|
||||
}
|
||||
#[cfg(not(feature = "unstable-locales"))]
|
||||
{
|
||||
self.recons = &$slice[1..];
|
||||
}
|
||||
$slice[0].clone()
|
||||
}};
|
||||
}
|
||||
|
||||
let item = match spec {
|
||||
'A' => fix!(LongWeekdayName),
|
||||
'B' => fix!(LongMonthName),
|
||||
'C' => num0!(YearDiv100),
|
||||
'D' => {
|
||||
recons![num0!(Month), lit!("/"), num0!(Day), lit!("/"), num0!(YearMod100)]
|
||||
}
|
||||
'F' => recons![num0!(Year), lit!("-"), num0!(Month), lit!("-"), num0!(Day)],
|
||||
'G' => num0!(IsoYear),
|
||||
'H' => num0!(Hour),
|
||||
'I' => num0!(Hour12),
|
||||
'M' => num0!(Minute),
|
||||
'P' => fix!(LowerAmPm),
|
||||
'R' => recons![num0!(Hour), lit!(":"), num0!(Minute)],
|
||||
'S' => num0!(Second),
|
||||
'T' => recons![num0!(Hour), lit!(":"), num0!(Minute), lit!(":"), num0!(Second)],
|
||||
'U' => num0!(WeekFromSun),
|
||||
'V' => num0!(IsoWeek),
|
||||
'W' => num0!(WeekFromMon),
|
||||
'X' => recons_from_slice!(self.t_fmt),
|
||||
'Y' => num0!(Year),
|
||||
'Z' => fix!(TimezoneName),
|
||||
'a' => fix!(ShortWeekdayName),
|
||||
'b' | 'h' => fix!(ShortMonthName),
|
||||
'c' => recons_from_slice!(self.d_t_fmt),
|
||||
'd' => num0!(Day),
|
||||
'e' => nums!(Day),
|
||||
'f' => num0!(Nanosecond),
|
||||
'g' => num0!(IsoYearMod100),
|
||||
'j' => num0!(Ordinal),
|
||||
'k' => nums!(Hour),
|
||||
'l' => nums!(Hour12),
|
||||
'm' => num0!(Month),
|
||||
'n' => sp!("\n"),
|
||||
'p' => fix!(UpperAmPm),
|
||||
'r' => recons![
|
||||
num0!(Hour12),
|
||||
lit!(":"),
|
||||
num0!(Minute),
|
||||
lit!(":"),
|
||||
num0!(Second),
|
||||
sp!(" "),
|
||||
fix!(UpperAmPm)
|
||||
],
|
||||
's' => num!(Timestamp),
|
||||
't' => sp!("\t"),
|
||||
'u' => num!(WeekdayFromMon),
|
||||
'v' => {
|
||||
recons![nums!(Day), lit!("-"), fix!(ShortMonthName), lit!("-"), num0!(Year)]
|
||||
}
|
||||
'w' => num!(NumDaysFromSun),
|
||||
'x' => recons_from_slice!(self.d_fmt),
|
||||
'y' => num0!(YearMod100),
|
||||
'z' => {
|
||||
if is_alternate {
|
||||
internal_fix!(TimezoneOffsetPermissive)
|
||||
} else {
|
||||
fix!(TimezoneOffset)
|
||||
}
|
||||
}
|
||||
'+' => fix!(RFC3339),
|
||||
':' => match next!() {
|
||||
'z' => fix!(TimezoneOffsetColon),
|
||||
_ => Item::Error,
|
||||
},
|
||||
'.' => match next!() {
|
||||
'3' => match next!() {
|
||||
'f' => fix!(Nanosecond3),
|
||||
_ => Item::Error,
|
||||
},
|
||||
'6' => match next!() {
|
||||
'f' => fix!(Nanosecond6),
|
||||
_ => Item::Error,
|
||||
},
|
||||
'9' => match next!() {
|
||||
'f' => fix!(Nanosecond9),
|
||||
_ => Item::Error,
|
||||
},
|
||||
'f' => fix!(Nanosecond),
|
||||
_ => Item::Error,
|
||||
},
|
||||
'3' => match next!() {
|
||||
'f' => internal_fix!(Nanosecond3NoDot),
|
||||
_ => Item::Error,
|
||||
},
|
||||
'6' => match next!() {
|
||||
'f' => internal_fix!(Nanosecond6NoDot),
|
||||
_ => Item::Error,
|
||||
},
|
||||
'9' => match next!() {
|
||||
'f' => internal_fix!(Nanosecond9NoDot),
|
||||
_ => Item::Error,
|
||||
},
|
||||
'%' => lit!("%"),
|
||||
_ => Item::Error, // no such specifier
|
||||
};
|
||||
|
||||
// adjust `item` if we have any padding modifier
|
||||
if let Some(new_pad) = pad_override {
|
||||
match item {
|
||||
Item::Numeric(ref kind, _pad) if self.recons.is_empty() => {
|
||||
Some(Item::Numeric(kind.clone(), new_pad))
|
||||
}
|
||||
_ => Some(Item::Error), // no reconstructed or non-numeric item allowed
|
||||
}
|
||||
} else {
|
||||
Some(item)
|
||||
}
|
||||
}
|
||||
|
||||
// the next item is space
|
||||
Some(c) if c.is_whitespace() => {
|
||||
// `%` is not a whitespace, so `c != '%'` is redundant
|
||||
let nextspec = self
|
||||
.remainder
|
||||
.find(|c: char| !c.is_whitespace())
|
||||
.unwrap_or_else(|| self.remainder.len());
|
||||
assert!(nextspec > 0);
|
||||
let item = sp!(&self.remainder[..nextspec]);
|
||||
self.remainder = &self.remainder[nextspec..];
|
||||
Some(item)
|
||||
}
|
||||
|
||||
// the next item is literal
|
||||
_ => {
|
||||
let nextspec = self
|
||||
.remainder
|
||||
.find(|c: char| c.is_whitespace() || c == '%')
|
||||
.unwrap_or_else(|| self.remainder.len());
|
||||
assert!(nextspec > 0);
|
||||
let item = lit!(&self.remainder[..nextspec]);
|
||||
self.remainder = &self.remainder[nextspec..];
|
||||
Some(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn test_strftime_items() {
|
||||
fn parse_and_collect<'a>(s: &'a str) -> Vec<Item<'a>> {
|
||||
// map any error into `[Item::Error]`. useful for easy testing.
|
||||
let items = StrftimeItems::new(s);
|
||||
let items = items.map(|spec| if spec == Item::Error { None } else { Some(spec) });
|
||||
items.collect::<Option<Vec<_>>>().unwrap_or(vec![Item::Error])
|
||||
}
|
||||
|
||||
assert_eq!(parse_and_collect(""), []);
|
||||
assert_eq!(parse_and_collect(" \t\n\r "), [sp!(" \t\n\r ")]);
|
||||
assert_eq!(parse_and_collect("hello?"), [lit!("hello?")]);
|
||||
assert_eq!(
|
||||
parse_and_collect("a b\t\nc"),
|
||||
[lit!("a"), sp!(" "), lit!("b"), sp!("\t\n"), lit!("c")]
|
||||
);
|
||||
assert_eq!(parse_and_collect("100%%"), [lit!("100"), lit!("%")]);
|
||||
assert_eq!(parse_and_collect("100%% ok"), [lit!("100"), lit!("%"), sp!(" "), lit!("ok")]);
|
||||
assert_eq!(parse_and_collect("%%PDF-1.0"), [lit!("%"), lit!("PDF-1.0")]);
|
||||
assert_eq!(
|
||||
parse_and_collect("%Y-%m-%d"),
|
||||
[num0!(Year), lit!("-"), num0!(Month), lit!("-"), num0!(Day)]
|
||||
);
|
||||
assert_eq!(parse_and_collect("[%F]"), parse_and_collect("[%Y-%m-%d]"));
|
||||
assert_eq!(parse_and_collect("%m %d"), [num0!(Month), sp!(" "), num0!(Day)]);
|
||||
assert_eq!(parse_and_collect("%"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%%"), [lit!("%")]);
|
||||
assert_eq!(parse_and_collect("%%%"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%%%%"), [lit!("%"), lit!("%")]);
|
||||
assert_eq!(parse_and_collect("foo%?"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("bar%42"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("quux% +"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%.Z"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%:Z"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%-Z"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%0Z"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%_Z"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%.j"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%:j"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%-j"), [num!(Ordinal)]);
|
||||
assert_eq!(parse_and_collect("%0j"), [num0!(Ordinal)]);
|
||||
assert_eq!(parse_and_collect("%_j"), [nums!(Ordinal)]);
|
||||
assert_eq!(parse_and_collect("%.e"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%:e"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%-e"), [num!(Day)]);
|
||||
assert_eq!(parse_and_collect("%0e"), [num0!(Day)]);
|
||||
assert_eq!(parse_and_collect("%_e"), [nums!(Day)]);
|
||||
assert_eq!(parse_and_collect("%z"), [fix!(TimezoneOffset)]);
|
||||
assert_eq!(parse_and_collect("%#z"), [internal_fix!(TimezoneOffsetPermissive)]);
|
||||
assert_eq!(parse_and_collect("%#m"), [Item::Error]);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn test_strftime_docs() {
|
||||
use {FixedOffset, TimeZone, Timelike};
|
||||
|
||||
let dt = FixedOffset::east(34200).ymd(2001, 7, 8).and_hms_nano(0, 34, 59, 1_026_490_708);
|
||||
|
||||
// date specifiers
|
||||
assert_eq!(dt.format("%Y").to_string(), "2001");
|
||||
assert_eq!(dt.format("%C").to_string(), "20");
|
||||
assert_eq!(dt.format("%y").to_string(), "01");
|
||||
assert_eq!(dt.format("%m").to_string(), "07");
|
||||
assert_eq!(dt.format("%b").to_string(), "Jul");
|
||||
assert_eq!(dt.format("%B").to_string(), "July");
|
||||
assert_eq!(dt.format("%h").to_string(), "Jul");
|
||||
assert_eq!(dt.format("%d").to_string(), "08");
|
||||
assert_eq!(dt.format("%e").to_string(), " 8");
|
||||
assert_eq!(dt.format("%e").to_string(), dt.format("%_d").to_string());
|
||||
assert_eq!(dt.format("%a").to_string(), "Sun");
|
||||
assert_eq!(dt.format("%A").to_string(), "Sunday");
|
||||
assert_eq!(dt.format("%w").to_string(), "0");
|
||||
assert_eq!(dt.format("%u").to_string(), "7");
|
||||
assert_eq!(dt.format("%U").to_string(), "28");
|
||||
assert_eq!(dt.format("%W").to_string(), "27");
|
||||
assert_eq!(dt.format("%G").to_string(), "2001");
|
||||
assert_eq!(dt.format("%g").to_string(), "01");
|
||||
assert_eq!(dt.format("%V").to_string(), "27");
|
||||
assert_eq!(dt.format("%j").to_string(), "189");
|
||||
assert_eq!(dt.format("%D").to_string(), "07/08/01");
|
||||
assert_eq!(dt.format("%x").to_string(), "07/08/01");
|
||||
assert_eq!(dt.format("%F").to_string(), "2001-07-08");
|
||||
assert_eq!(dt.format("%v").to_string(), " 8-Jul-2001");
|
||||
|
||||
// time specifiers
|
||||
assert_eq!(dt.format("%H").to_string(), "00");
|
||||
assert_eq!(dt.format("%k").to_string(), " 0");
|
||||
assert_eq!(dt.format("%k").to_string(), dt.format("%_H").to_string());
|
||||
assert_eq!(dt.format("%I").to_string(), "12");
|
||||
assert_eq!(dt.format("%l").to_string(), "12");
|
||||
assert_eq!(dt.format("%l").to_string(), dt.format("%_I").to_string());
|
||||
assert_eq!(dt.format("%P").to_string(), "am");
|
||||
assert_eq!(dt.format("%p").to_string(), "AM");
|
||||
assert_eq!(dt.format("%M").to_string(), "34");
|
||||
assert_eq!(dt.format("%S").to_string(), "60");
|
||||
assert_eq!(dt.format("%f").to_string(), "026490708");
|
||||
assert_eq!(dt.format("%.f").to_string(), ".026490708");
|
||||
assert_eq!(dt.with_nanosecond(1_026_490_000).unwrap().format("%.f").to_string(), ".026490");
|
||||
assert_eq!(dt.format("%.3f").to_string(), ".026");
|
||||
assert_eq!(dt.format("%.6f").to_string(), ".026490");
|
||||
assert_eq!(dt.format("%.9f").to_string(), ".026490708");
|
||||
assert_eq!(dt.format("%3f").to_string(), "026");
|
||||
assert_eq!(dt.format("%6f").to_string(), "026490");
|
||||
assert_eq!(dt.format("%9f").to_string(), "026490708");
|
||||
assert_eq!(dt.format("%R").to_string(), "00:34");
|
||||
assert_eq!(dt.format("%T").to_string(), "00:34:60");
|
||||
assert_eq!(dt.format("%X").to_string(), "00:34:60");
|
||||
assert_eq!(dt.format("%r").to_string(), "12:34:60 AM");
|
||||
|
||||
// time zone specifiers
|
||||
//assert_eq!(dt.format("%Z").to_string(), "ACST");
|
||||
assert_eq!(dt.format("%z").to_string(), "+0930");
|
||||
assert_eq!(dt.format("%:z").to_string(), "+09:30");
|
||||
|
||||
// date & time specifiers
|
||||
assert_eq!(dt.format("%c").to_string(), "Sun Jul 8 00:34:60 2001");
|
||||
assert_eq!(dt.format("%+").to_string(), "2001-07-08T00:34:60.026490708+09:30");
|
||||
assert_eq!(
|
||||
dt.with_nanosecond(1_026_490_000).unwrap().format("%+").to_string(),
|
||||
"2001-07-08T00:34:60.026490+09:30"
|
||||
);
|
||||
assert_eq!(dt.format("%s").to_string(), "994518299");
|
||||
|
||||
// special specifiers
|
||||
assert_eq!(dt.format("%t").to_string(), "\t");
|
||||
assert_eq!(dt.format("%n").to_string(), "\n");
|
||||
assert_eq!(dt.format("%%").to_string(), "%");
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
#[test]
|
||||
fn test_strftime_docs_localized() {
|
||||
use {FixedOffset, TimeZone};
|
||||
|
||||
let dt = FixedOffset::east(34200).ymd(2001, 7, 8).and_hms_nano(0, 34, 59, 1_026_490_708);
|
||||
|
||||
// date specifiers
|
||||
assert_eq!(dt.format_localized("%b", Locale::fr_BE).to_string(), "jui");
|
||||
assert_eq!(dt.format_localized("%B", Locale::fr_BE).to_string(), "juillet");
|
||||
assert_eq!(dt.format_localized("%h", Locale::fr_BE).to_string(), "jui");
|
||||
assert_eq!(dt.format_localized("%a", Locale::fr_BE).to_string(), "dim");
|
||||
assert_eq!(dt.format_localized("%A", Locale::fr_BE).to_string(), "dimanche");
|
||||
assert_eq!(dt.format_localized("%D", Locale::fr_BE).to_string(), "07/08/01");
|
||||
assert_eq!(dt.format_localized("%x", Locale::fr_BE).to_string(), "08/07/01");
|
||||
assert_eq!(dt.format_localized("%F", Locale::fr_BE).to_string(), "2001-07-08");
|
||||
assert_eq!(dt.format_localized("%v", Locale::fr_BE).to_string(), " 8-jui-2001");
|
||||
|
||||
// time specifiers
|
||||
assert_eq!(dt.format_localized("%P", Locale::fr_BE).to_string(), "");
|
||||
assert_eq!(dt.format_localized("%p", Locale::fr_BE).to_string(), "");
|
||||
assert_eq!(dt.format_localized("%R", Locale::fr_BE).to_string(), "00:34");
|
||||
assert_eq!(dt.format_localized("%T", Locale::fr_BE).to_string(), "00:34:60");
|
||||
assert_eq!(dt.format_localized("%X", Locale::fr_BE).to_string(), "00:34:60");
|
||||
assert_eq!(dt.format_localized("%r", Locale::fr_BE).to_string(), "12:34:60 ");
|
||||
|
||||
// date & time specifiers
|
||||
assert_eq!(
|
||||
dt.format_localized("%c", Locale::fr_BE).to_string(),
|
||||
"dim 08 jui 2001 00:34:60 +09:30"
|
||||
);
|
||||
}
|
||||
1535
zeroidc/vendor/chrono/src/lib.rs
vendored
Normal file
1535
zeroidc/vendor/chrono/src/lib.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2392
zeroidc/vendor/chrono/src/naive/date.rs
vendored
Normal file
2392
zeroidc/vendor/chrono/src/naive/date.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2507
zeroidc/vendor/chrono/src/naive/datetime.rs
vendored
Normal file
2507
zeroidc/vendor/chrono/src/naive/datetime.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
815
zeroidc/vendor/chrono/src/naive/internals.rs
vendored
Normal file
815
zeroidc/vendor/chrono/src/naive/internals.rs
vendored
Normal file
@@ -0,0 +1,815 @@
|
||||
// This is a part of Chrono.
|
||||
// See README.md and LICENSE.txt for details.
|
||||
|
||||
//! The internal implementation of the calendar and ordinal date.
|
||||
//!
|
||||
//! The current implementation is optimized for determining year, month, day and day of week.
|
||||
//! 4-bit `YearFlags` map to one of 14 possible classes of year in the Gregorian calendar,
|
||||
//! which are included in every packed `NaiveDate` instance.
|
||||
//! The conversion between the packed calendar date (`Mdf`) and the ordinal date (`Of`) is
|
||||
//! based on the moderately-sized lookup table (~1.5KB)
|
||||
//! and the packed representation is chosen for the efficient lookup.
|
||||
//! Every internal data structure does not validate its input,
|
||||
//! but the conversion keeps the valid value valid and the invalid value invalid
|
||||
//! so that the user-facing `NaiveDate` can validate the input as late as possible.
|
||||
|
||||
#![allow(dead_code)] // some internal methods have been left for consistency
|
||||
#![cfg_attr(feature = "__internal_bench", allow(missing_docs))]
|
||||
|
||||
use core::{fmt, i32};
|
||||
use div::{div_rem, mod_floor};
|
||||
use num_traits::FromPrimitive;
|
||||
use Weekday;
|
||||
|
||||
/// The internal date representation. This also includes the packed `Mdf` value.
|
||||
pub type DateImpl = i32;
|
||||
|
||||
pub const MAX_YEAR: DateImpl = i32::MAX >> 13;
|
||||
pub const MIN_YEAR: DateImpl = i32::MIN >> 13;
|
||||
|
||||
/// The year flags (aka the dominical letter).
|
||||
///
|
||||
/// There are 14 possible classes of year in the Gregorian calendar:
|
||||
/// common and leap years starting with Monday through Sunday.
|
||||
/// The `YearFlags` stores this information into 4 bits `abbb`,
|
||||
/// where `a` is `1` for the common year (simplifies the `Of` validation)
|
||||
/// and `bbb` is a non-zero `Weekday` (mapping `Mon` to 7) of the last day in the past year
|
||||
/// (simplifies the day of week calculation from the 1-based ordinal).
|
||||
#[derive(PartialEq, Eq, Copy, Clone)]
|
||||
pub struct YearFlags(pub u8);
|
||||
|
||||
pub const A: YearFlags = YearFlags(0o15);
|
||||
pub const AG: YearFlags = YearFlags(0o05);
|
||||
pub const B: YearFlags = YearFlags(0o14);
|
||||
pub const BA: YearFlags = YearFlags(0o04);
|
||||
pub const C: YearFlags = YearFlags(0o13);
|
||||
pub const CB: YearFlags = YearFlags(0o03);
|
||||
pub const D: YearFlags = YearFlags(0o12);
|
||||
pub const DC: YearFlags = YearFlags(0o02);
|
||||
pub const E: YearFlags = YearFlags(0o11);
|
||||
pub const ED: YearFlags = YearFlags(0o01);
|
||||
pub const F: YearFlags = YearFlags(0o17);
|
||||
pub const FE: YearFlags = YearFlags(0o07);
|
||||
pub const G: YearFlags = YearFlags(0o16);
|
||||
pub const GF: YearFlags = YearFlags(0o06);
|
||||
|
||||
static YEAR_TO_FLAGS: [YearFlags; 400] = [
|
||||
BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA,
|
||||
G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G,
|
||||
F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F,
|
||||
E, DC, B, A, G, FE, D, C, B, AG, F, E, D, // 100
|
||||
C, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC,
|
||||
B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B,
|
||||
A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A,
|
||||
G, FE, D, C, B, AG, F, E, D, CB, A, G, F, // 200
|
||||
E, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G, FE,
|
||||
D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D,
|
||||
C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, C,
|
||||
B, AG, F, E, D, CB, A, G, F, ED, C, B, A, // 300
|
||||
G, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, C, B, AG,
|
||||
F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F,
|
||||
E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E,
|
||||
D, CB, A, G, F, ED, C, B, A, GF, E, D, C, // 400
|
||||
];
|
||||
|
||||
static YEAR_DELTAS: [u8; 401] = [
|
||||
0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8,
|
||||
8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14,
|
||||
15, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20,
|
||||
21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, 25, // 100
|
||||
25, 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30,
|
||||
30, 31, 31, 31, 31, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 36,
|
||||
36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 39, 39, 39, 40, 40, 40, 40, 41, 41, 41, 41, 42, 42, 42,
|
||||
42, 43, 43, 43, 43, 44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47, 48, 48, 48,
|
||||
48, 49, 49, 49, // 200
|
||||
49, 49, 49, 49, 49, 50, 50, 50, 50, 51, 51, 51, 51, 52, 52, 52, 52, 53, 53, 53, 53, 54, 54, 54,
|
||||
54, 55, 55, 55, 55, 56, 56, 56, 56, 57, 57, 57, 57, 58, 58, 58, 58, 59, 59, 59, 59, 60, 60, 60,
|
||||
60, 61, 61, 61, 61, 62, 62, 62, 62, 63, 63, 63, 63, 64, 64, 64, 64, 65, 65, 65, 65, 66, 66, 66,
|
||||
66, 67, 67, 67, 67, 68, 68, 68, 68, 69, 69, 69, 69, 70, 70, 70, 70, 71, 71, 71, 71, 72, 72, 72,
|
||||
72, 73, 73, 73, // 300
|
||||
73, 73, 73, 73, 73, 74, 74, 74, 74, 75, 75, 75, 75, 76, 76, 76, 76, 77, 77, 77, 77, 78, 78, 78,
|
||||
78, 79, 79, 79, 79, 80, 80, 80, 80, 81, 81, 81, 81, 82, 82, 82, 82, 83, 83, 83, 83, 84, 84, 84,
|
||||
84, 85, 85, 85, 85, 86, 86, 86, 86, 87, 87, 87, 87, 88, 88, 88, 88, 89, 89, 89, 89, 90, 90, 90,
|
||||
90, 91, 91, 91, 91, 92, 92, 92, 92, 93, 93, 93, 93, 94, 94, 94, 94, 95, 95, 95, 95, 96, 96, 96,
|
||||
96, 97, 97, 97, 97, // 400+1
|
||||
];
|
||||
|
||||
pub fn cycle_to_yo(cycle: u32) -> (u32, u32) {
|
||||
let (mut year_mod_400, mut ordinal0) = div_rem(cycle, 365);
|
||||
let delta = u32::from(YEAR_DELTAS[year_mod_400 as usize]);
|
||||
if ordinal0 < delta {
|
||||
year_mod_400 -= 1;
|
||||
ordinal0 += 365 - u32::from(YEAR_DELTAS[year_mod_400 as usize]);
|
||||
} else {
|
||||
ordinal0 -= delta;
|
||||
}
|
||||
(year_mod_400, ordinal0 + 1)
|
||||
}
|
||||
|
||||
pub fn yo_to_cycle(year_mod_400: u32, ordinal: u32) -> u32 {
|
||||
year_mod_400 * 365 + u32::from(YEAR_DELTAS[year_mod_400 as usize]) + ordinal - 1
|
||||
}
|
||||
|
||||
impl YearFlags {
|
||||
#[inline]
|
||||
pub fn from_year(year: i32) -> YearFlags {
|
||||
let year = mod_floor(year, 400);
|
||||
YearFlags::from_year_mod_400(year)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_year_mod_400(year: i32) -> YearFlags {
|
||||
YEAR_TO_FLAGS[year as usize]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn ndays(&self) -> u32 {
|
||||
let YearFlags(flags) = *self;
|
||||
366 - u32::from(flags >> 3)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn isoweek_delta(&self) -> u32 {
|
||||
let YearFlags(flags) = *self;
|
||||
let mut delta = u32::from(flags) & 0b0111;
|
||||
if delta < 3 {
|
||||
delta += 7;
|
||||
}
|
||||
delta
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn nisoweeks(&self) -> u32 {
|
||||
let YearFlags(flags) = *self;
|
||||
52 + ((0b0000_0100_0000_0110 >> flags as usize) & 1)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for YearFlags {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let YearFlags(flags) = *self;
|
||||
match flags {
|
||||
0o15 => "A".fmt(f),
|
||||
0o05 => "AG".fmt(f),
|
||||
0o14 => "B".fmt(f),
|
||||
0o04 => "BA".fmt(f),
|
||||
0o13 => "C".fmt(f),
|
||||
0o03 => "CB".fmt(f),
|
||||
0o12 => "D".fmt(f),
|
||||
0o02 => "DC".fmt(f),
|
||||
0o11 => "E".fmt(f),
|
||||
0o01 => "ED".fmt(f),
|
||||
0o10 => "F?".fmt(f),
|
||||
0o00 => "FE?".fmt(f), // non-canonical
|
||||
0o17 => "F".fmt(f),
|
||||
0o07 => "FE".fmt(f),
|
||||
0o16 => "G".fmt(f),
|
||||
0o06 => "GF".fmt(f),
|
||||
_ => write!(f, "YearFlags({})", flags),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const MIN_OL: u32 = 1 << 1;
|
||||
pub const MAX_OL: u32 = 366 << 1; // larger than the non-leap last day `(365 << 1) | 1`
|
||||
pub const MIN_MDL: u32 = (1 << 6) | (1 << 1);
|
||||
pub const MAX_MDL: u32 = (12 << 6) | (31 << 1) | 1;
|
||||
|
||||
const XX: i8 = -128;
|
||||
static MDL_TO_OL: [i8; MAX_MDL as usize + 1] = [
|
||||
XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
|
||||
XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
|
||||
XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, // 0
|
||||
XX, XX, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 1
|
||||
XX, XX, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
|
||||
66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
|
||||
66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, XX, XX, XX, XX, XX, // 2
|
||||
XX, XX, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74,
|
||||
72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74,
|
||||
72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, // 3
|
||||
XX, XX, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76,
|
||||
74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76,
|
||||
74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, XX, XX, // 4
|
||||
XX, XX, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80,
|
||||
78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80,
|
||||
78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, // 5
|
||||
XX, XX, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82,
|
||||
80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82,
|
||||
80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, XX, XX, // 6
|
||||
XX, XX, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86,
|
||||
84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86,
|
||||
84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, // 7
|
||||
XX, XX, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88,
|
||||
86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88,
|
||||
86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, // 8
|
||||
XX, XX, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90,
|
||||
88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90,
|
||||
88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, XX, XX, // 9
|
||||
XX, XX, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94,
|
||||
92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94,
|
||||
92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, // 10
|
||||
XX, XX, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96,
|
||||
94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96,
|
||||
94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, XX, XX, // 11
|
||||
XX, XX, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98,
|
||||
100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100,
|
||||
98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98,
|
||||
100, // 12
|
||||
];
|
||||
|
||||
static OL_TO_MDL: [u8; MAX_OL as usize + 1] = [
|
||||
0, 0, // 0
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 1
|
||||
66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
|
||||
66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
|
||||
66, 66, 66, 66, 66, 66, 66, 66, 66, // 2
|
||||
74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72,
|
||||
74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72,
|
||||
74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, // 3
|
||||
76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74,
|
||||
76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74,
|
||||
76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, // 4
|
||||
80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78,
|
||||
80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78,
|
||||
80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, // 5
|
||||
82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80,
|
||||
82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80,
|
||||
82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, // 6
|
||||
86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84,
|
||||
86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84,
|
||||
86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, // 7
|
||||
88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86,
|
||||
88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86,
|
||||
88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, // 8
|
||||
90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88,
|
||||
90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88,
|
||||
90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, // 9
|
||||
94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92,
|
||||
94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92,
|
||||
94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, // 10
|
||||
96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94,
|
||||
96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94,
|
||||
96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, // 11
|
||||
100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100,
|
||||
98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98,
|
||||
100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100,
|
||||
98, // 12
|
||||
];
|
||||
|
||||
/// Ordinal (day of year) and year flags: `(ordinal << 4) | flags`.
|
||||
///
|
||||
/// The whole bits except for the least 3 bits are referred as `Ol` (ordinal and leap flag),
|
||||
/// which is an index to the `OL_TO_MDL` lookup table.
|
||||
#[derive(PartialEq, PartialOrd, Copy, Clone)]
|
||||
pub struct Of(pub u32);
|
||||
|
||||
impl Of {
|
||||
#[inline]
|
||||
fn clamp_ordinal(ordinal: u32) -> u32 {
|
||||
if ordinal > 366 {
|
||||
0
|
||||
} else {
|
||||
ordinal
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn new(ordinal: u32, YearFlags(flags): YearFlags) -> Of {
|
||||
let ordinal = Of::clamp_ordinal(ordinal);
|
||||
Of((ordinal << 4) | u32::from(flags))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_mdf(Mdf(mdf): Mdf) -> Of {
|
||||
let mdl = mdf >> 3;
|
||||
match MDL_TO_OL.get(mdl as usize) {
|
||||
Some(&v) => Of(mdf.wrapping_sub((i32::from(v) as u32 & 0x3ff) << 3)),
|
||||
None => Of(0),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn valid(&self) -> bool {
|
||||
let Of(of) = *self;
|
||||
let ol = of >> 3;
|
||||
MIN_OL <= ol && ol <= MAX_OL
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn ordinal(&self) -> u32 {
|
||||
let Of(of) = *self;
|
||||
of >> 4
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn with_ordinal(&self, ordinal: u32) -> Of {
|
||||
let ordinal = Of::clamp_ordinal(ordinal);
|
||||
let Of(of) = *self;
|
||||
Of((of & 0b1111) | (ordinal << 4))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn flags(&self) -> YearFlags {
|
||||
let Of(of) = *self;
|
||||
YearFlags((of & 0b1111) as u8)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn with_flags(&self, YearFlags(flags): YearFlags) -> Of {
|
||||
let Of(of) = *self;
|
||||
Of((of & !0b1111) | u32::from(flags))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn weekday(&self) -> Weekday {
|
||||
let Of(of) = *self;
|
||||
Weekday::from_u32(((of >> 4) + (of & 0b111)) % 7).unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn isoweekdate_raw(&self) -> (u32, Weekday) {
|
||||
// week ordinal = ordinal + delta
|
||||
let Of(of) = *self;
|
||||
let weekord = (of >> 4).wrapping_add(self.flags().isoweek_delta());
|
||||
(weekord / 7, Weekday::from_u32(weekord % 7).unwrap())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_mdf(&self) -> Mdf {
|
||||
Mdf::from_of(*self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn succ(&self) -> Of {
|
||||
let Of(of) = *self;
|
||||
Of(of + (1 << 4))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn pred(&self) -> Of {
|
||||
let Of(of) = *self;
|
||||
Of(of - (1 << 4))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Of {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let Of(of) = *self;
|
||||
write!(
|
||||
f,
|
||||
"Of(({} << 4) | {:#04o} /*{:?}*/)",
|
||||
of >> 4,
|
||||
of & 0b1111,
|
||||
YearFlags((of & 0b1111) as u8)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Month, day of month and year flags: `(month << 9) | (day << 4) | flags`
|
||||
///
|
||||
/// The whole bits except for the least 3 bits are referred as `Mdl`
|
||||
/// (month, day of month and leap flag),
|
||||
/// which is an index to the `MDL_TO_OL` lookup table.
|
||||
#[derive(PartialEq, PartialOrd, Copy, Clone)]
|
||||
pub struct Mdf(pub u32);
|
||||
|
||||
impl Mdf {
|
||||
#[inline]
|
||||
fn clamp_month(month: u32) -> u32 {
|
||||
if month > 12 {
|
||||
0
|
||||
} else {
|
||||
month
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn clamp_day(day: u32) -> u32 {
|
||||
if day > 31 {
|
||||
0
|
||||
} else {
|
||||
day
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn new(month: u32, day: u32, YearFlags(flags): YearFlags) -> Mdf {
|
||||
let month = Mdf::clamp_month(month);
|
||||
let day = Mdf::clamp_day(day);
|
||||
Mdf((month << 9) | (day << 4) | u32::from(flags))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_of(Of(of): Of) -> Mdf {
|
||||
let ol = of >> 3;
|
||||
match OL_TO_MDL.get(ol as usize) {
|
||||
Some(&v) => Mdf(of + (u32::from(v) << 3)),
|
||||
None => Mdf(0),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn valid(&self) -> bool {
|
||||
let Mdf(mdf) = *self;
|
||||
let mdl = mdf >> 3;
|
||||
match MDL_TO_OL.get(mdl as usize) {
|
||||
Some(&v) => v >= 0,
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn month(&self) -> u32 {
|
||||
let Mdf(mdf) = *self;
|
||||
mdf >> 9
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn with_month(&self, month: u32) -> Mdf {
|
||||
let month = Mdf::clamp_month(month);
|
||||
let Mdf(mdf) = *self;
|
||||
Mdf((mdf & 0b1_1111_1111) | (month << 9))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn day(&self) -> u32 {
|
||||
let Mdf(mdf) = *self;
|
||||
(mdf >> 4) & 0b1_1111
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn with_day(&self, day: u32) -> Mdf {
|
||||
let day = Mdf::clamp_day(day);
|
||||
let Mdf(mdf) = *self;
|
||||
Mdf((mdf & !0b1_1111_0000) | (day << 4))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn flags(&self) -> YearFlags {
|
||||
let Mdf(mdf) = *self;
|
||||
YearFlags((mdf & 0b1111) as u8)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn with_flags(&self, YearFlags(flags): YearFlags) -> Mdf {
|
||||
let Mdf(mdf) = *self;
|
||||
Mdf((mdf & !0b1111) | u32::from(flags))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_of(&self) -> Of {
|
||||
Of::from_mdf(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Mdf {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let Mdf(mdf) = *self;
|
||||
write!(
|
||||
f,
|
||||
"Mdf(({} << 9) | ({} << 4) | {:#04o} /*{:?}*/)",
|
||||
mdf >> 9,
|
||||
(mdf >> 4) & 0b1_1111,
|
||||
mdf & 0b1111,
|
||||
YearFlags((mdf & 0b1111) as u8)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[cfg(test)]
|
||||
extern crate num_iter;
|
||||
|
||||
use self::num_iter::range_inclusive;
|
||||
use super::{Mdf, Of};
|
||||
use super::{YearFlags, A, AG, B, BA, C, CB, D, DC, E, ED, F, FE, G, GF};
|
||||
use std::u32;
|
||||
use Weekday;
|
||||
|
||||
const NONLEAP_FLAGS: [YearFlags; 7] = [A, B, C, D, E, F, G];
|
||||
const LEAP_FLAGS: [YearFlags; 7] = [AG, BA, CB, DC, ED, FE, GF];
|
||||
const FLAGS: [YearFlags; 14] = [A, B, C, D, E, F, G, AG, BA, CB, DC, ED, FE, GF];
|
||||
|
||||
#[test]
|
||||
fn test_year_flags_ndays_from_year() {
|
||||
assert_eq!(YearFlags::from_year(2014).ndays(), 365);
|
||||
assert_eq!(YearFlags::from_year(2012).ndays(), 366);
|
||||
assert_eq!(YearFlags::from_year(2000).ndays(), 366);
|
||||
assert_eq!(YearFlags::from_year(1900).ndays(), 365);
|
||||
assert_eq!(YearFlags::from_year(1600).ndays(), 366);
|
||||
assert_eq!(YearFlags::from_year(1).ndays(), 365);
|
||||
assert_eq!(YearFlags::from_year(0).ndays(), 366); // 1 BCE (proleptic Gregorian)
|
||||
assert_eq!(YearFlags::from_year(-1).ndays(), 365); // 2 BCE
|
||||
assert_eq!(YearFlags::from_year(-4).ndays(), 366); // 5 BCE
|
||||
assert_eq!(YearFlags::from_year(-99).ndays(), 365); // 100 BCE
|
||||
assert_eq!(YearFlags::from_year(-100).ndays(), 365); // 101 BCE
|
||||
assert_eq!(YearFlags::from_year(-399).ndays(), 365); // 400 BCE
|
||||
assert_eq!(YearFlags::from_year(-400).ndays(), 366); // 401 BCE
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_year_flags_nisoweeks() {
|
||||
assert_eq!(A.nisoweeks(), 52);
|
||||
assert_eq!(B.nisoweeks(), 52);
|
||||
assert_eq!(C.nisoweeks(), 52);
|
||||
assert_eq!(D.nisoweeks(), 53);
|
||||
assert_eq!(E.nisoweeks(), 52);
|
||||
assert_eq!(F.nisoweeks(), 52);
|
||||
assert_eq!(G.nisoweeks(), 52);
|
||||
assert_eq!(AG.nisoweeks(), 52);
|
||||
assert_eq!(BA.nisoweeks(), 52);
|
||||
assert_eq!(CB.nisoweeks(), 52);
|
||||
assert_eq!(DC.nisoweeks(), 53);
|
||||
assert_eq!(ED.nisoweeks(), 53);
|
||||
assert_eq!(FE.nisoweeks(), 52);
|
||||
assert_eq!(GF.nisoweeks(), 52);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_of() {
|
||||
fn check(expected: bool, flags: YearFlags, ordinal1: u32, ordinal2: u32) {
|
||||
for ordinal in range_inclusive(ordinal1, ordinal2) {
|
||||
let of = Of::new(ordinal, flags);
|
||||
assert!(
|
||||
of.valid() == expected,
|
||||
"ordinal {} = {:?} should be {} for dominical year {:?}",
|
||||
ordinal,
|
||||
of,
|
||||
if expected { "valid" } else { "invalid" },
|
||||
flags
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for &flags in NONLEAP_FLAGS.iter() {
|
||||
check(false, flags, 0, 0);
|
||||
check(true, flags, 1, 365);
|
||||
check(false, flags, 366, 1024);
|
||||
check(false, flags, u32::MAX, u32::MAX);
|
||||
}
|
||||
|
||||
for &flags in LEAP_FLAGS.iter() {
|
||||
check(false, flags, 0, 0);
|
||||
check(true, flags, 1, 366);
|
||||
check(false, flags, 367, 1024);
|
||||
check(false, flags, u32::MAX, u32::MAX);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mdf_valid() {
|
||||
fn check(expected: bool, flags: YearFlags, month1: u32, day1: u32, month2: u32, day2: u32) {
|
||||
for month in range_inclusive(month1, month2) {
|
||||
for day in range_inclusive(day1, day2) {
|
||||
let mdf = Mdf::new(month, day, flags);
|
||||
assert!(
|
||||
mdf.valid() == expected,
|
||||
"month {} day {} = {:?} should be {} for dominical year {:?}",
|
||||
month,
|
||||
day,
|
||||
mdf,
|
||||
if expected { "valid" } else { "invalid" },
|
||||
flags
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for &flags in NONLEAP_FLAGS.iter() {
|
||||
check(false, flags, 0, 0, 0, 1024);
|
||||
check(false, flags, 0, 0, 16, 0);
|
||||
check(true, flags, 1, 1, 1, 31);
|
||||
check(false, flags, 1, 32, 1, 1024);
|
||||
check(true, flags, 2, 1, 2, 28);
|
||||
check(false, flags, 2, 29, 2, 1024);
|
||||
check(true, flags, 3, 1, 3, 31);
|
||||
check(false, flags, 3, 32, 3, 1024);
|
||||
check(true, flags, 4, 1, 4, 30);
|
||||
check(false, flags, 4, 31, 4, 1024);
|
||||
check(true, flags, 5, 1, 5, 31);
|
||||
check(false, flags, 5, 32, 5, 1024);
|
||||
check(true, flags, 6, 1, 6, 30);
|
||||
check(false, flags, 6, 31, 6, 1024);
|
||||
check(true, flags, 7, 1, 7, 31);
|
||||
check(false, flags, 7, 32, 7, 1024);
|
||||
check(true, flags, 8, 1, 8, 31);
|
||||
check(false, flags, 8, 32, 8, 1024);
|
||||
check(true, flags, 9, 1, 9, 30);
|
||||
check(false, flags, 9, 31, 9, 1024);
|
||||
check(true, flags, 10, 1, 10, 31);
|
||||
check(false, flags, 10, 32, 10, 1024);
|
||||
check(true, flags, 11, 1, 11, 30);
|
||||
check(false, flags, 11, 31, 11, 1024);
|
||||
check(true, flags, 12, 1, 12, 31);
|
||||
check(false, flags, 12, 32, 12, 1024);
|
||||
check(false, flags, 13, 0, 16, 1024);
|
||||
check(false, flags, u32::MAX, 0, u32::MAX, 1024);
|
||||
check(false, flags, 0, u32::MAX, 16, u32::MAX);
|
||||
check(false, flags, u32::MAX, u32::MAX, u32::MAX, u32::MAX);
|
||||
}
|
||||
|
||||
for &flags in LEAP_FLAGS.iter() {
|
||||
check(false, flags, 0, 0, 0, 1024);
|
||||
check(false, flags, 0, 0, 16, 0);
|
||||
check(true, flags, 1, 1, 1, 31);
|
||||
check(false, flags, 1, 32, 1, 1024);
|
||||
check(true, flags, 2, 1, 2, 29);
|
||||
check(false, flags, 2, 30, 2, 1024);
|
||||
check(true, flags, 3, 1, 3, 31);
|
||||
check(false, flags, 3, 32, 3, 1024);
|
||||
check(true, flags, 4, 1, 4, 30);
|
||||
check(false, flags, 4, 31, 4, 1024);
|
||||
check(true, flags, 5, 1, 5, 31);
|
||||
check(false, flags, 5, 32, 5, 1024);
|
||||
check(true, flags, 6, 1, 6, 30);
|
||||
check(false, flags, 6, 31, 6, 1024);
|
||||
check(true, flags, 7, 1, 7, 31);
|
||||
check(false, flags, 7, 32, 7, 1024);
|
||||
check(true, flags, 8, 1, 8, 31);
|
||||
check(false, flags, 8, 32, 8, 1024);
|
||||
check(true, flags, 9, 1, 9, 30);
|
||||
check(false, flags, 9, 31, 9, 1024);
|
||||
check(true, flags, 10, 1, 10, 31);
|
||||
check(false, flags, 10, 32, 10, 1024);
|
||||
check(true, flags, 11, 1, 11, 30);
|
||||
check(false, flags, 11, 31, 11, 1024);
|
||||
check(true, flags, 12, 1, 12, 31);
|
||||
check(false, flags, 12, 32, 12, 1024);
|
||||
check(false, flags, 13, 0, 16, 1024);
|
||||
check(false, flags, u32::MAX, 0, u32::MAX, 1024);
|
||||
check(false, flags, 0, u32::MAX, 16, u32::MAX);
|
||||
check(false, flags, u32::MAX, u32::MAX, u32::MAX, u32::MAX);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_of_fields() {
|
||||
for &flags in FLAGS.iter() {
|
||||
for ordinal in range_inclusive(1u32, 366) {
|
||||
let of = Of::new(ordinal, flags);
|
||||
if of.valid() {
|
||||
assert_eq!(of.ordinal(), ordinal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_of_with_fields() {
|
||||
fn check(flags: YearFlags, ordinal: u32) {
|
||||
let of = Of::new(ordinal, flags);
|
||||
|
||||
for ordinal in range_inclusive(0u32, 1024) {
|
||||
let of = of.with_ordinal(ordinal);
|
||||
assert_eq!(of.valid(), Of::new(ordinal, flags).valid());
|
||||
if of.valid() {
|
||||
assert_eq!(of.ordinal(), ordinal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for &flags in NONLEAP_FLAGS.iter() {
|
||||
check(flags, 1);
|
||||
check(flags, 365);
|
||||
}
|
||||
for &flags in LEAP_FLAGS.iter() {
|
||||
check(flags, 1);
|
||||
check(flags, 366);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_of_weekday() {
|
||||
assert_eq!(Of::new(1, A).weekday(), Weekday::Sun);
|
||||
assert_eq!(Of::new(1, B).weekday(), Weekday::Sat);
|
||||
assert_eq!(Of::new(1, C).weekday(), Weekday::Fri);
|
||||
assert_eq!(Of::new(1, D).weekday(), Weekday::Thu);
|
||||
assert_eq!(Of::new(1, E).weekday(), Weekday::Wed);
|
||||
assert_eq!(Of::new(1, F).weekday(), Weekday::Tue);
|
||||
assert_eq!(Of::new(1, G).weekday(), Weekday::Mon);
|
||||
assert_eq!(Of::new(1, AG).weekday(), Weekday::Sun);
|
||||
assert_eq!(Of::new(1, BA).weekday(), Weekday::Sat);
|
||||
assert_eq!(Of::new(1, CB).weekday(), Weekday::Fri);
|
||||
assert_eq!(Of::new(1, DC).weekday(), Weekday::Thu);
|
||||
assert_eq!(Of::new(1, ED).weekday(), Weekday::Wed);
|
||||
assert_eq!(Of::new(1, FE).weekday(), Weekday::Tue);
|
||||
assert_eq!(Of::new(1, GF).weekday(), Weekday::Mon);
|
||||
|
||||
for &flags in FLAGS.iter() {
|
||||
let mut prev = Of::new(1, flags).weekday();
|
||||
for ordinal in range_inclusive(2u32, flags.ndays()) {
|
||||
let of = Of::new(ordinal, flags);
|
||||
let expected = prev.succ();
|
||||
assert_eq!(of.weekday(), expected);
|
||||
prev = expected;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mdf_fields() {
|
||||
for &flags in FLAGS.iter() {
|
||||
for month in range_inclusive(1u32, 12) {
|
||||
for day in range_inclusive(1u32, 31) {
|
||||
let mdf = Mdf::new(month, day, flags);
|
||||
if mdf.valid() {
|
||||
assert_eq!(mdf.month(), month);
|
||||
assert_eq!(mdf.day(), day);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mdf_with_fields() {
|
||||
fn check(flags: YearFlags, month: u32, day: u32) {
|
||||
let mdf = Mdf::new(month, day, flags);
|
||||
|
||||
for month in range_inclusive(0u32, 16) {
|
||||
let mdf = mdf.with_month(month);
|
||||
assert_eq!(mdf.valid(), Mdf::new(month, day, flags).valid());
|
||||
if mdf.valid() {
|
||||
assert_eq!(mdf.month(), month);
|
||||
assert_eq!(mdf.day(), day);
|
||||
}
|
||||
}
|
||||
|
||||
for day in range_inclusive(0u32, 1024) {
|
||||
let mdf = mdf.with_day(day);
|
||||
assert_eq!(mdf.valid(), Mdf::new(month, day, flags).valid());
|
||||
if mdf.valid() {
|
||||
assert_eq!(mdf.month(), month);
|
||||
assert_eq!(mdf.day(), day);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for &flags in NONLEAP_FLAGS.iter() {
|
||||
check(flags, 1, 1);
|
||||
check(flags, 1, 31);
|
||||
check(flags, 2, 1);
|
||||
check(flags, 2, 28);
|
||||
check(flags, 2, 29);
|
||||
check(flags, 12, 31);
|
||||
}
|
||||
for &flags in LEAP_FLAGS.iter() {
|
||||
check(flags, 1, 1);
|
||||
check(flags, 1, 31);
|
||||
check(flags, 2, 1);
|
||||
check(flags, 2, 29);
|
||||
check(flags, 2, 30);
|
||||
check(flags, 12, 31);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_of_isoweekdate_raw() {
|
||||
for &flags in FLAGS.iter() {
|
||||
// January 4 should be in the first week
|
||||
let (week, _) = Of::new(4 /* January 4 */, flags).isoweekdate_raw();
|
||||
assert_eq!(week, 1);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_of_to_mdf() {
|
||||
for i in range_inclusive(0u32, 8192) {
|
||||
let of = Of(i);
|
||||
assert_eq!(of.valid(), of.to_mdf().valid());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mdf_to_of() {
|
||||
for i in range_inclusive(0u32, 8192) {
|
||||
let mdf = Mdf(i);
|
||||
assert_eq!(mdf.valid(), mdf.to_of().valid());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_of_to_mdf_to_of() {
|
||||
for i in range_inclusive(0u32, 8192) {
|
||||
let of = Of(i);
|
||||
if of.valid() {
|
||||
assert_eq!(of, of.to_mdf().to_of());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mdf_to_of_to_mdf() {
|
||||
for i in range_inclusive(0u32, 8192) {
|
||||
let mdf = Mdf(i);
|
||||
if mdf.valid() {
|
||||
assert_eq!(mdf, mdf.to_of().to_mdf());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
163
zeroidc/vendor/chrono/src/naive/isoweek.rs
vendored
Normal file
163
zeroidc/vendor/chrono/src/naive/isoweek.rs
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
// This is a part of Chrono.
|
||||
// See README.md and LICENSE.txt for details.
|
||||
|
||||
//! ISO 8601 week.
|
||||
|
||||
use core::fmt;
|
||||
|
||||
use super::internals::{DateImpl, Of, YearFlags};
|
||||
|
||||
/// ISO 8601 week.
|
||||
///
|
||||
/// This type, combined with [`Weekday`](../enum.Weekday.html),
|
||||
/// constitues the ISO 8601 [week date](./struct.NaiveDate.html#week-date).
|
||||
/// One can retrieve this type from the existing [`Datelike`](../trait.Datelike.html) types
|
||||
/// via the [`Datelike::iso_week`](../trait.Datelike.html#tymethod.iso_week) method.
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
|
||||
pub struct IsoWeek {
|
||||
// note that this allows for larger year range than `NaiveDate`.
|
||||
// this is crucial because we have an edge case for the first and last week supported,
|
||||
// which year number might not match the calendar year number.
|
||||
ywf: DateImpl, // (year << 10) | (week << 4) | flag
|
||||
}
|
||||
|
||||
/// Returns the corresponding `IsoWeek` from the year and the `Of` internal value.
|
||||
//
|
||||
// internal use only. we don't expose the public constructor for `IsoWeek` for now,
|
||||
// because the year range for the week date and the calendar date do not match and
|
||||
// it is confusing to have a date that is out of range in one and not in another.
|
||||
// currently we sidestep this issue by making `IsoWeek` fully dependent of `Datelike`.
|
||||
pub fn iso_week_from_yof(year: i32, of: Of) -> IsoWeek {
|
||||
let (rawweek, _) = of.isoweekdate_raw();
|
||||
let (year, week) = if rawweek < 1 {
|
||||
// previous year
|
||||
let prevlastweek = YearFlags::from_year(year - 1).nisoweeks();
|
||||
(year - 1, prevlastweek)
|
||||
} else {
|
||||
let lastweek = of.flags().nisoweeks();
|
||||
if rawweek > lastweek {
|
||||
// next year
|
||||
(year + 1, 1)
|
||||
} else {
|
||||
(year, rawweek)
|
||||
}
|
||||
};
|
||||
IsoWeek { ywf: (year << 10) | (week << 4) as DateImpl | DateImpl::from(of.flags().0) }
|
||||
}
|
||||
|
||||
impl IsoWeek {
|
||||
/// Returns the year number for this ISO week.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{NaiveDate, Datelike, Weekday};
|
||||
///
|
||||
/// let d = NaiveDate::from_isoywd(2015, 1, Weekday::Mon);
|
||||
/// assert_eq!(d.iso_week().year(), 2015);
|
||||
/// ~~~~
|
||||
///
|
||||
/// This year number might not match the calendar year number.
|
||||
/// Continuing the example...
|
||||
///
|
||||
/// ~~~~
|
||||
/// # use chrono::{NaiveDate, Datelike, Weekday};
|
||||
/// # let d = NaiveDate::from_isoywd(2015, 1, Weekday::Mon);
|
||||
/// assert_eq!(d.year(), 2014);
|
||||
/// assert_eq!(d, NaiveDate::from_ymd(2014, 12, 29));
|
||||
/// ~~~~
|
||||
#[inline]
|
||||
pub fn year(&self) -> i32 {
|
||||
self.ywf >> 10
|
||||
}
|
||||
|
||||
/// Returns the ISO week number starting from 1.
|
||||
///
|
||||
/// The return value ranges from 1 to 53. (The last week of year differs by years.)
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{NaiveDate, Datelike, Weekday};
|
||||
///
|
||||
/// let d = NaiveDate::from_isoywd(2015, 15, Weekday::Mon);
|
||||
/// assert_eq!(d.iso_week().week(), 15);
|
||||
/// ~~~~
|
||||
#[inline]
|
||||
pub fn week(&self) -> u32 {
|
||||
((self.ywf >> 4) & 0x3f) as u32
|
||||
}
|
||||
|
||||
/// Returns the ISO week number starting from 0.
|
||||
///
|
||||
/// The return value ranges from 0 to 52. (The last week of year differs by years.)
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{NaiveDate, Datelike, Weekday};
|
||||
///
|
||||
/// let d = NaiveDate::from_isoywd(2015, 15, Weekday::Mon);
|
||||
/// assert_eq!(d.iso_week().week0(), 14);
|
||||
/// ~~~~
|
||||
#[inline]
|
||||
pub fn week0(&self) -> u32 {
|
||||
((self.ywf >> 4) & 0x3f) as u32 - 1
|
||||
}
|
||||
}
|
||||
|
||||
/// The `Debug` output of the ISO week `w` is the same as
|
||||
/// [`d.format("%G-W%V")`](../format/strftime/index.html)
|
||||
/// where `d` is any `NaiveDate` value in that week.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{NaiveDate, Datelike};
|
||||
///
|
||||
/// assert_eq!(format!("{:?}", NaiveDate::from_ymd(2015, 9, 5).iso_week()), "2015-W36");
|
||||
/// assert_eq!(format!("{:?}", NaiveDate::from_ymd( 0, 1, 3).iso_week()), "0000-W01");
|
||||
/// assert_eq!(format!("{:?}", NaiveDate::from_ymd(9999, 12, 31).iso_week()), "9999-W52");
|
||||
/// ~~~~
|
||||
///
|
||||
/// ISO 8601 requires an explicit sign for years before 1 BCE or after 9999 CE.
|
||||
///
|
||||
/// ~~~~
|
||||
/// # use chrono::{NaiveDate, Datelike};
|
||||
/// assert_eq!(format!("{:?}", NaiveDate::from_ymd( 0, 1, 2).iso_week()), "-0001-W52");
|
||||
/// assert_eq!(format!("{:?}", NaiveDate::from_ymd(10000, 12, 31).iso_week()), "+10000-W52");
|
||||
/// ~~~~
|
||||
impl fmt::Debug for IsoWeek {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let year = self.year();
|
||||
let week = self.week();
|
||||
if 0 <= year && year <= 9999 {
|
||||
write!(f, "{:04}-W{:02}", year, week)
|
||||
} else {
|
||||
// ISO 8601 requires the explicit sign for out-of-range years
|
||||
write!(f, "{:+05}-W{:02}", year, week)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use naive::{internals, MAX_DATE, MIN_DATE};
|
||||
use Datelike;
|
||||
|
||||
#[test]
|
||||
fn test_iso_week_extremes() {
|
||||
let minweek = MIN_DATE.iso_week();
|
||||
let maxweek = MAX_DATE.iso_week();
|
||||
|
||||
assert_eq!(minweek.year(), internals::MIN_YEAR);
|
||||
assert_eq!(minweek.week(), 1);
|
||||
assert_eq!(minweek.week0(), 0);
|
||||
assert_eq!(format!("{:?}", minweek), MIN_DATE.format("%G-W%V").to_string());
|
||||
|
||||
assert_eq!(maxweek.year(), internals::MAX_YEAR + 1);
|
||||
assert_eq!(maxweek.week(), 1);
|
||||
assert_eq!(maxweek.week0(), 0);
|
||||
assert_eq!(format!("{:?}", maxweek), MAX_DATE.format("%G-W%V").to_string());
|
||||
}
|
||||
}
|
||||
1814
zeroidc/vendor/chrono/src/naive/time.rs
vendored
Normal file
1814
zeroidc/vendor/chrono/src/naive/time.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
244
zeroidc/vendor/chrono/src/offset/fixed.rs
vendored
Normal file
244
zeroidc/vendor/chrono/src/offset/fixed.rs
vendored
Normal file
@@ -0,0 +1,244 @@
|
||||
// This is a part of Chrono.
|
||||
// See README.md and LICENSE.txt for details.
|
||||
|
||||
//! The time zone which has a fixed offset from UTC.
|
||||
|
||||
use core::fmt;
|
||||
use core::ops::{Add, Sub};
|
||||
use oldtime::Duration as OldDuration;
|
||||
|
||||
use super::{LocalResult, Offset, TimeZone};
|
||||
use div::div_mod_floor;
|
||||
use naive::{NaiveDate, NaiveDateTime, NaiveTime};
|
||||
use DateTime;
|
||||
use Timelike;
|
||||
|
||||
/// The time zone with fixed offset, from UTC-23:59:59 to UTC+23:59:59.
|
||||
///
|
||||
/// Using the [`TimeZone`](./trait.TimeZone.html) methods
|
||||
/// on a `FixedOffset` struct is the preferred way to construct
|
||||
/// `DateTime<FixedOffset>` instances. See the [`east`](#method.east) and
|
||||
/// [`west`](#method.west) methods for examples.
|
||||
#[derive(PartialEq, Eq, Hash, Copy, Clone)]
|
||||
pub struct FixedOffset {
|
||||
local_minus_utc: i32,
|
||||
}
|
||||
|
||||
impl FixedOffset {
|
||||
/// Makes a new `FixedOffset` for the Eastern Hemisphere with given timezone difference.
|
||||
/// The negative `secs` means the Western Hemisphere.
|
||||
///
|
||||
/// Panics on the out-of-bound `secs`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{FixedOffset, TimeZone};
|
||||
/// let hour = 3600;
|
||||
/// let datetime = FixedOffset::east(5 * hour).ymd(2016, 11, 08)
|
||||
/// .and_hms(0, 0, 0);
|
||||
/// assert_eq!(&datetime.to_rfc3339(), "2016-11-08T00:00:00+05:00")
|
||||
/// ~~~~
|
||||
pub fn east(secs: i32) -> FixedOffset {
|
||||
FixedOffset::east_opt(secs).expect("FixedOffset::east out of bounds")
|
||||
}
|
||||
|
||||
/// Makes a new `FixedOffset` for the Eastern Hemisphere with given timezone difference.
|
||||
/// The negative `secs` means the Western Hemisphere.
|
||||
///
|
||||
/// Returns `None` on the out-of-bound `secs`.
|
||||
pub fn east_opt(secs: i32) -> Option<FixedOffset> {
|
||||
if -86_400 < secs && secs < 86_400 {
|
||||
Some(FixedOffset { local_minus_utc: secs })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a new `FixedOffset` for the Western Hemisphere with given timezone difference.
|
||||
/// The negative `secs` means the Eastern Hemisphere.
|
||||
///
|
||||
/// Panics on the out-of-bound `secs`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{FixedOffset, TimeZone};
|
||||
/// let hour = 3600;
|
||||
/// let datetime = FixedOffset::west(5 * hour).ymd(2016, 11, 08)
|
||||
/// .and_hms(0, 0, 0);
|
||||
/// assert_eq!(&datetime.to_rfc3339(), "2016-11-08T00:00:00-05:00")
|
||||
/// ~~~~
|
||||
pub fn west(secs: i32) -> FixedOffset {
|
||||
FixedOffset::west_opt(secs).expect("FixedOffset::west out of bounds")
|
||||
}
|
||||
|
||||
/// Makes a new `FixedOffset` for the Western Hemisphere with given timezone difference.
|
||||
/// The negative `secs` means the Eastern Hemisphere.
|
||||
///
|
||||
/// Returns `None` on the out-of-bound `secs`.
|
||||
pub fn west_opt(secs: i32) -> Option<FixedOffset> {
|
||||
if -86_400 < secs && secs < 86_400 {
|
||||
Some(FixedOffset { local_minus_utc: -secs })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of seconds to add to convert from UTC to the local time.
|
||||
#[inline]
|
||||
pub fn local_minus_utc(&self) -> i32 {
|
||||
self.local_minus_utc
|
||||
}
|
||||
|
||||
/// Returns the number of seconds to add to convert from the local time to UTC.
|
||||
#[inline]
|
||||
pub fn utc_minus_local(&self) -> i32 {
|
||||
-self.local_minus_utc
|
||||
}
|
||||
}
|
||||
|
||||
impl TimeZone for FixedOffset {
|
||||
type Offset = FixedOffset;
|
||||
|
||||
fn from_offset(offset: &FixedOffset) -> FixedOffset {
|
||||
*offset
|
||||
}
|
||||
|
||||
fn offset_from_local_date(&self, _local: &NaiveDate) -> LocalResult<FixedOffset> {
|
||||
LocalResult::Single(*self)
|
||||
}
|
||||
fn offset_from_local_datetime(&self, _local: &NaiveDateTime) -> LocalResult<FixedOffset> {
|
||||
LocalResult::Single(*self)
|
||||
}
|
||||
|
||||
fn offset_from_utc_date(&self, _utc: &NaiveDate) -> FixedOffset {
|
||||
*self
|
||||
}
|
||||
fn offset_from_utc_datetime(&self, _utc: &NaiveDateTime) -> FixedOffset {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl Offset for FixedOffset {
|
||||
fn fix(&self) -> FixedOffset {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for FixedOffset {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let offset = self.local_minus_utc;
|
||||
let (sign, offset) = if offset < 0 { ('-', -offset) } else { ('+', offset) };
|
||||
let (mins, sec) = div_mod_floor(offset, 60);
|
||||
let (hour, min) = div_mod_floor(mins, 60);
|
||||
if sec == 0 {
|
||||
write!(f, "{}{:02}:{:02}", sign, hour, min)
|
||||
} else {
|
||||
write!(f, "{}{:02}:{:02}:{:02}", sign, hour, min, sec)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FixedOffset {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Debug::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
// addition or subtraction of FixedOffset to/from Timelike values is the same as
|
||||
// adding or subtracting the offset's local_minus_utc value
|
||||
// but keep keeps the leap second information.
|
||||
// this should be implemented more efficiently, but for the time being, this is generic right now.
|
||||
|
||||
fn add_with_leapsecond<T>(lhs: &T, rhs: i32) -> T
|
||||
where
|
||||
T: Timelike + Add<OldDuration, Output = T>,
|
||||
{
|
||||
// extract and temporarily remove the fractional part and later recover it
|
||||
let nanos = lhs.nanosecond();
|
||||
let lhs = lhs.with_nanosecond(0).unwrap();
|
||||
(lhs + OldDuration::seconds(i64::from(rhs))).with_nanosecond(nanos).unwrap()
|
||||
}
|
||||
|
||||
impl Add<FixedOffset> for NaiveTime {
|
||||
type Output = NaiveTime;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: FixedOffset) -> NaiveTime {
|
||||
add_with_leapsecond(&self, rhs.local_minus_utc)
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<FixedOffset> for NaiveTime {
|
||||
type Output = NaiveTime;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: FixedOffset) -> NaiveTime {
|
||||
add_with_leapsecond(&self, -rhs.local_minus_utc)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<FixedOffset> for NaiveDateTime {
|
||||
type Output = NaiveDateTime;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: FixedOffset) -> NaiveDateTime {
|
||||
add_with_leapsecond(&self, rhs.local_minus_utc)
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<FixedOffset> for NaiveDateTime {
|
||||
type Output = NaiveDateTime;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: FixedOffset) -> NaiveDateTime {
|
||||
add_with_leapsecond(&self, -rhs.local_minus_utc)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Tz: TimeZone> Add<FixedOffset> for DateTime<Tz> {
|
||||
type Output = DateTime<Tz>;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: FixedOffset) -> DateTime<Tz> {
|
||||
add_with_leapsecond(&self, rhs.local_minus_utc)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Tz: TimeZone> Sub<FixedOffset> for DateTime<Tz> {
|
||||
type Output = DateTime<Tz>;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: FixedOffset) -> DateTime<Tz> {
|
||||
add_with_leapsecond(&self, -rhs.local_minus_utc)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::FixedOffset;
|
||||
use offset::TimeZone;
|
||||
|
||||
#[test]
|
||||
fn test_date_extreme_offset() {
|
||||
// starting from 0.3 we don't have an offset exceeding one day.
|
||||
// this makes everything easier!
|
||||
assert_eq!(
|
||||
format!("{:?}", FixedOffset::east(86399).ymd(2012, 2, 29)),
|
||||
"2012-02-29+23:59:59".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{:?}", FixedOffset::east(86399).ymd(2012, 2, 29).and_hms(5, 6, 7)),
|
||||
"2012-02-29T05:06:07+23:59:59".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{:?}", FixedOffset::west(86399).ymd(2012, 3, 4)),
|
||||
"2012-03-04-23:59:59".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{:?}", FixedOffset::west(86399).ymd(2012, 3, 4).and_hms(5, 6, 7)),
|
||||
"2012-03-04T05:06:07-23:59:59".to_string()
|
||||
);
|
||||
}
|
||||
}
|
||||
227
zeroidc/vendor/chrono/src/offset/local.rs
vendored
Normal file
227
zeroidc/vendor/chrono/src/offset/local.rs
vendored
Normal file
@@ -0,0 +1,227 @@
|
||||
// This is a part of Chrono.
|
||||
// See README.md and LICENSE.txt for details.
|
||||
|
||||
//! The local (system) time zone.
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))]
|
||||
use sys::{self, Timespec};
|
||||
|
||||
use super::fixed::FixedOffset;
|
||||
use super::{LocalResult, TimeZone};
|
||||
#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))]
|
||||
use naive::NaiveTime;
|
||||
use naive::{NaiveDate, NaiveDateTime};
|
||||
use {Date, DateTime};
|
||||
#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))]
|
||||
use {Datelike, Timelike};
|
||||
|
||||
/// Converts a `time::Tm` struct into the timezone-aware `DateTime`.
|
||||
/// This assumes that `time` is working correctly, i.e. any error is fatal.
|
||||
#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))]
|
||||
fn tm_to_datetime(mut tm: sys::Tm) -> DateTime<Local> {
|
||||
if tm.tm_sec >= 60 {
|
||||
tm.tm_nsec += (tm.tm_sec - 59) * 1_000_000_000;
|
||||
tm.tm_sec = 59;
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn tm_to_naive_date(tm: &sys::Tm) -> NaiveDate {
|
||||
// from_yo is more efficient than from_ymd (since it's the internal representation).
|
||||
NaiveDate::from_yo(tm.tm_year + 1900, tm.tm_yday as u32 + 1)
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn tm_to_naive_date(tm: &sys::Tm) -> NaiveDate {
|
||||
// ...but tm_yday is broken in Windows (issue #85)
|
||||
NaiveDate::from_ymd(tm.tm_year + 1900, tm.tm_mon as u32 + 1, tm.tm_mday as u32)
|
||||
}
|
||||
|
||||
let date = tm_to_naive_date(&tm);
|
||||
let time = NaiveTime::from_hms_nano(
|
||||
tm.tm_hour as u32,
|
||||
tm.tm_min as u32,
|
||||
tm.tm_sec as u32,
|
||||
tm.tm_nsec as u32,
|
||||
);
|
||||
let offset = FixedOffset::east(tm.tm_utcoff);
|
||||
DateTime::from_utc(date.and_time(time) - offset, offset)
|
||||
}
|
||||
|
||||
/// Converts a local `NaiveDateTime` to the `time::Timespec`.
|
||||
#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))]
|
||||
fn datetime_to_timespec(d: &NaiveDateTime, local: bool) -> sys::Timespec {
|
||||
// well, this exploits an undocumented `Tm::to_timespec` behavior
|
||||
// to get the exact function we want (either `timegm` or `mktime`).
|
||||
// the number 1 is arbitrary but should be non-zero to trigger `mktime`.
|
||||
let tm_utcoff = if local { 1 } else { 0 };
|
||||
|
||||
let tm = sys::Tm {
|
||||
tm_sec: d.second() as i32,
|
||||
tm_min: d.minute() as i32,
|
||||
tm_hour: d.hour() as i32,
|
||||
tm_mday: d.day() as i32,
|
||||
tm_mon: d.month0() as i32, // yes, C is that strange...
|
||||
tm_year: d.year() - 1900, // this doesn't underflow, we know that d is `NaiveDateTime`.
|
||||
tm_wday: 0, // to_local ignores this
|
||||
tm_yday: 0, // and this
|
||||
tm_isdst: -1,
|
||||
tm_utcoff: tm_utcoff,
|
||||
// do not set this, OS APIs are heavily inconsistent in terms of leap second handling
|
||||
tm_nsec: 0,
|
||||
};
|
||||
|
||||
tm.to_timespec()
|
||||
}
|
||||
|
||||
/// The local timescale. This is implemented via the standard `time` crate.
|
||||
///
|
||||
/// Using the [`TimeZone`](./trait.TimeZone.html) methods
|
||||
/// on the Local struct is the preferred way to construct `DateTime<Local>`
|
||||
/// instances.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{Local, DateTime, TimeZone};
|
||||
///
|
||||
/// let dt: DateTime<Local> = Local::now();
|
||||
/// let dt: DateTime<Local> = Local.timestamp(0, 0);
|
||||
/// ~~~~
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Local;
|
||||
|
||||
impl Local {
|
||||
/// Returns a `Date` which corresponds to the current date.
|
||||
pub fn today() -> Date<Local> {
|
||||
Local::now().date()
|
||||
}
|
||||
|
||||
/// Returns a `DateTime` which corresponds to the current date.
|
||||
#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))]
|
||||
pub fn now() -> DateTime<Local> {
|
||||
tm_to_datetime(Timespec::now().local())
|
||||
}
|
||||
|
||||
/// Returns a `DateTime` which corresponds to the current date.
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))]
|
||||
pub fn now() -> DateTime<Local> {
|
||||
use super::Utc;
|
||||
let now: DateTime<Utc> = super::Utc::now();
|
||||
|
||||
// Workaround missing timezone logic in `time` crate
|
||||
let offset = FixedOffset::west((js_sys::Date::new_0().get_timezone_offset() as i32) * 60);
|
||||
DateTime::from_utc(now.naive_utc(), offset)
|
||||
}
|
||||
}
|
||||
|
||||
impl TimeZone for Local {
|
||||
type Offset = FixedOffset;
|
||||
|
||||
fn from_offset(_offset: &FixedOffset) -> Local {
|
||||
Local
|
||||
}
|
||||
|
||||
// they are easier to define in terms of the finished date and time unlike other offsets
|
||||
fn offset_from_local_date(&self, local: &NaiveDate) -> LocalResult<FixedOffset> {
|
||||
self.from_local_date(local).map(|date| *date.offset())
|
||||
}
|
||||
|
||||
fn offset_from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<FixedOffset> {
|
||||
self.from_local_datetime(local).map(|datetime| *datetime.offset())
|
||||
}
|
||||
|
||||
fn offset_from_utc_date(&self, utc: &NaiveDate) -> FixedOffset {
|
||||
*self.from_utc_date(utc).offset()
|
||||
}
|
||||
|
||||
fn offset_from_utc_datetime(&self, utc: &NaiveDateTime) -> FixedOffset {
|
||||
*self.from_utc_datetime(utc).offset()
|
||||
}
|
||||
|
||||
// override them for avoiding redundant works
|
||||
fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<Local>> {
|
||||
// this sounds very strange, but required for keeping `TimeZone::ymd` sane.
|
||||
// in the other words, we use the offset at the local midnight
|
||||
// but keep the actual date unaltered (much like `FixedOffset`).
|
||||
let midnight = self.from_local_datetime(&local.and_hms(0, 0, 0));
|
||||
midnight.map(|datetime| Date::from_utc(*local, *datetime.offset()))
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))]
|
||||
fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<Local>> {
|
||||
let mut local = local.clone();
|
||||
// Get the offset from the js runtime
|
||||
let offset = FixedOffset::west((js_sys::Date::new_0().get_timezone_offset() as i32) * 60);
|
||||
local -= ::Duration::seconds(offset.local_minus_utc() as i64);
|
||||
LocalResult::Single(DateTime::from_utc(local, offset))
|
||||
}
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))]
|
||||
fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<Local>> {
|
||||
let timespec = datetime_to_timespec(local, true);
|
||||
|
||||
// datetime_to_timespec completely ignores leap seconds, so we need to adjust for them
|
||||
let mut tm = timespec.local();
|
||||
assert_eq!(tm.tm_nsec, 0);
|
||||
tm.tm_nsec = local.nanosecond() as i32;
|
||||
|
||||
LocalResult::Single(tm_to_datetime(tm))
|
||||
}
|
||||
|
||||
fn from_utc_date(&self, utc: &NaiveDate) -> Date<Local> {
|
||||
let midnight = self.from_utc_datetime(&utc.and_hms(0, 0, 0));
|
||||
Date::from_utc(*utc, *midnight.offset())
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))]
|
||||
fn from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime<Local> {
|
||||
// Get the offset from the js runtime
|
||||
let offset = FixedOffset::west((js_sys::Date::new_0().get_timezone_offset() as i32) * 60);
|
||||
DateTime::from_utc(*utc, offset)
|
||||
}
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))]
|
||||
fn from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime<Local> {
|
||||
let timespec = datetime_to_timespec(utc, false);
|
||||
|
||||
// datetime_to_timespec completely ignores leap seconds, so we need to adjust for them
|
||||
let mut tm = timespec.local();
|
||||
assert_eq!(tm.tm_nsec, 0);
|
||||
tm.tm_nsec = utc.nanosecond() as i32;
|
||||
|
||||
tm_to_datetime(tm)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Local;
|
||||
use offset::TimeZone;
|
||||
use Datelike;
|
||||
|
||||
#[test]
|
||||
fn test_local_date_sanity_check() {
|
||||
// issue #27
|
||||
assert_eq!(Local.ymd(2999, 12, 28).day(), 28);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_leap_second() {
|
||||
// issue #123
|
||||
let today = Local::today();
|
||||
|
||||
let dt = today.and_hms_milli(1, 2, 59, 1000);
|
||||
let timestr = dt.time().to_string();
|
||||
// the OS API may or may not support the leap second,
|
||||
// but there are only two sensible options.
|
||||
assert!(timestr == "01:02:60" || timestr == "01:03:00", "unexpected timestr {:?}", timestr);
|
||||
|
||||
let dt = today.and_hms_milli(1, 2, 3, 1234);
|
||||
let timestr = dt.time().to_string();
|
||||
assert!(
|
||||
timestr == "01:02:03.234" || timestr == "01:02:04.234",
|
||||
"unexpected timestr {:?}",
|
||||
timestr
|
||||
);
|
||||
}
|
||||
}
|
||||
531
zeroidc/vendor/chrono/src/offset/mod.rs
vendored
Normal file
531
zeroidc/vendor/chrono/src/offset/mod.rs
vendored
Normal file
@@ -0,0 +1,531 @@
|
||||
// This is a part of Chrono.
|
||||
// See README.md and LICENSE.txt for details.
|
||||
|
||||
//! The time zone, which calculates offsets from the local time to UTC.
|
||||
//!
|
||||
//! There are four operations provided by the `TimeZone` trait:
|
||||
//!
|
||||
//! 1. Converting the local `NaiveDateTime` to `DateTime<Tz>`
|
||||
//! 2. Converting the UTC `NaiveDateTime` to `DateTime<Tz>`
|
||||
//! 3. Converting `DateTime<Tz>` to the local `NaiveDateTime`
|
||||
//! 4. Constructing `DateTime<Tz>` objects from various offsets
|
||||
//!
|
||||
//! 1 is used for constructors. 2 is used for the `with_timezone` method of date and time types.
|
||||
//! 3 is used for other methods, e.g. `year()` or `format()`, and provided by an associated type
|
||||
//! which implements `Offset` (which then passed to `TimeZone` for actual implementations).
|
||||
//! Technically speaking `TimeZone` has a total knowledge about given timescale,
|
||||
//! but `Offset` is used as a cache to avoid the repeated conversion
|
||||
//! and provides implementations for 1 and 3.
|
||||
//! An `TimeZone` instance can be reconstructed from the corresponding `Offset` instance.
|
||||
|
||||
use core::fmt;
|
||||
|
||||
use format::{parse, ParseResult, Parsed, StrftimeItems};
|
||||
use naive::{NaiveDate, NaiveDateTime, NaiveTime};
|
||||
use Weekday;
|
||||
use {Date, DateTime};
|
||||
|
||||
/// The conversion result from the local time to the timezone-aware datetime types.
|
||||
#[derive(Clone, PartialEq, Debug, Copy, Eq, Hash)]
|
||||
pub enum LocalResult<T> {
|
||||
/// Given local time representation is invalid.
|
||||
/// This can occur when, for example, the positive timezone transition.
|
||||
None,
|
||||
/// Given local time representation has a single unique result.
|
||||
Single(T),
|
||||
/// Given local time representation has multiple results and thus ambiguous.
|
||||
/// This can occur when, for example, the negative timezone transition.
|
||||
Ambiguous(T /*min*/, T /*max*/),
|
||||
}
|
||||
|
||||
impl<T> LocalResult<T> {
|
||||
/// Returns `Some` only when the conversion result is unique, or `None` otherwise.
|
||||
pub fn single(self) -> Option<T> {
|
||||
match self {
|
||||
LocalResult::Single(t) => Some(t),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `Some` for the earliest possible conversion result, or `None` if none.
|
||||
pub fn earliest(self) -> Option<T> {
|
||||
match self {
|
||||
LocalResult::Single(t) | LocalResult::Ambiguous(t, _) => Some(t),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `Some` for the latest possible conversion result, or `None` if none.
|
||||
pub fn latest(self) -> Option<T> {
|
||||
match self {
|
||||
LocalResult::Single(t) | LocalResult::Ambiguous(_, t) => Some(t),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps a `LocalResult<T>` into `LocalResult<U>` with given function.
|
||||
pub fn map<U, F: FnMut(T) -> U>(self, mut f: F) -> LocalResult<U> {
|
||||
match self {
|
||||
LocalResult::None => LocalResult::None,
|
||||
LocalResult::Single(v) => LocalResult::Single(f(v)),
|
||||
LocalResult::Ambiguous(min, max) => LocalResult::Ambiguous(f(min), f(max)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Tz: TimeZone> LocalResult<Date<Tz>> {
|
||||
/// Makes a new `DateTime` from the current date and given `NaiveTime`.
|
||||
/// The offset in the current date is preserved.
|
||||
///
|
||||
/// Propagates any error. Ambiguous result would be discarded.
|
||||
#[inline]
|
||||
pub fn and_time(self, time: NaiveTime) -> LocalResult<DateTime<Tz>> {
|
||||
match self {
|
||||
LocalResult::Single(d) => {
|
||||
d.and_time(time).map_or(LocalResult::None, LocalResult::Single)
|
||||
}
|
||||
_ => LocalResult::None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a new `DateTime` from the current date, hour, minute and second.
|
||||
/// The offset in the current date is preserved.
|
||||
///
|
||||
/// Propagates any error. Ambiguous result would be discarded.
|
||||
#[inline]
|
||||
pub fn and_hms_opt(self, hour: u32, min: u32, sec: u32) -> LocalResult<DateTime<Tz>> {
|
||||
match self {
|
||||
LocalResult::Single(d) => {
|
||||
d.and_hms_opt(hour, min, sec).map_or(LocalResult::None, LocalResult::Single)
|
||||
}
|
||||
_ => LocalResult::None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a new `DateTime` from the current date, hour, minute, second and millisecond.
|
||||
/// The millisecond part can exceed 1,000 in order to represent the leap second.
|
||||
/// The offset in the current date is preserved.
|
||||
///
|
||||
/// Propagates any error. Ambiguous result would be discarded.
|
||||
#[inline]
|
||||
pub fn and_hms_milli_opt(
|
||||
self,
|
||||
hour: u32,
|
||||
min: u32,
|
||||
sec: u32,
|
||||
milli: u32,
|
||||
) -> LocalResult<DateTime<Tz>> {
|
||||
match self {
|
||||
LocalResult::Single(d) => d
|
||||
.and_hms_milli_opt(hour, min, sec, milli)
|
||||
.map_or(LocalResult::None, LocalResult::Single),
|
||||
_ => LocalResult::None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a new `DateTime` from the current date, hour, minute, second and microsecond.
|
||||
/// The microsecond part can exceed 1,000,000 in order to represent the leap second.
|
||||
/// The offset in the current date is preserved.
|
||||
///
|
||||
/// Propagates any error. Ambiguous result would be discarded.
|
||||
#[inline]
|
||||
pub fn and_hms_micro_opt(
|
||||
self,
|
||||
hour: u32,
|
||||
min: u32,
|
||||
sec: u32,
|
||||
micro: u32,
|
||||
) -> LocalResult<DateTime<Tz>> {
|
||||
match self {
|
||||
LocalResult::Single(d) => d
|
||||
.and_hms_micro_opt(hour, min, sec, micro)
|
||||
.map_or(LocalResult::None, LocalResult::Single),
|
||||
_ => LocalResult::None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a new `DateTime` from the current date, hour, minute, second and nanosecond.
|
||||
/// The nanosecond part can exceed 1,000,000,000 in order to represent the leap second.
|
||||
/// The offset in the current date is preserved.
|
||||
///
|
||||
/// Propagates any error. Ambiguous result would be discarded.
|
||||
#[inline]
|
||||
pub fn and_hms_nano_opt(
|
||||
self,
|
||||
hour: u32,
|
||||
min: u32,
|
||||
sec: u32,
|
||||
nano: u32,
|
||||
) -> LocalResult<DateTime<Tz>> {
|
||||
match self {
|
||||
LocalResult::Single(d) => d
|
||||
.and_hms_nano_opt(hour, min, sec, nano)
|
||||
.map_or(LocalResult::None, LocalResult::Single),
|
||||
_ => LocalResult::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> LocalResult<T> {
|
||||
/// Returns the single unique conversion result, or panics accordingly.
|
||||
pub fn unwrap(self) -> T {
|
||||
match self {
|
||||
LocalResult::None => panic!("No such local time"),
|
||||
LocalResult::Single(t) => t,
|
||||
LocalResult::Ambiguous(t1, t2) => {
|
||||
panic!("Ambiguous local time, ranging from {:?} to {:?}", t1, t2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The offset from the local time to UTC.
|
||||
pub trait Offset: Sized + Clone + fmt::Debug {
|
||||
/// Returns the fixed offset from UTC to the local time stored.
|
||||
fn fix(&self) -> FixedOffset;
|
||||
}
|
||||
|
||||
/// The time zone.
|
||||
///
|
||||
/// The methods here are the primarily constructors for [`Date`](../struct.Date.html) and
|
||||
/// [`DateTime`](../struct.DateTime.html) types.
|
||||
pub trait TimeZone: Sized + Clone {
|
||||
/// An associated offset type.
|
||||
/// This type is used to store the actual offset in date and time types.
|
||||
/// The original `TimeZone` value can be recovered via `TimeZone::from_offset`.
|
||||
type Offset: Offset;
|
||||
|
||||
/// Makes a new `Date` from year, month, day and the current time zone.
|
||||
/// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
|
||||
///
|
||||
/// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24),
|
||||
/// but it will propagate to the `DateTime` values constructed via this date.
|
||||
///
|
||||
/// Panics on the out-of-range date, invalid month and/or day.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{Utc, TimeZone};
|
||||
///
|
||||
/// assert_eq!(Utc.ymd(2015, 5, 15).to_string(), "2015-05-15UTC");
|
||||
/// ~~~~
|
||||
fn ymd(&self, year: i32, month: u32, day: u32) -> Date<Self> {
|
||||
self.ymd_opt(year, month, day).unwrap()
|
||||
}
|
||||
|
||||
/// Makes a new `Date` from year, month, day and the current time zone.
|
||||
/// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
|
||||
///
|
||||
/// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24),
|
||||
/// but it will propagate to the `DateTime` values constructed via this date.
|
||||
///
|
||||
/// Returns `None` on the out-of-range date, invalid month and/or day.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{Utc, LocalResult, TimeZone};
|
||||
///
|
||||
/// assert_eq!(Utc.ymd_opt(2015, 5, 15).unwrap().to_string(), "2015-05-15UTC");
|
||||
/// assert_eq!(Utc.ymd_opt(2000, 0, 0), LocalResult::None);
|
||||
/// ~~~~
|
||||
fn ymd_opt(&self, year: i32, month: u32, day: u32) -> LocalResult<Date<Self>> {
|
||||
match NaiveDate::from_ymd_opt(year, month, day) {
|
||||
Some(d) => self.from_local_date(&d),
|
||||
None => LocalResult::None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a new `Date` from year, day of year (DOY or "ordinal") and the current time zone.
|
||||
/// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
|
||||
///
|
||||
/// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24),
|
||||
/// but it will propagate to the `DateTime` values constructed via this date.
|
||||
///
|
||||
/// Panics on the out-of-range date and/or invalid DOY.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{Utc, TimeZone};
|
||||
///
|
||||
/// assert_eq!(Utc.yo(2015, 135).to_string(), "2015-05-15UTC");
|
||||
/// ~~~~
|
||||
fn yo(&self, year: i32, ordinal: u32) -> Date<Self> {
|
||||
self.yo_opt(year, ordinal).unwrap()
|
||||
}
|
||||
|
||||
/// Makes a new `Date` from year, day of year (DOY or "ordinal") and the current time zone.
|
||||
/// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
|
||||
///
|
||||
/// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24),
|
||||
/// but it will propagate to the `DateTime` values constructed via this date.
|
||||
///
|
||||
/// Returns `None` on the out-of-range date and/or invalid DOY.
|
||||
fn yo_opt(&self, year: i32, ordinal: u32) -> LocalResult<Date<Self>> {
|
||||
match NaiveDate::from_yo_opt(year, ordinal) {
|
||||
Some(d) => self.from_local_date(&d),
|
||||
None => LocalResult::None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a new `Date` from ISO week date (year and week number), day of the week (DOW) and
|
||||
/// the current time zone.
|
||||
/// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
|
||||
/// The resulting `Date` may have a different year from the input year.
|
||||
///
|
||||
/// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24),
|
||||
/// but it will propagate to the `DateTime` values constructed via this date.
|
||||
///
|
||||
/// Panics on the out-of-range date and/or invalid week number.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{Utc, Weekday, TimeZone};
|
||||
///
|
||||
/// assert_eq!(Utc.isoywd(2015, 20, Weekday::Fri).to_string(), "2015-05-15UTC");
|
||||
/// ~~~~
|
||||
fn isoywd(&self, year: i32, week: u32, weekday: Weekday) -> Date<Self> {
|
||||
self.isoywd_opt(year, week, weekday).unwrap()
|
||||
}
|
||||
|
||||
/// Makes a new `Date` from ISO week date (year and week number), day of the week (DOW) and
|
||||
/// the current time zone.
|
||||
/// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
|
||||
/// The resulting `Date` may have a different year from the input year.
|
||||
///
|
||||
/// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24),
|
||||
/// but it will propagate to the `DateTime` values constructed via this date.
|
||||
///
|
||||
/// Returns `None` on the out-of-range date and/or invalid week number.
|
||||
fn isoywd_opt(&self, year: i32, week: u32, weekday: Weekday) -> LocalResult<Date<Self>> {
|
||||
match NaiveDate::from_isoywd_opt(year, week, weekday) {
|
||||
Some(d) => self.from_local_date(&d),
|
||||
None => LocalResult::None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a new `DateTime` from the number of non-leap seconds
|
||||
/// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp")
|
||||
/// and the number of nanoseconds since the last whole non-leap second.
|
||||
///
|
||||
/// Panics on the out-of-range number of seconds and/or invalid nanosecond,
|
||||
/// for a non-panicking version see [`timestamp_opt`](#method.timestamp_opt).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{Utc, TimeZone};
|
||||
///
|
||||
/// assert_eq!(Utc.timestamp(1431648000, 0).to_string(), "2015-05-15 00:00:00 UTC");
|
||||
/// ~~~~
|
||||
fn timestamp(&self, secs: i64, nsecs: u32) -> DateTime<Self> {
|
||||
self.timestamp_opt(secs, nsecs).unwrap()
|
||||
}
|
||||
|
||||
/// Makes a new `DateTime` from the number of non-leap seconds
|
||||
/// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp")
|
||||
/// and the number of nanoseconds since the last whole non-leap second.
|
||||
///
|
||||
/// Returns `LocalResult::None` on out-of-range number of seconds and/or
|
||||
/// invalid nanosecond, otherwise always returns `LocalResult::Single`.
|
||||
fn timestamp_opt(&self, secs: i64, nsecs: u32) -> LocalResult<DateTime<Self>> {
|
||||
match NaiveDateTime::from_timestamp_opt(secs, nsecs) {
|
||||
Some(dt) => LocalResult::Single(self.from_utc_datetime(&dt)),
|
||||
None => LocalResult::None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a new `DateTime` from the number of non-leap milliseconds
|
||||
/// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp").
|
||||
///
|
||||
/// Panics on out-of-range number of milliseconds for a non-panicking
|
||||
/// version see [`timestamp_millis_opt`](#method.timestamp_millis_opt).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{Utc, TimeZone};
|
||||
///
|
||||
/// assert_eq!(Utc.timestamp_millis(1431648000).timestamp(), 1431648);
|
||||
/// ~~~~
|
||||
fn timestamp_millis(&self, millis: i64) -> DateTime<Self> {
|
||||
self.timestamp_millis_opt(millis).unwrap()
|
||||
}
|
||||
|
||||
/// Makes a new `DateTime` from the number of non-leap milliseconds
|
||||
/// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp").
|
||||
///
|
||||
///
|
||||
/// Returns `LocalResult::None` on out-of-range number of milliseconds
|
||||
/// and/or invalid nanosecond, otherwise always returns
|
||||
/// `LocalResult::Single`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{Utc, TimeZone, LocalResult};
|
||||
/// match Utc.timestamp_millis_opt(1431648000) {
|
||||
/// LocalResult::Single(dt) => assert_eq!(dt.timestamp(), 1431648),
|
||||
/// _ => panic!("Incorrect timestamp_millis"),
|
||||
/// };
|
||||
/// ~~~~
|
||||
fn timestamp_millis_opt(&self, millis: i64) -> LocalResult<DateTime<Self>> {
|
||||
let (mut secs, mut millis) = (millis / 1000, millis % 1000);
|
||||
if millis < 0 {
|
||||
secs -= 1;
|
||||
millis += 1000;
|
||||
}
|
||||
self.timestamp_opt(secs, millis as u32 * 1_000_000)
|
||||
}
|
||||
|
||||
/// Makes a new `DateTime` from the number of non-leap nanoseconds
|
||||
/// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp").
|
||||
///
|
||||
/// Unlike [`timestamp_millis`](#method.timestamp_millis), this never
|
||||
/// panics.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{Utc, TimeZone};
|
||||
///
|
||||
/// assert_eq!(Utc.timestamp_nanos(1431648000000000).timestamp(), 1431648);
|
||||
/// ~~~~
|
||||
fn timestamp_nanos(&self, nanos: i64) -> DateTime<Self> {
|
||||
let (mut secs, mut nanos) = (nanos / 1_000_000_000, nanos % 1_000_000_000);
|
||||
if nanos < 0 {
|
||||
secs -= 1;
|
||||
nanos += 1_000_000_000;
|
||||
}
|
||||
self.timestamp_opt(secs, nanos as u32).unwrap()
|
||||
}
|
||||
|
||||
/// Parses a string with the specified format string and
|
||||
/// returns a `DateTime` with the current offset.
|
||||
/// See the [`format::strftime` module](../format/strftime/index.html)
|
||||
/// on the supported escape sequences.
|
||||
///
|
||||
/// If the format does not include offsets, the current offset is assumed;
|
||||
/// otherwise the input should have a matching UTC offset.
|
||||
///
|
||||
/// See also `DateTime::parse_from_str` which gives a local `DateTime`
|
||||
/// with parsed `FixedOffset`.
|
||||
fn datetime_from_str(&self, s: &str, fmt: &str) -> ParseResult<DateTime<Self>> {
|
||||
let mut parsed = Parsed::new();
|
||||
parse(&mut parsed, s, StrftimeItems::new(fmt))?;
|
||||
parsed.to_datetime_with_timezone(self)
|
||||
}
|
||||
|
||||
/// Reconstructs the time zone from the offset.
|
||||
fn from_offset(offset: &Self::Offset) -> Self;
|
||||
|
||||
/// Creates the offset(s) for given local `NaiveDate` if possible.
|
||||
fn offset_from_local_date(&self, local: &NaiveDate) -> LocalResult<Self::Offset>;
|
||||
|
||||
/// Creates the offset(s) for given local `NaiveDateTime` if possible.
|
||||
fn offset_from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<Self::Offset>;
|
||||
|
||||
/// Converts the local `NaiveDate` to the timezone-aware `Date` if possible.
|
||||
fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<Self>> {
|
||||
self.offset_from_local_date(local).map(|offset| {
|
||||
// since FixedOffset is within +/- 1 day, the date is never affected
|
||||
Date::from_utc(*local, offset)
|
||||
})
|
||||
}
|
||||
|
||||
/// Converts the local `NaiveDateTime` to the timezone-aware `DateTime` if possible.
|
||||
fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<Self>> {
|
||||
self.offset_from_local_datetime(local)
|
||||
.map(|offset| DateTime::from_utc(*local - offset.fix(), offset))
|
||||
}
|
||||
|
||||
/// Creates the offset for given UTC `NaiveDate`. This cannot fail.
|
||||
fn offset_from_utc_date(&self, utc: &NaiveDate) -> Self::Offset;
|
||||
|
||||
/// Creates the offset for given UTC `NaiveDateTime`. This cannot fail.
|
||||
fn offset_from_utc_datetime(&self, utc: &NaiveDateTime) -> Self::Offset;
|
||||
|
||||
/// Converts the UTC `NaiveDate` to the local time.
|
||||
/// The UTC is continuous and thus this cannot fail (but can give the duplicate local time).
|
||||
fn from_utc_date(&self, utc: &NaiveDate) -> Date<Self> {
|
||||
Date::from_utc(*utc, self.offset_from_utc_date(utc))
|
||||
}
|
||||
|
||||
/// Converts the UTC `NaiveDateTime` to the local time.
|
||||
/// The UTC is continuous and thus this cannot fail (but can give the duplicate local time).
|
||||
fn from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime<Self> {
|
||||
DateTime::from_utc(*utc, self.offset_from_utc_datetime(utc))
|
||||
}
|
||||
}
|
||||
|
||||
mod fixed;
|
||||
#[cfg(feature = "clock")]
|
||||
mod local;
|
||||
mod utc;
|
||||
|
||||
pub use self::fixed::FixedOffset;
|
||||
#[cfg(feature = "clock")]
|
||||
pub use self::local::Local;
|
||||
pub use self::utc::Utc;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_negative_millis() {
|
||||
let dt = Utc.timestamp_millis(-1000);
|
||||
assert_eq!(dt.to_string(), "1969-12-31 23:59:59 UTC");
|
||||
let dt = Utc.timestamp_millis(-7000);
|
||||
assert_eq!(dt.to_string(), "1969-12-31 23:59:53 UTC");
|
||||
let dt = Utc.timestamp_millis(-7001);
|
||||
assert_eq!(dt.to_string(), "1969-12-31 23:59:52.999 UTC");
|
||||
let dt = Utc.timestamp_millis(-7003);
|
||||
assert_eq!(dt.to_string(), "1969-12-31 23:59:52.997 UTC");
|
||||
let dt = Utc.timestamp_millis(-999);
|
||||
assert_eq!(dt.to_string(), "1969-12-31 23:59:59.001 UTC");
|
||||
let dt = Utc.timestamp_millis(-1);
|
||||
assert_eq!(dt.to_string(), "1969-12-31 23:59:59.999 UTC");
|
||||
let dt = Utc.timestamp_millis(-60000);
|
||||
assert_eq!(dt.to_string(), "1969-12-31 23:59:00 UTC");
|
||||
let dt = Utc.timestamp_millis(-3600000);
|
||||
assert_eq!(dt.to_string(), "1969-12-31 23:00:00 UTC");
|
||||
|
||||
for (millis, expected) in &[
|
||||
(-7000, "1969-12-31 23:59:53 UTC"),
|
||||
(-7001, "1969-12-31 23:59:52.999 UTC"),
|
||||
(-7003, "1969-12-31 23:59:52.997 UTC"),
|
||||
] {
|
||||
match Utc.timestamp_millis_opt(*millis) {
|
||||
LocalResult::Single(dt) => {
|
||||
assert_eq!(dt.to_string(), *expected);
|
||||
}
|
||||
e => panic!("Got {:?} instead of an okay answer", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_negative_nanos() {
|
||||
let dt = Utc.timestamp_nanos(-1_000_000_000);
|
||||
assert_eq!(dt.to_string(), "1969-12-31 23:59:59 UTC");
|
||||
let dt = Utc.timestamp_nanos(-999_999_999);
|
||||
assert_eq!(dt.to_string(), "1969-12-31 23:59:59.000000001 UTC");
|
||||
let dt = Utc.timestamp_nanos(-1);
|
||||
assert_eq!(dt.to_string(), "1969-12-31 23:59:59.999999999 UTC");
|
||||
let dt = Utc.timestamp_nanos(-60_000_000_000);
|
||||
assert_eq!(dt.to_string(), "1969-12-31 23:59:00 UTC");
|
||||
let dt = Utc.timestamp_nanos(-3_600_000_000_000);
|
||||
assert_eq!(dt.to_string(), "1969-12-31 23:00:00 UTC");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nanos_never_panics() {
|
||||
Utc.timestamp_nanos(i64::max_value());
|
||||
Utc.timestamp_nanos(i64::default());
|
||||
Utc.timestamp_nanos(i64::min_value());
|
||||
}
|
||||
}
|
||||
100
zeroidc/vendor/chrono/src/offset/utc.rs
vendored
Normal file
100
zeroidc/vendor/chrono/src/offset/utc.rs
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
// This is a part of Chrono.
|
||||
// See README.md and LICENSE.txt for details.
|
||||
|
||||
//! The UTC (Coordinated Universal Time) time zone.
|
||||
|
||||
use core::fmt;
|
||||
|
||||
use super::{FixedOffset, LocalResult, Offset, TimeZone};
|
||||
use naive::{NaiveDate, NaiveDateTime};
|
||||
#[cfg(all(
|
||||
feature = "clock",
|
||||
not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))
|
||||
))]
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
#[cfg(feature = "clock")]
|
||||
use {Date, DateTime};
|
||||
|
||||
/// The UTC time zone. This is the most efficient time zone when you don't need the local time.
|
||||
/// It is also used as an offset (which is also a dummy type).
|
||||
///
|
||||
/// Using the [`TimeZone`](./trait.TimeZone.html) methods
|
||||
/// on the UTC struct is the preferred way to construct `DateTime<Utc>`
|
||||
/// instances.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{DateTime, TimeZone, NaiveDateTime, Utc};
|
||||
///
|
||||
/// let dt = DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(61, 0), Utc);
|
||||
///
|
||||
/// assert_eq!(Utc.timestamp(61, 0), dt);
|
||||
/// assert_eq!(Utc.ymd(1970, 1, 1).and_hms(0, 1, 1), dt);
|
||||
/// ~~~~
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Utc;
|
||||
|
||||
#[cfg(feature = "clock")]
|
||||
impl Utc {
|
||||
/// Returns a `Date` which corresponds to the current date.
|
||||
pub fn today() -> Date<Utc> {
|
||||
Utc::now().date()
|
||||
}
|
||||
|
||||
/// Returns a `DateTime` which corresponds to the current date.
|
||||
#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))]
|
||||
pub fn now() -> DateTime<Utc> {
|
||||
let now =
|
||||
SystemTime::now().duration_since(UNIX_EPOCH).expect("system time before Unix epoch");
|
||||
let naive = NaiveDateTime::from_timestamp(now.as_secs() as i64, now.subsec_nanos() as u32);
|
||||
DateTime::from_utc(naive, Utc)
|
||||
}
|
||||
|
||||
/// Returns a `DateTime` which corresponds to the current date.
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))]
|
||||
pub fn now() -> DateTime<Utc> {
|
||||
let now = js_sys::Date::new_0();
|
||||
DateTime::<Utc>::from(now)
|
||||
}
|
||||
}
|
||||
|
||||
impl TimeZone for Utc {
|
||||
type Offset = Utc;
|
||||
|
||||
fn from_offset(_state: &Utc) -> Utc {
|
||||
Utc
|
||||
}
|
||||
|
||||
fn offset_from_local_date(&self, _local: &NaiveDate) -> LocalResult<Utc> {
|
||||
LocalResult::Single(Utc)
|
||||
}
|
||||
fn offset_from_local_datetime(&self, _local: &NaiveDateTime) -> LocalResult<Utc> {
|
||||
LocalResult::Single(Utc)
|
||||
}
|
||||
|
||||
fn offset_from_utc_date(&self, _utc: &NaiveDate) -> Utc {
|
||||
Utc
|
||||
}
|
||||
fn offset_from_utc_datetime(&self, _utc: &NaiveDateTime) -> Utc {
|
||||
Utc
|
||||
}
|
||||
}
|
||||
|
||||
impl Offset for Utc {
|
||||
fn fix(&self) -> FixedOffset {
|
||||
FixedOffset::east(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Utc {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Z")
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Utc {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "UTC")
|
||||
}
|
||||
}
|
||||
684
zeroidc/vendor/chrono/src/oldtime.rs
vendored
Normal file
684
zeroidc/vendor/chrono/src/oldtime.rs
vendored
Normal file
@@ -0,0 +1,684 @@
|
||||
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Temporal quantification
|
||||
|
||||
use core::ops::{Add, Div, Mul, Neg, Sub};
|
||||
use core::time::Duration as StdDuration;
|
||||
use core::{fmt, i64};
|
||||
#[cfg(any(feature = "std", test))]
|
||||
use std::error::Error;
|
||||
|
||||
/// The number of nanoseconds in a microsecond.
|
||||
const NANOS_PER_MICRO: i32 = 1000;
|
||||
/// The number of nanoseconds in a millisecond.
|
||||
const NANOS_PER_MILLI: i32 = 1000_000;
|
||||
/// The number of nanoseconds in seconds.
|
||||
const NANOS_PER_SEC: i32 = 1_000_000_000;
|
||||
/// The number of microseconds per second.
|
||||
const MICROS_PER_SEC: i64 = 1000_000;
|
||||
/// The number of milliseconds per second.
|
||||
const MILLIS_PER_SEC: i64 = 1000;
|
||||
/// The number of seconds in a minute.
|
||||
const SECS_PER_MINUTE: i64 = 60;
|
||||
/// The number of seconds in an hour.
|
||||
const SECS_PER_HOUR: i64 = 3600;
|
||||
/// The number of (non-leap) seconds in days.
|
||||
const SECS_PER_DAY: i64 = 86400;
|
||||
/// The number of (non-leap) seconds in a week.
|
||||
const SECS_PER_WEEK: i64 = 604800;
|
||||
|
||||
macro_rules! try_opt {
|
||||
($e:expr) => {
|
||||
match $e {
|
||||
Some(v) => v,
|
||||
None => return None,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// ISO 8601 time duration with nanosecond precision.
|
||||
/// This also allows for the negative duration; see individual methods for details.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
||||
pub struct Duration {
|
||||
secs: i64,
|
||||
nanos: i32, // Always 0 <= nanos < NANOS_PER_SEC
|
||||
}
|
||||
|
||||
/// The minimum possible `Duration`: `i64::MIN` milliseconds.
|
||||
pub const MIN: Duration = Duration {
|
||||
secs: i64::MIN / MILLIS_PER_SEC - 1,
|
||||
nanos: NANOS_PER_SEC + (i64::MIN % MILLIS_PER_SEC) as i32 * NANOS_PER_MILLI,
|
||||
};
|
||||
|
||||
/// The maximum possible `Duration`: `i64::MAX` milliseconds.
|
||||
pub const MAX: Duration = Duration {
|
||||
secs: i64::MAX / MILLIS_PER_SEC,
|
||||
nanos: (i64::MAX % MILLIS_PER_SEC) as i32 * NANOS_PER_MILLI,
|
||||
};
|
||||
|
||||
impl Duration {
|
||||
/// Makes a new `Duration` with given number of weeks.
|
||||
/// Equivalent to `Duration::seconds(weeks * 7 * 24 * 60 * 60)` with overflow checks.
|
||||
/// Panics when the duration is out of bounds.
|
||||
#[inline]
|
||||
pub fn weeks(weeks: i64) -> Duration {
|
||||
let secs = weeks.checked_mul(SECS_PER_WEEK).expect("Duration::weeks out of bounds");
|
||||
Duration::seconds(secs)
|
||||
}
|
||||
|
||||
/// Makes a new `Duration` with given number of days.
|
||||
/// Equivalent to `Duration::seconds(days * 24 * 60 * 60)` with overflow checks.
|
||||
/// Panics when the duration is out of bounds.
|
||||
#[inline]
|
||||
pub fn days(days: i64) -> Duration {
|
||||
let secs = days.checked_mul(SECS_PER_DAY).expect("Duration::days out of bounds");
|
||||
Duration::seconds(secs)
|
||||
}
|
||||
|
||||
/// Makes a new `Duration` with given number of hours.
|
||||
/// Equivalent to `Duration::seconds(hours * 60 * 60)` with overflow checks.
|
||||
/// Panics when the duration is out of bounds.
|
||||
#[inline]
|
||||
pub fn hours(hours: i64) -> Duration {
|
||||
let secs = hours.checked_mul(SECS_PER_HOUR).expect("Duration::hours ouf of bounds");
|
||||
Duration::seconds(secs)
|
||||
}
|
||||
|
||||
/// Makes a new `Duration` with given number of minutes.
|
||||
/// Equivalent to `Duration::seconds(minutes * 60)` with overflow checks.
|
||||
/// Panics when the duration is out of bounds.
|
||||
#[inline]
|
||||
pub fn minutes(minutes: i64) -> Duration {
|
||||
let secs = minutes.checked_mul(SECS_PER_MINUTE).expect("Duration::minutes out of bounds");
|
||||
Duration::seconds(secs)
|
||||
}
|
||||
|
||||
/// Makes a new `Duration` with given number of seconds.
|
||||
/// Panics when the duration is more than `i64::MAX` seconds
|
||||
/// or less than `i64::MIN` seconds.
|
||||
#[inline]
|
||||
pub fn seconds(seconds: i64) -> Duration {
|
||||
let d = Duration { secs: seconds, nanos: 0 };
|
||||
if d < MIN || d > MAX {
|
||||
panic!("Duration::seconds out of bounds");
|
||||
}
|
||||
d
|
||||
}
|
||||
|
||||
/// Makes a new `Duration` with given number of milliseconds.
|
||||
#[inline]
|
||||
pub fn milliseconds(milliseconds: i64) -> Duration {
|
||||
let (secs, millis) = div_mod_floor_64(milliseconds, MILLIS_PER_SEC);
|
||||
let nanos = millis as i32 * NANOS_PER_MILLI;
|
||||
Duration { secs: secs, nanos: nanos }
|
||||
}
|
||||
|
||||
/// Makes a new `Duration` with given number of microseconds.
|
||||
#[inline]
|
||||
pub fn microseconds(microseconds: i64) -> Duration {
|
||||
let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC);
|
||||
let nanos = micros as i32 * NANOS_PER_MICRO;
|
||||
Duration { secs: secs, nanos: nanos }
|
||||
}
|
||||
|
||||
/// Makes a new `Duration` with given number of nanoseconds.
|
||||
#[inline]
|
||||
pub fn nanoseconds(nanos: i64) -> Duration {
|
||||
let (secs, nanos) = div_mod_floor_64(nanos, NANOS_PER_SEC as i64);
|
||||
Duration { secs: secs, nanos: nanos as i32 }
|
||||
}
|
||||
|
||||
/// Returns the total number of whole weeks in the duration.
|
||||
#[inline]
|
||||
pub fn num_weeks(&self) -> i64 {
|
||||
self.num_days() / 7
|
||||
}
|
||||
|
||||
/// Returns the total number of whole days in the duration.
|
||||
pub fn num_days(&self) -> i64 {
|
||||
self.num_seconds() / SECS_PER_DAY
|
||||
}
|
||||
|
||||
/// Returns the total number of whole hours in the duration.
|
||||
#[inline]
|
||||
pub fn num_hours(&self) -> i64 {
|
||||
self.num_seconds() / SECS_PER_HOUR
|
||||
}
|
||||
|
||||
/// Returns the total number of whole minutes in the duration.
|
||||
#[inline]
|
||||
pub fn num_minutes(&self) -> i64 {
|
||||
self.num_seconds() / SECS_PER_MINUTE
|
||||
}
|
||||
|
||||
/// Returns the total number of whole seconds in the duration.
|
||||
pub fn num_seconds(&self) -> i64 {
|
||||
// If secs is negative, nanos should be subtracted from the duration.
|
||||
if self.secs < 0 && self.nanos > 0 {
|
||||
self.secs + 1
|
||||
} else {
|
||||
self.secs
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of nanoseconds such that
|
||||
/// `nanos_mod_sec() + num_seconds() * NANOS_PER_SEC` is the total number of
|
||||
/// nanoseconds in the duration.
|
||||
fn nanos_mod_sec(&self) -> i32 {
|
||||
if self.secs < 0 && self.nanos > 0 {
|
||||
self.nanos - NANOS_PER_SEC
|
||||
} else {
|
||||
self.nanos
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the total number of whole milliseconds in the duration,
|
||||
pub fn num_milliseconds(&self) -> i64 {
|
||||
// A proper Duration will not overflow, because MIN and MAX are defined
|
||||
// such that the range is exactly i64 milliseconds.
|
||||
let secs_part = self.num_seconds() * MILLIS_PER_SEC;
|
||||
let nanos_part = self.nanos_mod_sec() / NANOS_PER_MILLI;
|
||||
secs_part + nanos_part as i64
|
||||
}
|
||||
|
||||
/// Returns the total number of whole microseconds in the duration,
|
||||
/// or `None` on overflow (exceeding 2^63 microseconds in either direction).
|
||||
pub fn num_microseconds(&self) -> Option<i64> {
|
||||
let secs_part = try_opt!(self.num_seconds().checked_mul(MICROS_PER_SEC));
|
||||
let nanos_part = self.nanos_mod_sec() / NANOS_PER_MICRO;
|
||||
secs_part.checked_add(nanos_part as i64)
|
||||
}
|
||||
|
||||
/// Returns the total number of whole nanoseconds in the duration,
|
||||
/// or `None` on overflow (exceeding 2^63 nanoseconds in either direction).
|
||||
pub fn num_nanoseconds(&self) -> Option<i64> {
|
||||
let secs_part = try_opt!(self.num_seconds().checked_mul(NANOS_PER_SEC as i64));
|
||||
let nanos_part = self.nanos_mod_sec();
|
||||
secs_part.checked_add(nanos_part as i64)
|
||||
}
|
||||
|
||||
/// Add two durations, returning `None` if overflow occurred.
|
||||
pub fn checked_add(&self, rhs: &Duration) -> Option<Duration> {
|
||||
let mut secs = try_opt!(self.secs.checked_add(rhs.secs));
|
||||
let mut nanos = self.nanos + rhs.nanos;
|
||||
if nanos >= NANOS_PER_SEC {
|
||||
nanos -= NANOS_PER_SEC;
|
||||
secs = try_opt!(secs.checked_add(1));
|
||||
}
|
||||
let d = Duration { secs: secs, nanos: nanos };
|
||||
// Even if d is within the bounds of i64 seconds,
|
||||
// it might still overflow i64 milliseconds.
|
||||
if d < MIN || d > MAX {
|
||||
None
|
||||
} else {
|
||||
Some(d)
|
||||
}
|
||||
}
|
||||
|
||||
/// Subtract two durations, returning `None` if overflow occurred.
|
||||
pub fn checked_sub(&self, rhs: &Duration) -> Option<Duration> {
|
||||
let mut secs = try_opt!(self.secs.checked_sub(rhs.secs));
|
||||
let mut nanos = self.nanos - rhs.nanos;
|
||||
if nanos < 0 {
|
||||
nanos += NANOS_PER_SEC;
|
||||
secs = try_opt!(secs.checked_sub(1));
|
||||
}
|
||||
let d = Duration { secs: secs, nanos: nanos };
|
||||
// Even if d is within the bounds of i64 seconds,
|
||||
// it might still overflow i64 milliseconds.
|
||||
if d < MIN || d > MAX {
|
||||
None
|
||||
} else {
|
||||
Some(d)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the duration as an absolute (non-negative) value.
|
||||
#[inline]
|
||||
pub fn abs(&self) -> Duration {
|
||||
Duration { secs: self.secs.abs(), nanos: self.nanos }
|
||||
}
|
||||
|
||||
/// The minimum possible `Duration`: `i64::MIN` milliseconds.
|
||||
#[inline]
|
||||
pub fn min_value() -> Duration {
|
||||
MIN
|
||||
}
|
||||
|
||||
/// The maximum possible `Duration`: `i64::MAX` milliseconds.
|
||||
#[inline]
|
||||
pub fn max_value() -> Duration {
|
||||
MAX
|
||||
}
|
||||
|
||||
/// A duration where the stored seconds and nanoseconds are equal to zero.
|
||||
#[inline]
|
||||
pub fn zero() -> Duration {
|
||||
Duration { secs: 0, nanos: 0 }
|
||||
}
|
||||
|
||||
/// Returns `true` if the duration equals `Duration::zero()`.
|
||||
#[inline]
|
||||
pub fn is_zero(&self) -> bool {
|
||||
self.secs == 0 && self.nanos == 0
|
||||
}
|
||||
|
||||
/// Creates a `time::Duration` object from `std::time::Duration`
|
||||
///
|
||||
/// This function errors when original duration is larger than the maximum
|
||||
/// value supported for this type.
|
||||
pub fn from_std(duration: StdDuration) -> Result<Duration, OutOfRangeError> {
|
||||
// We need to check secs as u64 before coercing to i64
|
||||
if duration.as_secs() > MAX.secs as u64 {
|
||||
return Err(OutOfRangeError(()));
|
||||
}
|
||||
let d = Duration { secs: duration.as_secs() as i64, nanos: duration.subsec_nanos() as i32 };
|
||||
if d > MAX {
|
||||
return Err(OutOfRangeError(()));
|
||||
}
|
||||
Ok(d)
|
||||
}
|
||||
|
||||
/// Creates a `std::time::Duration` object from `time::Duration`
|
||||
///
|
||||
/// This function errors when duration is less than zero. As standard
|
||||
/// library implementation is limited to non-negative values.
|
||||
pub fn to_std(&self) -> Result<StdDuration, OutOfRangeError> {
|
||||
if self.secs < 0 {
|
||||
return Err(OutOfRangeError(()));
|
||||
}
|
||||
Ok(StdDuration::new(self.secs as u64, self.nanos as u32))
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Duration {
|
||||
type Output = Duration;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Duration {
|
||||
if self.nanos == 0 {
|
||||
Duration { secs: -self.secs, nanos: 0 }
|
||||
} else {
|
||||
Duration { secs: -self.secs - 1, nanos: NANOS_PER_SEC - self.nanos }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Duration {
|
||||
type Output = Duration;
|
||||
|
||||
fn add(self, rhs: Duration) -> Duration {
|
||||
let mut secs = self.secs + rhs.secs;
|
||||
let mut nanos = self.nanos + rhs.nanos;
|
||||
if nanos >= NANOS_PER_SEC {
|
||||
nanos -= NANOS_PER_SEC;
|
||||
secs += 1;
|
||||
}
|
||||
Duration { secs: secs, nanos: nanos }
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for Duration {
|
||||
type Output = Duration;
|
||||
|
||||
fn sub(self, rhs: Duration) -> Duration {
|
||||
let mut secs = self.secs - rhs.secs;
|
||||
let mut nanos = self.nanos - rhs.nanos;
|
||||
if nanos < 0 {
|
||||
nanos += NANOS_PER_SEC;
|
||||
secs -= 1;
|
||||
}
|
||||
Duration { secs: secs, nanos: nanos }
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<i32> for Duration {
|
||||
type Output = Duration;
|
||||
|
||||
fn mul(self, rhs: i32) -> Duration {
|
||||
// Multiply nanoseconds as i64, because it cannot overflow that way.
|
||||
let total_nanos = self.nanos as i64 * rhs as i64;
|
||||
let (extra_secs, nanos) = div_mod_floor_64(total_nanos, NANOS_PER_SEC as i64);
|
||||
let secs = self.secs * rhs as i64 + extra_secs;
|
||||
Duration { secs: secs, nanos: nanos as i32 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<i32> for Duration {
|
||||
type Output = Duration;
|
||||
|
||||
fn div(self, rhs: i32) -> Duration {
|
||||
let mut secs = self.secs / rhs as i64;
|
||||
let carry = self.secs - secs * rhs as i64;
|
||||
let extra_nanos = carry * NANOS_PER_SEC as i64 / rhs as i64;
|
||||
let mut nanos = self.nanos / rhs + extra_nanos as i32;
|
||||
if nanos >= NANOS_PER_SEC {
|
||||
nanos -= NANOS_PER_SEC;
|
||||
secs += 1;
|
||||
}
|
||||
if nanos < 0 {
|
||||
nanos += NANOS_PER_SEC;
|
||||
secs -= 1;
|
||||
}
|
||||
Duration { secs: secs, nanos: nanos }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Duration {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
// technically speaking, negative duration is not valid ISO 8601,
|
||||
// but we need to print it anyway.
|
||||
let (abs, sign) = if self.secs < 0 { (-*self, "-") } else { (*self, "") };
|
||||
|
||||
let days = abs.secs / SECS_PER_DAY;
|
||||
let secs = abs.secs - days * SECS_PER_DAY;
|
||||
let hasdate = days != 0;
|
||||
let hastime = (secs != 0 || abs.nanos != 0) || !hasdate;
|
||||
|
||||
write!(f, "{}P", sign)?;
|
||||
|
||||
if hasdate {
|
||||
write!(f, "{}D", days)?;
|
||||
}
|
||||
if hastime {
|
||||
if abs.nanos == 0 {
|
||||
write!(f, "T{}S", secs)?;
|
||||
} else if abs.nanos % NANOS_PER_MILLI == 0 {
|
||||
write!(f, "T{}.{:03}S", secs, abs.nanos / NANOS_PER_MILLI)?;
|
||||
} else if abs.nanos % NANOS_PER_MICRO == 0 {
|
||||
write!(f, "T{}.{:06}S", secs, abs.nanos / NANOS_PER_MICRO)?;
|
||||
} else {
|
||||
write!(f, "T{}.{:09}S", secs, abs.nanos)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents error when converting `Duration` to/from a standard library
|
||||
/// implementation
|
||||
///
|
||||
/// The `std::time::Duration` supports a range from zero to `u64::MAX`
|
||||
/// *seconds*, while this module supports signed range of up to
|
||||
/// `i64::MAX` of *milliseconds*.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct OutOfRangeError(());
|
||||
|
||||
impl fmt::Display for OutOfRangeError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Source duration value is out of range for the target type")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
impl Error for OutOfRangeError {
|
||||
#[allow(deprecated)]
|
||||
fn description(&self) -> &str {
|
||||
"out of range error"
|
||||
}
|
||||
}
|
||||
|
||||
// Copied from libnum
|
||||
#[inline]
|
||||
fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) {
|
||||
(div_floor_64(this, other), mod_floor_64(this, other))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn div_floor_64(this: i64, other: i64) -> i64 {
|
||||
match div_rem_64(this, other) {
|
||||
(d, r) if (r > 0 && other < 0) || (r < 0 && other > 0) => d - 1,
|
||||
(d, _) => d,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn mod_floor_64(this: i64, other: i64) -> i64 {
|
||||
match this % other {
|
||||
r if (r > 0 && other < 0) || (r < 0 && other > 0) => r + other,
|
||||
r => r,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn div_rem_64(this: i64, other: i64) -> (i64, i64) {
|
||||
(this / other, this % other)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Duration, OutOfRangeError, MAX, MIN};
|
||||
use std::time::Duration as StdDuration;
|
||||
use std::{i32, i64};
|
||||
|
||||
#[test]
|
||||
fn test_duration() {
|
||||
assert!(Duration::seconds(1) != Duration::zero());
|
||||
assert_eq!(Duration::seconds(1) + Duration::seconds(2), Duration::seconds(3));
|
||||
assert_eq!(
|
||||
Duration::seconds(86399) + Duration::seconds(4),
|
||||
Duration::days(1) + Duration::seconds(3)
|
||||
);
|
||||
assert_eq!(Duration::days(10) - Duration::seconds(1000), Duration::seconds(863000));
|
||||
assert_eq!(Duration::days(10) - Duration::seconds(1000000), Duration::seconds(-136000));
|
||||
assert_eq!(
|
||||
Duration::days(2) + Duration::seconds(86399) + Duration::nanoseconds(1234567890),
|
||||
Duration::days(3) + Duration::nanoseconds(234567890)
|
||||
);
|
||||
assert_eq!(-Duration::days(3), Duration::days(-3));
|
||||
assert_eq!(
|
||||
-(Duration::days(3) + Duration::seconds(70)),
|
||||
Duration::days(-4) + Duration::seconds(86400 - 70)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_duration_num_days() {
|
||||
assert_eq!(Duration::zero().num_days(), 0);
|
||||
assert_eq!(Duration::days(1).num_days(), 1);
|
||||
assert_eq!(Duration::days(-1).num_days(), -1);
|
||||
assert_eq!(Duration::seconds(86399).num_days(), 0);
|
||||
assert_eq!(Duration::seconds(86401).num_days(), 1);
|
||||
assert_eq!(Duration::seconds(-86399).num_days(), 0);
|
||||
assert_eq!(Duration::seconds(-86401).num_days(), -1);
|
||||
assert_eq!(Duration::days(i32::MAX as i64).num_days(), i32::MAX as i64);
|
||||
assert_eq!(Duration::days(i32::MIN as i64).num_days(), i32::MIN as i64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_duration_num_seconds() {
|
||||
assert_eq!(Duration::zero().num_seconds(), 0);
|
||||
assert_eq!(Duration::seconds(1).num_seconds(), 1);
|
||||
assert_eq!(Duration::seconds(-1).num_seconds(), -1);
|
||||
assert_eq!(Duration::milliseconds(999).num_seconds(), 0);
|
||||
assert_eq!(Duration::milliseconds(1001).num_seconds(), 1);
|
||||
assert_eq!(Duration::milliseconds(-999).num_seconds(), 0);
|
||||
assert_eq!(Duration::milliseconds(-1001).num_seconds(), -1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_duration_num_milliseconds() {
|
||||
assert_eq!(Duration::zero().num_milliseconds(), 0);
|
||||
assert_eq!(Duration::milliseconds(1).num_milliseconds(), 1);
|
||||
assert_eq!(Duration::milliseconds(-1).num_milliseconds(), -1);
|
||||
assert_eq!(Duration::microseconds(999).num_milliseconds(), 0);
|
||||
assert_eq!(Duration::microseconds(1001).num_milliseconds(), 1);
|
||||
assert_eq!(Duration::microseconds(-999).num_milliseconds(), 0);
|
||||
assert_eq!(Duration::microseconds(-1001).num_milliseconds(), -1);
|
||||
assert_eq!(Duration::milliseconds(i64::MAX).num_milliseconds(), i64::MAX);
|
||||
assert_eq!(Duration::milliseconds(i64::MIN).num_milliseconds(), i64::MIN);
|
||||
assert_eq!(MAX.num_milliseconds(), i64::MAX);
|
||||
assert_eq!(MIN.num_milliseconds(), i64::MIN);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_duration_num_microseconds() {
|
||||
assert_eq!(Duration::zero().num_microseconds(), Some(0));
|
||||
assert_eq!(Duration::microseconds(1).num_microseconds(), Some(1));
|
||||
assert_eq!(Duration::microseconds(-1).num_microseconds(), Some(-1));
|
||||
assert_eq!(Duration::nanoseconds(999).num_microseconds(), Some(0));
|
||||
assert_eq!(Duration::nanoseconds(1001).num_microseconds(), Some(1));
|
||||
assert_eq!(Duration::nanoseconds(-999).num_microseconds(), Some(0));
|
||||
assert_eq!(Duration::nanoseconds(-1001).num_microseconds(), Some(-1));
|
||||
assert_eq!(Duration::microseconds(i64::MAX).num_microseconds(), Some(i64::MAX));
|
||||
assert_eq!(Duration::microseconds(i64::MIN).num_microseconds(), Some(i64::MIN));
|
||||
assert_eq!(MAX.num_microseconds(), None);
|
||||
assert_eq!(MIN.num_microseconds(), None);
|
||||
|
||||
// overflow checks
|
||||
const MICROS_PER_DAY: i64 = 86400_000_000;
|
||||
assert_eq!(
|
||||
Duration::days(i64::MAX / MICROS_PER_DAY).num_microseconds(),
|
||||
Some(i64::MAX / MICROS_PER_DAY * MICROS_PER_DAY)
|
||||
);
|
||||
assert_eq!(
|
||||
Duration::days(i64::MIN / MICROS_PER_DAY).num_microseconds(),
|
||||
Some(i64::MIN / MICROS_PER_DAY * MICROS_PER_DAY)
|
||||
);
|
||||
assert_eq!(Duration::days(i64::MAX / MICROS_PER_DAY + 1).num_microseconds(), None);
|
||||
assert_eq!(Duration::days(i64::MIN / MICROS_PER_DAY - 1).num_microseconds(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_duration_num_nanoseconds() {
|
||||
assert_eq!(Duration::zero().num_nanoseconds(), Some(0));
|
||||
assert_eq!(Duration::nanoseconds(1).num_nanoseconds(), Some(1));
|
||||
assert_eq!(Duration::nanoseconds(-1).num_nanoseconds(), Some(-1));
|
||||
assert_eq!(Duration::nanoseconds(i64::MAX).num_nanoseconds(), Some(i64::MAX));
|
||||
assert_eq!(Duration::nanoseconds(i64::MIN).num_nanoseconds(), Some(i64::MIN));
|
||||
assert_eq!(MAX.num_nanoseconds(), None);
|
||||
assert_eq!(MIN.num_nanoseconds(), None);
|
||||
|
||||
// overflow checks
|
||||
const NANOS_PER_DAY: i64 = 86400_000_000_000;
|
||||
assert_eq!(
|
||||
Duration::days(i64::MAX / NANOS_PER_DAY).num_nanoseconds(),
|
||||
Some(i64::MAX / NANOS_PER_DAY * NANOS_PER_DAY)
|
||||
);
|
||||
assert_eq!(
|
||||
Duration::days(i64::MIN / NANOS_PER_DAY).num_nanoseconds(),
|
||||
Some(i64::MIN / NANOS_PER_DAY * NANOS_PER_DAY)
|
||||
);
|
||||
assert_eq!(Duration::days(i64::MAX / NANOS_PER_DAY + 1).num_nanoseconds(), None);
|
||||
assert_eq!(Duration::days(i64::MIN / NANOS_PER_DAY - 1).num_nanoseconds(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_duration_checked_ops() {
|
||||
assert_eq!(
|
||||
Duration::milliseconds(i64::MAX - 1).checked_add(&Duration::microseconds(999)),
|
||||
Some(Duration::milliseconds(i64::MAX - 2) + Duration::microseconds(1999))
|
||||
);
|
||||
assert!(Duration::milliseconds(i64::MAX)
|
||||
.checked_add(&Duration::microseconds(1000))
|
||||
.is_none());
|
||||
|
||||
assert_eq!(
|
||||
Duration::milliseconds(i64::MIN).checked_sub(&Duration::milliseconds(0)),
|
||||
Some(Duration::milliseconds(i64::MIN))
|
||||
);
|
||||
assert!(Duration::milliseconds(i64::MIN).checked_sub(&Duration::milliseconds(1)).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_duration_mul() {
|
||||
assert_eq!(Duration::zero() * i32::MAX, Duration::zero());
|
||||
assert_eq!(Duration::zero() * i32::MIN, Duration::zero());
|
||||
assert_eq!(Duration::nanoseconds(1) * 0, Duration::zero());
|
||||
assert_eq!(Duration::nanoseconds(1) * 1, Duration::nanoseconds(1));
|
||||
assert_eq!(Duration::nanoseconds(1) * 1_000_000_000, Duration::seconds(1));
|
||||
assert_eq!(Duration::nanoseconds(1) * -1_000_000_000, -Duration::seconds(1));
|
||||
assert_eq!(-Duration::nanoseconds(1) * 1_000_000_000, -Duration::seconds(1));
|
||||
assert_eq!(
|
||||
Duration::nanoseconds(30) * 333_333_333,
|
||||
Duration::seconds(10) - Duration::nanoseconds(10)
|
||||
);
|
||||
assert_eq!(
|
||||
(Duration::nanoseconds(1) + Duration::seconds(1) + Duration::days(1)) * 3,
|
||||
Duration::nanoseconds(3) + Duration::seconds(3) + Duration::days(3)
|
||||
);
|
||||
assert_eq!(Duration::milliseconds(1500) * -2, Duration::seconds(-3));
|
||||
assert_eq!(Duration::milliseconds(-1500) * 2, Duration::seconds(-3));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_duration_div() {
|
||||
assert_eq!(Duration::zero() / i32::MAX, Duration::zero());
|
||||
assert_eq!(Duration::zero() / i32::MIN, Duration::zero());
|
||||
assert_eq!(Duration::nanoseconds(123_456_789) / 1, Duration::nanoseconds(123_456_789));
|
||||
assert_eq!(Duration::nanoseconds(123_456_789) / -1, -Duration::nanoseconds(123_456_789));
|
||||
assert_eq!(-Duration::nanoseconds(123_456_789) / -1, Duration::nanoseconds(123_456_789));
|
||||
assert_eq!(-Duration::nanoseconds(123_456_789) / 1, -Duration::nanoseconds(123_456_789));
|
||||
assert_eq!(Duration::seconds(1) / 3, Duration::nanoseconds(333_333_333));
|
||||
assert_eq!(Duration::seconds(4) / 3, Duration::nanoseconds(1_333_333_333));
|
||||
assert_eq!(Duration::seconds(-1) / 2, Duration::milliseconds(-500));
|
||||
assert_eq!(Duration::seconds(1) / -2, Duration::milliseconds(-500));
|
||||
assert_eq!(Duration::seconds(-1) / -2, Duration::milliseconds(500));
|
||||
assert_eq!(Duration::seconds(-4) / 3, Duration::nanoseconds(-1_333_333_333));
|
||||
assert_eq!(Duration::seconds(-4) / -3, Duration::nanoseconds(1_333_333_333));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_duration_fmt() {
|
||||
assert_eq!(Duration::zero().to_string(), "PT0S");
|
||||
assert_eq!(Duration::days(42).to_string(), "P42D");
|
||||
assert_eq!(Duration::days(-42).to_string(), "-P42D");
|
||||
assert_eq!(Duration::seconds(42).to_string(), "PT42S");
|
||||
assert_eq!(Duration::milliseconds(42).to_string(), "PT0.042S");
|
||||
assert_eq!(Duration::microseconds(42).to_string(), "PT0.000042S");
|
||||
assert_eq!(Duration::nanoseconds(42).to_string(), "PT0.000000042S");
|
||||
assert_eq!((Duration::days(7) + Duration::milliseconds(6543)).to_string(), "P7DT6.543S");
|
||||
assert_eq!(Duration::seconds(-86401).to_string(), "-P1DT1S");
|
||||
assert_eq!(Duration::nanoseconds(-1).to_string(), "-PT0.000000001S");
|
||||
|
||||
// the format specifier should have no effect on `Duration`
|
||||
assert_eq!(
|
||||
format!("{:30}", Duration::days(1) + Duration::milliseconds(2345)),
|
||||
"P1DT2.345S"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_std() {
|
||||
assert_eq!(Duration::seconds(1).to_std(), Ok(StdDuration::new(1, 0)));
|
||||
assert_eq!(Duration::seconds(86401).to_std(), Ok(StdDuration::new(86401, 0)));
|
||||
assert_eq!(Duration::milliseconds(123).to_std(), Ok(StdDuration::new(0, 123000000)));
|
||||
assert_eq!(Duration::milliseconds(123765).to_std(), Ok(StdDuration::new(123, 765000000)));
|
||||
assert_eq!(Duration::nanoseconds(777).to_std(), Ok(StdDuration::new(0, 777)));
|
||||
assert_eq!(MAX.to_std(), Ok(StdDuration::new(9223372036854775, 807000000)));
|
||||
assert_eq!(Duration::seconds(-1).to_std(), Err(OutOfRangeError(())));
|
||||
assert_eq!(Duration::milliseconds(-1).to_std(), Err(OutOfRangeError(())));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_std() {
|
||||
assert_eq!(Ok(Duration::seconds(1)), Duration::from_std(StdDuration::new(1, 0)));
|
||||
assert_eq!(Ok(Duration::seconds(86401)), Duration::from_std(StdDuration::new(86401, 0)));
|
||||
assert_eq!(
|
||||
Ok(Duration::milliseconds(123)),
|
||||
Duration::from_std(StdDuration::new(0, 123000000))
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(Duration::milliseconds(123765)),
|
||||
Duration::from_std(StdDuration::new(123, 765000000))
|
||||
);
|
||||
assert_eq!(Ok(Duration::nanoseconds(777)), Duration::from_std(StdDuration::new(0, 777)));
|
||||
assert_eq!(Ok(MAX), Duration::from_std(StdDuration::new(9223372036854775, 807000000)));
|
||||
assert_eq!(
|
||||
Duration::from_std(StdDuration::new(9223372036854776, 0)),
|
||||
Err(OutOfRangeError(()))
|
||||
);
|
||||
assert_eq!(
|
||||
Duration::from_std(StdDuration::new(9223372036854775, 807000001)),
|
||||
Err(OutOfRangeError(()))
|
||||
);
|
||||
}
|
||||
}
|
||||
456
zeroidc/vendor/chrono/src/round.rs
vendored
Normal file
456
zeroidc/vendor/chrono/src/round.rs
vendored
Normal file
@@ -0,0 +1,456 @@
|
||||
// This is a part of Chrono.
|
||||
// See README.md and LICENSE.txt for details.
|
||||
|
||||
use core::cmp::Ordering;
|
||||
use core::fmt;
|
||||
use core::marker::Sized;
|
||||
use core::ops::{Add, Sub};
|
||||
use datetime::DateTime;
|
||||
use oldtime::Duration;
|
||||
#[cfg(any(feature = "std", test))]
|
||||
use std;
|
||||
use TimeZone;
|
||||
use Timelike;
|
||||
|
||||
/// Extension trait for subsecond rounding or truncation to a maximum number
|
||||
/// of digits. Rounding can be used to decrease the error variance when
|
||||
/// serializing/persisting to lower precision. Truncation is the default
|
||||
/// behavior in Chrono display formatting. Either can be used to guarantee
|
||||
/// equality (e.g. for testing) when round-tripping through a lower precision
|
||||
/// format.
|
||||
pub trait SubsecRound {
|
||||
/// Return a copy rounded to the specified number of subsecond digits. With
|
||||
/// 9 or more digits, self is returned unmodified. Halfway values are
|
||||
/// rounded up (away from zero).
|
||||
///
|
||||
/// # Example
|
||||
/// ``` rust
|
||||
/// # use chrono::{DateTime, SubsecRound, Timelike, TimeZone, Utc};
|
||||
/// let dt = Utc.ymd(2018, 1, 11).and_hms_milli(12, 0, 0, 154);
|
||||
/// assert_eq!(dt.round_subsecs(2).nanosecond(), 150_000_000);
|
||||
/// assert_eq!(dt.round_subsecs(1).nanosecond(), 200_000_000);
|
||||
/// ```
|
||||
fn round_subsecs(self, digits: u16) -> Self;
|
||||
|
||||
/// Return a copy truncated to the specified number of subsecond
|
||||
/// digits. With 9 or more digits, self is returned unmodified.
|
||||
///
|
||||
/// # Example
|
||||
/// ``` rust
|
||||
/// # use chrono::{DateTime, SubsecRound, Timelike, TimeZone, Utc};
|
||||
/// let dt = Utc.ymd(2018, 1, 11).and_hms_milli(12, 0, 0, 154);
|
||||
/// assert_eq!(dt.trunc_subsecs(2).nanosecond(), 150_000_000);
|
||||
/// assert_eq!(dt.trunc_subsecs(1).nanosecond(), 100_000_000);
|
||||
/// ```
|
||||
fn trunc_subsecs(self, digits: u16) -> Self;
|
||||
}
|
||||
|
||||
impl<T> SubsecRound for T
|
||||
where
|
||||
T: Timelike + Add<Duration, Output = T> + Sub<Duration, Output = T>,
|
||||
{
|
||||
fn round_subsecs(self, digits: u16) -> T {
|
||||
let span = span_for_digits(digits);
|
||||
let delta_down = self.nanosecond() % span;
|
||||
if delta_down > 0 {
|
||||
let delta_up = span - delta_down;
|
||||
if delta_up <= delta_down {
|
||||
self + Duration::nanoseconds(delta_up.into())
|
||||
} else {
|
||||
self - Duration::nanoseconds(delta_down.into())
|
||||
}
|
||||
} else {
|
||||
self // unchanged
|
||||
}
|
||||
}
|
||||
|
||||
fn trunc_subsecs(self, digits: u16) -> T {
|
||||
let span = span_for_digits(digits);
|
||||
let delta_down = self.nanosecond() % span;
|
||||
if delta_down > 0 {
|
||||
self - Duration::nanoseconds(delta_down.into())
|
||||
} else {
|
||||
self // unchanged
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the maximum span in nanoseconds for the target number of digits.
|
||||
fn span_for_digits(digits: u16) -> u32 {
|
||||
// fast lookup form of: 10^(9-min(9,digits))
|
||||
match digits {
|
||||
0 => 1_000_000_000,
|
||||
1 => 100_000_000,
|
||||
2 => 10_000_000,
|
||||
3 => 1_000_000,
|
||||
4 => 100_000,
|
||||
5 => 10_000,
|
||||
6 => 1_000,
|
||||
7 => 100,
|
||||
8 => 10,
|
||||
_ => 1,
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait for rounding or truncating a DateTime by a Duration.
|
||||
///
|
||||
/// # Limitations
|
||||
/// Both rounding and truncating are done via [`Duration::num_nanoseconds`] and
|
||||
/// [`DateTime::timestamp_nanos`]. This means that they will fail if either the
|
||||
/// `Duration` or the `DateTime` are too big to represented as nanoseconds. They
|
||||
/// will also fail if the `Duration` is bigger than the timestamp.
|
||||
pub trait DurationRound: Sized {
|
||||
/// Error that can occur in rounding or truncating
|
||||
#[cfg(any(feature = "std", test))]
|
||||
type Err: std::error::Error;
|
||||
|
||||
/// Error that can occur in rounding or truncating
|
||||
#[cfg(not(any(feature = "std", test)))]
|
||||
type Err: fmt::Debug + fmt::Display;
|
||||
|
||||
/// Return a copy rounded by Duration.
|
||||
///
|
||||
/// # Example
|
||||
/// ``` rust
|
||||
/// # use chrono::{DateTime, DurationRound, Duration, TimeZone, Utc};
|
||||
/// let dt = Utc.ymd(2018, 1, 11).and_hms_milli(12, 0, 0, 154);
|
||||
/// assert_eq!(
|
||||
/// dt.duration_round(Duration::milliseconds(10)).unwrap().to_string(),
|
||||
/// "2018-01-11 12:00:00.150 UTC"
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// dt.duration_round(Duration::days(1)).unwrap().to_string(),
|
||||
/// "2018-01-12 00:00:00 UTC"
|
||||
/// );
|
||||
/// ```
|
||||
fn duration_round(self, duration: Duration) -> Result<Self, Self::Err>;
|
||||
|
||||
/// Return a copy truncated by Duration.
|
||||
///
|
||||
/// # Example
|
||||
/// ``` rust
|
||||
/// # use chrono::{DateTime, DurationRound, Duration, TimeZone, Utc};
|
||||
/// let dt = Utc.ymd(2018, 1, 11).and_hms_milli(12, 0, 0, 154);
|
||||
/// assert_eq!(
|
||||
/// dt.duration_trunc(Duration::milliseconds(10)).unwrap().to_string(),
|
||||
/// "2018-01-11 12:00:00.150 UTC"
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// dt.duration_trunc(Duration::days(1)).unwrap().to_string(),
|
||||
/// "2018-01-11 00:00:00 UTC"
|
||||
/// );
|
||||
/// ```
|
||||
fn duration_trunc(self, duration: Duration) -> Result<Self, Self::Err>;
|
||||
}
|
||||
|
||||
/// The maximum number of seconds a DateTime can be to be represented as nanoseconds
|
||||
const MAX_SECONDS_TIMESTAMP_FOR_NANOS: i64 = 9_223_372_036;
|
||||
|
||||
impl<Tz: TimeZone> DurationRound for DateTime<Tz> {
|
||||
type Err = RoundingError;
|
||||
|
||||
fn duration_round(self, duration: Duration) -> Result<Self, Self::Err> {
|
||||
if let Some(span) = duration.num_nanoseconds() {
|
||||
if self.timestamp().abs() > MAX_SECONDS_TIMESTAMP_FOR_NANOS {
|
||||
return Err(RoundingError::TimestampExceedsLimit);
|
||||
}
|
||||
let stamp = self.timestamp_nanos();
|
||||
if span > stamp.abs() {
|
||||
return Err(RoundingError::DurationExceedsTimestamp);
|
||||
}
|
||||
let delta_down = stamp % span;
|
||||
if delta_down == 0 {
|
||||
Ok(self)
|
||||
} else {
|
||||
let (delta_up, delta_down) = if delta_down < 0 {
|
||||
(delta_down.abs(), span - delta_down.abs())
|
||||
} else {
|
||||
(span - delta_down, delta_down)
|
||||
};
|
||||
if delta_up <= delta_down {
|
||||
Ok(self + Duration::nanoseconds(delta_up))
|
||||
} else {
|
||||
Ok(self - Duration::nanoseconds(delta_down))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(RoundingError::DurationExceedsLimit)
|
||||
}
|
||||
}
|
||||
|
||||
fn duration_trunc(self, duration: Duration) -> Result<Self, Self::Err> {
|
||||
if let Some(span) = duration.num_nanoseconds() {
|
||||
if self.timestamp().abs() > MAX_SECONDS_TIMESTAMP_FOR_NANOS {
|
||||
return Err(RoundingError::TimestampExceedsLimit);
|
||||
}
|
||||
let stamp = self.timestamp_nanos();
|
||||
if span > stamp.abs() {
|
||||
return Err(RoundingError::DurationExceedsTimestamp);
|
||||
}
|
||||
let delta_down = stamp % span;
|
||||
match delta_down.cmp(&0) {
|
||||
Ordering::Equal => Ok(self),
|
||||
Ordering::Greater => Ok(self - Duration::nanoseconds(delta_down)),
|
||||
Ordering::Less => Ok(self - Duration::nanoseconds(span - delta_down.abs())),
|
||||
}
|
||||
} else {
|
||||
Err(RoundingError::DurationExceedsLimit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An error from rounding by `Duration`
|
||||
///
|
||||
/// See: [`DurationRound`]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||
pub enum RoundingError {
|
||||
/// Error when the Duration exceeds the Duration from or until the Unix epoch.
|
||||
///
|
||||
/// ``` rust
|
||||
/// # use chrono::{DateTime, DurationRound, Duration, RoundingError, TimeZone, Utc};
|
||||
/// let dt = Utc.ymd(1970, 12, 12).and_hms(0, 0, 0);
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// dt.duration_round(Duration::days(365)),
|
||||
/// Err(RoundingError::DurationExceedsTimestamp),
|
||||
/// );
|
||||
/// ```
|
||||
DurationExceedsTimestamp,
|
||||
|
||||
/// Error when `Duration.num_nanoseconds` exceeds the limit.
|
||||
///
|
||||
/// ``` rust
|
||||
/// # use chrono::{DateTime, DurationRound, Duration, RoundingError, TimeZone, Utc};
|
||||
/// let dt = Utc.ymd(2260, 12, 31).and_hms_nano(23, 59, 59, 1_75_500_000);
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// dt.duration_round(Duration::days(300 * 365)),
|
||||
/// Err(RoundingError::DurationExceedsLimit)
|
||||
/// );
|
||||
/// ```
|
||||
DurationExceedsLimit,
|
||||
|
||||
/// Error when `DateTime.timestamp_nanos` exceeds the limit.
|
||||
///
|
||||
/// ``` rust
|
||||
/// # use chrono::{DateTime, DurationRound, Duration, RoundingError, TimeZone, Utc};
|
||||
/// let dt = Utc.ymd(2300, 12, 12).and_hms(0, 0, 0);
|
||||
///
|
||||
/// assert_eq!(dt.duration_round(Duration::days(1)), Err(RoundingError::TimestampExceedsLimit),);
|
||||
/// ```
|
||||
TimestampExceedsLimit,
|
||||
}
|
||||
|
||||
impl fmt::Display for RoundingError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
RoundingError::DurationExceedsTimestamp => {
|
||||
write!(f, "duration in nanoseconds exceeds timestamp")
|
||||
}
|
||||
RoundingError::DurationExceedsLimit => {
|
||||
write!(f, "duration exceeds num_nanoseconds limit")
|
||||
}
|
||||
RoundingError::TimestampExceedsLimit => {
|
||||
write!(f, "timestamp exceeds num_nanoseconds limit")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
impl std::error::Error for RoundingError {
|
||||
#[allow(deprecated)]
|
||||
fn description(&self) -> &str {
|
||||
"error from rounding or truncating with DurationRound"
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Duration, DurationRound, SubsecRound};
|
||||
use offset::{FixedOffset, TimeZone, Utc};
|
||||
use Timelike;
|
||||
|
||||
#[test]
|
||||
fn test_round_subsecs() {
|
||||
let pst = FixedOffset::east(8 * 60 * 60);
|
||||
let dt = pst.ymd(2018, 1, 11).and_hms_nano(10, 5, 13, 084_660_684);
|
||||
|
||||
assert_eq!(dt.round_subsecs(10), dt);
|
||||
assert_eq!(dt.round_subsecs(9), dt);
|
||||
assert_eq!(dt.round_subsecs(8).nanosecond(), 084_660_680);
|
||||
assert_eq!(dt.round_subsecs(7).nanosecond(), 084_660_700);
|
||||
assert_eq!(dt.round_subsecs(6).nanosecond(), 084_661_000);
|
||||
assert_eq!(dt.round_subsecs(5).nanosecond(), 084_660_000);
|
||||
assert_eq!(dt.round_subsecs(4).nanosecond(), 084_700_000);
|
||||
assert_eq!(dt.round_subsecs(3).nanosecond(), 085_000_000);
|
||||
assert_eq!(dt.round_subsecs(2).nanosecond(), 080_000_000);
|
||||
assert_eq!(dt.round_subsecs(1).nanosecond(), 100_000_000);
|
||||
|
||||
assert_eq!(dt.round_subsecs(0).nanosecond(), 0);
|
||||
assert_eq!(dt.round_subsecs(0).second(), 13);
|
||||
|
||||
let dt = Utc.ymd(2018, 1, 11).and_hms_nano(10, 5, 27, 750_500_000);
|
||||
assert_eq!(dt.round_subsecs(9), dt);
|
||||
assert_eq!(dt.round_subsecs(4), dt);
|
||||
assert_eq!(dt.round_subsecs(3).nanosecond(), 751_000_000);
|
||||
assert_eq!(dt.round_subsecs(2).nanosecond(), 750_000_000);
|
||||
assert_eq!(dt.round_subsecs(1).nanosecond(), 800_000_000);
|
||||
|
||||
assert_eq!(dt.round_subsecs(0).nanosecond(), 0);
|
||||
assert_eq!(dt.round_subsecs(0).second(), 28);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_round_leap_nanos() {
|
||||
let dt = Utc.ymd(2016, 12, 31).and_hms_nano(23, 59, 59, 1_750_500_000);
|
||||
assert_eq!(dt.round_subsecs(9), dt);
|
||||
assert_eq!(dt.round_subsecs(4), dt);
|
||||
assert_eq!(dt.round_subsecs(2).nanosecond(), 1_750_000_000);
|
||||
assert_eq!(dt.round_subsecs(1).nanosecond(), 1_800_000_000);
|
||||
assert_eq!(dt.round_subsecs(1).second(), 59);
|
||||
|
||||
assert_eq!(dt.round_subsecs(0).nanosecond(), 0);
|
||||
assert_eq!(dt.round_subsecs(0).second(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trunc_subsecs() {
|
||||
let pst = FixedOffset::east(8 * 60 * 60);
|
||||
let dt = pst.ymd(2018, 1, 11).and_hms_nano(10, 5, 13, 084_660_684);
|
||||
|
||||
assert_eq!(dt.trunc_subsecs(10), dt);
|
||||
assert_eq!(dt.trunc_subsecs(9), dt);
|
||||
assert_eq!(dt.trunc_subsecs(8).nanosecond(), 084_660_680);
|
||||
assert_eq!(dt.trunc_subsecs(7).nanosecond(), 084_660_600);
|
||||
assert_eq!(dt.trunc_subsecs(6).nanosecond(), 084_660_000);
|
||||
assert_eq!(dt.trunc_subsecs(5).nanosecond(), 084_660_000);
|
||||
assert_eq!(dt.trunc_subsecs(4).nanosecond(), 084_600_000);
|
||||
assert_eq!(dt.trunc_subsecs(3).nanosecond(), 084_000_000);
|
||||
assert_eq!(dt.trunc_subsecs(2).nanosecond(), 080_000_000);
|
||||
assert_eq!(dt.trunc_subsecs(1).nanosecond(), 0);
|
||||
|
||||
assert_eq!(dt.trunc_subsecs(0).nanosecond(), 0);
|
||||
assert_eq!(dt.trunc_subsecs(0).second(), 13);
|
||||
|
||||
let dt = pst.ymd(2018, 1, 11).and_hms_nano(10, 5, 27, 750_500_000);
|
||||
assert_eq!(dt.trunc_subsecs(9), dt);
|
||||
assert_eq!(dt.trunc_subsecs(4), dt);
|
||||
assert_eq!(dt.trunc_subsecs(3).nanosecond(), 750_000_000);
|
||||
assert_eq!(dt.trunc_subsecs(2).nanosecond(), 750_000_000);
|
||||
assert_eq!(dt.trunc_subsecs(1).nanosecond(), 700_000_000);
|
||||
|
||||
assert_eq!(dt.trunc_subsecs(0).nanosecond(), 0);
|
||||
assert_eq!(dt.trunc_subsecs(0).second(), 27);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trunc_leap_nanos() {
|
||||
let dt = Utc.ymd(2016, 12, 31).and_hms_nano(23, 59, 59, 1_750_500_000);
|
||||
assert_eq!(dt.trunc_subsecs(9), dt);
|
||||
assert_eq!(dt.trunc_subsecs(4), dt);
|
||||
assert_eq!(dt.trunc_subsecs(2).nanosecond(), 1_750_000_000);
|
||||
assert_eq!(dt.trunc_subsecs(1).nanosecond(), 1_700_000_000);
|
||||
assert_eq!(dt.trunc_subsecs(1).second(), 59);
|
||||
|
||||
assert_eq!(dt.trunc_subsecs(0).nanosecond(), 1_000_000_000);
|
||||
assert_eq!(dt.trunc_subsecs(0).second(), 59);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_duration_round() {
|
||||
let dt = Utc.ymd(2016, 12, 31).and_hms_nano(23, 59, 59, 175_500_000);
|
||||
|
||||
assert_eq!(
|
||||
dt.duration_round(Duration::milliseconds(10)).unwrap().to_string(),
|
||||
"2016-12-31 23:59:59.180 UTC"
|
||||
);
|
||||
|
||||
// round up
|
||||
let dt = Utc.ymd(2012, 12, 12).and_hms_milli(18, 22, 30, 0);
|
||||
assert_eq!(
|
||||
dt.duration_round(Duration::minutes(5)).unwrap().to_string(),
|
||||
"2012-12-12 18:25:00 UTC"
|
||||
);
|
||||
// round down
|
||||
let dt = Utc.ymd(2012, 12, 12).and_hms_milli(18, 22, 29, 999);
|
||||
assert_eq!(
|
||||
dt.duration_round(Duration::minutes(5)).unwrap().to_string(),
|
||||
"2012-12-12 18:20:00 UTC"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
dt.duration_round(Duration::minutes(10)).unwrap().to_string(),
|
||||
"2012-12-12 18:20:00 UTC"
|
||||
);
|
||||
assert_eq!(
|
||||
dt.duration_round(Duration::minutes(30)).unwrap().to_string(),
|
||||
"2012-12-12 18:30:00 UTC"
|
||||
);
|
||||
assert_eq!(
|
||||
dt.duration_round(Duration::hours(1)).unwrap().to_string(),
|
||||
"2012-12-12 18:00:00 UTC"
|
||||
);
|
||||
assert_eq!(
|
||||
dt.duration_round(Duration::days(1)).unwrap().to_string(),
|
||||
"2012-12-13 00:00:00 UTC"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_duration_round_pre_epoch() {
|
||||
let dt = Utc.ymd(1969, 12, 12).and_hms(12, 12, 12);
|
||||
assert_eq!(
|
||||
dt.duration_round(Duration::minutes(10)).unwrap().to_string(),
|
||||
"1969-12-12 12:10:00 UTC"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_duration_trunc() {
|
||||
let dt = Utc.ymd(2016, 12, 31).and_hms_nano(23, 59, 59, 1_75_500_000);
|
||||
|
||||
assert_eq!(
|
||||
dt.duration_trunc(Duration::milliseconds(10)).unwrap().to_string(),
|
||||
"2016-12-31 23:59:59.170 UTC"
|
||||
);
|
||||
|
||||
// would round up
|
||||
let dt = Utc.ymd(2012, 12, 12).and_hms_milli(18, 22, 30, 0);
|
||||
assert_eq!(
|
||||
dt.duration_trunc(Duration::minutes(5)).unwrap().to_string(),
|
||||
"2012-12-12 18:20:00 UTC"
|
||||
);
|
||||
// would round down
|
||||
let dt = Utc.ymd(2012, 12, 12).and_hms_milli(18, 22, 29, 999);
|
||||
assert_eq!(
|
||||
dt.duration_trunc(Duration::minutes(5)).unwrap().to_string(),
|
||||
"2012-12-12 18:20:00 UTC"
|
||||
);
|
||||
assert_eq!(
|
||||
dt.duration_trunc(Duration::minutes(10)).unwrap().to_string(),
|
||||
"2012-12-12 18:20:00 UTC"
|
||||
);
|
||||
assert_eq!(
|
||||
dt.duration_trunc(Duration::minutes(30)).unwrap().to_string(),
|
||||
"2012-12-12 18:00:00 UTC"
|
||||
);
|
||||
assert_eq!(
|
||||
dt.duration_trunc(Duration::hours(1)).unwrap().to_string(),
|
||||
"2012-12-12 18:00:00 UTC"
|
||||
);
|
||||
assert_eq!(
|
||||
dt.duration_trunc(Duration::days(1)).unwrap().to_string(),
|
||||
"2012-12-12 00:00:00 UTC"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_duration_trunc_pre_epoch() {
|
||||
let dt = Utc.ymd(1969, 12, 12).and_hms(12, 12, 12);
|
||||
assert_eq!(
|
||||
dt.duration_trunc(Duration::minutes(10)).unwrap().to_string(),
|
||||
"1969-12-12 12:10:00 UTC"
|
||||
);
|
||||
}
|
||||
}
|
||||
126
zeroidc/vendor/chrono/src/sys.rs
vendored
Normal file
126
zeroidc/vendor/chrono/src/sys.rs
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Platform wrappers for converting UTC times to and from the local time zone.
|
||||
//!
|
||||
//! This code was rescued from v0.1 of the time crate, which is no longer
|
||||
//! maintained. It has been substantially stripped down to the bare minimum
|
||||
//! required by chrono.
|
||||
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
#[cfg(any(target_arch = "wasm32", target_env = "sgx"))]
|
||||
#[path = "sys/stub.rs"]
|
||||
mod inner;
|
||||
|
||||
#[cfg(unix)]
|
||||
#[path = "sys/unix.rs"]
|
||||
mod inner;
|
||||
|
||||
#[cfg(windows)]
|
||||
#[path = "sys/windows.rs"]
|
||||
mod inner;
|
||||
|
||||
/// A record specifying a time value in seconds and nanoseconds, where
|
||||
/// nanoseconds represent the offset from the given second.
|
||||
///
|
||||
/// For example a timespec of 1.2 seconds after the beginning of the epoch would
|
||||
/// be represented as {sec: 1, nsec: 200000000}.
|
||||
pub struct Timespec {
|
||||
pub sec: i64,
|
||||
pub nsec: i32,
|
||||
}
|
||||
|
||||
impl Timespec {
|
||||
/// Constructs a timespec representing the current time in UTC.
|
||||
pub fn now() -> Timespec {
|
||||
let st =
|
||||
SystemTime::now().duration_since(UNIX_EPOCH).expect("system time before Unix epoch");
|
||||
Timespec { sec: st.as_secs() as i64, nsec: st.subsec_nanos() as i32 }
|
||||
}
|
||||
|
||||
/// Converts this timespec into the system's local time.
|
||||
pub fn local(self) -> Tm {
|
||||
let mut tm = Tm {
|
||||
tm_sec: 0,
|
||||
tm_min: 0,
|
||||
tm_hour: 0,
|
||||
tm_mday: 0,
|
||||
tm_mon: 0,
|
||||
tm_year: 0,
|
||||
tm_wday: 0,
|
||||
tm_yday: 0,
|
||||
tm_isdst: 0,
|
||||
tm_utcoff: 0,
|
||||
tm_nsec: 0,
|
||||
};
|
||||
inner::time_to_local_tm(self.sec, &mut tm);
|
||||
tm.tm_nsec = self.nsec;
|
||||
tm
|
||||
}
|
||||
}
|
||||
|
||||
/// Holds a calendar date and time broken down into its components (year, month,
|
||||
/// day, and so on), also called a broken-down time value.
|
||||
// FIXME: use c_int instead of i32?
|
||||
#[cfg(feature = "clock")]
|
||||
#[repr(C)]
|
||||
pub struct Tm {
|
||||
/// Seconds after the minute - [0, 60]
|
||||
pub tm_sec: i32,
|
||||
|
||||
/// Minutes after the hour - [0, 59]
|
||||
pub tm_min: i32,
|
||||
|
||||
/// Hours after midnight - [0, 23]
|
||||
pub tm_hour: i32,
|
||||
|
||||
/// Day of the month - [1, 31]
|
||||
pub tm_mday: i32,
|
||||
|
||||
/// Months since January - [0, 11]
|
||||
pub tm_mon: i32,
|
||||
|
||||
/// Years since 1900
|
||||
pub tm_year: i32,
|
||||
|
||||
/// Days since Sunday - [0, 6]. 0 = Sunday, 1 = Monday, ..., 6 = Saturday.
|
||||
pub tm_wday: i32,
|
||||
|
||||
/// Days since January 1 - [0, 365]
|
||||
pub tm_yday: i32,
|
||||
|
||||
/// Daylight Saving Time flag.
|
||||
///
|
||||
/// This value is positive if Daylight Saving Time is in effect, zero if
|
||||
/// Daylight Saving Time is not in effect, and negative if this information
|
||||
/// is not available.
|
||||
pub tm_isdst: i32,
|
||||
|
||||
/// Identifies the time zone that was used to compute this broken-down time
|
||||
/// value, including any adjustment for Daylight Saving Time. This is the
|
||||
/// number of seconds east of UTC. For example, for U.S. Pacific Daylight
|
||||
/// Time, the value is `-7*60*60 = -25200`.
|
||||
pub tm_utcoff: i32,
|
||||
|
||||
/// Nanoseconds after the second - [0, 10<sup>9</sup> - 1]
|
||||
pub tm_nsec: i32,
|
||||
}
|
||||
|
||||
impl Tm {
|
||||
/// Convert time to the seconds from January 1, 1970
|
||||
pub fn to_timespec(&self) -> Timespec {
|
||||
let sec = match self.tm_utcoff {
|
||||
0 => inner::utc_tm_to_time(self),
|
||||
_ => inner::local_tm_to_time(self),
|
||||
};
|
||||
Timespec { sec: sec, nsec: self.tm_nsec }
|
||||
}
|
||||
}
|
||||
80
zeroidc/vendor/chrono/src/sys/stub.rs
vendored
Normal file
80
zeroidc/vendor/chrono/src/sys/stub.rs
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use super::Tm;
|
||||
|
||||
fn time_to_tm(ts: i64, tm: &mut Tm) {
|
||||
let leapyear = |year| -> bool { year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) };
|
||||
|
||||
static YTAB: [[i64; 12]; 2] = [
|
||||
[31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
|
||||
[31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
|
||||
];
|
||||
|
||||
let mut year = 1970;
|
||||
|
||||
let dayclock = ts % 86400;
|
||||
let mut dayno = ts / 86400;
|
||||
|
||||
tm.tm_sec = (dayclock % 60) as i32;
|
||||
tm.tm_min = ((dayclock % 3600) / 60) as i32;
|
||||
tm.tm_hour = (dayclock / 3600) as i32;
|
||||
tm.tm_wday = ((dayno + 4) % 7) as i32;
|
||||
loop {
|
||||
let yearsize = if leapyear(year) { 366 } else { 365 };
|
||||
if dayno >= yearsize {
|
||||
dayno -= yearsize;
|
||||
year += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
tm.tm_year = (year - 1900) as i32;
|
||||
tm.tm_yday = dayno as i32;
|
||||
let mut mon = 0;
|
||||
while dayno >= YTAB[if leapyear(year) { 1 } else { 0 }][mon] {
|
||||
dayno -= YTAB[if leapyear(year) { 1 } else { 0 }][mon];
|
||||
mon += 1;
|
||||
}
|
||||
tm.tm_mon = mon as i32;
|
||||
tm.tm_mday = dayno as i32 + 1;
|
||||
tm.tm_isdst = 0;
|
||||
}
|
||||
|
||||
fn tm_to_time(tm: &Tm) -> i64 {
|
||||
let mut y = tm.tm_year as i64 + 1900;
|
||||
let mut m = tm.tm_mon as i64 + 1;
|
||||
if m <= 2 {
|
||||
y -= 1;
|
||||
m += 12;
|
||||
}
|
||||
let d = tm.tm_mday as i64;
|
||||
let h = tm.tm_hour as i64;
|
||||
let mi = tm.tm_min as i64;
|
||||
let s = tm.tm_sec as i64;
|
||||
(365 * y + y / 4 - y / 100 + y / 400 + 3 * (m + 1) / 5 + 30 * m + d - 719561) * 86400
|
||||
+ 3600 * h
|
||||
+ 60 * mi
|
||||
+ s
|
||||
}
|
||||
|
||||
pub fn time_to_local_tm(sec: i64, tm: &mut Tm) {
|
||||
// FIXME: Add timezone logic
|
||||
time_to_tm(sec, tm);
|
||||
}
|
||||
|
||||
pub fn utc_tm_to_time(tm: &Tm) -> i64 {
|
||||
tm_to_time(tm)
|
||||
}
|
||||
|
||||
pub fn local_tm_to_time(tm: &Tm) -> i64 {
|
||||
// FIXME: Add timezone logic
|
||||
tm_to_time(tm)
|
||||
}
|
||||
126
zeroidc/vendor/chrono/src/sys/unix.rs
vendored
Normal file
126
zeroidc/vendor/chrono/src/sys/unix.rs
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use super::Tm;
|
||||
use libc::{self, time_t};
|
||||
use std::io;
|
||||
use std::mem;
|
||||
|
||||
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
|
||||
extern "C" {
|
||||
static timezone: time_t;
|
||||
static altzone: time_t;
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
|
||||
fn tzset() {
|
||||
extern "C" {
|
||||
fn tzset();
|
||||
}
|
||||
unsafe { tzset() }
|
||||
}
|
||||
|
||||
fn rust_tm_to_tm(rust_tm: &Tm, tm: &mut libc::tm) {
|
||||
tm.tm_sec = rust_tm.tm_sec;
|
||||
tm.tm_min = rust_tm.tm_min;
|
||||
tm.tm_hour = rust_tm.tm_hour;
|
||||
tm.tm_mday = rust_tm.tm_mday;
|
||||
tm.tm_mon = rust_tm.tm_mon;
|
||||
tm.tm_year = rust_tm.tm_year;
|
||||
tm.tm_wday = rust_tm.tm_wday;
|
||||
tm.tm_yday = rust_tm.tm_yday;
|
||||
tm.tm_isdst = rust_tm.tm_isdst;
|
||||
}
|
||||
|
||||
fn tm_to_rust_tm(tm: &libc::tm, utcoff: i32, rust_tm: &mut Tm) {
|
||||
rust_tm.tm_sec = tm.tm_sec;
|
||||
rust_tm.tm_min = tm.tm_min;
|
||||
rust_tm.tm_hour = tm.tm_hour;
|
||||
rust_tm.tm_mday = tm.tm_mday;
|
||||
rust_tm.tm_mon = tm.tm_mon;
|
||||
rust_tm.tm_year = tm.tm_year;
|
||||
rust_tm.tm_wday = tm.tm_wday;
|
||||
rust_tm.tm_yday = tm.tm_yday;
|
||||
rust_tm.tm_isdst = tm.tm_isdst;
|
||||
rust_tm.tm_utcoff = utcoff;
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "nacl", target_os = "solaris", target_os = "illumos"))]
|
||||
unsafe fn timegm(tm: *mut libc::tm) -> time_t {
|
||||
use std::env::{remove_var, set_var, var_os};
|
||||
extern "C" {
|
||||
fn tzset();
|
||||
}
|
||||
|
||||
let ret;
|
||||
|
||||
let current_tz = var_os("TZ");
|
||||
set_var("TZ", "UTC");
|
||||
tzset();
|
||||
|
||||
ret = libc::mktime(tm);
|
||||
|
||||
if let Some(tz) = current_tz {
|
||||
set_var("TZ", tz);
|
||||
} else {
|
||||
remove_var("TZ");
|
||||
}
|
||||
tzset();
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn time_to_local_tm(sec: i64, tm: &mut Tm) {
|
||||
unsafe {
|
||||
let sec = sec as time_t;
|
||||
let mut out = mem::zeroed();
|
||||
if libc::localtime_r(&sec, &mut out).is_null() {
|
||||
panic!("localtime_r failed: {}", io::Error::last_os_error());
|
||||
}
|
||||
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
|
||||
let gmtoff = {
|
||||
tzset();
|
||||
// < 0 means we don't know; assume we're not in DST.
|
||||
if out.tm_isdst == 0 {
|
||||
// timezone is seconds west of UTC, tm_gmtoff is seconds east
|
||||
-timezone
|
||||
} else if out.tm_isdst > 0 {
|
||||
-altzone
|
||||
} else {
|
||||
-timezone
|
||||
}
|
||||
};
|
||||
#[cfg(not(any(target_os = "solaris", target_os = "illumos")))]
|
||||
let gmtoff = out.tm_gmtoff;
|
||||
tm_to_rust_tm(&out, gmtoff as i32, tm);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn utc_tm_to_time(rust_tm: &Tm) -> i64 {
|
||||
#[cfg(not(any(
|
||||
all(target_os = "android", target_pointer_width = "32"),
|
||||
target_os = "nacl",
|
||||
target_os = "solaris",
|
||||
target_os = "illumos"
|
||||
)))]
|
||||
use libc::timegm;
|
||||
#[cfg(all(target_os = "android", target_pointer_width = "32"))]
|
||||
use libc::timegm64 as timegm;
|
||||
|
||||
let mut tm = unsafe { mem::zeroed() };
|
||||
rust_tm_to_tm(rust_tm, &mut tm);
|
||||
unsafe { timegm(&mut tm) as i64 }
|
||||
}
|
||||
|
||||
pub fn local_tm_to_time(rust_tm: &Tm) -> i64 {
|
||||
let mut tm = unsafe { mem::zeroed() };
|
||||
rust_tm_to_tm(rust_tm, &mut tm);
|
||||
unsafe { libc::mktime(&mut tm) as i64 }
|
||||
}
|
||||
131
zeroidc/vendor/chrono/src/sys/windows.rs
vendored
Normal file
131
zeroidc/vendor/chrono/src/sys/windows.rs
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use super::Tm;
|
||||
use std::io;
|
||||
use std::mem;
|
||||
|
||||
use winapi::shared::minwindef::*;
|
||||
use winapi::um::minwinbase::SYSTEMTIME;
|
||||
use winapi::um::timezoneapi::*;
|
||||
|
||||
const HECTONANOSECS_IN_SEC: i64 = 10_000_000;
|
||||
const HECTONANOSEC_TO_UNIX_EPOCH: i64 = 11_644_473_600 * HECTONANOSECS_IN_SEC;
|
||||
|
||||
fn time_to_file_time(sec: i64) -> FILETIME {
|
||||
let t = ((sec * HECTONANOSECS_IN_SEC) + HECTONANOSEC_TO_UNIX_EPOCH) as u64;
|
||||
FILETIME { dwLowDateTime: t as DWORD, dwHighDateTime: (t >> 32) as DWORD }
|
||||
}
|
||||
|
||||
fn file_time_as_u64(ft: &FILETIME) -> u64 {
|
||||
((ft.dwHighDateTime as u64) << 32) | (ft.dwLowDateTime as u64)
|
||||
}
|
||||
|
||||
fn file_time_to_unix_seconds(ft: &FILETIME) -> i64 {
|
||||
let t = file_time_as_u64(ft) as i64;
|
||||
((t - HECTONANOSEC_TO_UNIX_EPOCH) / HECTONANOSECS_IN_SEC) as i64
|
||||
}
|
||||
|
||||
fn system_time_to_file_time(sys: &SYSTEMTIME) -> FILETIME {
|
||||
unsafe {
|
||||
let mut ft = mem::zeroed();
|
||||
SystemTimeToFileTime(sys, &mut ft);
|
||||
ft
|
||||
}
|
||||
}
|
||||
|
||||
fn tm_to_system_time(tm: &Tm) -> SYSTEMTIME {
|
||||
let mut sys: SYSTEMTIME = unsafe { mem::zeroed() };
|
||||
sys.wSecond = tm.tm_sec as WORD;
|
||||
sys.wMinute = tm.tm_min as WORD;
|
||||
sys.wHour = tm.tm_hour as WORD;
|
||||
sys.wDay = tm.tm_mday as WORD;
|
||||
sys.wDayOfWeek = tm.tm_wday as WORD;
|
||||
sys.wMonth = (tm.tm_mon + 1) as WORD;
|
||||
sys.wYear = (tm.tm_year + 1900) as WORD;
|
||||
sys
|
||||
}
|
||||
|
||||
fn system_time_to_tm(sys: &SYSTEMTIME, tm: &mut Tm) {
|
||||
tm.tm_sec = sys.wSecond as i32;
|
||||
tm.tm_min = sys.wMinute as i32;
|
||||
tm.tm_hour = sys.wHour as i32;
|
||||
tm.tm_mday = sys.wDay as i32;
|
||||
tm.tm_wday = sys.wDayOfWeek as i32;
|
||||
tm.tm_mon = (sys.wMonth - 1) as i32;
|
||||
tm.tm_year = (sys.wYear - 1900) as i32;
|
||||
tm.tm_yday = yday(tm.tm_year, tm.tm_mon + 1, tm.tm_mday);
|
||||
|
||||
fn yday(year: i32, month: i32, day: i32) -> i32 {
|
||||
let leap = if month > 2 {
|
||||
if year % 4 == 0 {
|
||||
1
|
||||
} else {
|
||||
2
|
||||
}
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let july = if month > 7 { 1 } else { 0 };
|
||||
|
||||
(month - 1) * 30 + month / 2 + (day - 1) - leap + july
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! call {
|
||||
($name:ident($($arg:expr),*)) => {
|
||||
if $name($($arg),*) == 0 {
|
||||
panic!(concat!(stringify!($name), " failed with: {}"),
|
||||
io::Error::last_os_error());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn time_to_local_tm(sec: i64, tm: &mut Tm) {
|
||||
let ft = time_to_file_time(sec);
|
||||
unsafe {
|
||||
let mut utc = mem::zeroed();
|
||||
let mut local = mem::zeroed();
|
||||
call!(FileTimeToSystemTime(&ft, &mut utc));
|
||||
call!(SystemTimeToTzSpecificLocalTime(0 as *const _, &mut utc, &mut local));
|
||||
system_time_to_tm(&local, tm);
|
||||
|
||||
let local = system_time_to_file_time(&local);
|
||||
let local_sec = file_time_to_unix_seconds(&local);
|
||||
|
||||
let mut tz = mem::zeroed();
|
||||
GetTimeZoneInformation(&mut tz);
|
||||
|
||||
// SystemTimeToTzSpecificLocalTime already applied the biases so
|
||||
// check if it non standard
|
||||
tm.tm_utcoff = (local_sec - sec) as i32;
|
||||
tm.tm_isdst = if tm.tm_utcoff == -60 * (tz.Bias + tz.StandardBias) { 0 } else { 1 };
|
||||
}
|
||||
}
|
||||
|
||||
pub fn utc_tm_to_time(tm: &Tm) -> i64 {
|
||||
unsafe {
|
||||
let mut ft = mem::zeroed();
|
||||
let sys_time = tm_to_system_time(tm);
|
||||
call!(SystemTimeToFileTime(&sys_time, &mut ft));
|
||||
file_time_to_unix_seconds(&ft)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn local_tm_to_time(tm: &Tm) -> i64 {
|
||||
unsafe {
|
||||
let mut ft = mem::zeroed();
|
||||
let mut utc = mem::zeroed();
|
||||
let mut sys_time = tm_to_system_time(tm);
|
||||
call!(TzSpecificLocalTimeToSystemTime(0 as *mut _, &mut sys_time, &mut utc));
|
||||
call!(SystemTimeToFileTime(&utc, &mut ft));
|
||||
file_time_to_unix_seconds(&ft)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user