@@ -612,7 +612,8 @@ fn map_local<Tz: TimeZone, F>(dt: &DateTime<Tz>, mut f: F) -> Option<DateTime<Tz
612612where
613613 F : FnMut ( NaiveDateTime ) -> Option < NaiveDateTime > ,
614614{
615- f ( dt. naive_local ( ) ) . and_then ( |datetime| datetime. and_local_timezone ( dt. timezone ( ) ) . single ( ) )
615+ f ( dt. overflowing_naive_local ( ) )
616+ . and_then ( |datetime| datetime. and_local_timezone ( dt. timezone ( ) ) . single ( ) )
616617}
617618
618619impl DateTime < FixedOffset > {
@@ -940,6 +941,18 @@ impl<Tz: TimeZone> Datelike for DateTime<Tz> {
940941 self . overflowing_naive_local ( ) . iso_week ( )
941942 }
942943
944+ // Note on short-circuiting.
945+ //
946+ // The `with_*` methods have an interesting property: if the local `NaiveDateTime` would be
947+ // out-of-range, there is only exactly one year/month/day/ordinal they can be set to that would
948+ // result in a valid `DateTime`: the one that is already there.
949+ // This is thanks to the restriction that an offset is always less then one day, 24h.
950+ //
951+ // The methods below all end up constructing a new `NaiveDate`, which validates the
952+ // resulting `NaiveDateTime` is in range.
953+ // To prevent failing when the resulting `DateTime` could be in range, all the following
954+ // methods short-circuit when possible.
955+
943956 #[ inline]
944957 /// Makes a new `DateTime` with the year number changed, while keeping the same month and day.
945958 ///
@@ -953,6 +966,9 @@ impl<Tz: TimeZone> Datelike for DateTime<Tz> {
953966 /// - The local time at the resulting date does not exist or is ambiguous, for example during a
954967 /// daylight saving time transition.
955968 fn with_year ( & self , year : i32 ) -> Option < DateTime < Tz > > {
969+ if self . year ( ) == year {
970+ return Some ( self . clone ( ) ) ; // See note on short-circuiting above.
971+ }
956972 map_local ( self , |datetime| datetime. with_year ( year) )
957973 }
958974
@@ -969,6 +985,9 @@ impl<Tz: TimeZone> Datelike for DateTime<Tz> {
969985 /// daylight saving time transition.
970986 #[ inline]
971987 fn with_month ( & self , month : u32 ) -> Option < DateTime < Tz > > {
988+ if self . month ( ) == month {
989+ return Some ( self . clone ( ) ) ; // See note on short-circuiting above.
990+ }
972991 map_local ( self , |datetime| datetime. with_month ( month) )
973992 }
974993
@@ -985,6 +1004,9 @@ impl<Tz: TimeZone> Datelike for DateTime<Tz> {
9851004 /// daylight saving time transition.
9861005 #[ inline]
9871006 fn with_month0 ( & self , month0 : u32 ) -> Option < DateTime < Tz > > {
1007+ if self . month0 ( ) == month0 {
1008+ return Some ( self . clone ( ) ) ; // See note on short-circuiting above.
1009+ }
9881010 map_local ( self , |datetime| datetime. with_month0 ( month0) )
9891011 }
9901012
@@ -1001,6 +1023,9 @@ impl<Tz: TimeZone> Datelike for DateTime<Tz> {
10011023 /// daylight saving time transition.
10021024 #[ inline]
10031025 fn with_day ( & self , day : u32 ) -> Option < DateTime < Tz > > {
1026+ if self . day ( ) == day {
1027+ return Some ( self . clone ( ) ) ; // See note on short-circuiting above.
1028+ }
10041029 map_local ( self , |datetime| datetime. with_day ( day) )
10051030 }
10061031
@@ -1017,6 +1042,9 @@ impl<Tz: TimeZone> Datelike for DateTime<Tz> {
10171042 /// daylight saving time transition.
10181043 #[ inline]
10191044 fn with_day0 ( & self , day0 : u32 ) -> Option < DateTime < Tz > > {
1045+ if self . day0 ( ) == day0 {
1046+ return Some ( self . clone ( ) ) ; // See note on short-circuiting above.
1047+ }
10201048 map_local ( self , |datetime| datetime. with_day0 ( day0) )
10211049 }
10221050
@@ -1033,6 +1061,9 @@ impl<Tz: TimeZone> Datelike for DateTime<Tz> {
10331061 /// daylight saving time transition.
10341062 #[ inline]
10351063 fn with_ordinal ( & self , ordinal : u32 ) -> Option < DateTime < Tz > > {
1064+ if self . ordinal ( ) == ordinal {
1065+ return Some ( self . clone ( ) ) ; // See note on short-circuiting above.
1066+ }
10361067 map_local ( self , |datetime| datetime. with_ordinal ( ordinal) )
10371068 }
10381069
@@ -1049,6 +1080,9 @@ impl<Tz: TimeZone> Datelike for DateTime<Tz> {
10491080 /// daylight saving time transition.
10501081 #[ inline]
10511082 fn with_ordinal0 ( & self , ordinal0 : u32 ) -> Option < DateTime < Tz > > {
1083+ if self . ordinal0 ( ) == ordinal0 {
1084+ return Some ( self . clone ( ) ) ; // See note on short-circuiting above.
1085+ }
10521086 map_local ( self , |datetime| datetime. with_ordinal0 ( ordinal0) )
10531087 }
10541088}
0 commit comments