11//! This module implements `DateTime` any directly related algorithms.
22
33use super :: {
4- duration:: normalized:: { NormalizedDurationRecord , NormalizedTimeDuration } ,
5- Duration , PartialTime , PlainDate , PlainTime , ZonedDateTime ,
4+ duration:: normalized:: InternalDurationRecord , Duration , PartialTime , PlainDate , PlainTime ,
5+ ZonedDateTime ,
66} ;
77use crate :: parsed_intermediates:: ParsedDateTime ;
88use crate :: {
@@ -19,7 +19,7 @@ use crate::{
1919 parsers:: IxdtfStringBuilder ,
2020 primitive:: FiniteF64 ,
2121 provider:: { NeverProvider , TimeZoneProvider } ,
22- temporal_assert , MonthCode , TemporalError , TemporalResult , TimeZone ,
22+ MonthCode , TemporalError , TemporalResult , TimeZone ,
2323} ;
2424use alloc:: string:: String ;
2525use core:: { cmp:: Ordering , str:: FromStr } ;
@@ -225,33 +225,25 @@ impl PlainDateTime {
225225 overflow : Option < ArithmeticOverflow > ,
226226 ) -> TemporalResult < Self > {
227227 // SKIP: 1, 2, 3, 4
228- // 1. If operation is subtract, let sign be -1. Otherwise, let sign be 1.
229- // 2. Let duration be ? ToTemporalDurationRecord(temporalDurationLike).
230- // 3. Set options to ? GetOptionsObject(options).
231- // 4. Let calendarRec be ? CreateCalendarMethodsRecord(dateTime.[[Calendar]], « date-add »).
232-
233- // 5. Let norm be NormalizeTimeDuration(sign × duration.[[Hours]], sign × duration.[[Minutes]], sign × duration.[[Seconds]], sign × duration.[[Milliseconds]], sign × duration.[[Microseconds]], sign × duration.[[Nanoseconds]]).
234- let norm = NormalizedTimeDuration :: from_time_duration ( duration. time ( ) ) ;
235-
236- // TODO: validate Constrain is default with all the recent changes.
237- // 6. Let result be ? AddDateTime(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]], calendarRec, sign × duration.[[Years]], sign × duration.[[Months]], sign × duration.[[Weeks]], sign × duration.[[Days]], norm, options).
238- let result =
239- self . iso
240- . add_date_duration ( self . calendar ( ) . clone ( ) , duration. date ( ) , norm, overflow) ?;
241-
242- // 7. Assert: IsValidISODate(result.[[Year]], result.[[Month]], result.[[Day]]) is true.
243- // 8. Assert: IsValidTime(result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]],
244- // result.[[Microsecond]], result.[[Nanosecond]]) is true.
245- temporal_assert ! (
246- result. is_within_limits( ) ,
247- "Assertion failed: the below datetime is not within valid limits:\n {:?}" ,
248- result
249- ) ;
250-
251- // 9. Return ? CreateTemporalDateTime(result.[[Year]], result.[[Month]], result.[[Day]], result.[[Hour]],
252- // result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]],
253- // result.[[Nanosecond]], dateTime.[[Calendar]]).
254- Ok ( Self :: new_unchecked ( result, self . calendar . clone ( ) ) )
228+ // 5. Let internalDuration be ToInternalDurationRecordWith24HourDays(duration).
229+ let internal_duration = InternalDurationRecord :: from_duration_with_24_hour_days ( duration) ?;
230+ // 6. Let timeResult be AddTime(dateTime.[[ISODateTime]].[[Time]], internalDuration.[[Time]]).
231+ let ( days, time_result) = self
232+ . iso
233+ . time
234+ . add ( internal_duration. normalized_time_duration ( ) ) ;
235+ // 7. Let dateDuration be ? AdjustDateDurationRecord(internalDuration.[[Date]], timeResult.[[Days]]).
236+ let date_duration = internal_duration. date ( ) . adjust ( days, None , None ) ?;
237+ // 8. Let addedDate be ? CalendarDateAdd(dateTime.[[Calendar]], dateTime.[[ISODateTime]].[[ISODate]], dateDuration, overflow).
238+ let added_date = self . calendar ( ) . date_add (
239+ & self . iso . date ,
240+ & date_duration,
241+ overflow. unwrap_or ( ArithmeticOverflow :: Constrain ) ,
242+ ) ?;
243+ // 9. Let result be CombineISODateAndTimeRecord(addedDate, timeResult).
244+ let result = IsoDateTime :: new ( added_date. iso , time_result) ?;
245+ // 10. Return ? CreateTemporalDateTime(result, dateTime.[[Calendar]]).
246+ Ok ( Self :: new_unchecked ( result, self . calendar ( ) . clone ( ) ) )
255247 }
256248
257249 /// Difference two `DateTime`s together.
@@ -284,7 +276,7 @@ impl PlainDateTime {
284276 // Step 10-11.
285277 let norm_record = self . diff_dt_with_rounding ( other, options) ?;
286278
287- let result = Duration :: from_normalized ( norm_record, options. largest_unit ) ?;
279+ let result = Duration :: from_internal ( norm_record, options. largest_unit ) ?;
288280
289281 // Step 12
290282 match op {
@@ -300,12 +292,12 @@ impl PlainDateTime {
300292 & self ,
301293 other : & Self ,
302294 options : ResolvedRoundingOptions ,
303- ) -> TemporalResult < NormalizedDurationRecord > {
295+ ) -> TemporalResult < InternalDurationRecord > {
304296 // 1. If CompareISODateTime(y1, mon1, d1, h1, min1, s1, ms1, mus1, ns1, y2, mon2, d2, h2, min2, s2, ms2, mus2, ns2) = 0, then
305297 if matches ! ( self . iso. cmp( & other. iso) , Ordering :: Equal ) {
306298 // a. Let durationRecord be CreateDurationRecord(0, 0, 0, 0, 0, 0, 0, 0, 0, 0).
307299 // b. Return the Record { [[DurationRecord]]: durationRecord, [[Total]]: 0 }.
308- return Ok ( NormalizedDurationRecord :: default ( ) ) ;
300+ return Ok ( InternalDurationRecord :: default ( ) ) ;
309301 }
310302 // 2. If ISODateTimeWithinLimits(isoDateTime1) is false or ISODateTimeWithinLimits(isoDateTime2) is false, throw a RangeError exception.
311303 self . iso . check_validity ( ) ?;
@@ -1248,13 +1240,13 @@ mod tests {
12481240 PlainDateTime :: try_new ( 2019 , 10 , 29 , 10 , 46 , 38 , 271 , 986 , 102 , Calendar :: default ( ) )
12491241 . unwrap ( ) ;
12501242
1251- let result = dt. subtract ( & Duration :: hour ( 12 ) , None ) . unwrap ( ) ;
1243+ let result = dt. subtract ( & Duration :: from_hours ( 12 ) , None ) . unwrap ( ) ;
12521244 assert_datetime (
12531245 result,
12541246 ( 2019 , 10 , tinystr ! ( 4 , "M10" ) , 28 , 22 , 46 , 38 , 271 , 986 , 102 ) ,
12551247 ) ;
12561248
1257- let result = dt. add ( & Duration :: hour ( -12 ) , None ) . unwrap ( ) ;
1249+ let result = dt. add ( & Duration :: from_hours ( -12 ) , None ) . unwrap ( ) ;
12581250 assert_datetime (
12591251 result,
12601252 ( 2019 , 10 , tinystr ! ( 4 , "M10" ) , 28 , 22 , 46 , 38 , 271 , 986 , 102 ) ,
@@ -1543,4 +1535,21 @@ mod tests {
15431535 "pads 4 decimal places to 9"
15441536 ) ;
15451537 }
1538+
1539+ #[ test]
1540+ fn datetime_add ( ) {
1541+ use crate :: { Duration , PlainDateTime } ;
1542+ use core:: str:: FromStr ;
1543+
1544+ let dt = PlainDateTime :: from_str ( "2024-01-15T12:00:00" ) . unwrap ( ) ;
1545+
1546+ let duration = Duration :: from_str ( "P1M2DT3H4M" ) . unwrap ( ) ;
1547+
1548+ // Add duration
1549+ let later = dt. add ( & duration, None ) . unwrap ( ) ;
1550+ assert_eq ! ( later. month( ) , 2 ) ;
1551+ assert_eq ! ( later. day( ) , 17 ) ;
1552+ assert_eq ! ( later. hour( ) , 15 ) ;
1553+ assert_eq ! ( later. minute( ) , 4 ) ;
1554+ }
15461555}
0 commit comments