mirror of
https://github.com/chronotope/chrono.git
synced 2025-09-29 22:11:59 +00:00
Merge branch 'main'
This commit is contained in:
commit
bc95157246
4
.github/workflows/lint.yml
vendored
4
.github/workflows/lint.yml
vendored
@ -29,10 +29,10 @@ jobs:
|
||||
cargo clippy --target=x86_64-pc-windows-msvc --all-targets --color=always \
|
||||
-- -D warnings
|
||||
- run: |
|
||||
cargo clippy --manifest-path fuzz/Cargo.toml --color=always \
|
||||
cargo clippy --manifest-path fuzz/Cargo.toml --all-targets --color=always \
|
||||
-- -D warnings
|
||||
- run: |
|
||||
cargo clippy --manifest-path bench/Cargo.toml --color=always \
|
||||
cargo clippy --manifest-path bench/Cargo.toml --all-targets --color=always \
|
||||
-- -D warnings
|
||||
env:
|
||||
RUSTFLAGS: "-Dwarnings"
|
||||
|
@ -51,7 +51,7 @@ js-sys = { version = "0.3", optional = true } # contains FFI bindings for
|
||||
windows-targets = { version = "0.52", optional = true }
|
||||
|
||||
[target.'cfg(windows)'.dev-dependencies]
|
||||
windows-bindgen = { version = "0.54" } # The MSRV of its windows-metatada 0.54 dependency is 1.70
|
||||
windows-bindgen = { version = "0.55" } # The MSRV of its windows-metatada dependency is 1.70
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
iana-time-zone = { version = "0.1.45", optional = true, features = ["fallback"] }
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
//! ISO 8601 date and time with time zone.
|
||||
|
||||
#[cfg(all(not(feature = "std"), feature = "alloc"))]
|
||||
#[cfg(all(feature = "alloc", not(feature = "std"), not(test)))]
|
||||
use alloc::string::String;
|
||||
use core::borrow::Borrow;
|
||||
use core::cmp::Ordering;
|
||||
@ -46,7 +46,7 @@ mod tests;
|
||||
/// There are some constructors implemented here (the `from_*` methods), but
|
||||
/// the general-purpose constructors are all via the methods on the
|
||||
/// [`TimeZone`](./offset/trait.TimeZone.html) implementations.
|
||||
#[derive(Clone)]
|
||||
#[derive(Copy, Clone)]
|
||||
#[cfg_attr(
|
||||
any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"),
|
||||
derive(Archive, Deserialize, Serialize),
|
||||
@ -1260,10 +1260,6 @@ impl<Tz: TimeZone> Timelike for DateTime<Tz> {
|
||||
}
|
||||
}
|
||||
|
||||
// we need them as automatic impls cannot handle associated types
|
||||
impl<Tz: TimeZone> Copy for DateTime<Tz> where <Tz as TimeZone>::Offset: Copy {}
|
||||
unsafe impl<Tz: TimeZone> Send for DateTime<Tz> where <Tz as TimeZone>::Offset: Send {}
|
||||
|
||||
impl<Tz: TimeZone, Tz2: TimeZone> PartialEq<DateTime<Tz2>> for DateTime<Tz> {
|
||||
fn eq(&self, other: &DateTime<Tz2>) -> bool {
|
||||
self.datetime == other.datetime
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
//! Date and time formatting routines.
|
||||
|
||||
#[cfg(all(not(feature = "std"), feature = "alloc"))]
|
||||
#[cfg(all(feature = "alloc", not(feature = "std"), not(test)))]
|
||||
use alloc::string::{String, ToString};
|
||||
#[cfg(feature = "alloc")]
|
||||
use core::borrow::Borrow;
|
||||
@ -20,8 +20,6 @@ use crate::{NaiveDate, NaiveTime, Weekday};
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use super::locales;
|
||||
#[cfg(all(feature = "unstable-locales", feature = "alloc"))]
|
||||
use super::Locale;
|
||||
#[cfg(any(feature = "alloc", feature = "serde", feature = "rustc-serialize"))]
|
||||
use super::{Colons, OffsetFormat, OffsetPrecision, Pad};
|
||||
#[cfg(feature = "alloc")]
|
||||
|
@ -30,7 +30,7 @@
|
||||
//! # Ok::<(), chrono::ParseError>(())
|
||||
//! ```
|
||||
|
||||
#[cfg(all(not(feature = "std"), feature = "alloc"))]
|
||||
#[cfg(all(feature = "alloc", not(feature = "std"), not(test)))]
|
||||
use alloc::boxed::Box;
|
||||
use core::fmt;
|
||||
use core::str::FromStr;
|
||||
|
@ -165,7 +165,7 @@ use super::{locales, Locale};
|
||||
use super::{Fixed, InternalInternal, Item, Numeric, Pad};
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
use super::{ParseError, BAD_FORMAT};
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg(all(feature = "alloc", not(feature = "std"), not(test)))]
|
||||
use alloc::vec::Vec;
|
||||
|
||||
/// Parsing iterator for `strftime`-like format strings.
|
||||
|
@ -442,98 +442,102 @@ fn test_date_pred() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_add() {
|
||||
fn check((y1, m1, d1): (i32, u32, u32), rhs: TimeDelta, ymd: Option<(i32, u32, u32)>) {
|
||||
let lhs = NaiveDate::from_ymd(y1, m1, d1).unwrap();
|
||||
let sum = ymd.map(|(y, m, d)| NaiveDate::from_ymd(y, m, d).unwrap());
|
||||
assert_eq!(lhs.checked_add_signed(rhs), sum);
|
||||
assert_eq!(lhs.checked_sub_signed(-rhs), sum);
|
||||
fn test_date_checked_add_signed() {
|
||||
fn check(lhs: Option<NaiveDate>, delta: TimeDelta, rhs: Option<NaiveDate>) {
|
||||
assert_eq!(lhs.unwrap().checked_add_signed(delta), rhs);
|
||||
assert_eq!(lhs.unwrap().checked_sub_signed(-delta), rhs);
|
||||
}
|
||||
let ymd = |y, m, d| NaiveDate::from_ymd(y, m, d).ok();
|
||||
|
||||
check((2014, 1, 1), TimeDelta::zero(), Some((2014, 1, 1)));
|
||||
check((2014, 1, 1), TimeDelta::seconds(86399).unwrap(), Some((2014, 1, 1)));
|
||||
check(ymd(2014, 1, 1), TimeDelta::zero(), ymd(2014, 1, 1));
|
||||
check(ymd(2014, 1, 1), TimeDelta::seconds(86399).unwrap(), ymd(2014, 1, 1));
|
||||
// always round towards zero
|
||||
check((2014, 1, 1), TimeDelta::seconds(-86399).unwrap(), Some((2014, 1, 1)));
|
||||
check((2014, 1, 1), TimeDelta::days(1).unwrap(), Some((2014, 1, 2)));
|
||||
check((2014, 1, 1), TimeDelta::days(-1).unwrap(), Some((2013, 12, 31)));
|
||||
check((2014, 1, 1), TimeDelta::days(364).unwrap(), Some((2014, 12, 31)));
|
||||
check((2014, 1, 1), TimeDelta::days(365 * 4 + 1).unwrap(), Some((2018, 1, 1)));
|
||||
check((2014, 1, 1), TimeDelta::days(365 * 400 + 97).unwrap(), Some((2414, 1, 1)));
|
||||
check(ymd(2014, 1, 1), TimeDelta::seconds(-86399).unwrap(), ymd(2014, 1, 1));
|
||||
check(ymd(2014, 1, 1), TimeDelta::days(1).unwrap(), ymd(2014, 1, 2));
|
||||
check(ymd(2014, 1, 1), TimeDelta::days(-1).unwrap(), ymd(2013, 12, 31));
|
||||
check(ymd(2014, 1, 1), TimeDelta::days(364).unwrap(), ymd(2014, 12, 31));
|
||||
check(ymd(2014, 1, 1), TimeDelta::days(365 * 4 + 1).unwrap(), ymd(2018, 1, 1));
|
||||
check(ymd(2014, 1, 1), TimeDelta::days(365 * 400 + 97).unwrap(), ymd(2414, 1, 1));
|
||||
|
||||
check((-7, 1, 1), TimeDelta::days(365 * 12 + 3).unwrap(), Some((5, 1, 1)));
|
||||
check(ymd(-7, 1, 1), TimeDelta::days(365 * 12 + 3).unwrap(), ymd(5, 1, 1));
|
||||
|
||||
// overflow check
|
||||
check(
|
||||
(0, 1, 1),
|
||||
ymd(0, 1, 1),
|
||||
TimeDelta::days(MAX_DAYS_FROM_YEAR_0 as i64).unwrap(),
|
||||
Some((MAX_YEAR, 12, 31)),
|
||||
ymd(MAX_YEAR, 12, 31),
|
||||
);
|
||||
check((0, 1, 1), TimeDelta::days(MAX_DAYS_FROM_YEAR_0 as i64 + 1).unwrap(), None);
|
||||
check((0, 1, 1), TimeDelta::max_value(), None);
|
||||
check((0, 1, 1), TimeDelta::days(MIN_DAYS_FROM_YEAR_0 as i64).unwrap(), Some((MIN_YEAR, 1, 1)));
|
||||
check((0, 1, 1), TimeDelta::days(MIN_DAYS_FROM_YEAR_0 as i64 - 1).unwrap(), None);
|
||||
check((0, 1, 1), TimeDelta::min_value(), None);
|
||||
check(ymd(0, 1, 1), TimeDelta::days(MAX_DAYS_FROM_YEAR_0 as i64 + 1).unwrap(), None);
|
||||
check(ymd(0, 1, 1), TimeDelta::max_value(), None);
|
||||
check(ymd(0, 1, 1), TimeDelta::days(MIN_DAYS_FROM_YEAR_0 as i64).unwrap(), ymd(MIN_YEAR, 1, 1));
|
||||
check(ymd(0, 1, 1), TimeDelta::days(MIN_DAYS_FROM_YEAR_0 as i64 - 1).unwrap(), None);
|
||||
check(ymd(0, 1, 1), TimeDelta::min_value(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_sub() {
|
||||
fn check((y1, m1, d1): (i32, u32, u32), (y2, m2, d2): (i32, u32, u32), diff: TimeDelta) {
|
||||
let lhs = NaiveDate::from_ymd(y1, m1, d1).unwrap();
|
||||
let rhs = NaiveDate::from_ymd(y2, m2, d2).unwrap();
|
||||
assert_eq!(lhs.signed_duration_since(rhs), diff);
|
||||
assert_eq!(rhs.signed_duration_since(lhs), -diff);
|
||||
fn test_date_signed_duration_since() {
|
||||
fn check(lhs: Option<NaiveDate>, rhs: Option<NaiveDate>, delta: TimeDelta) {
|
||||
assert_eq!(lhs.unwrap().signed_duration_since(rhs.unwrap()), delta);
|
||||
assert_eq!(rhs.unwrap().signed_duration_since(lhs.unwrap()), -delta);
|
||||
}
|
||||
let ymd = |y, m, d| NaiveDate::from_ymd(y, m, d).ok();
|
||||
|
||||
check((2014, 1, 1), (2014, 1, 1), TimeDelta::zero());
|
||||
check((2014, 1, 2), (2014, 1, 1), TimeDelta::days(1).unwrap());
|
||||
check((2014, 12, 31), (2014, 1, 1), TimeDelta::days(364).unwrap());
|
||||
check((2015, 1, 3), (2014, 1, 1), TimeDelta::days(365 + 2).unwrap());
|
||||
check((2018, 1, 1), (2014, 1, 1), TimeDelta::days(365 * 4 + 1).unwrap());
|
||||
check((2414, 1, 1), (2014, 1, 1), TimeDelta::days(365 * 400 + 97).unwrap());
|
||||
check(ymd(2014, 1, 1), ymd(2014, 1, 1), TimeDelta::zero());
|
||||
check(ymd(2014, 1, 2), ymd(2014, 1, 1), TimeDelta::days(1).unwrap());
|
||||
check(ymd(2014, 12, 31), ymd(2014, 1, 1), TimeDelta::days(364).unwrap());
|
||||
check(ymd(2015, 1, 3), ymd(2014, 1, 1), TimeDelta::days(365 + 2).unwrap());
|
||||
check(ymd(2018, 1, 1), ymd(2014, 1, 1), TimeDelta::days(365 * 4 + 1).unwrap());
|
||||
check(ymd(2414, 1, 1), ymd(2014, 1, 1), TimeDelta::days(365 * 400 + 97).unwrap());
|
||||
|
||||
check((MAX_YEAR, 12, 31), (0, 1, 1), TimeDelta::days(MAX_DAYS_FROM_YEAR_0 as i64).unwrap());
|
||||
check((MIN_YEAR, 1, 1), (0, 1, 1), TimeDelta::days(MIN_DAYS_FROM_YEAR_0 as i64).unwrap());
|
||||
check(
|
||||
ymd(MAX_YEAR, 12, 31),
|
||||
ymd(0, 1, 1),
|
||||
TimeDelta::days(MAX_DAYS_FROM_YEAR_0 as i64).unwrap(),
|
||||
);
|
||||
check(ymd(MIN_YEAR, 1, 1), ymd(0, 1, 1), TimeDelta::days(MIN_DAYS_FROM_YEAR_0 as i64).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_add_days() {
|
||||
fn check((y1, m1, d1): (i32, u32, u32), rhs: Days, ymd: Option<(i32, u32, u32)>) {
|
||||
let lhs = NaiveDate::from_ymd(y1, m1, d1).unwrap();
|
||||
let sum = ymd.map(|(y, m, d)| NaiveDate::from_ymd(y, m, d).unwrap());
|
||||
assert_eq!(lhs.checked_add_days(rhs), sum);
|
||||
fn check(lhs: Option<NaiveDate>, days: Days, rhs: Option<NaiveDate>) {
|
||||
assert_eq!(lhs.unwrap().checked_add_days(days), rhs);
|
||||
}
|
||||
let ymd = |y, m, d| NaiveDate::from_ymd(y, m, d).ok();
|
||||
|
||||
check((2014, 1, 1), Days::new(0), Some((2014, 1, 1)));
|
||||
check(ymd(2014, 1, 1), Days::new(0), ymd(2014, 1, 1));
|
||||
// always round towards zero
|
||||
check((2014, 1, 1), Days::new(1), Some((2014, 1, 2)));
|
||||
check((2014, 1, 1), Days::new(364), Some((2014, 12, 31)));
|
||||
check((2014, 1, 1), Days::new(365 * 4 + 1), Some((2018, 1, 1)));
|
||||
check((2014, 1, 1), Days::new(365 * 400 + 97), Some((2414, 1, 1)));
|
||||
check(ymd(2014, 1, 1), Days::new(1), ymd(2014, 1, 2));
|
||||
check(ymd(2014, 1, 1), Days::new(364), ymd(2014, 12, 31));
|
||||
check(ymd(2014, 1, 1), Days::new(365 * 4 + 1), ymd(2018, 1, 1));
|
||||
check(ymd(2014, 1, 1), Days::new(365 * 400 + 97), ymd(2414, 1, 1));
|
||||
|
||||
check((-7, 1, 1), Days::new(365 * 12 + 3), Some((5, 1, 1)));
|
||||
check(ymd(-7, 1, 1), Days::new(365 * 12 + 3), ymd(5, 1, 1));
|
||||
|
||||
// overflow check
|
||||
check((0, 1, 1), Days::new(MAX_DAYS_FROM_YEAR_0.try_into().unwrap()), Some((MAX_YEAR, 12, 31)));
|
||||
check((0, 1, 1), Days::new(u64::try_from(MAX_DAYS_FROM_YEAR_0).unwrap() + 1), None);
|
||||
check(ymd(0, 1, 1), Days::new(MAX_DAYS_FROM_YEAR_0.try_into().unwrap()), ymd(MAX_YEAR, 12, 31));
|
||||
check(ymd(0, 1, 1), Days::new(u64::try_from(MAX_DAYS_FROM_YEAR_0).unwrap() + 1), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_sub_days() {
|
||||
fn check((y1, m1, d1): (i32, u32, u32), (y2, m2, d2): (i32, u32, u32), diff: Days) {
|
||||
let lhs = NaiveDate::from_ymd(y1, m1, d1).unwrap();
|
||||
let rhs = NaiveDate::from_ymd(y2, m2, d2).unwrap();
|
||||
assert_eq!(lhs - diff, rhs);
|
||||
fn check(lhs: Option<NaiveDate>, days: Days, rhs: Option<NaiveDate>) {
|
||||
assert_eq!(lhs.unwrap().checked_sub_days(days), rhs);
|
||||
}
|
||||
let ymd = |y, m, d| NaiveDate::from_ymd(y, m, d).ok();
|
||||
|
||||
check((2014, 1, 1), (2014, 1, 1), Days::new(0));
|
||||
check((2014, 1, 2), (2014, 1, 1), Days::new(1));
|
||||
check((2014, 12, 31), (2014, 1, 1), Days::new(364));
|
||||
check((2015, 1, 3), (2014, 1, 1), Days::new(365 + 2));
|
||||
check((2018, 1, 1), (2014, 1, 1), Days::new(365 * 4 + 1));
|
||||
check((2414, 1, 1), (2014, 1, 1), Days::new(365 * 400 + 97));
|
||||
check(ymd(2014, 1, 1), Days::new(0), ymd(2014, 1, 1));
|
||||
check(ymd(2014, 1, 2), Days::new(1), ymd(2014, 1, 1));
|
||||
check(ymd(2014, 12, 31), Days::new(364), ymd(2014, 1, 1));
|
||||
check(ymd(2015, 1, 3), Days::new(365 + 2), ymd(2014, 1, 1));
|
||||
check(ymd(2018, 1, 1), Days::new(365 * 4 + 1), ymd(2014, 1, 1));
|
||||
check(ymd(2414, 1, 1), Days::new(365 * 400 + 97), ymd(2014, 1, 1));
|
||||
|
||||
check((MAX_YEAR, 12, 31), (0, 1, 1), Days::new(MAX_DAYS_FROM_YEAR_0.try_into().unwrap()));
|
||||
check((0, 1, 1), (MIN_YEAR, 1, 1), Days::new((-MIN_DAYS_FROM_YEAR_0).try_into().unwrap()));
|
||||
check(ymd(MAX_YEAR, 12, 31), Days::new(MAX_DAYS_FROM_YEAR_0.try_into().unwrap()), ymd(0, 1, 1));
|
||||
check(
|
||||
ymd(0, 1, 1),
|
||||
Days::new((-MIN_DAYS_FROM_YEAR_0).try_into().unwrap()),
|
||||
ymd(MIN_YEAR, 1, 1),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1,10 +1,8 @@
|
||||
// Bindings generated by `windows-bindgen` 0.54.0
|
||||
|
||||
#![allow(non_snake_case, non_upper_case_globals, non_camel_case_types, dead_code, clippy::all)]
|
||||
::windows_targets::link!("kernel32.dll" "system" fn GetTimeZoneInformationForYear(wyear : u16, pdtzi : *const DYNAMIC_TIME_ZONE_INFORMATION, ptzi : *mut TIME_ZONE_INFORMATION) -> BOOL);
|
||||
::windows_targets::link!("kernel32.dll" "system" fn SystemTimeToFileTime(lpsystemtime : *const SYSTEMTIME, lpfiletime : *mut FILETIME) -> BOOL);
|
||||
::windows_targets::link!("kernel32.dll" "system" fn SystemTimeToTzSpecificLocalTime(lptimezoneinformation : *const TIME_ZONE_INFORMATION, lpuniversaltime : *const SYSTEMTIME, lplocaltime : *mut SYSTEMTIME) -> BOOL);
|
||||
::windows_targets::link!("kernel32.dll" "system" fn TzSpecificLocalTimeToSystemTime(lptimezoneinformation : *const TIME_ZONE_INFORMATION, lplocaltime : *const SYSTEMTIME, lpuniversaltime : *mut SYSTEMTIME) -> BOOL);
|
||||
windows_targets::link!("kernel32.dll" "system" fn GetTimeZoneInformationForYear(wyear : u16, pdtzi : *const DYNAMIC_TIME_ZONE_INFORMATION, ptzi : *mut TIME_ZONE_INFORMATION) -> BOOL);
|
||||
windows_targets::link!("kernel32.dll" "system" fn SystemTimeToFileTime(lpsystemtime : *const SYSTEMTIME, lpfiletime : *mut FILETIME) -> BOOL);
|
||||
windows_targets::link!("kernel32.dll" "system" fn SystemTimeToTzSpecificLocalTime(lptimezoneinformation : *const TIME_ZONE_INFORMATION, lpuniversaltime : *const SYSTEMTIME, lplocaltime : *mut SYSTEMTIME) -> BOOL);
|
||||
windows_targets::link!("kernel32.dll" "system" fn TzSpecificLocalTimeToSystemTime(lptimezoneinformation : *const TIME_ZONE_INFORMATION, lplocaltime : *const SYSTEMTIME, lpuniversaltime : *mut SYSTEMTIME) -> BOOL);
|
||||
pub type BOOL = i32;
|
||||
pub type BOOLEAN = u8;
|
||||
#[repr(C)]
|
||||
@ -19,8 +17,8 @@ pub struct DYNAMIC_TIME_ZONE_INFORMATION {
|
||||
pub TimeZoneKeyName: [u16; 128],
|
||||
pub DynamicDaylightTimeDisabled: BOOLEAN,
|
||||
}
|
||||
impl ::core::marker::Copy for DYNAMIC_TIME_ZONE_INFORMATION {}
|
||||
impl ::core::clone::Clone for DYNAMIC_TIME_ZONE_INFORMATION {
|
||||
impl Copy for DYNAMIC_TIME_ZONE_INFORMATION {}
|
||||
impl Clone for DYNAMIC_TIME_ZONE_INFORMATION {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
@ -30,8 +28,8 @@ pub struct FILETIME {
|
||||
pub dwLowDateTime: u32,
|
||||
pub dwHighDateTime: u32,
|
||||
}
|
||||
impl ::core::marker::Copy for FILETIME {}
|
||||
impl ::core::clone::Clone for FILETIME {
|
||||
impl Copy for FILETIME {}
|
||||
impl Clone for FILETIME {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
@ -47,8 +45,8 @@ pub struct SYSTEMTIME {
|
||||
pub wSecond: u16,
|
||||
pub wMilliseconds: u16,
|
||||
}
|
||||
impl ::core::marker::Copy for SYSTEMTIME {}
|
||||
impl ::core::clone::Clone for SYSTEMTIME {
|
||||
impl Copy for SYSTEMTIME {}
|
||||
impl Clone for SYSTEMTIME {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
@ -63,8 +61,8 @@ pub struct TIME_ZONE_INFORMATION {
|
||||
pub DaylightDate: SYSTEMTIME,
|
||||
pub DaylightBias: i32,
|
||||
}
|
||||
impl ::core::marker::Copy for TIME_ZONE_INFORMATION {}
|
||||
impl ::core::clone::Clone for TIME_ZONE_INFORMATION {
|
||||
impl Copy for TIME_ZONE_INFORMATION {}
|
||||
impl Clone for TIME_ZONE_INFORMATION {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
--out src/offset/local/win_bindings.rs
|
||||
--config flatten sys
|
||||
--config flatten sys no-bindgen-comment
|
||||
--filter
|
||||
Windows.Win32.System.Time.GetTimeZoneInformationForYear
|
||||
Windows.Win32.System.Time.SystemTimeToFileTime
|
||||
|
@ -9,7 +9,6 @@
|
||||
// except according to those terms.
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::convert::TryFrom;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::ptr;
|
||||
|
||||
@ -141,18 +140,38 @@ impl TzInfo {
|
||||
}
|
||||
tz_info.assume_init()
|
||||
};
|
||||
let std_offset = (tz_info.Bias)
|
||||
.checked_add(tz_info.StandardBias)
|
||||
.and_then(|o| o.checked_mul(60))
|
||||
.and_then(|o| FixedOffset::west(o).ok())?;
|
||||
let dst_offset = (tz_info.Bias)
|
||||
.checked_add(tz_info.DaylightBias)
|
||||
.and_then(|o| o.checked_mul(60))
|
||||
.and_then(|o| FixedOffset::west(o).ok())?;
|
||||
Some(TzInfo {
|
||||
std_offset: FixedOffset::west((tz_info.Bias + tz_info.StandardBias) * 60).ok()?,
|
||||
dst_offset: FixedOffset::west((tz_info.Bias + tz_info.DaylightBias) * 60).ok()?,
|
||||
std_transition: system_time_from_naive_date_time(tz_info.StandardDate, year),
|
||||
dst_transition: system_time_from_naive_date_time(tz_info.DaylightDate, year),
|
||||
std_offset,
|
||||
dst_offset,
|
||||
std_transition: naive_date_time_from_system_time(tz_info.StandardDate, year).ok()?,
|
||||
dst_transition: naive_date_time_from_system_time(tz_info.DaylightDate, year).ok()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn system_time_from_naive_date_time(st: SYSTEMTIME, year: i32) -> Option<NaiveDateTime> {
|
||||
/// Resolve a `SYSTEMTIME` object to an `Option<NaiveDateTime>`.
|
||||
///
|
||||
/// A `SYSTEMTIME` within a `TIME_ZONE_INFORMATION` struct can be zero to indicate there is no
|
||||
/// transition.
|
||||
/// If it has year, month and day values it is a concrete date.
|
||||
/// If the year is missing the `SYSTEMTIME` is a rule, which this method resolves for the provided
|
||||
/// year. A rule has a month, weekday, and nth weekday of the month as components.
|
||||
///
|
||||
/// Returns `Err` if any of the values is invalid, which should never happen.
|
||||
fn naive_date_time_from_system_time(
|
||||
st: SYSTEMTIME,
|
||||
year: i32,
|
||||
) -> Result<Option<NaiveDateTime>, ()> {
|
||||
if st.wYear == 0 && st.wMonth == 0 {
|
||||
return None; // No DST transitions for this year in this timezone.
|
||||
return Ok(None);
|
||||
}
|
||||
let time = NaiveTime::from_hms_milli(
|
||||
st.wHour as u32,
|
||||
@ -160,25 +179,35 @@ fn system_time_from_naive_date_time(st: SYSTEMTIME, year: i32) -> Option<NaiveDa
|
||||
st.wSecond as u32,
|
||||
st.wMilliseconds as u32,
|
||||
)
|
||||
.ok()?;
|
||||
// In Chrono's Weekday, Monday is 0 whereas in SYSTEMTIME Monday is 1 and Sunday is 0.
|
||||
// Therefore we move back one day after converting the u16 value to a Weekday.
|
||||
let day_of_week = Weekday::try_from(u8::try_from(st.wDayOfWeek).ok()?).ok()?.pred();
|
||||
.map_err(|_| ())?;
|
||||
|
||||
if st.wYear != 0 {
|
||||
return NaiveDate::from_ymd(st.wYear as i32, st.wMonth as u32, st.wDay as u32)
|
||||
.ok()
|
||||
.map(|d| d.and_time(time));
|
||||
// We have a concrete date.
|
||||
let date = NaiveDate::from_ymd(st.wYear as i32, st.wMonth as u32, st.wDay as u32)
|
||||
.map_err(|_| ())?;
|
||||
return Ok(Some(date.and_time(time)));
|
||||
}
|
||||
let date = if let Some(date) =
|
||||
NaiveDate::from_weekday_of_month(year, st.wMonth as u32, day_of_week, st.wDay as u8)
|
||||
{
|
||||
date
|
||||
} else if st.wDay == 5 {
|
||||
NaiveDate::from_weekday_of_month(year, st.wMonth as u32, day_of_week, 4)?
|
||||
} else {
|
||||
return None;
|
||||
|
||||
// Resolve a rule with month, weekday, and nth weekday of the month to a date in the current
|
||||
// year.
|
||||
let weekday = match st.wDayOfWeek {
|
||||
0 => Weekday::Sun,
|
||||
1 => Weekday::Mon,
|
||||
2 => Weekday::Tue,
|
||||
3 => Weekday::Wed,
|
||||
4 => Weekday::Thu,
|
||||
5 => Weekday::Fri,
|
||||
6 => Weekday::Sat,
|
||||
_ => return Err(()),
|
||||
};
|
||||
Some(date.and_time(time))
|
||||
let nth_day = match st.wDay {
|
||||
1..=5 => st.wDay as u8,
|
||||
_ => return Err(()),
|
||||
};
|
||||
let date = NaiveDate::from_weekday_of_month(year, st.wMonth as u32, weekday, nth_day)
|
||||
.or_else(|| NaiveDate::from_weekday_of_month(year, st.wMonth as u32, weekday, 4))
|
||||
.ok_or(())?; // `st.wMonth` must be invalid
|
||||
Ok(Some(date.and_time(time)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -6,7 +6,6 @@
|
||||
use crate::{DateTime, NaiveDateTime, TimeDelta, TimeZone, Timelike};
|
||||
use core::cmp::Ordering;
|
||||
use core::fmt;
|
||||
use core::marker::Sized;
|
||||
use core::ops::{Add, Sub};
|
||||
|
||||
/// Extension trait for subsecond rounding or truncation to a maximum number
|
||||
|
Loading…
x
Reference in New Issue
Block a user