@@ -168,6 +168,14 @@ impl<Tz: TimeZone> DateTime<Tz> {
168
168
/// [`NaiveDate`] is a more well-defined type, and has more traits implemented on it,
169
169
/// so should be preferred to [`Date`] any time you truly want to operate on Dates.
170
170
///
171
+ /// # Panics
172
+ ///
173
+ /// [`DateTime`] internally stores the date and time in UTC with a [`NaiveDateTime`]. This
174
+ /// method will panic if the offset from UTC would push the local datetime outside of the
175
+ /// representable range of a [`DateTime`].
176
+ ///
177
+ /// # Example
178
+ ///
171
179
/// ```
172
180
/// use chrono::prelude::*;
173
181
///
@@ -352,7 +360,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
352
360
/// See [`NaiveDate::checked_add_months`] for more details on behavior
353
361
#[ must_use]
354
362
pub fn checked_add_months ( self , rhs : Months ) -> Option < DateTime < Tz > > {
355
- self . naive_local ( )
363
+ self . overflowing_naive_local ( )
356
364
. checked_add_months ( rhs) ?
357
365
. and_local_timezone ( Tz :: from_offset ( & self . offset ) )
358
366
. single ( )
@@ -377,7 +385,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
377
385
/// See [`NaiveDate::checked_sub_months`] for more details on behavior
378
386
#[ must_use]
379
387
pub fn checked_sub_months ( self , rhs : Months ) -> Option < DateTime < Tz > > {
380
- self . naive_local ( )
388
+ self . overflowing_naive_local ( )
381
389
. checked_sub_months ( rhs) ?
382
390
. and_local_timezone ( Tz :: from_offset ( & self . offset ) )
383
391
. single ( )
@@ -388,7 +396,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
388
396
/// Returns `None` if the resulting date would be out of range.
389
397
#[ must_use]
390
398
pub fn checked_add_days ( self , days : Days ) -> Option < Self > {
391
- self . naive_local ( )
399
+ self . overflowing_naive_local ( )
392
400
. checked_add_days ( days) ?
393
401
. and_local_timezone ( TimeZone :: from_offset ( & self . offset ) )
394
402
. single ( )
@@ -399,7 +407,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
399
407
/// Returns `None` if the resulting date would be out of range.
400
408
#[ must_use]
401
409
pub fn checked_sub_days ( self , days : Days ) -> Option < Self > {
402
- self . naive_local ( )
410
+ self . overflowing_naive_local ( )
403
411
. checked_sub_days ( days) ?
404
412
. and_local_timezone ( TimeZone :: from_offset ( & self . offset ) )
405
413
. single ( )
@@ -421,10 +429,30 @@ impl<Tz: TimeZone> DateTime<Tz> {
421
429
}
422
430
423
431
/// Returns a view to the naive local datetime.
432
+ ///
433
+ /// # Panics
434
+ ///
435
+ /// [`DateTime`] internally stores the date and time in UTC with a [`NaiveDateTime`]. This
436
+ /// method will panic if the offset from UTC would push the local datetime outside of the
437
+ /// representable range of a [`NaiveDateTime`].
424
438
#[ inline]
425
439
#[ must_use]
426
440
pub fn naive_local ( & self ) -> NaiveDateTime {
427
- self . datetime + self . offset . fix ( )
441
+ self . datetime
442
+ . checked_add_offset ( self . offset . fix ( ) )
443
+ . expect ( "Local time out of range for `NaiveDateTime`" )
444
+ }
445
+
446
+ /// Returns a view to the naive local datetime.
447
+ ///
448
+ /// FOR INTERNAL USE ONLY.
449
+ /// This makes use of the buffer space outside of the representable range of values of
450
+ /// `NaiveDateTime`. The result can be user as intermediate value, but should never be exposed
451
+ /// outside chrono.
452
+ #[ inline]
453
+ #[ must_use]
454
+ pub ( crate ) fn overflowing_naive_local ( & self ) -> NaiveDateTime {
455
+ self . datetime . unchecked_add_offset ( self . offset . fix ( ) )
428
456
}
429
457
430
458
/// Retrieve the elapsed years from now to the given [`DateTime`].
@@ -548,7 +576,8 @@ fn map_local<Tz: TimeZone, F>(dt: &DateTime<Tz>, mut f: F) -> Option<DateTime<Tz
548
576
where
549
577
F : FnMut ( NaiveDateTime ) -> Option < NaiveDateTime > ,
550
578
{
551
- f ( dt. naive_local ( ) ) . and_then ( |datetime| dt. timezone ( ) . from_local_datetime ( & datetime) . single ( ) )
579
+ f ( dt. overflowing_naive_local ( ) )
580
+ . and_then ( |datetime| dt. timezone ( ) . from_local_datetime ( & datetime) . single ( ) )
552
581
}
553
582
554
583
impl DateTime < FixedOffset > {
@@ -635,8 +664,12 @@ where
635
664
#[ must_use]
636
665
pub fn to_rfc2822 ( & self ) -> String {
637
666
let mut result = String :: with_capacity ( 32 ) ;
638
- crate :: format:: write_rfc2822 ( & mut result, self . naive_local ( ) , self . offset . fix ( ) )
639
- . expect ( "writing rfc2822 datetime to string should never fail" ) ;
667
+ crate :: format:: write_rfc2822 (
668
+ & mut result,
669
+ self . overflowing_naive_local ( ) ,
670
+ self . offset . fix ( ) ,
671
+ )
672
+ . expect ( "writing rfc2822 datetime to string should never fail" ) ;
640
673
result
641
674
}
642
675
@@ -646,8 +679,12 @@ where
646
679
#[ must_use]
647
680
pub fn to_rfc3339 ( & self ) -> String {
648
681
let mut result = String :: with_capacity ( 32 ) ;
649
- crate :: format:: write_rfc3339 ( & mut result, self . naive_local ( ) , self . offset . fix ( ) )
650
- . expect ( "writing rfc3339 datetime to string should never fail" ) ;
682
+ crate :: format:: write_rfc3339 (
683
+ & mut result,
684
+ self . overflowing_naive_local ( ) ,
685
+ self . offset . fix ( ) ,
686
+ )
687
+ . expect ( "writing rfc3339 datetime to string should never fail" ) ;
651
688
result
652
689
}
653
690
@@ -730,7 +767,7 @@ where
730
767
I : Iterator < Item = B > + Clone ,
731
768
B : Borrow < Item < ' a > > ,
732
769
{
733
- let local = self . naive_local ( ) ;
770
+ let local = self . overflowing_naive_local ( ) ;
734
771
DelayedFormat :: new_with_offset ( Some ( local. date ( ) ) , Some ( local. time ( ) ) , & self . offset , items)
735
772
}
736
773
@@ -799,93 +836,124 @@ where
799
836
impl < Tz : TimeZone > Datelike for DateTime < Tz > {
800
837
#[ inline]
801
838
fn year ( & self ) -> i32 {
802
- self . naive_local ( ) . year ( )
839
+ self . overflowing_naive_local ( ) . year ( )
803
840
}
804
841
#[ inline]
805
842
fn month ( & self ) -> u32 {
806
- self . naive_local ( ) . month ( )
843
+ self . overflowing_naive_local ( ) . month ( )
807
844
}
808
845
#[ inline]
809
846
fn month0 ( & self ) -> u32 {
810
- self . naive_local ( ) . month0 ( )
847
+ self . overflowing_naive_local ( ) . month0 ( )
811
848
}
812
849
#[ inline]
813
850
fn day ( & self ) -> u32 {
814
- self . naive_local ( ) . day ( )
851
+ self . overflowing_naive_local ( ) . day ( )
815
852
}
816
853
#[ inline]
817
854
fn day0 ( & self ) -> u32 {
818
- self . naive_local ( ) . day0 ( )
855
+ self . overflowing_naive_local ( ) . day0 ( )
819
856
}
820
857
#[ inline]
821
858
fn ordinal ( & self ) -> u32 {
822
- self . naive_local ( ) . ordinal ( )
859
+ self . overflowing_naive_local ( ) . ordinal ( )
823
860
}
824
861
#[ inline]
825
862
fn ordinal0 ( & self ) -> u32 {
826
- self . naive_local ( ) . ordinal0 ( )
863
+ self . overflowing_naive_local ( ) . ordinal0 ( )
827
864
}
828
865
#[ inline]
829
866
fn weekday ( & self ) -> Weekday {
830
- self . naive_local ( ) . weekday ( )
867
+ self . overflowing_naive_local ( ) . weekday ( )
831
868
}
832
869
#[ inline]
833
870
fn iso_week ( & self ) -> IsoWeek {
834
- self . naive_local ( ) . iso_week ( )
871
+ self . overflowing_naive_local ( ) . iso_week ( )
835
872
}
836
873
874
+ // Note on short-circuiting.
875
+ //
876
+ // The `with_*` methods have an interesting property: if the local `NaiveDateTime` would be
877
+ // out-of-range, there is only exactly one year/month/day/ordinal they can be set to that would
878
+ // result in a valid `DateTime`: the one that is already there.
879
+ // This is thanks to the restriction that offset is always less then 24h.
880
+ //
881
+ // To prevent creating an out-of-range `NaiveDateTime` all the following methods short-circuit
882
+ // when possible.
883
+
837
884
#[ inline]
838
885
fn with_year ( & self , year : i32 ) -> Option < DateTime < Tz > > {
886
+ if self . year ( ) == year {
887
+ return Some ( self . clone ( ) ) ; // See note on short-circuiting above.
888
+ }
839
889
map_local ( self , |datetime| datetime. with_year ( year) )
840
890
}
841
891
842
892
#[ inline]
843
893
fn with_month ( & self , month : u32 ) -> Option < DateTime < Tz > > {
894
+ if self . month ( ) == month {
895
+ return Some ( self . clone ( ) ) ; // See note on short-circuiting above.
896
+ }
844
897
map_local ( self , |datetime| datetime. with_month ( month) )
845
898
}
846
899
847
900
#[ inline]
848
901
fn with_month0 ( & self , month0 : u32 ) -> Option < DateTime < Tz > > {
902
+ if self . month0 ( ) == month0 {
903
+ return Some ( self . clone ( ) ) ; // See note on short-circuiting above.
904
+ }
849
905
map_local ( self , |datetime| datetime. with_month0 ( month0) )
850
906
}
851
907
852
908
#[ inline]
853
909
fn with_day ( & self , day : u32 ) -> Option < DateTime < Tz > > {
910
+ if self . day ( ) == day {
911
+ return Some ( self . clone ( ) ) ; // See note on short-circuiting above.
912
+ }
854
913
map_local ( self , |datetime| datetime. with_day ( day) )
855
914
}
856
915
857
916
#[ inline]
858
917
fn with_day0 ( & self , day0 : u32 ) -> Option < DateTime < Tz > > {
918
+ if self . day0 ( ) == day0 {
919
+ return Some ( self . clone ( ) ) ; // See note on short-circuiting above.
920
+ }
859
921
map_local ( self , |datetime| datetime. with_day0 ( day0) )
860
922
}
861
923
862
924
#[ inline]
863
925
fn with_ordinal ( & self , ordinal : u32 ) -> Option < DateTime < Tz > > {
926
+ if self . ordinal ( ) == ordinal {
927
+ return Some ( self . clone ( ) ) ; // See note on short-circuiting above.
928
+ }
864
929
map_local ( self , |datetime| datetime. with_ordinal ( ordinal) )
865
930
}
866
931
867
932
#[ inline]
868
933
fn with_ordinal0 ( & self , ordinal0 : u32 ) -> Option < DateTime < Tz > > {
934
+ if self . ordinal0 ( ) == ordinal0 {
935
+ return Some ( self . clone ( ) ) ; // See note on short-circuiting above.
936
+ }
869
937
map_local ( self , |datetime| datetime. with_ordinal0 ( ordinal0) )
870
938
}
871
939
}
872
940
873
941
impl < Tz : TimeZone > Timelike for DateTime < Tz > {
874
942
#[ inline]
875
943
fn hour ( & self ) -> u32 {
876
- self . naive_local ( ) . hour ( )
944
+ self . overflowing_naive_local ( ) . hour ( )
877
945
}
878
946
#[ inline]
879
947
fn minute ( & self ) -> u32 {
880
- self . naive_local ( ) . minute ( )
948
+ self . overflowing_naive_local ( ) . minute ( )
881
949
}
882
950
#[ inline]
883
951
fn second ( & self ) -> u32 {
884
- self . naive_local ( ) . second ( )
952
+ self . overflowing_naive_local ( ) . second ( )
885
953
}
886
954
#[ inline]
887
955
fn nanosecond ( & self ) -> u32 {
888
- self . naive_local ( ) . nanosecond ( )
956
+ self . overflowing_naive_local ( ) . nanosecond ( )
889
957
}
890
958
891
959
#[ inline]
@@ -1055,7 +1123,7 @@ impl<Tz: TimeZone> Sub<Days> for DateTime<Tz> {
1055
1123
1056
1124
impl < Tz : TimeZone > fmt:: Debug for DateTime < Tz > {
1057
1125
fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
1058
- self . naive_local ( ) . fmt ( f) ?;
1126
+ self . overflowing_naive_local ( ) . fmt ( f) ?;
1059
1127
self . offset . fmt ( f)
1060
1128
}
1061
1129
}
0 commit comments