1818use crate :: Weekday ;
1919use core:: { fmt, i32} ;
2020
21- /// The internal date representation. This also includes the packed `Mdf` value.
21+ /// The internal date representation: `year << 13 | Of`
2222pub ( super ) type DateImpl = i32 ;
2323
2424pub ( super ) const MAX_YEAR : DateImpl = i32:: MAX >> 13 ;
@@ -172,8 +172,9 @@ impl fmt::Debug for YearFlags {
172172 }
173173}
174174
175+ // OL: (ordinal << 1) | leap year flag
175176pub ( super ) const MIN_OL : u32 = 1 << 1 ;
176- pub ( super ) const MAX_OL : u32 = 366 << 1 ; // larger than the non-leap last day `(365 << 1) | 1`
177+ pub ( super ) const MAX_OL : u32 = 366 << 1 ; // `(366 << 1) | 1` would be day 366 in a non-leap year
177178pub ( super ) const MAX_MDL : u32 = ( 12 << 6 ) | ( 31 << 1 ) | 1 ;
178179
179180const XX : i8 = -128 ;
@@ -265,58 +266,70 @@ const OL_TO_MDL: &[u8; MAX_OL as usize + 1] = &[
265266///
266267/// The whole bits except for the least 3 bits are referred as `Ol` (ordinal and leap flag),
267268/// which is an index to the `OL_TO_MDL` lookup table.
269+ ///
270+ /// The methods implemented on `Of` always return a valid value.
268271#[ derive( PartialEq , PartialOrd , Copy , Clone ) ]
269- pub ( super ) struct Of ( pub ( crate ) u32 ) ;
272+ pub ( super ) struct Of ( u32 ) ;
270273
271274impl Of {
272275 #[ inline]
273276 pub ( super ) const fn new ( ordinal : u32 , YearFlags ( flags) : YearFlags ) -> Option < Of > {
274- match ordinal <= 366 {
275- true => Some ( Of ( ( ordinal << 4 ) | flags as u32 ) ) ,
276- false => None ,
277- }
277+ let of = Of ( ( ordinal << 4 ) | flags as u32 ) ;
278+ of. validate ( )
279+ }
280+
281+ pub ( super ) const fn from_date_impl ( date_impl : DateImpl ) -> Of {
282+ // We assume the value in the `DateImpl` is valid.
283+ Of ( ( date_impl & 0b1_1111_1111_1111 ) as u32 )
278284 }
279285
280286 #[ inline]
281- pub ( super ) const fn from_mdf ( Mdf ( mdf) : Mdf ) -> Of {
287+ pub ( super ) const fn from_mdf ( Mdf ( mdf) : Mdf ) -> Option < Of > {
282288 let mdl = mdf >> 3 ;
283- if mdl <= MAX_MDL {
284- // Array is indexed from `[1..=MAX_MDL]`, with a `0` index having a meaningless value.
285- let v = MDL_TO_OL [ mdl as usize ] ;
286- Of ( mdf. wrapping_sub ( ( v as i32 as u32 & 0x3ff ) << 3 ) )
287- } else {
288- // Panicking here would be reasonable, but we are just going on with a safe value.
289- Of ( 0 )
289+ if mdl > MAX_MDL {
290+ // Panicking on out-of-bounds indexing would be reasonable, but just return `None`.
291+ return None ;
290292 }
293+ // Array is indexed from `[1..=MAX_MDL]`, with a `0` index having a meaningless value.
294+ let v = MDL_TO_OL [ mdl as usize ] ;
295+ let of = Of ( mdf. wrapping_sub ( ( v as i32 as u32 & 0x3ff ) << 3 ) ) ;
296+ of. validate ( )
291297 }
292298
293299 #[ inline]
294- pub ( super ) const fn valid ( & self ) -> bool {
295- let Of ( of) = * self ;
296- let ol = of >> 3 ;
297- ol >= MIN_OL && ol <= MAX_OL
300+ pub ( super ) const fn inner ( & self ) -> u32 {
301+ self . 0
298302 }
299303
304+ /// Returns `(ordinal << 1) | leap-year-flag`.
300305 #[ inline]
301- pub ( super ) const fn ordinal ( & self ) -> u32 {
302- let Of ( of) = * self ;
303- of >> 4
306+ const fn ol ( & self ) -> u32 {
307+ self . 0 >> 3
304308 }
305309
306310 #[ inline]
307- pub ( super ) const fn with_ordinal ( & self , ordinal : u32 ) -> Option < Of > {
308- if ordinal > 366 {
309- return None ;
311+ const fn validate ( self ) -> Option < Of > {
312+ let ol = self . ol ( ) ;
313+ match ol >= MIN_OL && ol <= MAX_OL {
314+ true => Some ( self ) ,
315+ false => None ,
310316 }
317+ }
311318
312- let Of ( of) = * self ;
313- Some ( Of ( ( of & 0b1111 ) | ( ordinal << 4 ) ) )
319+ #[ inline]
320+ pub ( super ) const fn ordinal ( & self ) -> u32 {
321+ self . 0 >> 4
322+ }
323+
324+ #[ inline]
325+ pub ( super ) const fn with_ordinal ( & self , ordinal : u32 ) -> Option < Of > {
326+ let of = Of ( ( ordinal << 4 ) | ( self . 0 & 0b1111 ) ) ;
327+ of. validate ( )
314328 }
315329
316330 #[ inline]
317331 pub ( super ) const fn flags ( & self ) -> YearFlags {
318- let Of ( of) = * self ;
319- YearFlags ( ( of & 0b1111 ) as u8 )
332+ YearFlags ( ( self . 0 & 0b1111 ) as u8 )
320333 }
321334
322335 #[ inline]
@@ -339,16 +352,20 @@ impl Of {
339352 Mdf :: from_of ( * self )
340353 }
341354
355+ /// Returns an `Of` with the next day, or `None` if this is the last day of the year.
342356 #[ inline]
343- pub ( super ) const fn succ ( & self ) -> Of {
344- let Of ( of ) = * self ;
345- Of ( of + ( 1 << 4 ) )
357+ pub ( super ) const fn succ ( & self ) -> Option < Of > {
358+ let of = Of ( self . 0 + ( 1 << 4 ) ) ;
359+ of . validate ( )
346360 }
347361
362+ /// Returns an `Of` with the previous day, or `None` if this is the first day of the year.
348363 #[ inline]
349- pub ( super ) const fn pred ( & self ) -> Of {
350- let Of ( of) = * self ;
351- Of ( of - ( 1 << 4 ) )
364+ pub ( super ) const fn pred ( & self ) -> Option < Of > {
365+ if self . ordinal ( ) == 1 {
366+ return None ;
367+ }
368+ Some ( Of ( self . 0 - ( 1 << 4 ) ) )
352369 }
353370}
354371
@@ -370,13 +387,17 @@ impl fmt::Debug for Of {
370387/// The whole bits except for the least 3 bits are referred as `Mdl`
371388/// (month, day of month and leap flag),
372389/// which is an index to the `MDL_TO_OL` lookup table.
390+ ///
391+ /// The methods implemented on `Mdf` do not always return a valid value.
392+ /// Dates than can't exist, like February 30, can still be represented.
393+ /// Use `Mdl::valid` to check whether the date is valid.
373394#[ derive( PartialEq , PartialOrd , Copy , Clone ) ]
374395pub ( super ) struct Mdf ( u32 ) ;
375396
376397impl Mdf {
377398 #[ inline]
378399 pub ( super ) const fn new ( month : u32 , day : u32 , YearFlags ( flags) : YearFlags ) -> Option < Mdf > {
379- match month <= 12 && day <= 31 {
400+ match month >= 1 && month <= 12 && day >= 1 && day <= 31 {
380401 true => Some ( Mdf ( ( month << 9 ) | ( day << 4 ) | flags as u32 ) ) ,
381402 false => None ,
382403 }
@@ -447,7 +468,7 @@ impl Mdf {
447468
448469 #[ cfg_attr( feature = "cargo-clippy" , allow( clippy:: wrong_self_convention) ) ]
449470 #[ inline]
450- pub ( super ) const fn to_of ( & self ) -> Of {
471+ pub ( super ) const fn to_of ( & self ) -> Option < Of > {
451472 Of :: from_mdf ( * self )
452473 }
453474}
@@ -542,7 +563,7 @@ mod tests {
542563 } ;
543564
544565 assert ! (
545- of. valid ( ) == expected,
566+ of. validate ( ) . is_some ( ) == expected,
546567 "ordinal {} = {:?} should be {} for dominical year {:?}" ,
547568 ordinal,
548569 of,
@@ -662,8 +683,7 @@ mod tests {
662683 fn test_of_fields ( ) {
663684 for & flags in FLAGS . iter ( ) {
664685 for ordinal in range_inclusive ( 1u32 , 366 ) {
665- let of = Of :: new ( ordinal, flags) . unwrap ( ) ;
666- if of. valid ( ) {
686+ if let Some ( of) = Of :: new ( ordinal, flags) {
667687 assert_eq ! ( of. ordinal( ) , ordinal) ;
668688 }
669689 }
@@ -676,14 +696,9 @@ mod tests {
676696 let of = Of :: new ( ordinal, flags) . unwrap ( ) ;
677697
678698 for ordinal in range_inclusive ( 0u32 , 1024 ) {
679- let of = match of. with_ordinal ( ordinal) {
680- Some ( of) => of,
681- None if ordinal > 366 => continue ,
682- None => panic ! ( "failed to create Of with ordinal {}" , ordinal) ,
683- } ;
684-
685- assert_eq ! ( of. valid( ) , Of :: new( ordinal, flags) . unwrap( ) . valid( ) ) ;
686- if of. valid ( ) {
699+ let of = of. with_ordinal ( ordinal) ;
700+ assert_eq ! ( of, Of :: new( ordinal, flags) ) ;
701+ if let Some ( of) = of {
687702 assert_eq ! ( of. ordinal( ) , ordinal) ;
688703 }
689704 }
@@ -808,25 +823,25 @@ mod tests {
808823 #[ test]
809824 fn test_of_to_mdf ( ) {
810825 for i in range_inclusive ( 0u32 , 8192 ) {
811- let of = Of ( i) ;
812- assert_eq ! ( of. valid( ) , of. to_mdf( ) . valid( ) ) ;
826+ if let Some ( of) = Of ( i) . validate ( ) {
827+ assert ! ( of. to_mdf( ) . valid( ) ) ;
828+ }
813829 }
814830 }
815831
816832 #[ test]
817833 fn test_mdf_to_of ( ) {
818834 for i in range_inclusive ( 0u32 , 8192 ) {
819835 let mdf = Mdf ( i) ;
820- assert_eq ! ( mdf. valid( ) , mdf. to_of( ) . valid ( ) ) ;
836+ assert_eq ! ( mdf. valid( ) , mdf. to_of( ) . is_some ( ) ) ;
821837 }
822838 }
823839
824840 #[ test]
825841 fn test_of_to_mdf_to_of ( ) {
826842 for i in range_inclusive ( 0u32 , 8192 ) {
827- let of = Of ( i) ;
828- if of. valid ( ) {
829- assert_eq ! ( of, of. to_mdf( ) . to_of( ) ) ;
843+ if let Some ( of) = Of ( i) . validate ( ) {
844+ assert_eq ! ( of, of. to_mdf( ) . to_of( ) . unwrap( ) ) ;
830845 }
831846 }
832847 }
@@ -836,11 +851,40 @@ mod tests {
836851 for i in range_inclusive ( 0u32 , 8192 ) {
837852 let mdf = Mdf ( i) ;
838853 if mdf. valid ( ) {
839- assert_eq ! ( mdf, mdf. to_of( ) . to_mdf( ) ) ;
854+ assert_eq ! ( mdf, mdf. to_of( ) . unwrap ( ) . to_mdf( ) ) ;
840855 }
841856 }
842857 }
843858
859+ #[ test]
860+ fn test_invalid_returns_none ( ) {
861+ let regular_year = YearFlags :: from_year ( 2023 ) ;
862+ let leap_year = YearFlags :: from_year ( 2024 ) ;
863+ assert ! ( Of :: new( 0 , regular_year) . is_none( ) ) ;
864+ assert ! ( Of :: new( 366 , regular_year) . is_none( ) ) ;
865+ assert ! ( Of :: new( 366 , leap_year) . is_some( ) ) ;
866+ assert ! ( Of :: new( 367 , regular_year) . is_none( ) ) ;
867+
868+ assert ! ( Mdf :: new( 0 , 1 , regular_year) . is_none( ) ) ;
869+ assert ! ( Mdf :: new( 13 , 1 , regular_year) . is_none( ) ) ;
870+ assert ! ( Mdf :: new( 1 , 0 , regular_year) . is_none( ) ) ;
871+ assert ! ( Mdf :: new( 1 , 32 , regular_year) . is_none( ) ) ;
872+ assert ! ( Mdf :: new( 2 , 31 , regular_year) . is_some( ) ) ;
873+
874+ assert ! ( Of :: from_mdf( Mdf :: new( 2 , 30 , regular_year) . unwrap( ) ) . is_none( ) ) ;
875+ assert ! ( Of :: from_mdf( Mdf :: new( 2 , 30 , leap_year) . unwrap( ) ) . is_none( ) ) ;
876+ assert ! ( Of :: from_mdf( Mdf :: new( 2 , 29 , regular_year) . unwrap( ) ) . is_none( ) ) ;
877+ assert ! ( Of :: from_mdf( Mdf :: new( 2 , 29 , leap_year) . unwrap( ) ) . is_some( ) ) ;
878+ assert ! ( Of :: from_mdf( Mdf :: new( 2 , 28 , regular_year) . unwrap( ) ) . is_some( ) ) ;
879+
880+ assert ! ( Of :: new( 365 , regular_year) . unwrap( ) . succ( ) . is_none( ) ) ;
881+ assert ! ( Of :: new( 365 , leap_year) . unwrap( ) . succ( ) . is_some( ) ) ;
882+ assert ! ( Of :: new( 366 , leap_year) . unwrap( ) . succ( ) . is_none( ) ) ;
883+
884+ assert ! ( Of :: new( 1 , regular_year) . unwrap( ) . pred( ) . is_none( ) ) ;
885+ assert ! ( Of :: new( 1 , leap_year) . unwrap( ) . pred( ) . is_none( ) ) ;
886+ }
887+
844888 #[ test]
845889 fn test_weekday_from_u32_mod7 ( ) {
846890 for i in 0 ..=1000 {
0 commit comments