Skip to content

Commit 9b40c51

Browse files
committed
uefi-raw: impl TryFrom for OffsetDateTime to Time
1 parent 15262aa commit 9b40c51

File tree

2 files changed

+146
-4
lines changed

2 files changed

+146
-4
lines changed

uefi-raw/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
`OffsetDateTime` from the [time crate](https://crates.io/crates/time).
1717
- `Time::to_offset_date_time`
1818
- `Time::to_offset_date_time_with_default_timezone`
19+
- implemented `TryFrom` from `OffsetDateTime` for `Time`
1920

2021
## Changed
2122
- Switched `*const Self` to `*mut Self` in `SerialIoProtocol::set_attributes()`

uefi-raw/src/time.rs

Lines changed: 145 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ impl error::Error for ToOffsetDateTimeError {
7272
///
7373
/// - [`Time::to_offset_date_time`]
7474
/// - [`Time::to_offset_date_time_with_default_timezone`]
75+
/// - [`TryFrom`] from [`OffsetDateTime`] to [`Time`]
7576
///
7677
/// [time](https://crates.io/crates/time)
7778
#[derive(Debug, Default, Copy, Clone, Eq)]
@@ -355,6 +356,44 @@ bitflags! {
355356
}
356357
}
357358

359+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
360+
pub enum FromOffsetDateTimeError {
361+
/// The year is not representable as `u16`.
362+
InvalidYear(i32),
363+
/// Time zone offsets with seconds are not representable.
364+
OffsetWithSeconds(i8),
365+
}
366+
367+
#[cfg(feature = "time")]
368+
impl TryFrom<OffsetDateTime> for Time {
369+
type Error = FromOffsetDateTimeError;
370+
371+
fn try_from(value: OffsetDateTime) -> Result<Self, Self::Error> {
372+
let this = Self {
373+
year: u16::try_from(value.date().year())
374+
.map_err(|_| Self::Error::InvalidYear(value.date().year()))?,
375+
// No checks needed: underlying type has repr `u8`
376+
month: value.date().month() as u8,
377+
day: value.date().day(),
378+
hour: value.time().hour(),
379+
minute: value.time().minute(),
380+
second: value.time().second(),
381+
pad1: 0,
382+
nanosecond: value.time().nanosecond(),
383+
time_zone: {
384+
let (h, m, s) = value.offset().as_hms();
385+
if s != 0 {
386+
return Err(Self::Error::OffsetWithSeconds(s));
387+
}
388+
(h as i16 * 60) + (m as i16)
389+
},
390+
daylight: Daylight::NONE,
391+
pad2: 0,
392+
};
393+
Ok(this)
394+
}
395+
}
396+
358397
#[cfg(test)]
359398
mod tests {
360399
extern crate alloc;
@@ -388,9 +427,9 @@ mod tests {
388427
}
389428

390429
#[cfg(all(test, feature = "time"))]
391-
mod time_crate_tests {
430+
mod to_offset_date_time_tests {
392431
use super::*;
393-
use time::{Date, Month, OffsetDateTime, Time as TimeOfDay, UtcOffset};
432+
use time::{Date, Month, OffsetDateTime, UtcOffset};
394433

395434
/// A valid base time for testing
396435
fn valid_base_time() -> Time {
@@ -416,7 +455,7 @@ mod time_crate_tests {
416455

417456
let expected = OffsetDateTime::new_in_offset(
418457
Date::from_calendar_date(2024, Month::March, 15).unwrap(),
419-
TimeOfDay::from_hms_nano(12, 34, 56, 123_456_789).unwrap(),
458+
time::Time::from_hms_nano(12, 34, 56, 123_456_789).unwrap(),
420459
UtcOffset::UTC,
421460
);
422461

@@ -432,7 +471,7 @@ mod time_crate_tests {
432471

433472
let expected = OffsetDateTime::new_in_offset(
434473
Date::from_calendar_date(2024, Month::March, 15).unwrap(),
435-
TimeOfDay::from_hms_nano(12, 34, 56, 123_456_789).unwrap(),
474+
time::Time::from_hms_nano(12, 34, 56, 123_456_789).unwrap(),
436475
offset,
437476
);
438477

@@ -603,3 +642,105 @@ mod time_crate_tests {
603642
assert_eq!(odt.offset(), offset);
604643
}
605644
}
645+
646+
#[cfg(all(test, feature = "time"))]
647+
mod from_offset_datetime_tests {
648+
use super::*;
649+
use time::{Date, Month, OffsetDateTime, Time as TimeOfDay, UtcOffset};
650+
651+
#[test]
652+
fn test_normal_conversion_utc() {
653+
let odt = OffsetDateTime::new_in_offset(
654+
Date::from_calendar_date(2024, Month::March, 15).unwrap(),
655+
TimeOfDay::from_hms_nano(12, 34, 56, 123_456_789).unwrap(),
656+
UtcOffset::UTC,
657+
);
658+
659+
let t = Time::try_from(odt).unwrap();
660+
assert_eq!(t.year, 2024);
661+
assert_eq!(t.month, 3);
662+
assert_eq!(t.day, 15);
663+
assert_eq!(t.hour, 12);
664+
assert_eq!(t.minute, 34);
665+
assert_eq!(t.second, 56);
666+
assert_eq!(t.nanosecond, 123_456_789);
667+
assert_eq!(t.time_zone, 0);
668+
}
669+
670+
#[test]
671+
fn test_conversion_positive_offset() {
672+
let offset = UtcOffset::from_hms(1, 30, 0).unwrap(); // +1:30
673+
let odt = OffsetDateTime::new_in_offset(
674+
Date::from_calendar_date(2024, Month::March, 15).unwrap(),
675+
TimeOfDay::from_hms(8, 0, 0).unwrap(),
676+
offset,
677+
);
678+
679+
let t = Time::try_from(odt).unwrap();
680+
assert_eq!(t.time_zone, 90); // 1*60 + 30
681+
}
682+
683+
#[test]
684+
fn test_conversion_negative_offset() {
685+
let offset = UtcOffset::from_hms(-2, -15, 0).unwrap(); // -2:15
686+
let odt = OffsetDateTime::new_in_offset(
687+
Date::from_calendar_date(2024, Month::March, 15).unwrap(),
688+
TimeOfDay::from_hms(20, 0, 0).unwrap(),
689+
offset,
690+
);
691+
692+
let t = Time::try_from(odt).unwrap();
693+
assert_eq!(t.time_zone, -135); // -2*60 + -15
694+
}
695+
696+
#[test]
697+
fn test_offset_with_seconds_should_fail() {
698+
let offset = UtcOffset::from_hms(0, 0, 1).unwrap(); // +0:0:1
699+
let odt = OffsetDateTime::new_in_offset(
700+
Date::from_calendar_date(2024, Month::March, 15).unwrap(),
701+
TimeOfDay::from_hms(12, 0, 0).unwrap(),
702+
offset,
703+
);
704+
705+
let err = Time::try_from(odt).unwrap_err();
706+
assert!(matches!(err, FromOffsetDateTimeError::OffsetWithSeconds(1)));
707+
}
708+
709+
#[test]
710+
fn test_invalid_year_negative() {
711+
let odt = OffsetDateTime::new_in_offset(
712+
Date::from_calendar_date(-1, Month::January, 1).unwrap(),
713+
TimeOfDay::from_hms(0, 0, 0).unwrap(),
714+
UtcOffset::UTC,
715+
);
716+
717+
let err = Time::try_from(odt).unwrap_err();
718+
assert!(matches!(err, FromOffsetDateTimeError::InvalidYear(-1)));
719+
}
720+
721+
#[test]
722+
fn test_maximum_supported_year() {
723+
let odt = OffsetDateTime::new_in_offset(
724+
Date::from_calendar_date(9999, Month::December, 31).unwrap(),
725+
TimeOfDay::from_hms(23, 59, 59).unwrap(),
726+
UtcOffset::UTC,
727+
);
728+
729+
let t = Time::try_from(odt).unwrap();
730+
assert_eq!(t.year, 9999);
731+
assert_eq!(t.month, 12);
732+
assert_eq!(t.day, 31);
733+
}
734+
735+
#[test]
736+
fn test_nanosecond_boundary() {
737+
let odt = OffsetDateTime::new_in_offset(
738+
Date::from_calendar_date(2024, Month::March, 15).unwrap(),
739+
TimeOfDay::from_hms_nano(12, 0, 0, 999_999_999).unwrap(),
740+
UtcOffset::UTC,
741+
);
742+
743+
let t = Time::try_from(odt).unwrap();
744+
assert_eq!(t.nanosecond, 999_999_999);
745+
}
746+
}

0 commit comments

Comments
 (0)