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,20 +266,25 @@ 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
289
if mdl <= MAX_MDL {
284
290
// Array is indexed from `[1..=MAX_MDL]`, with a `0` index having a meaningless value.
@@ -288,35 +294,45 @@ impl Of {
288
294
// Panicking here would be reasonable, but we are just going on with a safe value.
289
295
Of ( 0 )
290
296
}
297
+ let v = MDL_TO_OL [ mdl as usize ] ;
298
+ let of = Of ( mdf. wrapping_sub ( ( v as i32 as u32 & 0x3ff ) << 3 ) ) ;
299
+ of. validate ( )
291
300
}
292
301
293
302
#[ 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
303
+ pub ( super ) const fn inner ( & self ) -> u32 {
304
+ self . 0
298
305
}
299
306
307
+ /// Returns `(ordinal << 1) | leap-year-flag`.
300
308
#[ inline]
301
- pub ( super ) const fn ordinal ( & self ) -> u32 {
302
- let Of ( of) = * self ;
303
- of >> 4
309
+ const fn ol ( & self ) -> u32 {
310
+ self . 0 >> 3
304
311
}
305
312
306
313
#[ inline]
307
- pub ( super ) const fn with_ordinal ( & self , ordinal : u32 ) -> Option < Of > {
308
- if ordinal > 366 {
309
- return None ;
314
+ const fn validate ( self ) -> Option < Of > {
315
+ let ol = self . ol ( ) ;
316
+ match ol >= MIN_OL && ol <= MAX_OL {
317
+ true => Some ( self ) ,
318
+ false => None ,
310
319
}
320
+ }
311
321
312
- let Of ( of) = * self ;
313
- Some ( Of ( ( of & 0b1111 ) | ( ordinal << 4 ) ) )
322
+ #[ inline]
323
+ pub ( super ) const fn ordinal ( & self ) -> u32 {
324
+ self . 0 >> 4
325
+ }
326
+
327
+ #[ inline]
328
+ pub ( super ) const fn with_ordinal ( & self , ordinal : u32 ) -> Option < Of > {
329
+ let of = Of ( ( ordinal << 4 ) | ( self . 0 & 0b1111 ) ) ;
330
+ of. validate ( )
314
331
}
315
332
316
333
#[ inline]
317
334
pub ( super ) const fn flags ( & self ) -> YearFlags {
318
- let Of ( of) = * self ;
319
- YearFlags ( ( of & 0b1111 ) as u8 )
335
+ YearFlags ( ( self . 0 & 0b1111 ) as u8 )
320
336
}
321
337
322
338
#[ inline]
@@ -339,16 +355,20 @@ impl Of {
339
355
Mdf :: from_of ( * self )
340
356
}
341
357
358
+ /// Returns an `Of` with the next day, or `None` if this is the last day of the year.
342
359
#[ inline]
343
- pub ( super ) const fn succ ( & self ) -> Of {
344
- let Of ( of ) = * self ;
345
- Of ( of + ( 1 << 4 ) )
360
+ pub ( super ) const fn succ ( & self ) -> Option < Of > {
361
+ let of = Of ( self . 0 + ( 1 << 4 ) ) ;
362
+ of . validate ( )
346
363
}
347
364
365
+ /// Returns an `Of` with the previous day, or `None` if this is the first day of the year.
348
366
#[ inline]
349
- pub ( super ) const fn pred ( & self ) -> Of {
350
- let Of ( of) = * self ;
351
- Of ( of - ( 1 << 4 ) )
367
+ pub ( super ) const fn pred ( & self ) -> Option < Of > {
368
+ if self . ordinal ( ) == 1 {
369
+ return None ;
370
+ }
371
+ Some ( Of ( self . 0 - ( 1 << 4 ) ) )
352
372
}
353
373
}
354
374
@@ -370,13 +390,17 @@ impl fmt::Debug for Of {
370
390
/// The whole bits except for the least 3 bits are referred as `Mdl`
371
391
/// (month, day of month and leap flag),
372
392
/// which is an index to the `MDL_TO_OL` lookup table.
393
+ ///
394
+ /// The methods implemented on `Mdf` do not always return a valid value.
395
+ /// Dates than can't exist, like February 30, can still be represented.
396
+ /// Use `Mdl::valid` to check whether the date is valid.
373
397
#[ derive( PartialEq , PartialOrd , Copy , Clone ) ]
374
398
pub ( super ) struct Mdf ( u32 ) ;
375
399
376
400
impl Mdf {
377
401
#[ inline]
378
402
pub ( super ) const fn new ( month : u32 , day : u32 , YearFlags ( flags) : YearFlags ) -> Option < Mdf > {
379
- match month <= 12 && day <= 31 {
403
+ match month >= 1 && month <= 12 && day >= 1 && day <= 31 {
380
404
true => Some ( Mdf ( ( month << 9 ) | ( day << 4 ) | flags as u32 ) ) ,
381
405
false => None ,
382
406
}
@@ -447,7 +471,7 @@ impl Mdf {
447
471
448
472
#[ cfg_attr( feature = "cargo-clippy" , allow( clippy:: wrong_self_convention) ) ]
449
473
#[ inline]
450
- pub ( super ) const fn to_of ( & self ) -> Of {
474
+ pub ( super ) const fn to_of ( & self ) -> Option < Of > {
451
475
Of :: from_mdf ( * self )
452
476
}
453
477
}
@@ -542,7 +566,7 @@ mod tests {
542
566
} ;
543
567
544
568
assert ! (
545
- of. valid ( ) == expected,
569
+ of. validate ( ) . is_some ( ) == expected,
546
570
"ordinal {} = {:?} should be {} for dominical year {:?}" ,
547
571
ordinal,
548
572
of,
@@ -662,8 +686,7 @@ mod tests {
662
686
fn test_of_fields ( ) {
663
687
for & flags in FLAGS . iter ( ) {
664
688
for ordinal in range_inclusive ( 1u32 , 366 ) {
665
- let of = Of :: new ( ordinal, flags) . unwrap ( ) ;
666
- if of. valid ( ) {
689
+ if let Some ( of) = Of :: new ( ordinal, flags) {
667
690
assert_eq ! ( of. ordinal( ) , ordinal) ;
668
691
}
669
692
}
@@ -676,14 +699,9 @@ mod tests {
676
699
let of = Of :: new ( ordinal, flags) . unwrap ( ) ;
677
700
678
701
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 ( ) {
702
+ let of = of. with_ordinal ( ordinal) ;
703
+ assert_eq ! ( of, Of :: new( ordinal, flags) ) ;
704
+ if let Some ( of) = of {
687
705
assert_eq ! ( of. ordinal( ) , ordinal) ;
688
706
}
689
707
}
@@ -808,25 +826,25 @@ mod tests {
808
826
#[ test]
809
827
fn test_of_to_mdf ( ) {
810
828
for i in range_inclusive ( 0u32 , 8192 ) {
811
- let of = Of ( i) ;
812
- assert_eq ! ( of. valid( ) , of. to_mdf( ) . valid( ) ) ;
829
+ if let Some ( of) = Of ( i) . validate ( ) {
830
+ assert ! ( of. to_mdf( ) . valid( ) ) ;
831
+ }
813
832
}
814
833
}
815
834
816
835
#[ test]
817
836
fn test_mdf_to_of ( ) {
818
837
for i in range_inclusive ( 0u32 , 8192 ) {
819
838
let mdf = Mdf ( i) ;
820
- assert_eq ! ( mdf. valid( ) , mdf. to_of( ) . valid ( ) ) ;
839
+ assert_eq ! ( mdf. valid( ) , mdf. to_of( ) . is_some ( ) ) ;
821
840
}
822
841
}
823
842
824
843
#[ test]
825
844
fn test_of_to_mdf_to_of ( ) {
826
845
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( ) ) ;
846
+ if let Some ( of) = Of ( i) . validate ( ) {
847
+ assert_eq ! ( of, of. to_mdf( ) . to_of( ) . unwrap( ) ) ;
830
848
}
831
849
}
832
850
}
@@ -836,11 +854,40 @@ mod tests {
836
854
for i in range_inclusive ( 0u32 , 8192 ) {
837
855
let mdf = Mdf ( i) ;
838
856
if mdf. valid ( ) {
839
- assert_eq ! ( mdf, mdf. to_of( ) . to_mdf( ) ) ;
857
+ assert_eq ! ( mdf, mdf. to_of( ) . unwrap ( ) . to_mdf( ) ) ;
840
858
}
841
859
}
842
860
}
843
861
862
+ #[ test]
863
+ fn test_invalid_returns_none ( ) {
864
+ let regular_year = YearFlags :: from_year ( 2023 ) ;
865
+ let leap_year = YearFlags :: from_year ( 2024 ) ;
866
+ assert ! ( Of :: new( 0 , regular_year) . is_none( ) ) ;
867
+ assert ! ( Of :: new( 366 , regular_year) . is_none( ) ) ;
868
+ assert ! ( Of :: new( 366 , leap_year) . is_some( ) ) ;
869
+ assert ! ( Of :: new( 367 , regular_year) . is_none( ) ) ;
870
+
871
+ assert ! ( Mdf :: new( 0 , 1 , regular_year) . is_none( ) ) ;
872
+ assert ! ( Mdf :: new( 13 , 1 , regular_year) . is_none( ) ) ;
873
+ assert ! ( Mdf :: new( 1 , 0 , regular_year) . is_none( ) ) ;
874
+ assert ! ( Mdf :: new( 1 , 32 , regular_year) . is_none( ) ) ;
875
+ assert ! ( Mdf :: new( 2 , 31 , regular_year) . is_some( ) ) ;
876
+
877
+ assert ! ( Of :: from_mdf( Mdf :: new( 2 , 30 , regular_year) . unwrap( ) ) . is_none( ) ) ;
878
+ assert ! ( Of :: from_mdf( Mdf :: new( 2 , 30 , leap_year) . unwrap( ) ) . is_none( ) ) ;
879
+ assert ! ( Of :: from_mdf( Mdf :: new( 2 , 29 , regular_year) . unwrap( ) ) . is_none( ) ) ;
880
+ assert ! ( Of :: from_mdf( Mdf :: new( 2 , 29 , leap_year) . unwrap( ) ) . is_some( ) ) ;
881
+ assert ! ( Of :: from_mdf( Mdf :: new( 2 , 28 , regular_year) . unwrap( ) ) . is_some( ) ) ;
882
+
883
+ assert ! ( Of :: new( 365 , regular_year) . unwrap( ) . succ( ) . is_none( ) ) ;
884
+ assert ! ( Of :: new( 365 , leap_year) . unwrap( ) . succ( ) . is_some( ) ) ;
885
+ assert ! ( Of :: new( 366 , leap_year) . unwrap( ) . succ( ) . is_none( ) ) ;
886
+
887
+ assert ! ( Of :: new( 1 , regular_year) . unwrap( ) . pred( ) . is_none( ) ) ;
888
+ assert ! ( Of :: new( 1 , leap_year) . unwrap( ) . pred( ) . is_none( ) ) ;
889
+ }
890
+
844
891
#[ test]
845
892
fn test_weekday_from_u32_mod7 ( ) {
846
893
for i in 0 ..=1000 {
0 commit comments