summaryrefslogtreecommitdiffstats
path: root/vendor/chrono/src/offset
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 02:49:42 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 02:49:42 +0000
commit837b550238aa671a591ccf282dddeab29cadb206 (patch)
tree914b6b8862bace72bd3245ca184d374b08d8a672 /vendor/chrono/src/offset
parentAdding debian version 1.70.0+dfsg2-1. (diff)
downloadrustc-837b550238aa671a591ccf282dddeab29cadb206.tar.xz
rustc-837b550238aa671a591ccf282dddeab29cadb206.zip
Merging upstream version 1.71.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/chrono/src/offset')
-rw-r--r--vendor/chrono/src/offset/fixed.rs112
-rw-r--r--vendor/chrono/src/offset/local.rs227
-rw-r--r--vendor/chrono/src/offset/local/mod.rs260
-rw-r--r--vendor/chrono/src/offset/local/stub.rs236
-rw-r--r--vendor/chrono/src/offset/local/tz_info/mod.rs131
-rw-r--r--vendor/chrono/src/offset/local/tz_info/parser.rs334
-rw-r--r--vendor/chrono/src/offset/local/tz_info/rule.rs1046
-rw-r--r--vendor/chrono/src/offset/local/tz_info/timezone.rs904
-rw-r--r--vendor/chrono/src/offset/local/unix.rs185
-rw-r--r--vendor/chrono/src/offset/local/windows.rs278
-rw-r--r--vendor/chrono/src/offset/mod.rs184
-rw-r--r--vendor/chrono/src/offset/utc.rs55
12 files changed, 3591 insertions, 361 deletions
diff --git a/vendor/chrono/src/offset/fixed.rs b/vendor/chrono/src/offset/fixed.rs
index 83f42a1a4..0989dfa5b 100644
--- a/vendor/chrono/src/offset/fixed.rs
+++ b/vendor/chrono/src/offset/fixed.rs
@@ -5,21 +5,25 @@
use core::fmt;
use core::ops::{Add, Sub};
-use oldtime::Duration as OldDuration;
+
+use num_integer::div_mod_floor;
+#[cfg(feature = "rkyv")]
+use rkyv::{Archive, Deserialize, Serialize};
use super::{LocalResult, Offset, TimeZone};
-use div::div_mod_floor;
-use naive::{NaiveDate, NaiveDateTime, NaiveTime};
-use DateTime;
-use Timelike;
+use crate::naive::{NaiveDate, NaiveDateTime, NaiveTime};
+use crate::oldtime::Duration as OldDuration;
+use crate::DateTime;
+use crate::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.
+/// `DateTime<FixedOffset>` instances. See the [`east_opt`](#method.east_opt) and
+/// [`west_opt`](#method.west_opt) methods for examples.
#[derive(PartialEq, Eq, Hash, Copy, Clone)]
+#[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))]
pub struct FixedOffset {
local_minus_utc: i32,
}
@@ -29,16 +33,7 @@ impl FixedOffset {
/// 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")
- /// ~~~~
+ #[deprecated(since = "0.4.23", note = "use `east_opt()` instead")]
pub fn east(secs: i32) -> FixedOffset {
FixedOffset::east_opt(secs).expect("FixedOffset::east out of bounds")
}
@@ -47,6 +42,16 @@ impl FixedOffset {
/// The negative `secs` means the Western Hemisphere.
///
/// Returns `None` on the out-of-bound `secs`.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use chrono::{FixedOffset, TimeZone};
+ /// let hour = 3600;
+ /// let datetime = FixedOffset::east_opt(5 * hour).unwrap().ymd_opt(2016, 11, 08).unwrap()
+ /// .and_hms_opt(0, 0, 0).unwrap();
+ /// assert_eq!(&datetime.to_rfc3339(), "2016-11-08T00:00:00+05:00")
+ /// ```
pub fn east_opt(secs: i32) -> Option<FixedOffset> {
if -86_400 < secs && secs < 86_400 {
Some(FixedOffset { local_minus_utc: secs })
@@ -59,16 +64,7 @@ impl FixedOffset {
/// 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")
- /// ~~~~
+ #[deprecated(since = "0.4.23", note = "use `west_opt()` instead")]
pub fn west(secs: i32) -> FixedOffset {
FixedOffset::west_opt(secs).expect("FixedOffset::west out of bounds")
}
@@ -77,6 +73,16 @@ impl FixedOffset {
/// The negative `secs` means the Eastern Hemisphere.
///
/// Returns `None` on the out-of-bound `secs`.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use chrono::{FixedOffset, TimeZone};
+ /// let hour = 3600;
+ /// let datetime = FixedOffset::west_opt(5 * hour).unwrap().ymd_opt(2016, 11, 08).unwrap()
+ /// .and_hms_opt(0, 0, 0).unwrap();
+ /// assert_eq!(&datetime.to_rfc3339(), "2016-11-08T00:00:00-05:00")
+ /// ```
pub fn west_opt(secs: i32) -> Option<FixedOffset> {
if -86_400 < secs && secs < 86_400 {
Some(FixedOffset { local_minus_utc: -secs })
@@ -87,13 +93,13 @@ impl FixedOffset {
/// Returns the number of seconds to add to convert from UTC to the local time.
#[inline]
- pub fn local_minus_utc(&self) -> i32 {
+ pub const 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 {
+ pub const fn utc_minus_local(&self) -> i32 {
-self.local_minus_utc
}
}
@@ -146,6 +152,16 @@ impl fmt::Display for FixedOffset {
}
}
+#[cfg(feature = "arbitrary")]
+impl arbitrary::Arbitrary<'_> for FixedOffset {
+ fn arbitrary(u: &mut arbitrary::Unstructured) -> arbitrary::Result<FixedOffset> {
+ let secs = u.int_in_range(-86_399..=86_399)?;
+ let fixed_offset = FixedOffset::east_opt(secs)
+ .expect("Could not generate a valid chrono::FixedOffset. It looks like implementation of Arbitrary for FixedOffset is erroneous.");
+ Ok(fixed_offset)
+ }
+}
+
// 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.
@@ -218,26 +234,50 @@ impl<Tz: TimeZone> Sub<FixedOffset> for DateTime<Tz> {
#[cfg(test)]
mod tests {
use super::FixedOffset;
- use offset::TimeZone;
+ use crate::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()
+ format!(
+ "{:?}",
+ FixedOffset::east_opt(86399)
+ .unwrap()
+ .with_ymd_and_hms(2012, 2, 29, 5, 6, 7)
+ .unwrap()
+ ),
+ "2012-02-29T05:06:07+23:59:59".to_string()
);
assert_eq!(
- format!("{:?}", FixedOffset::east(86399).ymd(2012, 2, 29).and_hms(5, 6, 7)),
+ format!(
+ "{:?}",
+ FixedOffset::east_opt(86399)
+ .unwrap()
+ .with_ymd_and_hms(2012, 2, 29, 5, 6, 7)
+ .unwrap()
+ ),
"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()
+ format!(
+ "{:?}",
+ FixedOffset::west_opt(86399)
+ .unwrap()
+ .with_ymd_and_hms(2012, 3, 4, 5, 6, 7)
+ .unwrap()
+ ),
+ "2012-03-04T05:06:07-23:59:59".to_string()
);
assert_eq!(
- format!("{:?}", FixedOffset::west(86399).ymd(2012, 3, 4).and_hms(5, 6, 7)),
+ format!(
+ "{:?}",
+ FixedOffset::west_opt(86399)
+ .unwrap()
+ .with_ymd_and_hms(2012, 3, 4, 5, 6, 7)
+ .unwrap()
+ ),
"2012-03-04T05:06:07-23:59:59".to_string()
);
}
diff --git a/vendor/chrono/src/offset/local.rs b/vendor/chrono/src/offset/local.rs
deleted file mode 100644
index 1abb3a9db..000000000
--- a/vendor/chrono/src/offset/local.rs
+++ /dev/null
@@ -1,227 +0,0 @@
-// 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
- );
- }
-}
diff --git a/vendor/chrono/src/offset/local/mod.rs b/vendor/chrono/src/offset/local/mod.rs
new file mode 100644
index 000000000..e280c7800
--- /dev/null
+++ b/vendor/chrono/src/offset/local/mod.rs
@@ -0,0 +1,260 @@
+// This is a part of Chrono.
+// See README.md and LICENSE.txt for details.
+
+//! The local (system) time zone.
+
+#[cfg(feature = "rkyv")]
+use rkyv::{Archive, Deserialize, Serialize};
+
+use super::fixed::FixedOffset;
+use super::{LocalResult, TimeZone};
+use crate::naive::{NaiveDate, NaiveDateTime};
+#[allow(deprecated)]
+use crate::{Date, DateTime};
+
+// we don't want `stub.rs` when the target_os is not wasi or emscripten
+// as we use js-sys to get the date instead
+#[cfg(all(
+ not(unix),
+ not(windows),
+ not(all(
+ target_arch = "wasm32",
+ feature = "wasmbind",
+ not(any(target_os = "emscripten", target_os = "wasi"))
+ ))
+))]
+#[path = "stub.rs"]
+mod inner;
+
+#[cfg(unix)]
+#[path = "unix.rs"]
+mod inner;
+
+#[cfg(windows)]
+#[path = "windows.rs"]
+mod inner;
+
+#[cfg(unix)]
+mod tz_info;
+
+/// 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)]
+#[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))]
+#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
+pub struct Local;
+
+impl Local {
+ /// Returns a `Date` which corresponds to the current date.
+ #[deprecated(since = "0.4.23", note = "use `Local::now()` instead")]
+ #[allow(deprecated)]
+ pub fn today() -> Date<Local> {
+ Local::now().date()
+ }
+
+ /// Returns a `DateTime` which corresponds to the current date and time.
+ #[cfg(not(all(
+ target_arch = "wasm32",
+ feature = "wasmbind",
+ not(any(target_os = "emscripten", target_os = "wasi"))
+ )))]
+ pub fn now() -> DateTime<Local> {
+ inner::now()
+ }
+
+ /// Returns a `DateTime` which corresponds to the current date and time.
+ #[cfg(all(
+ target_arch = "wasm32",
+ feature = "wasmbind",
+ not(any(target_os = "emscripten", target_os = "wasi"))
+ ))]
+ 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_opt((js_sys::Date::new_0().get_timezone_offset() as i32) * 60)
+ .unwrap();
+ 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
+ #[allow(deprecated)]
+ 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())
+ }
+
+ #[allow(deprecated)]
+ 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
+ #[allow(deprecated)]
+ 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_opt(0, 0, 0).unwrap());
+ midnight.map(|datetime| Date::from_utc(*local, *datetime.offset()))
+ }
+
+ #[cfg(all(
+ target_arch = "wasm32",
+ feature = "wasmbind",
+ not(any(target_os = "emscripten", target_os = "wasi"))
+ ))]
+ 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_opt((js_sys::Date::new_0().get_timezone_offset() as i32) * 60)
+ .unwrap();
+ local -= crate::Duration::seconds(offset.local_minus_utc() as i64);
+ LocalResult::Single(DateTime::from_utc(local, offset))
+ }
+
+ #[cfg(not(all(
+ target_arch = "wasm32",
+ feature = "wasmbind",
+ not(any(target_os = "emscripten", target_os = "wasi"))
+ )))]
+ fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<Local>> {
+ inner::naive_to_local(local, true)
+ }
+
+ #[allow(deprecated)]
+ fn from_utc_date(&self, utc: &NaiveDate) -> Date<Local> {
+ let midnight = self.from_utc_datetime(&utc.and_hms_opt(0, 0, 0).unwrap());
+ Date::from_utc(*utc, *midnight.offset())
+ }
+
+ #[cfg(all(
+ target_arch = "wasm32",
+ feature = "wasmbind",
+ not(any(target_os = "emscripten", target_os = "wasi"))
+ ))]
+ fn from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime<Local> {
+ // Get the offset from the js runtime
+ let offset =
+ FixedOffset::west_opt((js_sys::Date::new_0().get_timezone_offset() as i32) * 60)
+ .unwrap();
+ DateTime::from_utc(*utc, offset)
+ }
+
+ #[cfg(not(all(
+ target_arch = "wasm32",
+ feature = "wasmbind",
+ not(any(target_os = "emscripten", target_os = "wasi"))
+ )))]
+ fn from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime<Local> {
+ // this is OK to unwrap as getting local time from a UTC
+ // timestamp is never ambiguous
+ inner::naive_to_local(utc, false).unwrap()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::Local;
+ use crate::offset::TimeZone;
+ use crate::{Datelike, Duration, Utc};
+
+ #[test]
+ fn verify_correct_offsets() {
+ let now = Local::now();
+ let from_local = Local.from_local_datetime(&now.naive_local()).unwrap();
+ let from_utc = Local.from_utc_datetime(&now.naive_utc());
+
+ assert_eq!(now.offset().local_minus_utc(), from_local.offset().local_minus_utc());
+ assert_eq!(now.offset().local_minus_utc(), from_utc.offset().local_minus_utc());
+
+ assert_eq!(now, from_local);
+ assert_eq!(now, from_utc);
+ }
+
+ #[test]
+ fn verify_correct_offsets_distant_past() {
+ // let distant_past = Local::now() - Duration::days(365 * 100);
+ let distant_past = Local::now() - Duration::days(250 * 31);
+ let from_local = Local.from_local_datetime(&distant_past.naive_local()).unwrap();
+ let from_utc = Local.from_utc_datetime(&distant_past.naive_utc());
+
+ assert_eq!(distant_past.offset().local_minus_utc(), from_local.offset().local_minus_utc());
+ assert_eq!(distant_past.offset().local_minus_utc(), from_utc.offset().local_minus_utc());
+
+ assert_eq!(distant_past, from_local);
+ assert_eq!(distant_past, from_utc);
+ }
+
+ #[test]
+ fn verify_correct_offsets_distant_future() {
+ let distant_future = Local::now() + Duration::days(250 * 31);
+ let from_local = Local.from_local_datetime(&distant_future.naive_local()).unwrap();
+ let from_utc = Local.from_utc_datetime(&distant_future.naive_utc());
+
+ assert_eq!(
+ distant_future.offset().local_minus_utc(),
+ from_local.offset().local_minus_utc()
+ );
+ assert_eq!(distant_future.offset().local_minus_utc(), from_utc.offset().local_minus_utc());
+
+ assert_eq!(distant_future, from_local);
+ assert_eq!(distant_future, from_utc);
+ }
+
+ #[test]
+ fn test_local_date_sanity_check() {
+ // issue #27
+ assert_eq!(Local.with_ymd_and_hms(2999, 12, 28, 0, 0, 0).unwrap().day(), 28);
+ }
+
+ #[test]
+ fn test_leap_second() {
+ // issue #123
+ let today = Utc::now().date_naive();
+
+ let dt = today.and_hms_milli_opt(1, 2, 59, 1000).unwrap();
+ 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_opt(1, 2, 3, 1234).unwrap();
+ let timestr = dt.time().to_string();
+ assert!(
+ timestr == "01:02:03.234" || timestr == "01:02:04.234",
+ "unexpected timestr {:?}",
+ timestr
+ );
+ }
+}
diff --git a/vendor/chrono/src/offset/local/stub.rs b/vendor/chrono/src/offset/local/stub.rs
new file mode 100644
index 000000000..9ececd3c2
--- /dev/null
+++ b/vendor/chrono/src/offset/local/stub.rs
@@ -0,0 +1,236 @@
+// 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 std::time::{SystemTime, UNIX_EPOCH};
+
+use super::{FixedOffset, Local};
+use crate::{DateTime, Datelike, LocalResult, NaiveDate, NaiveDateTime, NaiveTime, Timelike};
+
+pub(super) fn now() -> DateTime<Local> {
+ tm_to_datetime(Timespec::now().local())
+}
+
+/// Converts a local `NaiveDateTime` to the `time::Timespec`.
+#[cfg(not(all(
+ target_arch = "wasm32",
+ feature = "wasmbind",
+ not(any(target_os = "emscripten", target_os = "wasi"))
+)))]
+pub(super) fn naive_to_local(d: &NaiveDateTime, local: bool) -> LocalResult<DateTime<Local>> {
+ let tm = 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,
+ // This seems pretty fake?
+ tm_utcoff: if local { 1 } else { 0 },
+ // do not set this, OS APIs are heavily inconsistent in terms of leap second handling
+ tm_nsec: 0,
+ };
+
+ let spec = Timespec {
+ sec: match local {
+ false => utc_tm_to_time(&tm),
+ true => local_tm_to_time(&tm),
+ },
+ nsec: tm.tm_nsec,
+ };
+
+ // Adjust for leap seconds
+ let mut tm = spec.local();
+ assert_eq!(tm.tm_nsec, 0);
+ tm.tm_nsec = d.nanosecond() as i32;
+
+ LocalResult::Single(tm_to_datetime(tm))
+}
+
+/// 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",
+ feature = "wasmbind",
+ not(any(target_os = "emscripten", target_os = "wasi"))
+)))]
+fn tm_to_datetime(mut tm: Tm) -> DateTime<Local> {
+ if tm.tm_sec >= 60 {
+ tm.tm_nsec += (tm.tm_sec - 59) * 1_000_000_000;
+ tm.tm_sec = 59;
+ }
+
+ let date = NaiveDate::from_yo(tm.tm_year + 1900, tm.tm_yday as u32 + 1);
+ 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_opt(tm.tm_utcoff).unwrap();
+ DateTime::from_utc(date.and_time(time) - offset, offset)
+}
+
+/// 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}.
+struct Timespec {
+ sec: i64,
+ nsec: i32,
+}
+
+impl Timespec {
+ /// Constructs a timespec representing the current time in UTC.
+ 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.
+ 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,
+ };
+ 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?
+#[repr(C)]
+pub(super) struct Tm {
+ /// Seconds after the minute - [0, 60]
+ tm_sec: i32,
+
+ /// Minutes after the hour - [0, 59]
+ tm_min: i32,
+
+ /// Hours after midnight - [0, 23]
+ tm_hour: i32,
+
+ /// Day of the month - [1, 31]
+ tm_mday: i32,
+
+ /// Months since January - [0, 11]
+ tm_mon: i32,
+
+ /// Years since 1900
+ tm_year: i32,
+
+ /// Days since Sunday - [0, 6]. 0 = Sunday, 1 = Monday, ..., 6 = Saturday.
+ tm_wday: i32,
+
+ /// Days since January 1 - [0, 365]
+ 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.
+ 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`.
+ tm_utcoff: i32,
+
+ /// Nanoseconds after the second - [0, 10<sup>9</sup> - 1]
+ tm_nsec: i32,
+}
+
+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(super) fn time_to_local_tm(sec: i64, tm: &mut Tm) {
+ // FIXME: Add timezone logic
+ time_to_tm(sec, tm);
+}
+
+pub(super) fn utc_tm_to_time(tm: &Tm) -> i64 {
+ tm_to_time(tm)
+}
+
+pub(super) fn local_tm_to_time(tm: &Tm) -> i64 {
+ // FIXME: Add timezone logic
+ tm_to_time(tm)
+}
diff --git a/vendor/chrono/src/offset/local/tz_info/mod.rs b/vendor/chrono/src/offset/local/tz_info/mod.rs
new file mode 100644
index 000000000..bd2693b6b
--- /dev/null
+++ b/vendor/chrono/src/offset/local/tz_info/mod.rs
@@ -0,0 +1,131 @@
+#![deny(missing_docs)]
+#![allow(dead_code)]
+#![warn(unreachable_pub)]
+
+use std::num::ParseIntError;
+use std::str::Utf8Error;
+use std::time::SystemTimeError;
+use std::{error, fmt, io};
+
+mod timezone;
+pub(crate) use timezone::TimeZone;
+
+mod parser;
+mod rule;
+
+/// Unified error type for everything in the crate
+#[derive(Debug)]
+pub(crate) enum Error {
+ /// Date time error
+ DateTime(&'static str),
+ /// Local time type search error
+ FindLocalTimeType(&'static str),
+ /// Local time type error
+ LocalTimeType(&'static str),
+ /// Invalid slice for integer conversion
+ InvalidSlice(&'static str),
+ /// Invalid Tzif file
+ InvalidTzFile(&'static str),
+ /// Invalid TZ string
+ InvalidTzString(&'static str),
+ /// I/O error
+ Io(io::Error),
+ /// Out of range error
+ OutOfRange(&'static str),
+ /// Integer parsing error
+ ParseInt(ParseIntError),
+ /// Date time projection error
+ ProjectDateTime(&'static str),
+ /// System time error
+ SystemTime(SystemTimeError),
+ /// Time zone error
+ TimeZone(&'static str),
+ /// Transition rule error
+ TransitionRule(&'static str),
+ /// Unsupported Tzif file
+ UnsupportedTzFile(&'static str),
+ /// Unsupported TZ string
+ UnsupportedTzString(&'static str),
+ /// UTF-8 error
+ Utf8(Utf8Error),
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use Error::*;
+ match self {
+ DateTime(error) => write!(f, "invalid date time: {}", error),
+ FindLocalTimeType(error) => error.fmt(f),
+ LocalTimeType(error) => write!(f, "invalid local time type: {}", error),
+ InvalidSlice(error) => error.fmt(f),
+ InvalidTzString(error) => write!(f, "invalid TZ string: {}", error),
+ InvalidTzFile(error) => error.fmt(f),
+ Io(error) => error.fmt(f),
+ OutOfRange(error) => error.fmt(f),
+ ParseInt(error) => error.fmt(f),
+ ProjectDateTime(error) => error.fmt(f),
+ SystemTime(error) => error.fmt(f),
+ TransitionRule(error) => write!(f, "invalid transition rule: {}", error),
+ TimeZone(error) => write!(f, "invalid time zone: {}", error),
+ UnsupportedTzFile(error) => error.fmt(f),
+ UnsupportedTzString(error) => write!(f, "unsupported TZ string: {}", error),
+ Utf8(error) => error.fmt(f),
+ }
+ }
+}
+
+impl error::Error for Error {}
+
+impl From<io::Error> for Error {
+ fn from(error: io::Error) -> Self {
+ Error::Io(error)
+ }
+}
+
+impl From<ParseIntError> for Error {
+ fn from(error: ParseIntError) -> Self {
+ Error::ParseInt(error)
+ }
+}
+
+impl From<SystemTimeError> for Error {
+ fn from(error: SystemTimeError) -> Self {
+ Error::SystemTime(error)
+ }
+}
+
+impl From<Utf8Error> for Error {
+ fn from(error: Utf8Error) -> Self {
+ Error::Utf8(error)
+ }
+}
+
+// MSRV: 1.38
+#[inline]
+fn rem_euclid(v: i64, rhs: i64) -> i64 {
+ let r = v % rhs;
+ if r < 0 {
+ if rhs < 0 {
+ r - rhs
+ } else {
+ r + rhs
+ }
+ } else {
+ r
+ }
+}
+
+/// Number of hours in one day
+const HOURS_PER_DAY: i64 = 24;
+/// Number of seconds in one hour
+const SECONDS_PER_HOUR: i64 = 3600;
+/// Number of seconds in one day
+const SECONDS_PER_DAY: i64 = SECONDS_PER_HOUR * HOURS_PER_DAY;
+/// Number of days in one week
+const DAYS_PER_WEEK: i64 = 7;
+
+/// Month days in a normal year
+const DAY_IN_MONTHS_NORMAL_YEAR: [i64; 12] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
+/// Cumulated month days in a normal year
+const CUMUL_DAY_IN_MONTHS_NORMAL_YEAR: [i64; 12] =
+ [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
diff --git a/vendor/chrono/src/offset/local/tz_info/parser.rs b/vendor/chrono/src/offset/local/tz_info/parser.rs
new file mode 100644
index 000000000..5652a0ea9
--- /dev/null
+++ b/vendor/chrono/src/offset/local/tz_info/parser.rs
@@ -0,0 +1,334 @@
+use std::io::{self, ErrorKind};
+use std::iter;
+use std::num::ParseIntError;
+use std::str::{self, FromStr};
+
+use super::rule::TransitionRule;
+use super::timezone::{LeapSecond, LocalTimeType, TimeZone, Transition};
+use super::Error;
+
+#[allow(clippy::map_clone)] // MSRV: 1.36
+pub(super) fn parse(bytes: &[u8]) -> Result<TimeZone, Error> {
+ let mut cursor = Cursor::new(bytes);
+ let state = State::new(&mut cursor, true)?;
+ let (state, footer) = match state.header.version {
+ Version::V1 => match cursor.is_empty() {
+ true => (state, None),
+ false => {
+ return Err(Error::InvalidTzFile("remaining data after end of TZif v1 data block"))
+ }
+ },
+ Version::V2 | Version::V3 => {
+ let state = State::new(&mut cursor, false)?;
+ (state, Some(cursor.remaining()))
+ }
+ };
+
+ let mut transitions = Vec::with_capacity(state.header.transition_count);
+ for (arr_time, &local_time_type_index) in
+ state.transition_times.chunks_exact(state.time_size).zip(state.transition_types)
+ {
+ let unix_leap_time =
+ state.parse_time(&arr_time[0..state.time_size], state.header.version)?;
+ let local_time_type_index = local_time_type_index as usize;
+ transitions.push(Transition::new(unix_leap_time, local_time_type_index));
+ }
+
+ let mut local_time_types = Vec::with_capacity(state.header.type_count);
+ for arr in state.local_time_types.chunks_exact(6) {
+ let ut_offset = read_be_i32(&arr[..4])?;
+
+ let is_dst = match arr[4] {
+ 0 => false,
+ 1 => true,
+ _ => return Err(Error::InvalidTzFile("invalid DST indicator")),
+ };
+
+ let char_index = arr[5] as usize;
+ if char_index >= state.header.char_count {
+ return Err(Error::InvalidTzFile("invalid time zone name char index"));
+ }
+
+ let position = match state.names[char_index..].iter().position(|&c| c == b'\0') {
+ Some(position) => position,
+ None => return Err(Error::InvalidTzFile("invalid time zone name char index")),
+ };
+
+ let name = &state.names[char_index..char_index + position];
+ let name = if !name.is_empty() { Some(name) } else { None };
+ local_time_types.push(LocalTimeType::new(ut_offset, is_dst, name)?);
+ }
+
+ let mut leap_seconds = Vec::with_capacity(state.header.leap_count);
+ for arr in state.leap_seconds.chunks_exact(state.time_size + 4) {
+ let unix_leap_time = state.parse_time(&arr[0..state.time_size], state.header.version)?;
+ let correction = read_be_i32(&arr[state.time_size..state.time_size + 4])?;
+ leap_seconds.push(LeapSecond::new(unix_leap_time, correction));
+ }
+
+ let std_walls_iter = state.std_walls.iter().map(|&i| i).chain(iter::repeat(0));
+ let ut_locals_iter = state.ut_locals.iter().map(|&i| i).chain(iter::repeat(0));
+ if std_walls_iter.zip(ut_locals_iter).take(state.header.type_count).any(|pair| pair == (0, 1)) {
+ return Err(Error::InvalidTzFile(
+ "invalid couple of standard/wall and UT/local indicators",
+ ));
+ }
+
+ let extra_rule = match footer {
+ Some(footer) => {
+ let footer = str::from_utf8(footer)?;
+ if !(footer.starts_with('\n') && footer.ends_with('\n')) {
+ return Err(Error::InvalidTzFile("invalid footer"));
+ }
+
+ let tz_string = footer.trim_matches(|c: char| c.is_ascii_whitespace());
+ if tz_string.starts_with(':') || tz_string.contains('\0') {
+ return Err(Error::InvalidTzFile("invalid footer"));
+ }
+
+ match tz_string.is_empty() {
+ true => None,
+ false => Some(TransitionRule::from_tz_string(
+ tz_string.as_bytes(),
+ state.header.version == Version::V3,
+ )?),
+ }
+ }
+ None => None,
+ };
+
+ TimeZone::new(transitions, local_time_types, leap_seconds, extra_rule)
+}
+
+/// TZif data blocks
+struct State<'a> {
+ header: Header,
+ /// Time size in bytes
+ time_size: usize,
+ /// Transition times data block
+ transition_times: &'a [u8],
+ /// Transition types data block
+ transition_types: &'a [u8],
+ /// Local time types data block
+ local_time_types: &'a [u8],
+ /// Time zone names data block
+ names: &'a [u8],
+ /// Leap seconds data block
+ leap_seconds: &'a [u8],
+ /// UT/local indicators data block
+ std_walls: &'a [u8],
+ /// Standard/wall indicators data block
+ ut_locals: &'a [u8],
+}
+
+impl<'a> State<'a> {
+ /// Read TZif data blocks
+ fn new(cursor: &mut Cursor<'a>, first: bool) -> Result<Self, Error> {
+ let header = Header::new(cursor)?;
+ let time_size = match first {
+ true => 4, // We always parse V1 first
+ false => 8,
+ };
+
+ Ok(Self {
+ time_size,
+ transition_times: cursor.read_exact(header.transition_count * time_size)?,
+ transition_types: cursor.read_exact(header.transition_count)?,
+ local_time_types: cursor.read_exact(header.type_count * 6)?,
+ names: cursor.read_exact(header.char_count)?,
+ leap_seconds: cursor.read_exact(header.leap_count * (time_size + 4))?,
+ std_walls: cursor.read_exact(header.std_wall_count)?,
+ ut_locals: cursor.read_exact(header.ut_local_count)?,
+ header,
+ })
+ }
+
+ /// Parse time values
+ fn parse_time(&self, arr: &[u8], version: Version) -> Result<i64, Error> {
+ match version {
+ Version::V1 => Ok(read_be_i32(&arr[..4])?.into()),
+ Version::V2 | Version::V3 => read_be_i64(arr),
+ }
+ }
+}
+
+/// TZif header
+#[derive(Debug)]
+struct Header {
+ /// TZif version
+ version: Version,
+ /// Number of UT/local indicators
+ ut_local_count: usize,
+ /// Number of standard/wall indicators
+ std_wall_count: usize,
+ /// Number of leap-second records
+ leap_count: usize,
+ /// Number of transition times
+ transition_count: usize,
+ /// Number of local time type records
+ type_count: usize,
+ /// Number of time zone names bytes
+ char_count: usize,
+}
+
+impl Header {
+ fn new(cursor: &mut Cursor) -> Result<Self, Error> {
+ let magic = cursor.read_exact(4)?;
+ if magic != *b"TZif" {
+ return Err(Error::InvalidTzFile("invalid magic number"));
+ }
+
+ let version = match cursor.read_exact(1)? {
+ [0x00] => Version::V1,
+ [0x32] => Version::V2,
+ [0x33] => Version::V3,
+ _ => return Err(Error::UnsupportedTzFile("unsupported TZif version")),
+ };
+
+ cursor.read_exact(15)?;
+ let ut_local_count = cursor.read_be_u32()?;
+ let std_wall_count = cursor.read_be_u32()?;
+ let leap_count = cursor.read_be_u32()?;
+ let transition_count = cursor.read_be_u32()?;
+ let type_count = cursor.read_be_u32()?;
+ let char_count = cursor.read_be_u32()?;
+
+ if !(type_count != 0
+ && char_count != 0
+ && (ut_local_count == 0 || ut_local_count == type_count)
+ && (std_wall_count == 0 || std_wall_count == type_count))
+ {
+ return Err(Error::InvalidTzFile("invalid header"));
+ }
+
+ Ok(Self {
+ version,
+ ut_local_count: ut_local_count as usize,
+ std_wall_count: std_wall_count as usize,
+ leap_count: leap_count as usize,
+ transition_count: transition_count as usize,
+ type_count: type_count as usize,
+ char_count: char_count as usize,
+ })
+ }
+}
+
+/// A `Cursor` contains a slice of a buffer and a read count.
+#[derive(Debug, Eq, PartialEq)]
+pub(crate) struct Cursor<'a> {
+ /// Slice representing the remaining data to be read
+ remaining: &'a [u8],
+ /// Number of already read bytes
+ read_count: usize,
+}
+
+impl<'a> Cursor<'a> {
+ /// Construct a new `Cursor` from remaining data
+ pub(crate) const fn new(remaining: &'a [u8]) -> Self {
+ Self { remaining, read_count: 0 }
+ }
+
+ pub(crate) fn peek(&self) -> Option<&u8> {
+ self.remaining().first()
+ }
+
+ /// Returns remaining data
+ pub(crate) const fn remaining(&self) -> &'a [u8] {
+ self.remaining
+ }
+
+ /// Returns `true` if data is remaining
+ pub(crate) fn is_empty(&self) -> bool {
+ self.remaining.is_empty()
+ }
+
+ pub(crate) fn read_be_u32(&mut self) -> Result<u32, Error> {
+ let mut buf = [0; 4];
+ buf.copy_from_slice(self.read_exact(4)?);
+ Ok(u32::from_be_bytes(buf))
+ }
+
+ /// Read exactly `count` bytes, reducing remaining data and incrementing read count
+ pub(crate) fn read_exact(&mut self, count: usize) -> Result<&'a [u8], io::Error> {
+ match (self.remaining.get(..count), self.remaining.get(count..)) {
+ (Some(result), Some(remaining)) => {
+ self.remaining = remaining;
+ self.read_count += count;
+ Ok(result)
+ }
+ _ => Err(io::Error::from(ErrorKind::UnexpectedEof)),
+ }
+ }
+
+ /// Read bytes and compare them to the provided tag
+ pub(crate) fn read_tag(&mut self, tag: &[u8]) -> Result<(), io::Error> {
+ if self.read_exact(tag.len())? == tag {
+ Ok(())
+ } else {
+ Err(io::Error::from(ErrorKind::InvalidData))
+ }
+ }
+
+ /// Read bytes if the remaining data is prefixed by the provided tag
+ pub(crate) fn read_optional_tag(&mut self, tag: &[u8]) -> Result<bool, io::Error> {
+ if self.remaining.starts_with(tag) {
+ self.read_exact(tag.len())?;
+ Ok(true)
+ } else {
+ Ok(false)
+ }
+ }
+
+ /// Read bytes as long as the provided predicate is true
+ pub(crate) fn read_while<F: Fn(&u8) -> bool>(&mut self, f: F) -> Result<&'a [u8], io::Error> {
+ match self.remaining.iter().position(|x| !f(x)) {
+ None => self.read_exact(self.remaining.len()),
+ Some(position) => self.read_exact(position),
+ }
+ }
+
+ // Parse an integer out of the ASCII digits
+ pub(crate) fn read_int<T: FromStr<Err = ParseIntError>>(&mut self) -> Result<T, Error> {
+ let bytes = self.read_while(u8::is_ascii_digit)?;
+ Ok(str::from_utf8(bytes)?.parse()?)
+ }
+
+ /// Read bytes until the provided predicate is true
+ pub(crate) fn read_until<F: Fn(&u8) -> bool>(&mut self, f: F) -> Result<&'a [u8], io::Error> {
+ match self.remaining.iter().position(f) {
+ None => self.read_exact(self.remaining.len()),
+ Some(position) => self.read_exact(position),
+ }
+ }
+}
+
+pub(crate) fn read_be_i32(bytes: &[u8]) -> Result<i32, Error> {
+ if bytes.len() != 4 {
+ return Err(Error::InvalidSlice("too short for i32"));
+ }
+
+ let mut buf = [0; 4];
+ buf.copy_from_slice(bytes);
+ Ok(i32::from_be_bytes(buf))
+}
+
+pub(crate) fn read_be_i64(bytes: &[u8]) -> Result<i64, Error> {
+ if bytes.len() != 8 {
+ return Err(Error::InvalidSlice("too short for i64"));
+ }
+
+ let mut buf = [0; 8];
+ buf.copy_from_slice(bytes);
+ Ok(i64::from_be_bytes(buf))
+}
+
+/// TZif version
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+enum Version {
+ /// Version 1
+ V1,
+ /// Version 2
+ V2,
+ /// Version 3
+ V3,
+}
diff --git a/vendor/chrono/src/offset/local/tz_info/rule.rs b/vendor/chrono/src/offset/local/tz_info/rule.rs
new file mode 100644
index 000000000..7befddb5c
--- /dev/null
+++ b/vendor/chrono/src/offset/local/tz_info/rule.rs
@@ -0,0 +1,1046 @@
+use std::cmp::Ordering;
+
+use super::parser::Cursor;
+use super::timezone::{LocalTimeType, SECONDS_PER_WEEK};
+use super::{
+ rem_euclid, Error, CUMUL_DAY_IN_MONTHS_NORMAL_YEAR, DAYS_PER_WEEK, DAY_IN_MONTHS_NORMAL_YEAR,
+ SECONDS_PER_DAY,
+};
+
+/// Transition rule
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub(super) enum TransitionRule {
+ /// Fixed local time type
+ Fixed(LocalTimeType),
+ /// Alternate local time types
+ Alternate(AlternateTime),
+}
+
+impl TransitionRule {
+ /// Parse a POSIX TZ string containing a time zone description, as described in [the POSIX documentation of the `TZ` environment variable](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html).
+ ///
+ /// TZ string extensions from [RFC 8536](https://datatracker.ietf.org/doc/html/rfc8536#section-3.3.1) may be used.
+ ///
+ pub(super) fn from_tz_string(
+ tz_string: &[u8],
+ use_string_extensions: bool,
+ ) -> Result<Self, Error> {
+ let mut cursor = Cursor::new(tz_string);
+
+ let std_time_zone = Some(parse_name(&mut cursor)?);
+ let std_offset = parse_offset(&mut cursor)?;
+
+ if cursor.is_empty() {
+ return Ok(LocalTimeType::new(-std_offset, false, std_time_zone)?.into());
+ }
+
+ let dst_time_zone = Some(parse_name(&mut cursor)?);
+
+ let dst_offset = match cursor.peek() {
+ Some(&b',') => std_offset - 3600,
+ Some(_) => parse_offset(&mut cursor)?,
+ None => {
+ return Err(Error::UnsupportedTzString("DST start and end rules must be provided"))
+ }
+ };
+
+ if cursor.is_empty() {
+ return Err(Error::UnsupportedTzString("DST start and end rules must be provided"));
+ }
+
+ cursor.read_tag(b",")?;
+ let (dst_start, dst_start_time) = RuleDay::parse(&mut cursor, use_string_extensions)?;
+
+ cursor.read_tag(b",")?;
+ let (dst_end, dst_end_time) = RuleDay::parse(&mut cursor, use_string_extensions)?;
+
+ if !cursor.is_empty() {
+ return Err(Error::InvalidTzString("remaining data after parsing TZ string"));
+ }
+
+ Ok(AlternateTime::new(
+ LocalTimeType::new(-std_offset, false, std_time_zone)?,
+ LocalTimeType::new(-dst_offset, true, dst_time_zone)?,
+ dst_start,
+ dst_start_time,
+ dst_end,
+ dst_end_time,
+ )?
+ .into())
+ }
+
+ /// Find the local time type associated to the transition rule at the specified Unix time in seconds
+ pub(super) fn find_local_time_type(&self, unix_time: i64) -> Result<&LocalTimeType, Error> {
+ match self {
+ TransitionRule::Fixed(local_time_type) => Ok(local_time_type),
+ TransitionRule::Alternate(alternate_time) => {
+ alternate_time.find_local_time_type(unix_time)
+ }
+ }
+ }
+
+ /// Find the local time type associated to the transition rule at the specified Unix time in seconds
+ pub(super) fn find_local_time_type_from_local(
+ &self,
+ local_time: i64,
+ year: i32,
+ ) -> Result<crate::LocalResult<LocalTimeType>, Error> {
+ match self {
+ TransitionRule::Fixed(local_time_type) => {
+ Ok(crate::LocalResult::Single(*local_time_type))
+ }
+ TransitionRule::Alternate(alternate_time) => {
+ alternate_time.find_local_time_type_from_local(local_time, year)
+ }
+ }
+ }
+}
+
+impl From<LocalTimeType> for TransitionRule {
+ fn from(inner: LocalTimeType) -> Self {
+ TransitionRule::Fixed(inner)
+ }
+}
+
+impl From<AlternateTime> for TransitionRule {
+ fn from(inner: AlternateTime) -> Self {
+ TransitionRule::Alternate(inner)
+ }
+}
+
+/// Transition rule representing alternate local time types
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub(super) struct AlternateTime {
+ /// Local time type for standard time
+ pub(super) std: LocalTimeType,
+ /// Local time type for Daylight Saving Time
+ pub(super) dst: LocalTimeType,
+ /// Start day of Daylight Saving Time
+ dst_start: RuleDay,
+ /// Local start day time of Daylight Saving Time, in seconds
+ dst_start_time: i32,
+ /// End day of Daylight Saving Time
+ dst_end: RuleDay,
+ /// Local end day time of Daylight Saving Time, in seconds
+ dst_end_time: i32,
+}
+
+impl AlternateTime {
+ /// Construct a transition rule representing alternate local time types
+ fn new(
+ std: LocalTimeType,
+ dst: LocalTimeType,
+ dst_start: RuleDay,
+ dst_start_time: i32,
+ dst_end: RuleDay,
+ dst_end_time: i32,
+ ) -> Result<Self, Error> {
+ // Overflow is not possible
+ if !((dst_start_time as i64).abs() < SECONDS_PER_WEEK
+ && (dst_end_time as i64).abs() < SECONDS_PER_WEEK)
+ {
+ return Err(Error::TransitionRule("invalid DST start or end time"));
+ }
+
+ Ok(Self { std, dst, dst_start, dst_start_time, dst_end, dst_end_time })
+ }
+
+ /// Find the local time type associated to the alternate transition rule at the specified Unix time in seconds
+ fn find_local_time_type(&self, unix_time: i64) -> Result<&LocalTimeType, Error> {
+ // Overflow is not possible
+ let dst_start_time_in_utc = self.dst_start_time as i64 - self.std.ut_offset as i64;
+ let dst_end_time_in_utc = self.dst_end_time as i64 - self.dst.ut_offset as i64;
+
+ let current_year = match UtcDateTime::from_timespec(unix_time) {
+ Ok(dt) => dt.year,
+ Err(error) => return Err(error),
+ };
+
+ // Check if the current year is valid for the following computations
+ if !(i32::min_value() + 2 <= current_year && current_year <= i32::max_value() - 2) {
+ return Err(Error::OutOfRange("out of range date time"));
+ }
+
+ let current_year_dst_start_unix_time =
+ self.dst_start.unix_time(current_year, dst_start_time_in_utc);
+ let current_year_dst_end_unix_time =
+ self.dst_end.unix_time(current_year, dst_end_time_in_utc);
+
+ // Check DST start/end Unix times for previous/current/next years to support for transition day times outside of [0h, 24h] range
+ let is_dst =
+ match Ord::cmp(&current_year_dst_start_unix_time, &current_year_dst_end_unix_time) {
+ Ordering::Less | Ordering::Equal => {
+ if unix_time < current_year_dst_start_unix_time {
+ let previous_year_dst_end_unix_time =
+ self.dst_end.unix_time(current_year - 1, dst_end_time_in_utc);
+ if unix_time < previous_year_dst_end_unix_time {
+ let previous_year_dst_start_unix_time =
+ self.dst_start.unix_time(current_year - 1, dst_start_time_in_utc);
+ previous_year_dst_start_unix_time <= unix_time
+ } else {
+ false
+ }
+ } else if unix_time < current_year_dst_end_unix_time {
+ true
+ } else {
+ let next_year_dst_start_unix_time =
+ self.dst_start.unix_time(current_year + 1, dst_start_time_in_utc);
+ if next_year_dst_start_unix_time <= unix_time {
+ let next_year_dst_end_unix_time =
+ self.dst_end.unix_time(current_year + 1, dst_end_time_in_utc);
+ unix_time < next_year_dst_end_unix_time
+ } else {
+ false
+ }
+ }
+ }
+ Ordering::Greater => {
+ if unix_time < current_year_dst_end_unix_time {
+ let previous_year_dst_start_unix_time =
+ self.dst_start.unix_time(current_year - 1, dst_start_time_in_utc);
+ if unix_time < previous_year_dst_start_unix_time {
+ let previous_year_dst_end_unix_time =
+ self.dst_end.unix_time(current_year - 1, dst_end_time_in_utc);
+ unix_time < previous_year_dst_end_unix_time
+ } else {
+ true
+ }
+ } else if unix_time < current_year_dst_start_unix_time {
+ false
+ } else {
+ let next_year_dst_end_unix_time =
+ self.dst_end.unix_time(current_year + 1, dst_end_time_in_utc);
+ if next_year_dst_end_unix_time <= unix_time {
+ let next_year_dst_start_unix_time =
+ self.dst_start.unix_time(current_year + 1, dst_start_time_in_utc);
+ next_year_dst_start_unix_time <= unix_time
+ } else {
+ true
+ }
+ }
+ }
+ };
+
+ if is_dst {
+ Ok(&self.dst)
+ } else {
+ Ok(&self.std)
+ }
+ }
+
+ fn find_local_time_type_from_local(
+ &self,
+ local_time: i64,
+ current_year: i32,
+ ) -> Result<crate::LocalResult<LocalTimeType>, Error> {
+ // Check if the current year is valid for the following computations
+ if !(i32::min_value() + 2 <= current_year && current_year <= i32::max_value() - 2) {
+ return Err(Error::OutOfRange("out of range date time"));
+ }
+
+ let dst_start_transition_start =
+ self.dst_start.unix_time(current_year, 0) + i64::from(self.dst_start_time);
+ let dst_start_transition_end = self.dst_start.unix_time(current_year, 0)
+ + i64::from(self.dst_start_time)
+ + i64::from(self.dst.ut_offset)
+ - i64::from(self.std.ut_offset);
+
+ let dst_end_transition_start =
+ self.dst_end.unix_time(current_year, 0) + i64::from(self.dst_end_time);
+ let dst_end_transition_end = self.dst_end.unix_time(current_year, 0)
+ + i64::from(self.dst_end_time)
+ + i64::from(self.std.ut_offset)
+ - i64::from(self.dst.ut_offset);
+
+ match self.std.ut_offset.cmp(&self.dst.ut_offset) {
+ Ordering::Equal => Ok(crate::LocalResult::Single(self.std)),
+ Ordering::Less => {
+ if self.dst_start.transition_date(current_year).0
+ < self.dst_end.transition_date(current_year).0
+ {
+ // northern hemisphere
+ // For the DST END transition, the `start` happens at a later timestamp than the `end`.
+ if local_time <= dst_start_transition_start {
+ Ok(crate::LocalResult::Single(self.std))
+ } else if local_time > dst_start_transition_start
+ && local_time < dst_start_transition_end
+ {
+ Ok(crate::LocalResult::None)
+ } else if local_time >= dst_start_transition_end
+ && local_time < dst_end_transition_end
+ {
+ Ok(crate::LocalResult::Single(self.dst))
+ } else if local_time >= dst_end_transition_end
+ && local_time <= dst_end_transition_start
+ {
+ Ok(crate::LocalResult::Ambiguous(self.std, self.dst))
+ } else {
+ Ok(crate::LocalResult::Single(self.std))
+ }
+ } else {
+ // southern hemisphere regular DST
+ // For the DST END transition, the `start` happens at a later timestamp than the `end`.
+ if local_time < dst_end_transition_end {
+ Ok(crate::LocalResult::Single(self.dst))
+ } else if local_time >= dst_end_transition_end
+ && local_time <= dst_end_transition_start
+ {
+ Ok(crate::LocalResult::Ambiguous(self.std, self.dst))
+ } else if local_time > dst_end_transition_end
+ && local_time < dst_start_transition_start
+ {
+ Ok(crate::LocalResult::Single(self.std))
+ } else if local_time >= dst_start_transition_start
+ && local_time < dst_start_transition_end
+ {
+ Ok(crate::LocalResult::None)
+ } else {
+ Ok(crate::LocalResult::Single(self.dst))
+ }
+ }
+ }
+ Ordering::Greater => {
+ if self.dst_start.transition_date(current_year).0
+ < self.dst_end.transition_date(current_year).0
+ {
+ // southern hemisphere reverse DST
+ // For the DST END transition, the `start` happens at a later timestamp than the `end`.
+ if local_time < dst_start_transition_end {
+ Ok(crate::LocalResult::Single(self.std))
+ } else if local_time >= dst_start_transition_end
+ && local_time <= dst_start_transition_start
+ {
+ Ok(crate::LocalResult::Ambiguous(self.dst, self.std))
+ } else if local_time > dst_start_transition_start
+ && local_time < dst_end_transition_start
+ {
+ Ok(crate::LocalResult::Single(self.dst))
+ } else if local_time >= dst_end_transition_start
+ && local_time < dst_end_transition_end
+ {
+ Ok(crate::LocalResult::None)
+ } else {
+ Ok(crate::LocalResult::Single(self.std))
+ }
+ } else {
+ // northern hemisphere reverse DST
+ // For the DST END transition, the `start` happens at a later timestamp than the `end`.
+ if local_time <= dst_end_transition_start {
+ Ok(crate::LocalResult::Single(self.dst))
+ } else if local_time > dst_end_transition_start
+ && local_time < dst_end_transition_end
+ {
+ Ok(crate::LocalResult::None)
+ } else if local_time >= dst_end_transition_end
+ && local_time < dst_start_transition_end
+ {
+ Ok(crate::LocalResult::Single(self.std))
+ } else if local_time >= dst_start_transition_end
+ && local_time <= dst_start_transition_start
+ {
+ Ok(crate::LocalResult::Ambiguous(self.dst, self.std))
+ } else {
+ Ok(crate::LocalResult::Single(self.dst))
+ }
+ }
+ }
+ }
+ }
+}
+
+/// Parse time zone name
+fn parse_name<'a>(cursor: &mut Cursor<'a>) -> Result<&'a [u8], Error> {
+ match cursor.peek() {
+ Some(b'<') => {}
+ _ => return Ok(cursor.read_while(u8::is_ascii_alphabetic)?),
+ }
+
+ cursor.read_exact(1)?;
+ let unquoted = cursor.read_until(|&x| x == b'>')?;
+ cursor.read_exact(1)?;
+ Ok(unquoted)
+}
+
+/// Parse time zone offset
+fn parse_offset(cursor: &mut Cursor) -> Result<i32, Error> {
+ let (sign, hour, minute, second) = parse_signed_hhmmss(cursor)?;
+
+ if !(0..=24).contains(&hour) {
+ return Err(Error::InvalidTzString("invalid offset hour"));
+ }
+ if !(0..=59).contains(&minute) {
+ return Err(Error::InvalidTzString("invalid offset minute"));
+ }
+ if !(0..=59).contains(&second) {
+ return Err(Error::InvalidTzString("invalid offset second"));
+ }
+
+ Ok(sign * (hour * 3600 + minute * 60 + second))
+}
+
+/// Parse transition rule time
+fn parse_rule_time(cursor: &mut Cursor) -> Result<i32, Error> {
+ let (hour, minute, second) = parse_hhmmss(cursor)?;
+
+ if !(0..=24).contains(&hour) {
+ return Err(Error::InvalidTzString("invalid day time hour"));
+ }
+ if !(0..=59).contains(&minute) {
+ return Err(Error::InvalidTzString("invalid day time minute"));
+ }
+ if !(0..=59).contains(&second) {
+ return Err(Error::InvalidTzString("invalid day time second"));
+ }
+
+ Ok(hour * 3600 + minute * 60 + second)
+}
+
+/// Parse transition rule time with TZ string extensions
+fn parse_rule_time_extended(cursor: &mut Cursor) -> Result<i32, Error> {
+ let (sign, hour, minute, second) = parse_signed_hhmmss(cursor)?;
+
+ if !(-167..=167).contains(&hour) {
+ return Err(Error::InvalidTzString("invalid day time hour"));
+ }
+ if !(0..=59).contains(&minute) {
+ return Err(Error::InvalidTzString("invalid day time minute"));
+ }
+ if !(0..=59).contains(&second) {
+ return Err(Error::InvalidTzString("invalid day time second"));
+ }
+
+ Ok(sign * (hour * 3600 + minute * 60 + second))
+}
+
+/// Parse hours, minutes and seconds
+fn parse_hhmmss(cursor: &mut Cursor) -> Result<(i32, i32, i32), Error> {
+ let hour = cursor.read_int()?;
+
+ let mut minute = 0;
+ let mut second = 0;
+
+ if cursor.read_optional_tag(b":")? {
+ minute = cursor.read_int()?;
+
+ if cursor.read_optional_tag(b":")? {
+ second = cursor.read_int()?;
+ }
+ }
+
+ Ok((hour, minute, second))
+}
+
+/// Parse signed hours, minutes and seconds
+fn parse_signed_hhmmss(cursor: &mut Cursor) -> Result<(i32, i32, i32, i32), Error> {
+ let mut sign = 1;
+ if let Some(&c) = cursor.peek() {
+ if c == b'+' || c == b'-' {
+ cursor.read_exact(1)?;
+ if c == b'-' {
+ sign = -1;
+ }
+ }
+ }
+
+ let (hour, minute, second) = parse_hhmmss(cursor)?;
+ Ok((sign, hour, minute, second))
+}
+
+/// Transition rule day
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+enum RuleDay {
+ /// Julian day in `[1, 365]`, without taking occasional Feb 29 into account, which is not referenceable
+ Julian1WithoutLeap(u16),
+ /// Zero-based Julian day in `[0, 365]`, taking occasional Feb 29 into account
+ Julian0WithLeap(u16),
+ /// Day represented by a month, a month week and a week day
+ MonthWeekday {
+ /// Month in `[1, 12]`
+ month: u8,
+ /// Week of the month in `[1, 5]`, with `5` representing the last week of the month
+ week: u8,
+ /// Day of the week in `[0, 6]` from Sunday
+ week_day: u8,
+ },
+}
+
+impl RuleDay {
+ /// Parse transition rule
+ fn parse(cursor: &mut Cursor, use_string_extensions: bool) -> Result<(Self, i32), Error> {
+ let date = match cursor.peek() {
+ Some(b'M') => {
+ cursor.read_exact(1)?;
+ let month = cursor.read_int()?;
+ cursor.read_tag(b".")?;
+ let week = cursor.read_int()?;
+ cursor.read_tag(b".")?;
+ let week_day = cursor.read_int()?;
+ RuleDay::month_weekday(month, week, week_day)?
+ }
+ Some(b'J') => {
+ cursor.read_exact(1)?;
+ RuleDay::julian_1(cursor.read_int()?)?
+ }
+ _ => RuleDay::julian_0(cursor.read_int()?)?,
+ };
+
+ Ok((
+ date,
+ match (cursor.read_optional_tag(b"/")?, use_string_extensions) {
+ (false, _) => 2 * 3600,
+ (true, true) => parse_rule_time_extended(cursor)?,
+ (true, false) => parse_rule_time(cursor)?,
+ },
+ ))
+ }
+
+ /// Construct a transition rule day represented by a Julian day in `[1, 365]`, without taking occasional Feb 29 into account, which is not referenceable
+ fn julian_1(julian_day_1: u16) -> Result<Self, Error> {
+ if !(1..=365).contains(&julian_day_1) {
+ return Err(Error::TransitionRule("invalid rule day julian day"));
+ }
+
+ Ok(RuleDay::Julian1WithoutLeap(julian_day_1))
+ }
+
+ /// Construct a transition rule day represented by a zero-based Julian day in `[0, 365]`, taking occasional Feb 29 into account
+ fn julian_0(julian_day_0: u16) -> Result<Self, Error> {
+ if julian_day_0 > 365 {
+ return Err(Error::TransitionRule("invalid rule day julian day"));
+ }
+
+ Ok(RuleDay::Julian0WithLeap(julian_day_0))
+ }
+
+ /// Construct a transition rule day represented by a month, a month week and a week day
+ fn month_weekday(month: u8, week: u8, week_day: u8) -> Result<Self, Error> {
+ if !(1..=12).contains(&month) {
+ return Err(Error::TransitionRule("invalid rule day month"));
+ }
+
+ if !(1..=5).contains(&week) {
+ return Err(Error::TransitionRule("invalid rule day week"));
+ }
+
+ if week_day > 6 {
+ return Err(Error::TransitionRule("invalid rule day week day"));
+ }
+
+ Ok(RuleDay::MonthWeekday { month, week, week_day })
+ }
+
+ /// Get the transition date for the provided year
+ ///
+ /// ## Outputs
+ ///
+ /// * `month`: Month in `[1, 12]`
+ /// * `month_day`: Day of the month in `[1, 31]`
+ fn transition_date(&self, year: i32) -> (usize, i64) {
+ match *self {
+ RuleDay::Julian1WithoutLeap(year_day) => {
+ let year_day = year_day as i64;
+
+ let month = match CUMUL_DAY_IN_MONTHS_NORMAL_YEAR.binary_search(&(year_day - 1)) {
+ Ok(x) => x + 1,
+ Err(x) => x,
+ };
+
+ let month_day = year_day - CUMUL_DAY_IN_MONTHS_NORMAL_YEAR[month - 1];
+
+ (month, month_day)
+ }
+ RuleDay::Julian0WithLeap(year_day) => {
+ let leap = is_leap_year(year) as i64;
+
+ let cumul_day_in_months = [
+ 0,
+ 31,
+ 59 + leap,
+ 90 + leap,
+ 120 + leap,
+ 151 + leap,
+ 181 + leap,
+ 212 + leap,
+ 243 + leap,
+ 273 + leap,
+ 304 + leap,
+ 334 + leap,
+ ];
+
+ let year_day = year_day as i64;
+
+ let month = match cumul_day_in_months.binary_search(&year_day) {
+ Ok(x) => x + 1,
+ Err(x) => x,
+ };
+
+ let month_day = 1 + year_day - cumul_day_in_months[month - 1];
+
+ (month, month_day)
+ }
+ RuleDay::MonthWeekday { month: rule_month, week, week_day } => {
+ let leap = is_leap_year(year) as i64;
+
+ let month = rule_month as usize;
+
+ let mut day_in_month = DAY_IN_MONTHS_NORMAL_YEAR[month - 1];
+ if month == 2 {
+ day_in_month += leap;
+ }
+
+ let week_day_of_first_month_day =
+ rem_euclid(4 + days_since_unix_epoch(year, month, 1), DAYS_PER_WEEK);
+ let first_week_day_occurence_in_month =
+ 1 + rem_euclid(week_day as i64 - week_day_of_first_month_day, DAYS_PER_WEEK);
+
+ let mut month_day =
+ first_week_day_occurence_in_month + (week as i64 - 1) * DAYS_PER_WEEK;
+ if month_day > day_in_month {
+ month_day -= DAYS_PER_WEEK
+ }
+
+ (month, month_day)
+ }
+ }
+ }
+
+ /// Returns the UTC Unix time in seconds associated to the transition date for the provided year
+ fn unix_time(&self, year: i32, day_time_in_utc: i64) -> i64 {
+ let (month, month_day) = self.transition_date(year);
+ days_since_unix_epoch(year, month, month_day) * SECONDS_PER_DAY + day_time_in_utc
+ }
+}
+
+/// UTC date time exprimed in the [proleptic gregorian calendar](https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar)
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
+pub(crate) struct UtcDateTime {
+ /// Year
+ pub(crate) year: i32,
+ /// Month in `[1, 12]`
+ pub(crate) month: u8,
+ /// Day of the month in `[1, 31]`
+ pub(crate) month_day: u8,
+ /// Hours since midnight in `[0, 23]`
+ pub(crate) hour: u8,
+ /// Minutes in `[0, 59]`
+ pub(crate) minute: u8,
+ /// Seconds in `[0, 60]`, with a possible leap second
+ pub(crate) second: u8,
+}
+
+impl UtcDateTime {
+ /// Construct a UTC date time from a Unix time in seconds and nanoseconds
+ pub(crate) fn from_timespec(unix_time: i64) -> Result<Self, Error> {
+ let seconds = match unix_time.checked_sub(UNIX_OFFSET_SECS) {
+ Some(seconds) => seconds,
+ None => return Err(Error::OutOfRange("out of range operation")),
+ };
+
+ let mut remaining_days = seconds / SECONDS_PER_DAY;
+ let mut remaining_seconds = seconds % SECONDS_PER_DAY;
+ if remaining_seconds < 0 {
+ remaining_seconds += SECONDS_PER_DAY;
+ remaining_days -= 1;
+ }
+
+ let mut cycles_400_years = remaining_days / DAYS_PER_400_YEARS;
+ remaining_days %= DAYS_PER_400_YEARS;
+ if remaining_days < 0 {
+ remaining_days += DAYS_PER_400_YEARS;
+ cycles_400_years -= 1;
+ }
+
+ let cycles_100_years = Ord::min(remaining_days / DAYS_PER_100_YEARS, 3);
+ remaining_days -= cycles_100_years * DAYS_PER_100_YEARS;
+
+ let cycles_4_years = Ord::min(remaining_days / DAYS_PER_4_YEARS, 24);
+ remaining_days -= cycles_4_years * DAYS_PER_4_YEARS;
+
+ let remaining_years = Ord::min(remaining_days / DAYS_PER_NORMAL_YEAR, 3);
+ remaining_days -= remaining_years * DAYS_PER_NORMAL_YEAR;
+
+ let mut year = OFFSET_YEAR
+ + remaining_years
+ + cycles_4_years * 4
+ + cycles_100_years * 100
+ + cycles_400_years * 400;
+
+ let mut month = 0;
+ while month < DAY_IN_MONTHS_LEAP_YEAR_FROM_MARCH.len() {
+ let days = DAY_IN_MONTHS_LEAP_YEAR_FROM_MARCH[month];
+ if remaining_days < days {
+ break;
+ }
+ remaining_days -= days;
+ month += 1;
+ }
+ month += 2;
+
+ if month >= MONTHS_PER_YEAR as usize {
+ month -= MONTHS_PER_YEAR as usize;
+ year += 1;
+ }
+ month += 1;
+
+ let month_day = 1 + remaining_days;
+
+ let hour = remaining_seconds / SECONDS_PER_HOUR;
+ let minute = (remaining_seconds / SECONDS_PER_MINUTE) % MINUTES_PER_HOUR;
+ let second = remaining_seconds % SECONDS_PER_MINUTE;
+
+ let year = match year >= i32::min_value() as i64 && year <= i32::max_value() as i64 {
+ true => year as i32,
+ false => return Err(Error::OutOfRange("i64 is out of range for i32")),
+ };
+
+ Ok(Self {
+ year,
+ month: month as u8,
+ month_day: month_day as u8,
+ hour: hour as u8,
+ minute: minute as u8,
+ second: second as u8,
+ })
+ }
+}
+
+/// Number of nanoseconds in one second
+const NANOSECONDS_PER_SECOND: u32 = 1_000_000_000;
+/// Number of seconds in one minute
+const SECONDS_PER_MINUTE: i64 = 60;
+/// Number of seconds in one hour
+const SECONDS_PER_HOUR: i64 = 3600;
+/// Number of minutes in one hour
+const MINUTES_PER_HOUR: i64 = 60;
+/// Number of months in one year
+const MONTHS_PER_YEAR: i64 = 12;
+/// Number of days in a normal year
+const DAYS_PER_NORMAL_YEAR: i64 = 365;
+/// Number of days in 4 years (including 1 leap year)
+const DAYS_PER_4_YEARS: i64 = DAYS_PER_NORMAL_YEAR * 4 + 1;
+/// Number of days in 100 years (including 24 leap years)
+const DAYS_PER_100_YEARS: i64 = DAYS_PER_NORMAL_YEAR * 100 + 24;
+/// Number of days in 400 years (including 97 leap years)
+const DAYS_PER_400_YEARS: i64 = DAYS_PER_NORMAL_YEAR * 400 + 97;
+/// Unix time at `2000-03-01T00:00:00Z` (Wednesday)
+const UNIX_OFFSET_SECS: i64 = 951868800;
+/// Offset year
+const OFFSET_YEAR: i64 = 2000;
+/// Month days in a leap year from March
+const DAY_IN_MONTHS_LEAP_YEAR_FROM_MARCH: [i64; 12] =
+ [31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 29];
+
+/// Compute the number of days since Unix epoch (`1970-01-01T00:00:00Z`).
+///
+/// ## Inputs
+///
+/// * `year`: Year
+/// * `month`: Month in `[1, 12]`
+/// * `month_day`: Day of the month in `[1, 31]`
+pub(crate) fn days_since_unix_epoch(year: i32, month: usize, month_day: i64) -> i64 {
+ let is_leap_year = is_leap_year(year);
+
+ let year = year as i64;
+
+ let mut result = (year - 1970) * 365;
+
+ if year >= 1970 {
+ result += (year - 1968) / 4;
+ result -= (year - 1900) / 100;
+ result += (year - 1600) / 400;
+
+ if is_leap_year && month < 3 {
+ result -= 1;
+ }
+ } else {
+ result += (year - 1972) / 4;
+ result -= (year - 2000) / 100;
+ result += (year - 2000) / 400;
+
+ if is_leap_year && month >= 3 {
+ result += 1;
+ }
+ }
+
+ result += CUMUL_DAY_IN_MONTHS_NORMAL_YEAR[month - 1] + month_day - 1;
+
+ result
+}
+
+/// Check if a year is a leap year
+pub(crate) fn is_leap_year(year: i32) -> bool {
+ year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)
+}
+
+#[cfg(test)]
+mod tests {
+ use super::super::timezone::Transition;
+ use super::super::{Error, TimeZone};
+ use super::{AlternateTime, LocalTimeType, RuleDay, TransitionRule};
+ use crate::matches;
+
+ #[test]
+ fn test_quoted() -> Result<(), Error> {
+ let transition_rule = TransitionRule::from_tz_string(b"<-03>+3<+03>-3,J1,J365", false)?;
+ assert_eq!(
+ transition_rule,
+ AlternateTime::new(
+ LocalTimeType::new(-10800, false, Some(b"-03"))?,
+ LocalTimeType::new(10800, true, Some(b"+03"))?,
+ RuleDay::julian_1(1)?,
+ 7200,
+ RuleDay::julian_1(365)?,
+ 7200,
+ )?
+ .into()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn test_full() -> Result<(), Error> {
+ let tz_string = b"NZST-12:00:00NZDT-13:00:00,M10.1.0/02:00:00,M3.3.0/02:00:00";
+ let transition_rule = TransitionRule::from_tz_string(tz_string, false)?;
+ assert_eq!(
+ transition_rule,
+ AlternateTime::new(
+ LocalTimeType::new(43200, false, Some(b"NZST"))?,
+ LocalTimeType::new(46800, true, Some(b"NZDT"))?,
+ RuleDay::month_weekday(10, 1, 0)?,
+ 7200,
+ RuleDay::month_weekday(3, 3, 0)?,
+ 7200,
+ )?
+ .into()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn test_negative_dst() -> Result<(), Error> {
+ let tz_string = b"IST-1GMT0,M10.5.0,M3.5.0/1";
+ let transition_rule = TransitionRule::from_tz_string(tz_string, false)?;
+ assert_eq!(
+ transition_rule,
+ AlternateTime::new(
+ LocalTimeType::new(3600, false, Some(b"IST"))?,
+ LocalTimeType::new(0, true, Some(b"GMT"))?,
+ RuleDay::month_weekday(10, 5, 0)?,
+ 7200,
+ RuleDay::month_weekday(3, 5, 0)?,
+ 3600,
+ )?
+ .into()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn test_negative_hour() -> Result<(), Error> {
+ let tz_string = b"<-03>3<-02>,M3.5.0/-2,M10.5.0/-1";
+ assert!(TransitionRule::from_tz_string(tz_string, false).is_err());
+
+ assert_eq!(
+ TransitionRule::from_tz_string(tz_string, true)?,
+ AlternateTime::new(
+ LocalTimeType::new(-10800, false, Some(b"-03"))?,
+ LocalTimeType::new(-7200, true, Some(b"-02"))?,
+ RuleDay::month_weekday(3, 5, 0)?,
+ -7200,
+ RuleDay::month_weekday(10, 5, 0)?,
+ -3600,
+ )?
+ .into()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn test_all_year_dst() -> Result<(), Error> {
+ let tz_string = b"EST5EDT,0/0,J365/25";
+ assert!(TransitionRule::from_tz_string(tz_string, false).is_err());
+
+ assert_eq!(
+ TransitionRule::from_tz_string(tz_string, true)?,
+ AlternateTime::new(
+ LocalTimeType::new(-18000, false, Some(b"EST"))?,
+ LocalTimeType::new(-14400, true, Some(b"EDT"))?,
+ RuleDay::julian_0(0)?,
+ 0,
+ RuleDay::julian_1(365)?,
+ 90000,
+ )?
+ .into()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn test_v3_file() -> Result<(), Error> {
+ let bytes = b"TZif3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\x04\0\0\x1c\x20\0\0IST\0TZif3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\x01\0\0\0\0\0\0\0\x01\0\0\0\x01\0\0\0\x04\0\0\0\0\x7f\xe8\x17\x80\0\0\0\x1c\x20\0\0IST\0\x01\x01\x0aIST-2IDT,M3.4.4/26,M10.5.0\x0a";
+
+ let time_zone = TimeZone::from_tz_data(bytes)?;
+
+ let time_zone_result = TimeZone::new(
+ vec![Transition::new(2145916800, 0)],
+ vec![LocalTimeType::new(7200, false, Some(b"IST"))?],
+ Vec::new(),
+ Some(TransitionRule::from(AlternateTime::new(
+ LocalTimeType::new(7200, false, Some(b"IST"))?,
+ LocalTimeType::new(10800, true, Some(b"IDT"))?,
+ RuleDay::month_weekday(3, 4, 4)?,
+ 93600,
+ RuleDay::month_weekday(10, 5, 0)?,
+ 7200,
+ )?)),
+ )?;
+
+ assert_eq!(time_zone, time_zone_result);
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_rule_day() -> Result<(), Error> {
+ let rule_day_j1 = RuleDay::julian_1(60)?;
+ assert_eq!(rule_day_j1.transition_date(2000), (3, 1));
+ assert_eq!(rule_day_j1.transition_date(2001), (3, 1));
+ assert_eq!(rule_day_j1.unix_time(2000, 43200), 951912000);
+
+ let rule_day_j0 = RuleDay::julian_0(59)?;
+ assert_eq!(rule_day_j0.transition_date(2000), (2, 29));
+ assert_eq!(rule_day_j0.transition_date(2001), (3, 1));
+ assert_eq!(rule_day_j0.unix_time(2000, 43200), 951825600);
+
+ let rule_day_mwd = RuleDay::month_weekday(2, 5, 2)?;
+ assert_eq!(rule_day_mwd.transition_date(2000), (2, 29));
+ assert_eq!(rule_day_mwd.transition_date(2001), (2, 27));
+ assert_eq!(rule_day_mwd.unix_time(2000, 43200), 951825600);
+ assert_eq!(rule_day_mwd.unix_time(2001, 43200), 983275200);
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_transition_rule() -> Result<(), Error> {
+ let transition_rule_fixed = TransitionRule::from(LocalTimeType::new(-36000, false, None)?);
+ assert_eq!(transition_rule_fixed.find_local_time_type(0)?.offset(), -36000);
+
+ let transition_rule_dst = TransitionRule::from(AlternateTime::new(
+ LocalTimeType::new(43200, false, Some(b"NZST"))?,
+ LocalTimeType::new(46800, true, Some(b"NZDT"))?,
+ RuleDay::month_weekday(10, 1, 0)?,
+ 7200,
+ RuleDay::month_weekday(3, 3, 0)?,
+ 7200,
+ )?);
+
+ assert_eq!(transition_rule_dst.find_local_time_type(953384399)?.offset(), 46800);
+ assert_eq!(transition_rule_dst.find_local_time_type(953384400)?.offset(), 43200);
+ assert_eq!(transition_rule_dst.find_local_time_type(970322399)?.offset(), 43200);
+ assert_eq!(transition_rule_dst.find_local_time_type(970322400)?.offset(), 46800);
+
+ let transition_rule_negative_dst = TransitionRule::from(AlternateTime::new(
+ LocalTimeType::new(3600, false, Some(b"IST"))?,
+ LocalTimeType::new(0, true, Some(b"GMT"))?,
+ RuleDay::month_weekday(10, 5, 0)?,
+ 7200,
+ RuleDay::month_weekday(3, 5, 0)?,
+ 3600,
+ )?);
+
+ assert_eq!(transition_rule_negative_dst.find_local_time_type(954032399)?.offset(), 0);
+ assert_eq!(transition_rule_negative_dst.find_local_time_type(954032400)?.offset(), 3600);
+ assert_eq!(transition_rule_negative_dst.find_local_time_type(972781199)?.offset(), 3600);
+ assert_eq!(transition_rule_negative_dst.find_local_time_type(972781200)?.offset(), 0);
+
+ let transition_rule_negative_time_1 = TransitionRule::from(AlternateTime::new(
+ LocalTimeType::new(0, false, None)?,
+ LocalTimeType::new(0, true, None)?,
+ RuleDay::julian_0(100)?,
+ 0,
+ RuleDay::julian_0(101)?,
+ -86500,
+ )?);
+
+ assert!(transition_rule_negative_time_1.find_local_time_type(8639899)?.is_dst());
+ assert!(!transition_rule_negative_time_1.find_local_time_type(8639900)?.is_dst());
+ assert!(!transition_rule_negative_time_1.find_local_time_type(8639999)?.is_dst());
+ assert!(transition_rule_negative_time_1.find_local_time_type(8640000)?.is_dst());
+
+ let transition_rule_negative_time_2 = TransitionRule::from(AlternateTime::new(
+ LocalTimeType::new(-10800, false, Some(b"-03"))?,
+ LocalTimeType::new(-7200, true, Some(b"-02"))?,
+ RuleDay::month_weekday(3, 5, 0)?,
+ -7200,
+ RuleDay::month_weekday(10, 5, 0)?,
+ -3600,
+ )?);
+
+ assert_eq!(
+ transition_rule_negative_time_2.find_local_time_type(954032399)?.offset(),
+ -10800
+ );
+ assert_eq!(
+ transition_rule_negative_time_2.find_local_time_type(954032400)?.offset(),
+ -7200
+ );
+ assert_eq!(
+ transition_rule_negative_time_2.find_local_time_type(972781199)?.offset(),
+ -7200
+ );
+ assert_eq!(
+ transition_rule_negative_time_2.find_local_time_type(972781200)?.offset(),
+ -10800
+ );
+
+ let transition_rule_all_year_dst = TransitionRule::from(AlternateTime::new(
+ LocalTimeType::new(-18000, false, Some(b"EST"))?,
+ LocalTimeType::new(-14400, true, Some(b"EDT"))?,
+ RuleDay::julian_0(0)?,
+ 0,
+ RuleDay::julian_1(365)?,
+ 90000,
+ )?);
+
+ assert_eq!(transition_rule_all_year_dst.find_local_time_type(946702799)?.offset(), -14400);
+ assert_eq!(transition_rule_all_year_dst.find_local_time_type(946702800)?.offset(), -14400);
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_transition_rule_overflow() -> Result<(), Error> {
+ let transition_rule_1 = TransitionRule::from(AlternateTime::new(
+ LocalTimeType::new(-1, false, None)?,
+ LocalTimeType::new(-1, true, None)?,
+ RuleDay::julian_1(365)?,
+ 0,
+ RuleDay::julian_1(1)?,
+ 0,
+ )?);
+
+ let transition_rule_2 = TransitionRule::from(AlternateTime::new(
+ LocalTimeType::new(1, false, None)?,
+ LocalTimeType::new(1, true, None)?,
+ RuleDay::julian_1(365)?,
+ 0,
+ RuleDay::julian_1(1)?,
+ 0,
+ )?);
+
+ let min_unix_time = -67768100567971200;
+ let max_unix_time = 67767976233532799;
+
+ assert!(matches!(
+ transition_rule_1.find_local_time_type(min_unix_time),
+ Err(Error::OutOfRange(_))
+ ));
+ assert!(matches!(
+ transition_rule_2.find_local_time_type(max_unix_time),
+ Err(Error::OutOfRange(_))
+ ));
+
+ Ok(())
+ }
+}
diff --git a/vendor/chrono/src/offset/local/tz_info/timezone.rs b/vendor/chrono/src/offset/local/tz_info/timezone.rs
new file mode 100644
index 000000000..8572825a8
--- /dev/null
+++ b/vendor/chrono/src/offset/local/tz_info/timezone.rs
@@ -0,0 +1,904 @@
+//! Types related to a time zone.
+
+use std::fs::{self, File};
+use std::io::{self, Read};
+use std::path::{Path, PathBuf};
+use std::{cmp::Ordering, fmt, str};
+
+use super::rule::{AlternateTime, TransitionRule};
+use super::{parser, Error, DAYS_PER_WEEK, SECONDS_PER_DAY};
+
+/// Time zone
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub(crate) struct TimeZone {
+ /// List of transitions
+ transitions: Vec<Transition>,
+ /// List of local time types (cannot be empty)
+ local_time_types: Vec<LocalTimeType>,
+ /// List of leap seconds
+ leap_seconds: Vec<LeapSecond>,
+ /// Extra transition rule applicable after the last transition
+ extra_rule: Option<TransitionRule>,
+}
+
+impl TimeZone {
+ /// Returns local time zone.
+ ///
+ /// This method in not supported on non-UNIX platforms, and returns the UTC time zone instead.
+ ///
+ pub(crate) fn local(env_tz: Option<&str>) -> Result<Self, Error> {
+ match env_tz {
+ Some(tz) => Self::from_posix_tz(tz),
+ None => Self::from_posix_tz("localtime"),
+ }
+ }
+
+ /// Construct a time zone from a POSIX TZ string, as described in [the POSIX documentation of the `TZ` environment variable](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html).
+ fn from_posix_tz(tz_string: &str) -> Result<Self, Error> {
+ if tz_string.is_empty() {
+ return Err(Error::InvalidTzString("empty TZ string"));
+ }
+
+ if tz_string == "localtime" {
+ return Self::from_tz_data(&fs::read("/etc/localtime")?);
+ }
+
+ let mut chars = tz_string.chars();
+ if chars.next() == Some(':') {
+ return Self::from_file(&mut find_tz_file(chars.as_str())?);
+ }
+
+ if let Ok(mut file) = find_tz_file(tz_string) {
+ return Self::from_file(&mut file);
+ }
+
+ // TZ string extensions are not allowed
+ let tz_string = tz_string.trim_matches(|c: char| c.is_ascii_whitespace());
+ let rule = TransitionRule::from_tz_string(tz_string.as_bytes(), false)?;
+ Self::new(
+ vec![],
+ match rule {
+ TransitionRule::Fixed(local_time_type) => vec![local_time_type],
+ TransitionRule::Alternate(AlternateTime { std, dst, .. }) => vec![std, dst],
+ },
+ vec![],
+ Some(rule),
+ )
+ }
+
+ /// Construct a time zone
+ pub(super) fn new(
+ transitions: Vec<Transition>,
+ local_time_types: Vec<LocalTimeType>,
+ leap_seconds: Vec<LeapSecond>,
+ extra_rule: Option<TransitionRule>,
+ ) -> Result<Self, Error> {
+ let new = Self { transitions, local_time_types, leap_seconds, extra_rule };
+ new.as_ref().validate()?;
+ Ok(new)
+ }
+
+ /// Construct a time zone from the contents of a time zone file
+ fn from_file(file: &mut File) -> Result<Self, Error> {
+ let mut bytes = Vec::new();
+ file.read_to_end(&mut bytes)?;
+ Self::from_tz_data(&bytes)
+ }
+
+ /// Construct a time zone from the contents of a time zone file
+ ///
+ /// Parse TZif data as described in [RFC 8536](https://datatracker.ietf.org/doc/html/rfc8536).
+ pub(crate) fn from_tz_data(bytes: &[u8]) -> Result<Self, Error> {
+ parser::parse(bytes)
+ }
+
+ /// Construct a time zone with the specified UTC offset in seconds
+ fn fixed(ut_offset: i32) -> Result<Self, Error> {
+ Ok(Self {
+ transitions: Vec::new(),
+ local_time_types: vec![LocalTimeType::with_offset(ut_offset)?],
+ leap_seconds: Vec::new(),
+ extra_rule: None,
+ })
+ }
+
+ /// Construct the time zone associated to UTC
+ pub(crate) fn utc() -> Self {
+ Self {
+ transitions: Vec::new(),
+ local_time_types: vec![LocalTimeType::UTC],
+ leap_seconds: Vec::new(),
+ extra_rule: None,
+ }
+ }
+
+ /// Find the local time type associated to the time zone at the specified Unix time in seconds
+ pub(crate) fn find_local_time_type(&self, unix_time: i64) -> Result<&LocalTimeType, Error> {
+ self.as_ref().find_local_time_type(unix_time)
+ }
+
+ // should we pass NaiveDateTime all the way through to this fn?
+ pub(crate) fn find_local_time_type_from_local(
+ &self,
+ local_time: i64,
+ year: i32,
+ ) -> Result<crate::LocalResult<LocalTimeType>, Error> {
+ self.as_ref().find_local_time_type_from_local(local_time, year)
+ }
+
+ /// Returns a reference to the time zone
+ fn as_ref(&self) -> TimeZoneRef {
+ TimeZoneRef {
+ transitions: &self.transitions,
+ local_time_types: &self.local_time_types,
+ leap_seconds: &self.leap_seconds,
+ extra_rule: &self.extra_rule,
+ }
+ }
+}
+
+/// Reference to a time zone
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub(crate) struct TimeZoneRef<'a> {
+ /// List of transitions
+ transitions: &'a [Transition],
+ /// List of local time types (cannot be empty)
+ local_time_types: &'a [LocalTimeType],
+ /// List of leap seconds
+ leap_seconds: &'a [LeapSecond],
+ /// Extra transition rule applicable after the last transition
+ extra_rule: &'a Option<TransitionRule>,
+}
+
+impl<'a> TimeZoneRef<'a> {
+ /// Find the local time type associated to the time zone at the specified Unix time in seconds
+ pub(crate) fn find_local_time_type(&self, unix_time: i64) -> Result<&'a LocalTimeType, Error> {
+ let extra_rule = match self.transitions.last() {
+ None => match self.extra_rule {
+ Some(extra_rule) => extra_rule,
+ None => return Ok(&self.local_time_types[0]),
+ },
+ Some(last_transition) => {
+ let unix_leap_time = match self.unix_time_to_unix_leap_time(unix_time) {
+ Ok(unix_leap_time) => unix_leap_time,
+ Err(Error::OutOfRange(error)) => return Err(Error::FindLocalTimeType(error)),
+ Err(err) => return Err(err),
+ };
+
+ if unix_leap_time >= last_transition.unix_leap_time {
+ match self.extra_rule {
+ Some(extra_rule) => extra_rule,
+ None => {
+ return Err(Error::FindLocalTimeType(
+ "no local time type is available for the specified timestamp",
+ ))
+ }
+ }
+ } else {
+ let index = match self
+ .transitions
+ .binary_search_by_key(&unix_leap_time, Transition::unix_leap_time)
+ {
+ Ok(x) => x + 1,
+ Err(x) => x,
+ };
+
+ let local_time_type_index = if index > 0 {
+ self.transitions[index - 1].local_time_type_index
+ } else {
+ 0
+ };
+ return Ok(&self.local_time_types[local_time_type_index]);
+ }
+ }
+ };
+
+ match extra_rule.find_local_time_type(unix_time) {
+ Ok(local_time_type) => Ok(local_time_type),
+ Err(Error::OutOfRange(error)) => Err(Error::FindLocalTimeType(error)),
+ err => err,
+ }
+ }
+
+ pub(crate) fn find_local_time_type_from_local(
+ &self,
+ local_time: i64,
+ year: i32,
+ ) -> Result<crate::LocalResult<LocalTimeType>, Error> {
+ // #TODO: this is wrong as we need 'local_time_to_local_leap_time ?
+ // but ... does the local time even include leap seconds ??
+ // let unix_leap_time = match self.unix_time_to_unix_leap_time(local_time) {
+ // Ok(unix_leap_time) => unix_leap_time,
+ // Err(Error::OutOfRange(error)) => return Err(Error::FindLocalTimeType(error)),
+ // Err(err) => return Err(err),
+ // };
+ let local_leap_time = local_time;
+
+ // if we have at least one transition,
+ // we must check _all_ of them, incase of any Overlapping (LocalResult::Ambiguous) or Skipping (LocalResult::None) transitions
+ if !self.transitions.is_empty() {
+ let mut prev = Some(self.local_time_types[0]);
+
+ for transition in self.transitions {
+ let after_ltt = self.local_time_types[transition.local_time_type_index];
+
+ // the end and start here refers to where the time starts prior to the transition
+ // and where it ends up after. not the temporal relationship.
+ let transition_end = transition.unix_leap_time + i64::from(after_ltt.ut_offset);
+ let transition_start =
+ transition.unix_leap_time + i64::from(prev.unwrap().ut_offset);
+
+ match transition_start.cmp(&transition_end) {
+ Ordering::Greater => {
+ // bakwards transition, eg from DST to regular
+ // this means a given local time could have one of two possible offsets
+ if local_leap_time < transition_end {
+ return Ok(crate::LocalResult::Single(prev.unwrap()));
+ } else if local_leap_time >= transition_end
+ && local_leap_time <= transition_start
+ {
+ if prev.unwrap().ut_offset < after_ltt.ut_offset {
+ return Ok(crate::LocalResult::Ambiguous(prev.unwrap(), after_ltt));
+ } else {
+ return Ok(crate::LocalResult::Ambiguous(after_ltt, prev.unwrap()));
+ }
+ }
+ }
+ Ordering::Equal => {
+ // should this ever happen? presumably we have to handle it anyway.
+ if local_leap_time < transition_start {
+ return Ok(crate::LocalResult::Single(prev.unwrap()));
+ } else if local_leap_time == transition_end {
+ if prev.unwrap().ut_offset < after_ltt.ut_offset {
+ return Ok(crate::LocalResult::Ambiguous(prev.unwrap(), after_ltt));
+ } else {
+ return Ok(crate::LocalResult::Ambiguous(after_ltt, prev.unwrap()));
+ }
+ }
+ }
+ Ordering::Less => {
+ // forwards transition, eg from regular to DST
+ // this means that times that are skipped are invalid local times
+ if local_leap_time <= transition_start {
+ return Ok(crate::LocalResult::Single(prev.unwrap()));
+ } else if local_leap_time < transition_end {
+ return Ok(crate::LocalResult::None);
+ } else if local_leap_time == transition_end {
+ return Ok(crate::LocalResult::Single(after_ltt));
+ }
+ }
+ }
+
+ // try the next transition, we are fully after this one
+ prev = Some(after_ltt);
+ }
+ };
+
+ if let Some(extra_rule) = self.extra_rule {
+ match extra_rule.find_local_time_type_from_local(local_time, year) {
+ Ok(local_time_type) => Ok(local_time_type),
+ Err(Error::OutOfRange(error)) => Err(Error::FindLocalTimeType(error)),
+ err => err,
+ }
+ } else {
+ Ok(crate::LocalResult::Single(self.local_time_types[0]))
+ }
+ }
+
+ /// Check time zone inputs
+ fn validate(&self) -> Result<(), Error> {
+ // Check local time types
+ let local_time_types_size = self.local_time_types.len();
+ if local_time_types_size == 0 {
+ return Err(Error::TimeZone("list of local time types must not be empty"));
+ }
+
+ // Check transitions
+ let mut i_transition = 0;
+ while i_transition < self.transitions.len() {
+ if self.transitions[i_transition].local_time_type_index >= local_time_types_size {
+ return Err(Error::TimeZone("invalid local time type index"));
+ }
+
+ if i_transition + 1 < self.transitions.len()
+ && self.transitions[i_transition].unix_leap_time
+ >= self.transitions[i_transition + 1].unix_leap_time
+ {
+ return Err(Error::TimeZone("invalid transition"));
+ }
+
+ i_transition += 1;
+ }
+
+ // Check leap seconds
+ if !(self.leap_seconds.is_empty()
+ || self.leap_seconds[0].unix_leap_time >= 0
+ && saturating_abs(self.leap_seconds[0].correction) == 1)
+ {
+ return Err(Error::TimeZone("invalid leap second"));
+ }
+
+ let min_interval = SECONDS_PER_28_DAYS - 1;
+
+ let mut i_leap_second = 0;
+ while i_leap_second < self.leap_seconds.len() {
+ if i_leap_second + 1 < self.leap_seconds.len() {
+ let x0 = &self.leap_seconds[i_leap_second];
+ let x1 = &self.leap_seconds[i_leap_second + 1];
+
+ let diff_unix_leap_time = x1.unix_leap_time.saturating_sub(x0.unix_leap_time);
+ let abs_diff_correction =
+ saturating_abs(x1.correction.saturating_sub(x0.correction));
+
+ if !(diff_unix_leap_time >= min_interval && abs_diff_correction == 1) {
+ return Err(Error::TimeZone("invalid leap second"));
+ }
+ }
+ i_leap_second += 1;
+ }
+
+ // Check extra rule
+ let (extra_rule, last_transition) = match (&self.extra_rule, self.transitions.last()) {
+ (Some(rule), Some(trans)) => (rule, trans),
+ _ => return Ok(()),
+ };
+
+ let last_local_time_type = &self.local_time_types[last_transition.local_time_type_index];
+ let unix_time = match self.unix_leap_time_to_unix_time(last_transition.unix_leap_time) {
+ Ok(unix_time) => unix_time,
+ Err(Error::OutOfRange(error)) => return Err(Error::TimeZone(error)),
+ Err(err) => return Err(err),
+ };
+
+ let rule_local_time_type = match extra_rule.find_local_time_type(unix_time) {
+ Ok(rule_local_time_type) => rule_local_time_type,
+ Err(Error::OutOfRange(error)) => return Err(Error::TimeZone(error)),
+ Err(err) => return Err(err),
+ };
+
+ let check = last_local_time_type.ut_offset == rule_local_time_type.ut_offset
+ && last_local_time_type.is_dst == rule_local_time_type.is_dst
+ && match (&last_local_time_type.name, &rule_local_time_type.name) {
+ (Some(x), Some(y)) => x.equal(y),
+ (None, None) => true,
+ _ => false,
+ };
+
+ if !check {
+ return Err(Error::TimeZone(
+ "extra transition rule is inconsistent with the last transition",
+ ));
+ }
+
+ Ok(())
+ }
+
+ /// Convert Unix time to Unix leap time, from the list of leap seconds in a time zone
+ fn unix_time_to_unix_leap_time(&self, unix_time: i64) -> Result<i64, Error> {
+ let mut unix_leap_time = unix_time;
+
+ let mut i = 0;
+ while i < self.leap_seconds.len() {
+ let leap_second = &self.leap_seconds[i];
+
+ if unix_leap_time < leap_second.unix_leap_time {
+ break;
+ }
+
+ unix_leap_time = match unix_time.checked_add(leap_second.correction as i64) {
+ Some(unix_leap_time) => unix_leap_time,
+ None => return Err(Error::OutOfRange("out of range operation")),
+ };
+
+ i += 1;
+ }
+
+ Ok(unix_leap_time)
+ }
+
+ /// Convert Unix leap time to Unix time, from the list of leap seconds in a time zone
+ fn unix_leap_time_to_unix_time(&self, unix_leap_time: i64) -> Result<i64, Error> {
+ if unix_leap_time == i64::min_value() {
+ return Err(Error::OutOfRange("out of range operation"));
+ }
+
+ let index = match self
+ .leap_seconds
+ .binary_search_by_key(&(unix_leap_time - 1), LeapSecond::unix_leap_time)
+ {
+ Ok(x) => x + 1,
+ Err(x) => x,
+ };
+
+ let correction = if index > 0 { self.leap_seconds[index - 1].correction } else { 0 };
+
+ match unix_leap_time.checked_sub(correction as i64) {
+ Some(unix_time) => Ok(unix_time),
+ None => Err(Error::OutOfRange("out of range operation")),
+ }
+ }
+
+ /// The UTC time zone
+ const UTC: TimeZoneRef<'static> = TimeZoneRef {
+ transitions: &[],
+ local_time_types: &[LocalTimeType::UTC],
+ leap_seconds: &[],
+ extra_rule: &None,
+ };
+}
+
+/// Transition of a TZif file
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub(super) struct Transition {
+ /// Unix leap time
+ unix_leap_time: i64,
+ /// Index specifying the local time type of the transition
+ local_time_type_index: usize,
+}
+
+impl Transition {
+ /// Construct a TZif file transition
+ pub(super) const fn new(unix_leap_time: i64, local_time_type_index: usize) -> Self {
+ Self { unix_leap_time, local_time_type_index }
+ }
+
+ /// Returns Unix leap time
+ const fn unix_leap_time(&self) -> i64 {
+ self.unix_leap_time
+ }
+}
+
+/// Leap second of a TZif file
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub(super) struct LeapSecond {
+ /// Unix leap time
+ unix_leap_time: i64,
+ /// Leap second correction
+ correction: i32,
+}
+
+impl LeapSecond {
+ /// Construct a TZif file leap second
+ pub(super) const fn new(unix_leap_time: i64, correction: i32) -> Self {
+ Self { unix_leap_time, correction }
+ }
+
+ /// Returns Unix leap time
+ const fn unix_leap_time(&self) -> i64 {
+ self.unix_leap_time
+ }
+}
+
+/// ASCII-encoded fixed-capacity string, used for storing time zone names
+#[derive(Copy, Clone, Eq, PartialEq)]
+struct TimeZoneName {
+ /// Length-prefixed string buffer
+ bytes: [u8; 8],
+}
+
+impl TimeZoneName {
+ /// Construct a time zone name
+ fn new(input: &[u8]) -> Result<Self, Error> {
+ let len = input.len();
+
+ if !(3..=7).contains(&len) {
+ return Err(Error::LocalTimeType(
+ "time zone name must have between 3 and 7 characters",
+ ));
+ }
+
+ let mut bytes = [0; 8];
+ bytes[0] = input.len() as u8;
+
+ let mut i = 0;
+ while i < len {
+ let b = input[i];
+ match b {
+ b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z' | b'+' | b'-' => {}
+ _ => return Err(Error::LocalTimeType("invalid characters in time zone name")),
+ }
+
+ bytes[i + 1] = b;
+ i += 1;
+ }
+
+ Ok(Self { bytes })
+ }
+
+ /// Returns time zone name as a byte slice
+ fn as_bytes(&self) -> &[u8] {
+ match self.bytes[0] {
+ 3 => &self.bytes[1..4],
+ 4 => &self.bytes[1..5],
+ 5 => &self.bytes[1..6],
+ 6 => &self.bytes[1..7],
+ 7 => &self.bytes[1..8],
+ _ => unreachable!(),
+ }
+ }
+
+ /// Check if two time zone names are equal
+ fn equal(&self, other: &Self) -> bool {
+ self.bytes == other.bytes
+ }
+}
+
+impl AsRef<str> for TimeZoneName {
+ fn as_ref(&self) -> &str {
+ // SAFETY: ASCII is valid UTF-8
+ unsafe { str::from_utf8_unchecked(self.as_bytes()) }
+ }
+}
+
+impl fmt::Debug for TimeZoneName {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ self.as_ref().fmt(f)
+ }
+}
+
+/// Local time type associated to a time zone
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub(crate) struct LocalTimeType {
+ /// Offset from UTC in seconds
+ pub(super) ut_offset: i32,
+ /// Daylight Saving Time indicator
+ is_dst: bool,
+ /// Time zone name
+ name: Option<TimeZoneName>,
+}
+
+impl LocalTimeType {
+ /// Construct a local time type
+ pub(super) fn new(ut_offset: i32, is_dst: bool, name: Option<&[u8]>) -> Result<Self, Error> {
+ if ut_offset == i32::min_value() {
+ return Err(Error::LocalTimeType("invalid UTC offset"));
+ }
+
+ let name = match name {
+ Some(name) => TimeZoneName::new(name)?,
+ None => return Ok(Self { ut_offset, is_dst, name: None }),
+ };
+
+ Ok(Self { ut_offset, is_dst, name: Some(name) })
+ }
+
+ /// Construct a local time type with the specified UTC offset in seconds
+ pub(super) fn with_offset(ut_offset: i32) -> Result<Self, Error> {
+ if ut_offset == i32::min_value() {
+ return Err(Error::LocalTimeType("invalid UTC offset"));
+ }
+
+ Ok(Self { ut_offset, is_dst: false, name: None })
+ }
+
+ /// Returns offset from UTC in seconds
+ pub(crate) const fn offset(&self) -> i32 {
+ self.ut_offset
+ }
+
+ /// Returns daylight saving time indicator
+ pub(super) const fn is_dst(&self) -> bool {
+ self.is_dst
+ }
+
+ pub(super) const UTC: LocalTimeType = Self { ut_offset: 0, is_dst: false, name: None };
+}
+
+/// Open the TZif file corresponding to a TZ string
+fn find_tz_file(path: impl AsRef<Path>) -> Result<File, Error> {
+ // Don't check system timezone directories on non-UNIX platforms
+ #[cfg(not(unix))]
+ return Ok(File::open(path)?);
+
+ #[cfg(unix)]
+ {
+ let path = path.as_ref();
+ if path.is_absolute() {
+ return Ok(File::open(path)?);
+ }
+
+ for folder in &ZONE_INFO_DIRECTORIES {
+ if let Ok(file) = File::open(PathBuf::from(folder).join(path)) {
+ return Ok(file);
+ }
+ }
+
+ Err(Error::Io(io::ErrorKind::NotFound.into()))
+ }
+}
+
+#[inline]
+fn saturating_abs(v: i32) -> i32 {
+ if v.is_positive() {
+ v
+ } else if v == i32::min_value() {
+ i32::max_value()
+ } else {
+ -v
+ }
+}
+
+// Possible system timezone directories
+#[cfg(unix)]
+const ZONE_INFO_DIRECTORIES: [&str; 3] =
+ ["/usr/share/zoneinfo", "/share/zoneinfo", "/etc/zoneinfo"];
+
+/// Number of seconds in one week
+pub(crate) const SECONDS_PER_WEEK: i64 = SECONDS_PER_DAY * DAYS_PER_WEEK;
+/// Number of seconds in 28 days
+const SECONDS_PER_28_DAYS: i64 = SECONDS_PER_DAY * 28;
+
+#[cfg(test)]
+mod tests {
+ use super::super::Error;
+ use super::{LeapSecond, LocalTimeType, TimeZone, TimeZoneName, Transition, TransitionRule};
+ use crate::matches;
+
+ #[test]
+ fn test_no_dst() -> Result<(), Error> {
+ let tz_string = b"HST10";
+ let transition_rule = TransitionRule::from_tz_string(tz_string, false)?;
+ assert_eq!(transition_rule, LocalTimeType::new(-36000, false, Some(b"HST"))?.into());
+ Ok(())
+ }
+
+ #[test]
+ fn test_error() -> Result<(), Error> {
+ assert!(matches!(
+ TransitionRule::from_tz_string(b"IST-1GMT0", false),
+ Err(Error::UnsupportedTzString(_))
+ ));
+ assert!(matches!(
+ TransitionRule::from_tz_string(b"EET-2EEST", false),
+ Err(Error::UnsupportedTzString(_))
+ ));
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_v1_file_with_leap_seconds() -> Result<(), Error> {
+ let bytes = b"TZif\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\x01\0\0\0\x1b\0\0\0\0\0\0\0\x01\0\0\0\x04\0\0\0\0\0\0UTC\0\x04\xb2\x58\0\0\0\0\x01\x05\xa4\xec\x01\0\0\0\x02\x07\x86\x1f\x82\0\0\0\x03\x09\x67\x53\x03\0\0\0\x04\x0b\x48\x86\x84\0\0\0\x05\x0d\x2b\x0b\x85\0\0\0\x06\x0f\x0c\x3f\x06\0\0\0\x07\x10\xed\x72\x87\0\0\0\x08\x12\xce\xa6\x08\0\0\0\x09\x15\x9f\xca\x89\0\0\0\x0a\x17\x80\xfe\x0a\0\0\0\x0b\x19\x62\x31\x8b\0\0\0\x0c\x1d\x25\xea\x0c\0\0\0\x0d\x21\xda\xe5\x0d\0\0\0\x0e\x25\x9e\x9d\x8e\0\0\0\x0f\x27\x7f\xd1\x0f\0\0\0\x10\x2a\x50\xf5\x90\0\0\0\x11\x2c\x32\x29\x11\0\0\0\x12\x2e\x13\x5c\x92\0\0\0\x13\x30\xe7\x24\x13\0\0\0\x14\x33\xb8\x48\x94\0\0\0\x15\x36\x8c\x10\x15\0\0\0\x16\x43\xb7\x1b\x96\0\0\0\x17\x49\x5c\x07\x97\0\0\0\x18\x4f\xef\x93\x18\0\0\0\x19\x55\x93\x2d\x99\0\0\0\x1a\x58\x68\x46\x9a\0\0\0\x1b\0\0";
+
+ let time_zone = TimeZone::from_tz_data(bytes)?;
+
+ let time_zone_result = TimeZone::new(
+ Vec::new(),
+ vec![LocalTimeType::new(0, false, Some(b"UTC"))?],
+ vec![
+ LeapSecond::new(78796800, 1),
+ LeapSecond::new(94694401, 2),
+ LeapSecond::new(126230402, 3),
+ LeapSecond::new(157766403, 4),
+ LeapSecond::new(189302404, 5),
+ LeapSecond::new(220924805, 6),
+ LeapSecond::new(252460806, 7),
+ LeapSecond::new(283996807, 8),
+ LeapSecond::new(315532808, 9),
+ LeapSecond::new(362793609, 10),
+ LeapSecond::new(394329610, 11),
+ LeapSecond::new(425865611, 12),
+ LeapSecond::new(489024012, 13),
+ LeapSecond::new(567993613, 14),
+ LeapSecond::new(631152014, 15),
+ LeapSecond::new(662688015, 16),
+ LeapSecond::new(709948816, 17),
+ LeapSecond::new(741484817, 18),
+ LeapSecond::new(773020818, 19),
+ LeapSecond::new(820454419, 20),
+ LeapSecond::new(867715220, 21),
+ LeapSecond::new(915148821, 22),
+ LeapSecond::new(1136073622, 23),
+ LeapSecond::new(1230768023, 24),
+ LeapSecond::new(1341100824, 25),
+ LeapSecond::new(1435708825, 26),
+ LeapSecond::new(1483228826, 27),
+ ],
+ None,
+ )?;
+
+ assert_eq!(time_zone, time_zone_result);
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_v2_file() -> Result<(), Error> {
+ let bytes = b"TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x06\0\0\0\x06\0\0\0\0\0\0\0\x07\0\0\0\x06\0\0\0\x14\x80\0\0\0\xbb\x05\x43\x48\xbb\x21\x71\x58\xcb\x89\x3d\xc8\xd2\x23\xf4\x70\xd2\x61\x49\x38\xd5\x8d\x73\x48\x01\x02\x01\x03\x04\x01\x05\xff\xff\x6c\x02\0\0\xff\xff\x6c\x58\0\x04\xff\xff\x7a\x68\x01\x08\xff\xff\x7a\x68\x01\x0c\xff\xff\x7a\x68\x01\x10\xff\xff\x73\x60\0\x04LMT\0HST\0HDT\0HWT\0HPT\0\0\0\0\0\x01\0\0\0\0\0\x01\0TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x06\0\0\0\x06\0\0\0\0\0\0\0\x07\0\0\0\x06\0\0\0\x14\xff\xff\xff\xff\x74\xe0\x70\xbe\xff\xff\xff\xff\xbb\x05\x43\x48\xff\xff\xff\xff\xbb\x21\x71\x58\xff\xff\xff\xff\xcb\x89\x3d\xc8\xff\xff\xff\xff\xd2\x23\xf4\x70\xff\xff\xff\xff\xd2\x61\x49\x38\xff\xff\xff\xff\xd5\x8d\x73\x48\x01\x02\x01\x03\x04\x01\x05\xff\xff\x6c\x02\0\0\xff\xff\x6c\x58\0\x04\xff\xff\x7a\x68\x01\x08\xff\xff\x7a\x68\x01\x0c\xff\xff\x7a\x68\x01\x10\xff\xff\x73\x60\0\x04LMT\0HST\0HDT\0HWT\0HPT\0\0\0\0\0\x01\0\0\0\0\0\x01\0\x0aHST10\x0a";
+
+ let time_zone = TimeZone::from_tz_data(bytes)?;
+
+ let time_zone_result = TimeZone::new(
+ vec![
+ Transition::new(-2334101314, 1),
+ Transition::new(-1157283000, 2),
+ Transition::new(-1155436200, 1),
+ Transition::new(-880198200, 3),
+ Transition::new(-769395600, 4),
+ Transition::new(-765376200, 1),
+ Transition::new(-712150200, 5),
+ ],
+ vec![
+ LocalTimeType::new(-37886, false, Some(b"LMT"))?,
+ LocalTimeType::new(-37800, false, Some(b"HST"))?,
+ LocalTimeType::new(-34200, true, Some(b"HDT"))?,
+ LocalTimeType::new(-34200, true, Some(b"HWT"))?,
+ LocalTimeType::new(-34200, true, Some(b"HPT"))?,
+ LocalTimeType::new(-36000, false, Some(b"HST"))?,
+ ],
+ Vec::new(),
+ Some(TransitionRule::from(LocalTimeType::new(-36000, false, Some(b"HST"))?)),
+ )?;
+
+ assert_eq!(time_zone, time_zone_result);
+
+ assert_eq!(
+ *time_zone.find_local_time_type(-1156939200)?,
+ LocalTimeType::new(-34200, true, Some(b"HDT"))?
+ );
+ assert_eq!(
+ *time_zone.find_local_time_type(1546300800)?,
+ LocalTimeType::new(-36000, false, Some(b"HST"))?
+ );
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_tz_ascii_str() -> Result<(), Error> {
+ assert!(matches!(TimeZoneName::new(b""), Err(Error::LocalTimeType(_))));
+ assert!(matches!(TimeZoneName::new(b"1"), Err(Error::LocalTimeType(_))));
+ assert!(matches!(TimeZoneName::new(b"12"), Err(Error::LocalTimeType(_))));
+ assert_eq!(TimeZoneName::new(b"123")?.as_bytes(), b"123");
+ assert_eq!(TimeZoneName::new(b"1234")?.as_bytes(), b"1234");
+ assert_eq!(TimeZoneName::new(b"12345")?.as_bytes(), b"12345");
+ assert_eq!(TimeZoneName::new(b"123456")?.as_bytes(), b"123456");
+ assert_eq!(TimeZoneName::new(b"1234567")?.as_bytes(), b"1234567");
+ assert!(matches!(TimeZoneName::new(b"12345678"), Err(Error::LocalTimeType(_))));
+ assert!(matches!(TimeZoneName::new(b"123456789"), Err(Error::LocalTimeType(_))));
+ assert!(matches!(TimeZoneName::new(b"1234567890"), Err(Error::LocalTimeType(_))));
+
+ assert!(matches!(TimeZoneName::new(b"123\0\0\0"), Err(Error::LocalTimeType(_))));
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_time_zone() -> Result<(), Error> {
+ let utc = LocalTimeType::UTC;
+ let cet = LocalTimeType::with_offset(3600)?;
+
+ let utc_local_time_types = vec![utc];
+ let fixed_extra_rule = TransitionRule::from(cet);
+
+ let time_zone_1 = TimeZone::new(vec![], utc_local_time_types.clone(), vec![], None)?;
+ let time_zone_2 =
+ TimeZone::new(vec![], utc_local_time_types.clone(), vec![], Some(fixed_extra_rule))?;
+ let time_zone_3 =
+ TimeZone::new(vec![Transition::new(0, 0)], utc_local_time_types.clone(), vec![], None)?;
+ let time_zone_4 = TimeZone::new(
+ vec![Transition::new(i32::min_value().into(), 0), Transition::new(0, 1)],
+ vec![utc, cet],
+ Vec::new(),
+ Some(fixed_extra_rule),
+ )?;
+
+ assert_eq!(*time_zone_1.find_local_time_type(0)?, utc);
+ assert_eq!(*time_zone_2.find_local_time_type(0)?, cet);
+
+ assert_eq!(*time_zone_3.find_local_time_type(-1)?, utc);
+ assert!(matches!(time_zone_3.find_local_time_type(0), Err(Error::FindLocalTimeType(_))));
+
+ assert_eq!(*time_zone_4.find_local_time_type(-1)?, utc);
+ assert_eq!(*time_zone_4.find_local_time_type(0)?, cet);
+
+ let time_zone_err = TimeZone::new(
+ vec![Transition::new(0, 0)],
+ utc_local_time_types,
+ vec![],
+ Some(fixed_extra_rule),
+ );
+ assert!(time_zone_err.is_err());
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_time_zone_from_posix_tz() -> Result<(), Error> {
+ #[cfg(unix)]
+ {
+ // if the TZ var is set, this essentially _overrides_ the
+ // time set by the localtime symlink
+ // so just ensure that ::local() acts as expected
+ // in this case
+ if let Ok(tz) = std::env::var("TZ") {
+ let time_zone_local = TimeZone::local(Some(tz.as_str()))?;
+ let time_zone_local_1 = TimeZone::from_posix_tz(&tz)?;
+ assert_eq!(time_zone_local, time_zone_local_1);
+ }
+
+ let time_zone_utc = TimeZone::from_posix_tz("UTC")?;
+ assert_eq!(time_zone_utc.find_local_time_type(0)?.offset(), 0);
+ }
+
+ assert!(TimeZone::from_posix_tz("EST5EDT,0/0,J365/25").is_err());
+ assert!(TimeZone::from_posix_tz("").is_err());
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_leap_seconds() -> Result<(), Error> {
+ let time_zone = TimeZone::new(
+ Vec::new(),
+ vec![LocalTimeType::new(0, false, Some(b"UTC"))?],
+ vec![
+ LeapSecond::new(78796800, 1),
+ LeapSecond::new(94694401, 2),
+ LeapSecond::new(126230402, 3),
+ LeapSecond::new(157766403, 4),
+ LeapSecond::new(189302404, 5),
+ LeapSecond::new(220924805, 6),
+ LeapSecond::new(252460806, 7),
+ LeapSecond::new(283996807, 8),
+ LeapSecond::new(315532808, 9),
+ LeapSecond::new(362793609, 10),
+ LeapSecond::new(394329610, 11),
+ LeapSecond::new(425865611, 12),
+ LeapSecond::new(489024012, 13),
+ LeapSecond::new(567993613, 14),
+ LeapSecond::new(631152014, 15),
+ LeapSecond::new(662688015, 16),
+ LeapSecond::new(709948816, 17),
+ LeapSecond::new(741484817, 18),
+ LeapSecond::new(773020818, 19),
+ LeapSecond::new(820454419, 20),
+ LeapSecond::new(867715220, 21),
+ LeapSecond::new(915148821, 22),
+ LeapSecond::new(1136073622, 23),
+ LeapSecond::new(1230768023, 24),
+ LeapSecond::new(1341100824, 25),
+ LeapSecond::new(1435708825, 26),
+ LeapSecond::new(1483228826, 27),
+ ],
+ None,
+ )?;
+
+ let time_zone_ref = time_zone.as_ref();
+
+ assert!(matches!(time_zone_ref.unix_leap_time_to_unix_time(1136073621), Ok(1136073599)));
+ assert!(matches!(time_zone_ref.unix_leap_time_to_unix_time(1136073622), Ok(1136073600)));
+ assert!(matches!(time_zone_ref.unix_leap_time_to_unix_time(1136073623), Ok(1136073600)));
+ assert!(matches!(time_zone_ref.unix_leap_time_to_unix_time(1136073624), Ok(1136073601)));
+
+ assert!(matches!(time_zone_ref.unix_time_to_unix_leap_time(1136073599), Ok(1136073621)));
+ assert!(matches!(time_zone_ref.unix_time_to_unix_leap_time(1136073600), Ok(1136073623)));
+ assert!(matches!(time_zone_ref.unix_time_to_unix_leap_time(1136073601), Ok(1136073624)));
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_leap_seconds_overflow() -> Result<(), Error> {
+ let time_zone_err = TimeZone::new(
+ vec![Transition::new(i64::min_value(), 0)],
+ vec![LocalTimeType::UTC],
+ vec![LeapSecond::new(0, 1)],
+ Some(TransitionRule::from(LocalTimeType::UTC)),
+ );
+ assert!(time_zone_err.is_err());
+
+ let time_zone = TimeZone::new(
+ vec![Transition::new(i64::max_value(), 0)],
+ vec![LocalTimeType::UTC],
+ vec![LeapSecond::new(0, 1)],
+ None,
+ )?;
+ assert!(matches!(
+ time_zone.find_local_time_type(i64::max_value()),
+ Err(Error::FindLocalTimeType(_))
+ ));
+
+ Ok(())
+ }
+}
diff --git a/vendor/chrono/src/offset/local/unix.rs b/vendor/chrono/src/offset/local/unix.rs
new file mode 100644
index 000000000..32aa31618
--- /dev/null
+++ b/vendor/chrono/src/offset/local/unix.rs
@@ -0,0 +1,185 @@
+// 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 std::{cell::RefCell, collections::hash_map, env, fs, hash::Hasher, time::SystemTime};
+
+use super::tz_info::TimeZone;
+use super::{DateTime, FixedOffset, Local, NaiveDateTime};
+use crate::{Datelike, LocalResult, Utc};
+
+pub(super) fn now() -> DateTime<Local> {
+ let now = Utc::now().naive_utc();
+ naive_to_local(&now, false).unwrap()
+}
+
+pub(super) fn naive_to_local(d: &NaiveDateTime, local: bool) -> LocalResult<DateTime<Local>> {
+ TZ_INFO.with(|maybe_cache| {
+ maybe_cache.borrow_mut().get_or_insert_with(Cache::default).offset(*d, local)
+ })
+}
+
+// we have to store the `Cache` in an option as it can't
+// be initalized in a static context.
+thread_local! {
+ static TZ_INFO: RefCell<Option<Cache>> = Default::default();
+}
+
+enum Source {
+ LocalTime { mtime: SystemTime },
+ Environment { hash: u64 },
+}
+
+impl Source {
+ fn new(env_tz: Option<&str>) -> Source {
+ match env_tz {
+ Some(tz) => {
+ let mut hasher = hash_map::DefaultHasher::new();
+ hasher.write(tz.as_bytes());
+ let hash = hasher.finish();
+ Source::Environment { hash }
+ }
+ None => match fs::symlink_metadata("/etc/localtime") {
+ Ok(data) => Source::LocalTime {
+ // we have to pick a sensible default when the mtime fails
+ // by picking SystemTime::now() we raise the probability of
+ // the cache being invalidated if/when the mtime starts working
+ mtime: data.modified().unwrap_or_else(|_| SystemTime::now()),
+ },
+ Err(_) => {
+ // as above, now() should be a better default than some constant
+ // TODO: see if we can improve caching in the case where the fallback is a valid timezone
+ Source::LocalTime { mtime: SystemTime::now() }
+ }
+ },
+ }
+ }
+}
+
+struct Cache {
+ zone: TimeZone,
+ source: Source,
+ last_checked: SystemTime,
+}
+
+#[cfg(target_os = "android")]
+const TZDB_LOCATION: &str = " /system/usr/share/zoneinfo";
+
+#[cfg(target_os = "aix")]
+const TZDB_LOCATION: &str = "/usr/share/lib/zoneinfo";
+
+#[allow(dead_code)] // keeps the cfg simpler
+#[cfg(not(any(target_os = "android", target_os = "aix")))]
+const TZDB_LOCATION: &str = "/usr/share/zoneinfo";
+
+fn fallback_timezone() -> Option<TimeZone> {
+ let tz_name = iana_time_zone::get_timezone().ok()?;
+ let bytes = fs::read(format!("{}/{}", TZDB_LOCATION, tz_name)).ok()?;
+ TimeZone::from_tz_data(&bytes).ok()
+}
+
+impl Default for Cache {
+ fn default() -> Cache {
+ // default to UTC if no local timezone can be found
+ let env_tz = env::var("TZ").ok();
+ let env_ref = env_tz.as_ref().map(|s| s.as_str());
+ Cache {
+ last_checked: SystemTime::now(),
+ source: Source::new(env_ref),
+ zone: current_zone(env_ref),
+ }
+ }
+}
+
+fn current_zone(var: Option<&str>) -> TimeZone {
+ TimeZone::local(var).ok().or_else(fallback_timezone).unwrap_or_else(TimeZone::utc)
+}
+
+impl Cache {
+ fn offset(&mut self, d: NaiveDateTime, local: bool) -> LocalResult<DateTime<Local>> {
+ let now = SystemTime::now();
+
+ match now.duration_since(self.last_checked) {
+ // If the cache has been around for less than a second then we reuse it
+ // unconditionally. This is a reasonable tradeoff because the timezone
+ // generally won't be changing _that_ often, but if the time zone does
+ // change, it will reflect sufficiently quickly from an application
+ // user's perspective.
+ Ok(d) if d.as_secs() < 1 => (),
+ Ok(_) | Err(_) => {
+ let env_tz = env::var("TZ").ok();
+ let env_ref = env_tz.as_ref().map(|s| s.as_str());
+ let new_source = Source::new(env_ref);
+
+ let out_of_date = match (&self.source, &new_source) {
+ // change from env to file or file to env, must recreate the zone
+ (Source::Environment { .. }, Source::LocalTime { .. })
+ | (Source::LocalTime { .. }, Source::Environment { .. }) => true,
+ // stay as file, but mtime has changed
+ (Source::LocalTime { mtime: old_mtime }, Source::LocalTime { mtime })
+ if old_mtime != mtime =>
+ {
+ true
+ }
+ // stay as env, but hash of variable has changed
+ (Source::Environment { hash: old_hash }, Source::Environment { hash })
+ if old_hash != hash =>
+ {
+ true
+ }
+ // cache can be reused
+ _ => false,
+ };
+
+ if out_of_date {
+ self.zone = current_zone(env_ref);
+ }
+
+ self.last_checked = now;
+ self.source = new_source;
+ }
+ }
+
+ if !local {
+ let offset = self
+ .zone
+ .find_local_time_type(d.timestamp())
+ .expect("unable to select local time type")
+ .offset();
+
+ return match FixedOffset::east_opt(offset) {
+ Some(offset) => LocalResult::Single(DateTime::from_utc(d, offset)),
+ None => LocalResult::None,
+ };
+ }
+
+ // we pass through the year as the year of a local point in time must either be valid in that locale, or
+ // the entire time was skipped in which case we will return LocalResult::None anywa.
+ match self
+ .zone
+ .find_local_time_type_from_local(d.timestamp(), d.year())
+ .expect("unable to select local time type")
+ {
+ LocalResult::None => LocalResult::None,
+ LocalResult::Ambiguous(early, late) => {
+ let early_offset = FixedOffset::east_opt(early.offset()).unwrap();
+ let late_offset = FixedOffset::east_opt(late.offset()).unwrap();
+
+ LocalResult::Ambiguous(
+ DateTime::from_utc(d - early_offset, early_offset),
+ DateTime::from_utc(d - late_offset, late_offset),
+ )
+ }
+ LocalResult::Single(tt) => {
+ let offset = FixedOffset::east_opt(tt.offset()).unwrap();
+ LocalResult::Single(DateTime::from_utc(d - offset, offset))
+ }
+ }
+ }
+}
diff --git a/vendor/chrono/src/offset/local/windows.rs b/vendor/chrono/src/offset/local/windows.rs
new file mode 100644
index 000000000..e6415015c
--- /dev/null
+++ b/vendor/chrono/src/offset/local/windows.rs
@@ -0,0 +1,278 @@
+// 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 std::io;
+use std::mem;
+use std::time::{SystemTime, UNIX_EPOCH};
+
+use winapi::shared::minwindef::*;
+use winapi::um::minwinbase::SYSTEMTIME;
+use winapi::um::timezoneapi::*;
+
+use super::{FixedOffset, Local};
+use crate::{DateTime, Datelike, LocalResult, NaiveDate, NaiveDateTime, NaiveTime, Timelike};
+
+pub(super) fn now() -> DateTime<Local> {
+ tm_to_datetime(Timespec::now().local())
+}
+
+/// Converts a local `NaiveDateTime` to the `time::Timespec`.
+pub(super) fn naive_to_local(d: &NaiveDateTime, local: bool) -> LocalResult<DateTime<Local>> {
+ let tm = 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,
+ // This seems pretty fake?
+ tm_utcoff: if local { 1 } else { 0 },
+ // do not set this, OS APIs are heavily inconsistent in terms of leap second handling
+ tm_nsec: 0,
+ };
+
+ let spec = Timespec {
+ sec: match local {
+ false => utc_tm_to_time(&tm),
+ true => local_tm_to_time(&tm),
+ },
+ nsec: tm.tm_nsec,
+ };
+
+ // Adjust for leap seconds
+ let mut tm = spec.local();
+ assert_eq!(tm.tm_nsec, 0);
+ tm.tm_nsec = d.nanosecond() as i32;
+
+ // #TODO - there should be ambiguous cases, investigate?
+ LocalResult::Single(tm_to_datetime(tm))
+}
+
+/// Converts a `time::Tm` struct into the timezone-aware `DateTime`.
+fn tm_to_datetime(mut tm: Tm) -> DateTime<Local> {
+ if tm.tm_sec >= 60 {
+ tm.tm_nsec += (tm.tm_sec - 59) * 1_000_000_000;
+ tm.tm_sec = 59;
+ }
+
+ let date = NaiveDate::from_ymd_opt(tm.tm_year + 1900, tm.tm_mon as u32 + 1, tm.tm_mday as u32)
+ .unwrap();
+ 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_opt(tm.tm_utcoff).unwrap();
+ DateTime::from_utc(date.and_time(time) - offset, offset)
+}
+
+/// 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}.
+struct Timespec {
+ sec: i64,
+ nsec: i32,
+}
+
+impl Timespec {
+ /// Constructs a timespec representing the current time in UTC.
+ 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.
+ 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,
+ };
+ 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?
+#[repr(C)]
+struct Tm {
+ /// Seconds after the minute - [0, 60]
+ tm_sec: i32,
+
+ /// Minutes after the hour - [0, 59]
+ tm_min: i32,
+
+ /// Hours after midnight - [0, 23]
+ tm_hour: i32,
+
+ /// Day of the month - [1, 31]
+ tm_mday: i32,
+
+ /// Months since January - [0, 11]
+ tm_mon: i32,
+
+ /// Years since 1900
+ tm_year: i32,
+
+ /// Days since Sunday - [0, 6]. 0 = Sunday, 1 = Monday, ..., 6 = Saturday.
+ tm_wday: i32,
+
+ /// Days since January 1 - [0, 365]
+ 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.
+ 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`.
+ tm_utcoff: i32,
+
+ /// Nanoseconds after the second - [0, 10<sup>9</sup> - 1]
+ tm_nsec: i32,
+}
+
+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());
+ }
+ }
+}
+
+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 };
+ }
+}
+
+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)
+ }
+}
+
+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)
+ }
+}
diff --git a/vendor/chrono/src/offset/mod.rs b/vendor/chrono/src/offset/mod.rs
index 0da6bfb42..09d0714e9 100644
--- a/vendor/chrono/src/offset/mod.rs
+++ b/vendor/chrono/src/offset/mod.rs
@@ -20,10 +20,22 @@
use core::fmt;
-use format::{parse, ParseResult, Parsed, StrftimeItems};
-use naive::{NaiveDate, NaiveDateTime, NaiveTime};
-use Weekday;
-use {Date, DateTime};
+use crate::format::{parse, ParseResult, Parsed, StrftimeItems};
+use crate::naive::{NaiveDate, NaiveDateTime, NaiveTime};
+use crate::Weekday;
+#[allow(deprecated)]
+use crate::{Date, DateTime};
+
+mod fixed;
+pub use self::fixed::FixedOffset;
+
+#[cfg(feature = "clock")]
+mod local;
+#[cfg(feature = "clock")]
+pub use self::local::Local;
+
+mod utc;
+pub use self::utc::Utc;
/// The conversion result from the local time to the timezone-aware datetime types.
#[derive(Clone, PartialEq, Debug, Copy, Eq, Hash)]
@@ -73,6 +85,7 @@ impl<T> LocalResult<T> {
}
}
+#[allow(deprecated)]
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.
@@ -195,6 +208,27 @@ pub trait TimeZone: Sized + Clone {
/// The original `TimeZone` value can be recovered via `TimeZone::from_offset`.
type Offset: Offset;
+ /// Make a new `DateTime` from year, month, day, time components and current time zone.
+ ///
+ /// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
+ ///
+ /// Returns `LocalResult::None` on invalid input data.
+ fn with_ymd_and_hms(
+ &self,
+ year: i32,
+ month: u32,
+ day: u32,
+ hour: u32,
+ min: u32,
+ sec: u32,
+ ) -> LocalResult<DateTime<Self>> {
+ match NaiveDate::from_ymd_opt(year, month, day).and_then(|d| d.and_hms_opt(hour, min, sec))
+ {
+ Some(dt) => self.from_local_datetime(&dt),
+ None => LocalResult::None,
+ }
+ }
+
/// 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.
///
@@ -202,14 +236,8 @@ pub trait TimeZone: Sized + Clone {
/// 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");
- /// ~~~~
+ #[deprecated(since = "0.4.23", note = "use `with_ymd_and_hms()` instead")]
+ #[allow(deprecated)]
fn ymd(&self, year: i32, month: u32, day: u32) -> Date<Self> {
self.ymd_opt(year, month, day).unwrap()
}
@@ -221,15 +249,8 @@ pub trait TimeZone: Sized + Clone {
/// 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);
- /// ~~~~
+ #[deprecated(since = "0.4.23", note = "use `with_ymd_and_hms()` instead")]
+ #[allow(deprecated)]
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),
@@ -244,14 +265,11 @@ pub trait TimeZone: Sized + Clone {
/// 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");
- /// ~~~~
+ #[deprecated(
+ since = "0.4.23",
+ note = "use `from_local_datetime()` with a `NaiveDateTime` instead"
+ )]
+ #[allow(deprecated)]
fn yo(&self, year: i32, ordinal: u32) -> Date<Self> {
self.yo_opt(year, ordinal).unwrap()
}
@@ -263,6 +281,11 @@ pub trait TimeZone: Sized + Clone {
/// but it will propagate to the `DateTime` values constructed via this date.
///
/// Returns `None` on the out-of-range date and/or invalid DOY.
+ #[deprecated(
+ since = "0.4.23",
+ note = "use `from_local_datetime()` with a `NaiveDateTime` instead"
+ )]
+ #[allow(deprecated)]
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),
@@ -279,14 +302,11 @@ pub trait TimeZone: Sized + Clone {
/// 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");
- /// ~~~~
+ #[deprecated(
+ since = "0.4.23",
+ note = "use `from_local_datetime()` with a `NaiveDateTime` instead"
+ )]
+ #[allow(deprecated)]
fn isoywd(&self, year: i32, week: u32, weekday: Weekday) -> Date<Self> {
self.isoywd_opt(year, week, weekday).unwrap()
}
@@ -300,6 +320,11 @@ pub trait TimeZone: Sized + Clone {
/// 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.
+ #[deprecated(
+ since = "0.4.23",
+ note = "use `from_local_datetime()` with a `NaiveDateTime` instead"
+ )]
+ #[allow(deprecated)]
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),
@@ -313,14 +338,7 @@ pub trait TimeZone: Sized + Clone {
///
/// 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");
- /// ~~~~
+ #[deprecated(since = "0.4.23", note = "use `timestamp_opt()` instead")]
fn timestamp(&self, secs: i64, nsecs: u32) -> DateTime<Self> {
self.timestamp_opt(secs, nsecs).unwrap()
}
@@ -331,6 +349,14 @@ pub trait TimeZone: Sized + Clone {
///
/// Returns `LocalResult::None` on out-of-range number of seconds and/or
/// invalid nanosecond, otherwise always returns `LocalResult::Single`.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use chrono::{Utc, TimeZone};
+ ///
+ /// assert_eq!(Utc.timestamp_opt(1431648000, 0).unwrap().to_string(), "2015-05-15 00:00:00 UTC");
+ /// ```
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)),
@@ -343,14 +369,7 @@ pub trait TimeZone: Sized + Clone {
///
/// 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);
- /// ~~~~
+ #[deprecated(since = "0.4.23", note = "use `timestamp_millis_opt()` instead")]
fn timestamp_millis(&self, millis: i64) -> DateTime<Self> {
self.timestamp_millis_opt(millis).unwrap()
}
@@ -365,13 +384,13 @@ pub trait TimeZone: Sized + Clone {
///
/// # 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 {
@@ -389,11 +408,11 @@ pub trait TimeZone: Sized + Clone {
///
/// # 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 {
@@ -403,16 +422,17 @@ pub trait TimeZone: Sized + Clone {
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.
+ /// Parses a string with the specified format string and returns a
+ /// `DateTime` with the current offset.
+ ///
+ /// See the [`crate::format::strftime`] module 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.
+ /// If the to-be-parsed string includes an offset, it *must* match the
+ /// offset of the TimeZone, otherwise an error will be returned.
///
- /// See also `DateTime::parse_from_str` which gives a local `DateTime`
- /// with parsed `FixedOffset`.
+ /// See also [`DateTime::parse_from_str`] which gives a [`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))?;
@@ -429,6 +449,9 @@ pub trait TimeZone: Sized + Clone {
fn offset_from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<Self::Offset>;
/// Converts the local `NaiveDate` to the timezone-aware `Date` if possible.
+ #[allow(clippy::wrong_self_convention)]
+ #[deprecated(since = "0.4.23", note = "use `from_local_datetime()` instead")]
+ #[allow(deprecated)]
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
@@ -437,6 +460,7 @@ pub trait TimeZone: Sized + Clone {
}
/// Converts the local `NaiveDateTime` to the timezone-aware `DateTime` if possible.
+ #[allow(clippy::wrong_self_convention)]
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))
@@ -450,48 +474,42 @@ pub trait TimeZone: Sized + Clone {
/// Converts the UTC `NaiveDate` to the local time.
/// The UTC is continuous and thus this cannot fail (but can give the duplicate local time).
+ #[allow(clippy::wrong_self_convention)]
+ #[deprecated(since = "0.4.23", note = "use `from_utc_datetime()` instead")]
+ #[allow(deprecated)]
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).
+ #[allow(clippy::wrong_self_convention)]
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);
+ let dt = Utc.timestamp_millis_opt(-1000).unwrap();
assert_eq!(dt.to_string(), "1969-12-31 23:59:59 UTC");
- let dt = Utc.timestamp_millis(-7000);
+ let dt = Utc.timestamp_millis_opt(-7000).unwrap();
assert_eq!(dt.to_string(), "1969-12-31 23:59:53 UTC");
- let dt = Utc.timestamp_millis(-7001);
+ let dt = Utc.timestamp_millis_opt(-7001).unwrap();
assert_eq!(dt.to_string(), "1969-12-31 23:59:52.999 UTC");
- let dt = Utc.timestamp_millis(-7003);
+ let dt = Utc.timestamp_millis_opt(-7003).unwrap();
assert_eq!(dt.to_string(), "1969-12-31 23:59:52.997 UTC");
- let dt = Utc.timestamp_millis(-999);
+ let dt = Utc.timestamp_millis_opt(-999).unwrap();
assert_eq!(dt.to_string(), "1969-12-31 23:59:59.001 UTC");
- let dt = Utc.timestamp_millis(-1);
+ let dt = Utc.timestamp_millis_opt(-1).unwrap();
assert_eq!(dt.to_string(), "1969-12-31 23:59:59.999 UTC");
- let dt = Utc.timestamp_millis(-60000);
+ let dt = Utc.timestamp_millis_opt(-60000).unwrap();
assert_eq!(dt.to_string(), "1969-12-31 23:59:00 UTC");
- let dt = Utc.timestamp_millis(-3600000);
+ let dt = Utc.timestamp_millis_opt(-3600000).unwrap();
assert_eq!(dt.to_string(), "1969-12-31 23:00:00 UTC");
for (millis, expected) in &[
diff --git a/vendor/chrono/src/offset/utc.rs b/vendor/chrono/src/offset/utc.rs
index aec6667b0..cfed754b2 100644
--- a/vendor/chrono/src/offset/utc.rs
+++ b/vendor/chrono/src/offset/utc.rs
@@ -4,16 +4,24 @@
//! 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"))
+ not(all(
+ target_arch = "wasm32",
+ feature = "wasmbind",
+ not(any(target_os = "emscripten", target_os = "wasi"))
+ ))
))]
use std::time::{SystemTime, UNIX_EPOCH};
+
+#[cfg(feature = "rkyv")]
+use rkyv::{Archive, Deserialize, Serialize};
+
+use super::{FixedOffset, LocalResult, Offset, TimeZone};
+use crate::naive::{NaiveDate, NaiveDateTime};
#[cfg(feature = "clock")]
-use {Date, DateTime};
+#[allow(deprecated)]
+use crate::{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).
@@ -24,35 +32,52 @@ use {Date, DateTime};
///
/// # 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)]
+/// assert_eq!(Utc.with_ymd_and_hms(1970, 1, 1, 0, 1, 1).unwrap(), dt);
+/// ```
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
+#[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))]
+#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct Utc;
#[cfg(feature = "clock")]
+#[cfg_attr(docsrs, doc(cfg(feature = "clock")))]
impl Utc {
/// Returns a `Date` which corresponds to the current date.
+ #[deprecated(
+ since = "0.4.23",
+ note = "use `Utc::now()` instead, potentially with `.date_naive()`"
+ )]
+ #[allow(deprecated)]
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")))]
+ /// Returns a `DateTime` which corresponds to the current date and time.
+ #[cfg(not(all(
+ target_arch = "wasm32",
+ feature = "wasmbind",
+ not(any(target_os = "emscripten", target_os = "wasi"))
+ )))]
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);
+ let naive =
+ NaiveDateTime::from_timestamp_opt(now.as_secs() as i64, now.subsec_nanos()).unwrap();
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"))]
+ /// Returns a `DateTime` which corresponds to the current date and time.
+ #[cfg(all(
+ target_arch = "wasm32",
+ feature = "wasmbind",
+ not(any(target_os = "emscripten", target_os = "wasi"))
+ ))]
pub fn now() -> DateTime<Utc> {
let now = js_sys::Date::new_0();
DateTime::<Utc>::from(now)
@@ -83,7 +108,7 @@ impl TimeZone for Utc {
impl Offset for Utc {
fn fix(&self) -> FixedOffset {
- FixedOffset::east(0)
+ FixedOffset::east_opt(0).unwrap()
}
}