@@ -6,7 +6,7 @@ pub struct Instant(Duration);
66/// When a Timezone is specified, the stored Duration is in UTC. If timezone is unspecified, then
77/// the timezone is assumed to be in UTC.
88///
9- /// UEFI SystemTime is stored as Duration from 1900-01-01-00:00:00
9+ /// UEFI SystemTime is stored as Duration from 1900-01-01-00:00:00 with timezone -1440 as anchor
1010#[ derive( Copy , Clone , PartialEq , Eq , PartialOrd , Ord , Debug , Hash ) ]
1111pub struct SystemTime ( Duration ) ;
1212
@@ -24,6 +24,20 @@ pub const UNIX_EPOCH: SystemTime = SystemTime::from_uefi(r_efi::efi::Time {
2424 pad2 : 0 ,
2525} ) ;
2626
27+ const MAX_UEFI_TIME : SystemTime = SystemTime :: from_uefi ( r_efi:: efi:: Time {
28+ year : 9999 ,
29+ month : 12 ,
30+ day : 31 ,
31+ hour : 23 ,
32+ minute : 59 ,
33+ second : 59 ,
34+ nanosecond : 999_999_999 ,
35+ timezone : 1440 ,
36+ daylight : 0 ,
37+ pad1 : 0 ,
38+ pad2 : 0 ,
39+ } ) ;
40+
2741impl Instant {
2842 pub fn now ( ) -> Instant {
2943 // If we have a timestamp protocol, use it.
@@ -56,6 +70,7 @@ impl SystemTime {
5670 Self ( system_time_internal:: from_uefi ( & t) )
5771 }
5872
73+ #[ expect( dead_code) ]
5974 pub ( crate ) const fn to_uefi ( self , timezone : i16 , daylight : u8 ) -> Option < r_efi:: efi:: Time > {
6075 system_time_internal:: to_uefi ( & self . 0 , timezone, daylight)
6176 }
@@ -73,14 +88,11 @@ impl SystemTime {
7388 let temp = Self ( self . 0 . checked_add ( * other) ?) ;
7489
7590 // Check if can be represented in UEFI
76- if temp. to_uefi ( 0 , 0 ) . is_some ( ) { Some ( temp) } else { None }
91+ if temp <= MAX_UEFI_TIME { Some ( temp) } else { None }
7792 }
7893
7994 pub fn checked_sub_duration ( & self , other : & Duration ) -> Option < SystemTime > {
80- let temp = Self ( self . 0 . checked_sub ( * other) ?) ;
81-
82- // Check if can be represented in UEFI
83- if temp. to_uefi ( 0 , 0 ) . is_some ( ) { Some ( temp) } else { None }
95+ self . 0 . checked_sub ( * other) . map ( Self )
8496 }
8597}
8698
@@ -112,13 +124,30 @@ pub(crate) mod system_time_internal {
112124 Some ( SystemTime :: from_uefi ( t) )
113125 }
114126
127+ /// This algorithm is a modified form of the one described in the post
128+ /// https://blog.reverberate.org/2020/05/12/optimizing-date-algorithms.html
129+ ///
130+ /// The changes are to use 1900-01-01-00:00:00 with timezone -1440 as anchor instead of UNIX
131+ /// epoch used in the original algorithm.
115132 pub ( crate ) const fn from_uefi ( t : & Time ) -> Duration {
116- assert ! ( t. month <= 12 ) ;
117- assert ! ( t. month != 0 ) ;
133+ assert ! ( t. month <= 12 && t. month != 0 ) ;
134+ assert ! ( t. year >= 1900 && t. year <= 9999 ) ;
135+ assert ! ( t. day <= 31 && t. day != 0 ) ;
136+
137+ assert ! ( t. second < 60 ) ;
138+ assert ! ( t. minute < 60 ) ;
139+ assert ! ( t. hour < 24 ) ;
140+ assert ! ( t. nanosecond < 1_000_000_000 ) ;
141+
142+ assert ! (
143+ ( t. timezone <= 1440 && t. timezone >= -1440 )
144+ || t. timezone == r_efi:: efi:: UNSPECIFIED_TIMEZONE
145+ ) ;
118146
119147 const YEAR_BASE : u32 = 4800 ; /* Before min year, multiple of 400. */
120148
121- // Calculate the number of days since 1/1/1900
149+ // Calculate the number of days since 1/1/1900. This is the earliest supported date in UEFI
150+ // time.
122151 // Use 1 March as the start
123152 let ( m_adj, overflow) : ( u32 , bool ) = ( t. month as u32 ) . overflowing_sub ( 3 ) ;
124153 let ( carry, adjust) : ( u32 , u32 ) = if overflow { ( 1 , 12 ) } else { ( 0 , 0 ) } ;
@@ -146,19 +175,20 @@ pub(crate) mod system_time_internal {
146175 Duration :: new ( epoch, t. nanosecond )
147176 }
148177
178+ /// This algorithm is a modifed version of the one described in the post:
179+ /// https://howardhinnant.github.io/date_algorithms.html#clive_from_days
180+ ///
181+ /// The changes are to use 1900-01-01-00:00:00 with timezone -1440 as anchor instead of UNIX
182+ /// epoch used in the original algorithm.
149183 pub ( crate ) const fn to_uefi ( dur : & Duration , timezone : i16 , daylight : u8 ) -> Option < Time > {
150184 // Check timzone validity
151- assert ! ( timezone <= 1440 ) ;
152- assert ! ( timezone >= -1440 ) ;
185+ assert ! ( timezone <= 1440 && timezone >= -1440 ) ;
153186
154- let secs: u64 = if timezone == r_efi:: efi:: UNSPECIFIED_TIMEZONE {
155- dur. as_secs ( )
156- } else {
157- // FIXME: use checked_sub_signed once stablized
158- dur. as_secs ( ) . checked_add_signed ( ( -timezone as i64 ) * SECS_IN_MINUTE as i64 ) . unwrap ( )
159- } ;
187+ // FIXME(#126043): use checked_sub_signed once stablized
188+ let secs =
189+ dur. as_secs ( ) . checked_add_signed ( ( -timezone as i64 ) * SECS_IN_MINUTE as i64 ) . unwrap ( ) ;
160190
161- // Convert to UTC
191+ // Convert to seconds since 1900-01-01-00:00:00 in timezone.
162192 let Some ( secs) = secs. checked_sub ( TIMEZONE_DELTA ) else { return None } ;
163193
164194 let days = secs / SECS_IN_DAY ;
0 commit comments