mirror of
https://github.com/rust-osdev/uefi-rs
synced 2026-06-08 17:17:36 +00:00
uefi-raw: add time integration into module
This helps to better keep an overview.
This commit is contained in:
+271
-271
@@ -2,69 +2,11 @@
|
||||
|
||||
//! Date and time types.
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
pub use time_crate_integration::*;
|
||||
|
||||
use bitflags::bitflags;
|
||||
use core::error;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
#[cfg(feature = "time")]
|
||||
use time::{OffsetDateTime, UtcOffset};
|
||||
|
||||
/// The time is invalid as it doesn't fulfill the requirements of
|
||||
/// [`Time::is_valid`].
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg(feature = "time")]
|
||||
pub struct InvalidTimeError(Time);
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
impl Display for InvalidTimeError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Invalid EFI time (not valid): {}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
impl error::Error for InvalidTimeError {}
|
||||
|
||||
/// Errors that can happen when converting a [`Time`] to a [`OffsetDateTime`].
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg(feature = "time")]
|
||||
pub enum ToOffsetDateTimeError {
|
||||
/// See [`InvalidTimeError`].
|
||||
Invalid(InvalidTimeError),
|
||||
/// The timezone is a valid EFI value but [`Time::UNSPECIFIED_TIMEZONE`]
|
||||
/// means `local` which cannot be determined in a `no_std` context.
|
||||
UnspecifiedTimezone,
|
||||
/// The corresponding component has an invalid range (e.g. February 30th).
|
||||
ComponentRange(time::error::ComponentRange),
|
||||
}
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
impl Display for ToOffsetDateTimeError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Invalid(e) => {
|
||||
write!(f, "Cannot convert to OffsetDateTime: {}", e)
|
||||
}
|
||||
Self::UnspecifiedTimezone => write!(
|
||||
f,
|
||||
"Cannot convert to OffsetDateTime: the timezone (local) can't be determined in a no_std context"
|
||||
),
|
||||
Self::ComponentRange(e) => {
|
||||
write!(f, "Cannot convert to OffsetDateTime: {}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
impl error::Error for ToOffsetDateTimeError {
|
||||
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
||||
match self {
|
||||
Self::Invalid(e) => Some(e),
|
||||
Self::UnspecifiedTimezone => None,
|
||||
Self::ComponentRange(e) => Some(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Date and time representation.
|
||||
///
|
||||
@@ -120,150 +62,6 @@ impl Time {
|
||||
/// Indicates the time should be interpreted as local time.
|
||||
pub const UNSPECIFIED_TIMEZONE: i16 = 0x07ff;
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
fn _to_offset_date_time(
|
||||
&self,
|
||||
local_timezone: Option<UtcOffset>,
|
||||
) -> Result<OffsetDateTime, ToOffsetDateTimeError> {
|
||||
if !self.is_valid() {
|
||||
return Err(ToOffsetDateTimeError::Invalid(InvalidTimeError(*self)));
|
||||
}
|
||||
|
||||
// Special handling for `UNSPECIFIED_TIMEZONE` (local):
|
||||
// - specific time zone => all good
|
||||
// - `UNSPECIFIED_TIMEZONE` and no default time zone => return Err
|
||||
// - else: use the default time zone
|
||||
let offset_hms: (i8, i8, i8) = {
|
||||
if self.time_zone == Self::UNSPECIFIED_TIMEZONE {
|
||||
if let Some(local_timezone) = local_timezone {
|
||||
local_timezone.as_hms()
|
||||
} else {
|
||||
return Err(ToOffsetDateTimeError::UnspecifiedTimezone);
|
||||
}
|
||||
} else {
|
||||
let h = self.time_zone / 60;
|
||||
let m = self.time_zone % 60;
|
||||
|
||||
(h as i8, m as i8, 0)
|
||||
}
|
||||
};
|
||||
|
||||
// Emulated try {} block to keep the `?` error propagation scoped
|
||||
// (we have a different error type here)
|
||||
let datetime: Result<OffsetDateTime, time::error::ComponentRange> = (|| {
|
||||
let month = time::Month::try_from(self.month)?;
|
||||
let date = time::Date::from_calendar_date(self.year as i32, month, self.day)?;
|
||||
let time =
|
||||
time::Time::from_hms_nano(self.hour, self.minute, self.second, self.nanosecond)?;
|
||||
let offset = UtcOffset::from_hms(offset_hms.0, offset_hms.1, offset_hms.2)?;
|
||||
Ok(OffsetDateTime::new_in_offset(date, time, offset))
|
||||
})();
|
||||
|
||||
datetime.map_err(ToOffsetDateTimeError::ComponentRange)
|
||||
}
|
||||
|
||||
/// Converts this [`Time`] to a [`OffsetDateTime`] using the UTC offset
|
||||
/// stored in `self.time_zone`.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// - `Ok(OffsetDateTime)` if the time is valid and the timezone is fully
|
||||
/// specified (not `UNSPECIFIED_TIMEZONE`).
|
||||
/// - `Err(ToOffsetDateTimeError::Invalid(_))` if any of the time fields are
|
||||
/// out of valid EFI ranges (e.g., year < 1900, month > 12).
|
||||
/// - `Err(ToOffsetDateTimeError::UnspecifiedTimezone)` if the timezone is
|
||||
/// `UNSPECIFIED_TIMEZONE`, since no default local timezone was provided.
|
||||
/// - `Err(ToOffsetDateTimeError::ComponentRange(_))` if the combination of
|
||||
/// fields results in an invalid calendar date (e.g., February 30th).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use uefi_raw::time::{Time, ToOffsetDateTimeError};
|
||||
/// # use time::OffsetDateTime;
|
||||
/// let t = Time {
|
||||
/// year: 2024,
|
||||
/// month: 3,
|
||||
/// day: 15,
|
||||
/// hour: 12,
|
||||
/// minute: 0,
|
||||
/// second: 0,
|
||||
/// pad1: 0,
|
||||
/// nanosecond: 0,
|
||||
/// time_zone: 0, // UTC
|
||||
/// daylight: Default::default(),
|
||||
/// pad2: 0,
|
||||
/// };
|
||||
///
|
||||
/// let odt: OffsetDateTime = t.to_offset_date_time().unwrap();
|
||||
/// assert_eq!(odt.offset().whole_seconds(), 0);
|
||||
/// ```
|
||||
#[cfg(feature = "time")]
|
||||
pub fn to_offset_date_time(&self) -> Result<OffsetDateTime, ToOffsetDateTimeError> {
|
||||
self._to_offset_date_time(None)
|
||||
}
|
||||
|
||||
/// Converts this [`Time`] to a [`OffsetDateTime`] using a provided default
|
||||
/// timezone when `self.time_zone` is [`Self::UNSPECIFIED_TIMEZONE`].
|
||||
///
|
||||
/// If the stored `time_zone` field is `UNSPECIFIED_TIMEZONE`, this method
|
||||
/// uses the provided `local` [`UtcOffset`] as the offset. Otherwise, the
|
||||
/// stored `time_zone` in minutes is used.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `local`: The default [`UtcOffset`] to use if `time_zone` is
|
||||
/// [`Self::UNSPECIFIED_TIMEZONE`].
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// - `Ok(OffsetDateTime)` if the time is valid and the offset can be
|
||||
/// applied successfully.
|
||||
/// - `Err(ToOffsetDateTimeError::Invalid(_))` if any of the time fields are
|
||||
/// out of valid EFI ranges.
|
||||
/// - `Err(ToOffsetDateTimeError::ComponentRange(_))` if the combination of
|
||||
/// fields results in an invalid calendar date.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function does **not panic** under normal usage. It is impossible
|
||||
/// for `UnspecifiedTimezone` to be returned because the caller provides
|
||||
/// a valid default offset.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use uefi_raw::time::{Time, ToOffsetDateTimeError};
|
||||
/// # use time::{OffsetDateTime, UtcOffset};
|
||||
/// let mut t = Time {
|
||||
/// year: 2024,
|
||||
/// month: 3,
|
||||
/// day: 15,
|
||||
/// hour: 12,
|
||||
/// minute: 0,
|
||||
/// second: 0,
|
||||
/// pad1: 0,
|
||||
/// nanosecond: 0,
|
||||
/// time_zone: Time::UNSPECIFIED_TIMEZONE,
|
||||
/// daylight: Default::default(),
|
||||
/// pad2: 0,
|
||||
/// };
|
||||
///
|
||||
/// let default_offset = UtcOffset::from_hms(1, 30, 0).unwrap();
|
||||
/// let odt: OffsetDateTime = t.to_offset_date_time_with_default_timezone(default_offset).unwrap();
|
||||
/// assert_eq!(odt.offset(), default_offset);
|
||||
/// ```
|
||||
#[cfg(feature = "time")]
|
||||
pub fn to_offset_date_time_with_default_timezone(
|
||||
&self,
|
||||
local: UtcOffset,
|
||||
) -> Result<OffsetDateTime, ToOffsetDateTimeError> {
|
||||
match self._to_offset_date_time(Some(local)) {
|
||||
Err(ToOffsetDateTimeError::UnspecifiedTimezone) => unreachable!(),
|
||||
other => other,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an invalid `Time` with all fields set to zero.
|
||||
#[must_use]
|
||||
pub const fn invalid() -> Self {
|
||||
@@ -360,79 +158,281 @@ bitflags! {
|
||||
}
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum FromOffsetDateTimeError {
|
||||
/// The year is not representable as `u16`.
|
||||
InvalidYear(i32),
|
||||
/// Time zone offsets with seconds are not representable.
|
||||
OffsetWithSeconds(i8),
|
||||
InvalidTime(InvalidTimeError),
|
||||
}
|
||||
mod time_crate_integration {
|
||||
use super::*;
|
||||
use core::error;
|
||||
use time::{OffsetDateTime, UtcOffset};
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
impl Display for FromOffsetDateTimeError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::InvalidYear(y) => {
|
||||
write!(
|
||||
f,
|
||||
"Cannot to convert OffsetDateTime to EFI Time: invalid year {y}"
|
||||
)
|
||||
}
|
||||
Self::OffsetWithSeconds(s) => {
|
||||
write!(
|
||||
f,
|
||||
"Cannot to convert OffsetDateTime to EFI Time: time zone offset has seconds ({s}) which is unsupported"
|
||||
)
|
||||
}
|
||||
Self::InvalidTime(t) => {
|
||||
write!(f, "The time is invalid: {t}")
|
||||
}
|
||||
/// The time is invalid as it doesn't fulfill the requirements of
|
||||
/// [`Time::is_valid`].
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct InvalidTimeError(Time);
|
||||
|
||||
impl Display for InvalidTimeError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Invalid EFI time (not valid): {}", self.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
impl error::Error for FromOffsetDateTimeError {
|
||||
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
||||
match self {
|
||||
Self::InvalidYear(_) => None,
|
||||
Self::OffsetWithSeconds(_) => None,
|
||||
Self::InvalidTime(e) => Some(e),
|
||||
}
|
||||
impl error::Error for InvalidTimeError {}
|
||||
|
||||
/// Errors that can happen when converting a [`Time`] to a [`OffsetDateTime`].
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum ToOffsetDateTimeError {
|
||||
/// See [`InvalidTimeError`].
|
||||
Invalid(InvalidTimeError),
|
||||
/// The timezone is a valid EFI value but [`Time::UNSPECIFIED_TIMEZONE`]
|
||||
/// means `local` which cannot be determined in a `no_std` context.
|
||||
UnspecifiedTimezone,
|
||||
/// The corresponding component has an invalid range (e.g. February 30th).
|
||||
ComponentRange(time::error::ComponentRange),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
impl TryFrom<OffsetDateTime> for Time {
|
||||
type Error = FromOffsetDateTimeError;
|
||||
|
||||
fn try_from(value: OffsetDateTime) -> Result<Self, Self::Error> {
|
||||
let this = Self {
|
||||
year: u16::try_from(value.date().year())
|
||||
.map_err(|_| Self::Error::InvalidYear(value.date().year()))?,
|
||||
// No checks needed: underlying type has repr `u8`
|
||||
month: value.date().month() as u8,
|
||||
day: value.date().day(),
|
||||
hour: value.time().hour(),
|
||||
minute: value.time().minute(),
|
||||
second: value.time().second(),
|
||||
pad1: 0,
|
||||
nanosecond: value.time().nanosecond(),
|
||||
time_zone: {
|
||||
let (h, m, s) = value.offset().as_hms();
|
||||
if s != 0 {
|
||||
return Err(Self::Error::OffsetWithSeconds(s));
|
||||
impl Display for ToOffsetDateTimeError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Invalid(e) => {
|
||||
write!(f, "Cannot convert to OffsetDateTime: {}", e)
|
||||
}
|
||||
(h as i16 * 60) + (m as i16)
|
||||
},
|
||||
daylight: Daylight::NONE,
|
||||
pad2: 0,
|
||||
};
|
||||
if this.is_valid() {
|
||||
Ok(this)
|
||||
} else {
|
||||
Err(Self::Error::InvalidTime(InvalidTimeError(this)))
|
||||
Self::UnspecifiedTimezone => write!(
|
||||
f,
|
||||
"Cannot convert to OffsetDateTime: the timezone (local) can't be determined in a no_std context"
|
||||
),
|
||||
Self::ComponentRange(e) => {
|
||||
write!(f, "Cannot convert to OffsetDateTime: {}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for ToOffsetDateTimeError {
|
||||
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
||||
match self {
|
||||
Self::Invalid(e) => Some(e),
|
||||
Self::UnspecifiedTimezone => None,
|
||||
Self::ComponentRange(e) => Some(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Time {
|
||||
fn _to_offset_date_time(
|
||||
&self,
|
||||
local_timezone: Option<UtcOffset>,
|
||||
) -> Result<OffsetDateTime, ToOffsetDateTimeError> {
|
||||
if !self.is_valid() {
|
||||
return Err(ToOffsetDateTimeError::Invalid(InvalidTimeError(*self)));
|
||||
}
|
||||
|
||||
// Special handling for `UNSPECIFIED_TIMEZONE` (local):
|
||||
// - specific time zone => all good
|
||||
// - `UNSPECIFIED_TIMEZONE` and no default time zone => return Err
|
||||
// - else: use the default time zone
|
||||
let offset_hms: (i8, i8, i8) = {
|
||||
if self.time_zone == Self::UNSPECIFIED_TIMEZONE {
|
||||
if let Some(local_timezone) = local_timezone {
|
||||
local_timezone.as_hms()
|
||||
} else {
|
||||
return Err(ToOffsetDateTimeError::UnspecifiedTimezone);
|
||||
}
|
||||
} else {
|
||||
let h = self.time_zone / 60;
|
||||
let m = self.time_zone % 60;
|
||||
|
||||
(h as i8, m as i8, 0)
|
||||
}
|
||||
};
|
||||
|
||||
// Emulated try {} block to keep the `?` error propagation scoped
|
||||
// (we have a different error type here)
|
||||
let datetime: Result<OffsetDateTime, time::error::ComponentRange> = (|| {
|
||||
let month = time::Month::try_from(self.month)?;
|
||||
let date = time::Date::from_calendar_date(self.year as i32, month, self.day)?;
|
||||
let time = time::Time::from_hms_nano(
|
||||
self.hour,
|
||||
self.minute,
|
||||
self.second,
|
||||
self.nanosecond,
|
||||
)?;
|
||||
let offset = UtcOffset::from_hms(offset_hms.0, offset_hms.1, offset_hms.2)?;
|
||||
Ok(OffsetDateTime::new_in_offset(date, time, offset))
|
||||
})();
|
||||
|
||||
datetime.map_err(ToOffsetDateTimeError::ComponentRange)
|
||||
}
|
||||
|
||||
/// Converts this [`Time`] to a [`OffsetDateTime`] using the UTC offset
|
||||
/// stored in `self.time_zone`.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// - `Ok(OffsetDateTime)` if the time is valid and the timezone is fully
|
||||
/// specified (not `UNSPECIFIED_TIMEZONE`).
|
||||
/// - `Err(ToOffsetDateTimeError::Invalid(_))` if any of the time fields are
|
||||
/// out of valid EFI ranges (e.g., year < 1900, month > 12).
|
||||
/// - `Err(ToOffsetDateTimeError::UnspecifiedTimezone)` if the timezone is
|
||||
/// `UNSPECIFIED_TIMEZONE`, since no default local timezone was provided.
|
||||
/// - `Err(ToOffsetDateTimeError::ComponentRange(_))` if the combination of
|
||||
/// fields results in an invalid calendar date (e.g., February 30th).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use uefi_raw::time::{Time, ToOffsetDateTimeError};
|
||||
/// # use time::OffsetDateTime;
|
||||
/// let t = Time {
|
||||
/// year: 2024,
|
||||
/// month: 3,
|
||||
/// day: 15,
|
||||
/// hour: 12,
|
||||
/// minute: 0,
|
||||
/// second: 0,
|
||||
/// pad1: 0,
|
||||
/// nanosecond: 0,
|
||||
/// time_zone: 0, // UTC
|
||||
/// daylight: Default::default(),
|
||||
/// pad2: 0,
|
||||
/// };
|
||||
///
|
||||
/// let odt: OffsetDateTime = t.to_offset_date_time().unwrap();
|
||||
/// assert_eq!(odt.offset().whole_seconds(), 0);
|
||||
/// ```
|
||||
pub fn to_offset_date_time(&self) -> Result<OffsetDateTime, ToOffsetDateTimeError> {
|
||||
self._to_offset_date_time(None)
|
||||
}
|
||||
|
||||
/// Converts this [`Time`] to a [`OffsetDateTime`] using a provided default
|
||||
/// timezone when `self.time_zone` is [`Self::UNSPECIFIED_TIMEZONE`].
|
||||
///
|
||||
/// If the stored `time_zone` field is `UNSPECIFIED_TIMEZONE`, this method
|
||||
/// uses the provided `local` [`UtcOffset`] as the offset. Otherwise, the
|
||||
/// stored `time_zone` in minutes is used.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `local`: The default [`UtcOffset`] to use if `time_zone` is
|
||||
/// [`Self::UNSPECIFIED_TIMEZONE`].
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// - `Ok(OffsetDateTime)` if the time is valid and the offset can be
|
||||
/// applied successfully.
|
||||
/// - `Err(ToOffsetDateTimeError::Invalid(_))` if any of the time fields are
|
||||
/// out of valid EFI ranges.
|
||||
/// - `Err(ToOffsetDateTimeError::ComponentRange(_))` if the combination of
|
||||
/// fields results in an invalid calendar date.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function does **not panic** under normal usage. It is impossible
|
||||
/// for `UnspecifiedTimezone` to be returned because the caller provides
|
||||
/// a valid default offset.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use uefi_raw::time::{Time, ToOffsetDateTimeError};
|
||||
/// # use time::{OffsetDateTime, UtcOffset};
|
||||
/// let mut t = Time {
|
||||
/// year: 2024,
|
||||
/// month: 3,
|
||||
/// day: 15,
|
||||
/// hour: 12,
|
||||
/// minute: 0,
|
||||
/// second: 0,
|
||||
/// pad1: 0,
|
||||
/// nanosecond: 0,
|
||||
/// time_zone: Time::UNSPECIFIED_TIMEZONE,
|
||||
/// daylight: Default::default(),
|
||||
/// pad2: 0,
|
||||
/// };
|
||||
///
|
||||
/// let default_offset = UtcOffset::from_hms(1, 30, 0).unwrap();
|
||||
/// let odt: OffsetDateTime = t.to_offset_date_time_with_default_timezone(default_offset).unwrap();
|
||||
/// assert_eq!(odt.offset(), default_offset);
|
||||
/// ```
|
||||
pub fn to_offset_date_time_with_default_timezone(
|
||||
&self,
|
||||
local: UtcOffset,
|
||||
) -> Result<OffsetDateTime, ToOffsetDateTimeError> {
|
||||
match self._to_offset_date_time(Some(local)) {
|
||||
Err(ToOffsetDateTimeError::UnspecifiedTimezone) => unreachable!(),
|
||||
other => other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum FromOffsetDateTimeError {
|
||||
/// The year is not representable as `u16`.
|
||||
InvalidYear(i32),
|
||||
/// Time zone offsets with seconds are not representable.
|
||||
OffsetWithSeconds(i8),
|
||||
InvalidTime(InvalidTimeError),
|
||||
}
|
||||
|
||||
impl Display for FromOffsetDateTimeError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::InvalidYear(y) => {
|
||||
write!(
|
||||
f,
|
||||
"Cannot to convert OffsetDateTime to EFI Time: invalid year {y}"
|
||||
)
|
||||
}
|
||||
Self::OffsetWithSeconds(s) => {
|
||||
write!(
|
||||
f,
|
||||
"Cannot to convert OffsetDateTime to EFI Time: time zone offset has seconds ({s}) which is unsupported"
|
||||
)
|
||||
}
|
||||
Self::InvalidTime(t) => {
|
||||
write!(f, "The time is invalid: {t}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for FromOffsetDateTimeError {
|
||||
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
||||
match self {
|
||||
Self::InvalidYear(_) => None,
|
||||
Self::OffsetWithSeconds(_) => None,
|
||||
Self::InvalidTime(e) => Some(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<OffsetDateTime> for Time {
|
||||
type Error = FromOffsetDateTimeError;
|
||||
|
||||
fn try_from(value: OffsetDateTime) -> Result<Self, Self::Error> {
|
||||
let this = Self {
|
||||
year: u16::try_from(value.date().year())
|
||||
.map_err(|_| Self::Error::InvalidYear(value.date().year()))?,
|
||||
// No checks needed: underlying type has repr `u8`
|
||||
month: value.date().month() as u8,
|
||||
day: value.date().day(),
|
||||
hour: value.time().hour(),
|
||||
minute: value.time().minute(),
|
||||
second: value.time().second(),
|
||||
pad1: 0,
|
||||
nanosecond: value.time().nanosecond(),
|
||||
time_zone: {
|
||||
let (h, m, s) = value.offset().as_hms();
|
||||
if s != 0 {
|
||||
return Err(Self::Error::OffsetWithSeconds(s));
|
||||
}
|
||||
(h as i16 * 60) + (m as i16)
|
||||
},
|
||||
daylight: Daylight::NONE,
|
||||
pad2: 0,
|
||||
};
|
||||
if this.is_valid() {
|
||||
Ok(this)
|
||||
} else {
|
||||
Err(Self::Error::InvalidTime(InvalidTimeError(this)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user