18
18
use crate :: Weekday ;
19
19
use core:: { fmt, i32} ;
20
20
21
- /// The internal date representation. This also includes the packed `Mdf` value.
21
+ /// The internal date representation: `year << 13 | Of`
22
22
pub ( super ) type DateImpl = i32 ;
23
23
24
24
pub ( super ) const MAX_YEAR : DateImpl = i32:: MAX >> 13 ;
@@ -172,8 +172,9 @@ impl fmt::Debug for YearFlags {
172
172
}
173
173
}
174
174
175
+ // OL: (ordinal << 1) | leap year flag
175
176
pub ( 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
177
178
pub ( super ) const MAX_MDL : u32 = ( 12 << 6 ) | ( 31 << 1 ) | 1 ;
178
179
179
180
const XX : i8 = -128 ;
@@ -265,58 +266,70 @@ const OL_TO_MDL: &[u8; MAX_OL as usize + 1] = &[
265
266
///
266
267
/// The whole bits except for the least 3 bits are referred as `Ol` (ordinal and leap flag),
267
268
/// which is an index to the `OL_TO_MDL` lookup table.
269
+ ///
270
+ /// The methods implemented on `Of` always return a valid value.
268
271
#[ derive( PartialEq , PartialOrd , Copy , Clone ) ]
269
- pub ( super ) struct Of ( pub ( crate ) u32 ) ;
272
+ pub ( super ) struct Of ( u32 ) ;
270
273
271
274
impl Of {
272
275
#[ inline]
273
276
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 )
278
284
}
279
285
280
286
#[ inline]
281
- pub ( super ) const fn from_mdf ( Mdf ( mdf) : Mdf ) -> Of {
287
+ pub ( super ) const fn from_mdf ( Mdf ( mdf) : Mdf ) -> Option < Of > {
282
288
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 ;
290
292
}
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 ( )
291
297
}
292
298
293
299
#[ 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
298
302
}
299
303
304
+ /// Returns `(ordinal << 1) | leap-year-flag`.
300
305
#[ 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
304
308
}
305
309
306
310
#[ 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 ,
310
316
}
317
+ }
311
318
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 ( )
314
328
}
315
329
316
330
#[ inline]
317
331
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 )
320
333
}
321
334
322
335
#[ inline]
@@ -339,16 +352,20 @@ impl Of {
339
352
Mdf :: from_of ( * self )
340
353
}
341
354
355
+ /// Returns an `Of` with the next day, or `None` if this is the last day of the year.
342
356
#[ 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 ( )
346
360
}
347
361
362
+ /// Returns an `Of` with the previous day, or `None` if this is the first day of the year.
348
363
#[ 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
+ match self . ordinal ( ) {
366
+ 1 => None ,
367
+ _ => Some ( Of ( self . 0 - ( 1 << 4 ) ) ) ,
368
+ }
352
369
}
353
370
}
354
371
@@ -370,13 +387,17 @@ impl fmt::Debug for Of {
370
387
/// The whole bits except for the least 3 bits are referred as `Mdl`
371
388
/// (month, day of month and leap flag),
372
389
/// 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 that can't exist, like February 30, can still be represented.
393
+ /// Use `Mdl::valid` to check whether the date is valid.
373
394
#[ derive( PartialEq , PartialOrd , Copy , Clone ) ]
374
395
pub ( super ) struct Mdf ( u32 ) ;
375
396
376
397
impl Mdf {
377
398
#[ inline]
378
399
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 {
380
401
true => Some ( Mdf ( ( month << 9 ) | ( day << 4 ) | flags as u32 ) ) ,
381
402
false => None ,
382
403
}
@@ -447,7 +468,7 @@ impl Mdf {
447
468
448
469
#[ cfg_attr( feature = "cargo-clippy" , allow( clippy:: wrong_self_convention) ) ]
449
470
#[ inline]
450
- pub ( super ) const fn to_of ( & self ) -> Of {
471
+ pub ( super ) const fn to_of ( & self ) -> Option < Of > {
451
472
Of :: from_mdf ( * self )
452
473
}
453
474
}
@@ -542,7 +563,7 @@ mod tests {
542
563
} ;
543
564
544
565
assert ! (
545
- of. valid ( ) == expected,
566
+ of. validate ( ) . is_some ( ) == expected,
546
567
"ordinal {} = {:?} should be {} for dominical year {:?}" ,
547
568
ordinal,
548
569
of,
@@ -662,8 +683,7 @@ mod tests {
662
683
fn test_of_fields ( ) {
663
684
for & flags in FLAGS . iter ( ) {
664
685
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) {
667
687
assert_eq ! ( of. ordinal( ) , ordinal) ;
668
688
}
669
689
}
@@ -676,14 +696,9 @@ mod tests {
676
696
let of = Of :: new ( ordinal, flags) . unwrap ( ) ;
677
697
678
698
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 {
687
702
assert_eq ! ( of. ordinal( ) , ordinal) ;
688
703
}
689
704
}
@@ -808,25 +823,25 @@ mod tests {
808
823
#[ test]
809
824
fn test_of_to_mdf ( ) {
810
825
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
+ }
813
829
}
814
830
}
815
831
816
832
#[ test]
817
833
fn test_mdf_to_of ( ) {
818
834
for i in range_inclusive ( 0u32 , 8192 ) {
819
835
let mdf = Mdf ( i) ;
820
- assert_eq ! ( mdf. valid( ) , mdf. to_of( ) . valid ( ) ) ;
836
+ assert_eq ! ( mdf. valid( ) , mdf. to_of( ) . is_some ( ) ) ;
821
837
}
822
838
}
823
839
824
840
#[ test]
825
841
fn test_of_to_mdf_to_of ( ) {
826
842
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( ) ) ;
830
845
}
831
846
}
832
847
}
@@ -836,11 +851,40 @@ mod tests {
836
851
for i in range_inclusive ( 0u32 , 8192 ) {
837
852
let mdf = Mdf ( i) ;
838
853
if mdf. valid ( ) {
839
- assert_eq ! ( mdf, mdf. to_of( ) . to_mdf( ) ) ;
854
+ assert_eq ! ( mdf, mdf. to_of( ) . unwrap ( ) . to_mdf( ) ) ;
840
855
}
841
856
}
842
857
}
843
858
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
+
844
888
#[ test]
845
889
fn test_weekday_from_u32_mod7 ( ) {
846
890
for i in 0 ..=1000 {
0 commit comments