@@ -44,21 +44,38 @@ struct DayEntry {
4444impl DayEntry {
4545 fn new ( date : Date , tz : & TimeZone ) -> Self {
4646 let midnight = date. to_datetime ( Time :: midnight ( ) ) ;
47- let zoned = midnight
48- . to_zoned ( tz. clone ( ) )
49- . expect ( "construct timezone lut: convert midnight to zoned" ) ;
50-
51- let start_utc = zoned. timestamp ( ) . as_second ( ) ;
47+ let ambiguous = tz. to_ambiguous_zoned ( midnight) ;
48+ let needs_later = ambiguous. is_ambiguous ( ) ;
49+ let zoned = match ( needs_later, ambiguous) {
50+ ( true , ambiguous) => ambiguous
51+ . later ( )
52+ . expect ( "construct timezone lut: disambiguate midnight via later transition" ) ,
53+ ( false , ambiguous) => ambiguous
54+ . compatible ( )
55+ . expect ( "construct timezone lut: convert midnight to zoned" ) ,
56+ } ;
57+
58+ let start_ts = zoned. timestamp ( ) ;
59+ let start_utc = start_ts. as_second ( ) ;
5260 let offset = zoned. offset ( ) . seconds ( ) ;
5361
5462 let mut transition_utc = None ;
5563 let mut offset_change = 0 ;
5664
57- if let Some ( trans) = tz. following ( zoned. timestamp ( ) ) . next ( ) {
65+ let follow_start = start_ts
66+ . saturating_sub ( SignedDuration :: from_secs ( 1 ) )
67+ . unwrap_or ( start_ts) ;
68+
69+ if let Some ( trans) = tz. following ( follow_start) . next ( ) {
5870 let trans_sec = trans. timestamp ( ) . as_second ( ) ;
5971 if trans_sec < start_utc + SECONDS_PER_DAY {
6072 transition_utc = Some ( trans_sec) ;
61- offset_change = trans. offset ( ) . seconds ( ) - offset;
73+ let before_ts = trans
74+ . timestamp ( )
75+ . saturating_sub ( SignedDuration :: from_secs ( 1 ) )
76+ . unwrap_or ( trans. timestamp ( ) ) ;
77+ let before_offset = tz. to_offset ( before_ts) . seconds ( ) ;
78+ offset_change = trans. offset ( ) . seconds ( ) - before_offset;
6279 }
6380 }
6481
@@ -465,4 +482,40 @@ mod tests {
465482 assert ! ( fast_utc_from_local( & tz, 1850 , 1 , 1 , 0 , 0 , 0 , 0 ) . is_none( ) ) ;
466483 assert ! ( fast_utc_from_local( & tz, 2350 , 1 , 1 , 0 , 0 , 0 , 0 ) . is_none( ) ) ;
467484 }
485+
486+ #[ test]
487+ fn lut_prefers_later_midnight_in_fold ( ) {
488+ let tz = TimeZone :: get ( "Africa/Algiers" ) . unwrap ( ) ;
489+ let midnight = date ( 1939 , 11 , 19 ) . at ( 0 , 0 , 0 , 0 ) ;
490+ let amb_ts = tz. to_ambiguous_timestamp ( midnight) ;
491+ assert ! ( amb_ts. is_ambiguous( ) ) ;
492+
493+ let later = amb_ts. later ( ) . unwrap ( ) ;
494+ let before_second_midnight = later. as_microsecond ( ) - 1 ;
495+
496+ let before_components =
497+ fast_components_from_timestamp ( before_second_midnight, & tz) . unwrap ( ) ;
498+ assert_eq ! (
499+ (
500+ before_components. year,
501+ before_components. month,
502+ before_components. day
503+ ) ,
504+ ( 1939 , 11 , 18 )
505+ ) ;
506+
507+ let second_midnight_components =
508+ fast_components_from_timestamp ( later. as_microsecond ( ) , & tz) . unwrap ( ) ;
509+ assert_eq ! (
510+ (
511+ second_midnight_components. year,
512+ second_midnight_components. month,
513+ second_midnight_components. day
514+ ) ,
515+ ( 1939 , 11 , 19 )
516+ ) ;
517+ assert_eq ! ( second_midnight_components. hour, 0 ) ;
518+ assert_eq ! ( second_midnight_components. minute, 0 ) ;
519+ assert_eq ! ( second_midnight_components. second, 0 ) ;
520+ }
468521}
0 commit comments