@@ -14,7 +14,11 @@ use crate::{
14
14
use std:: str:: FromStr ;
15
15
use tinystr:: TinyAsciiStr ;
16
16
17
- use super :: calendar:: { CalendarDateLike , GetCalendarSlot } ;
17
+ use super :: {
18
+ calendar:: { CalendarDateLike , GetCalendarSlot } ,
19
+ duration:: normalized:: NormalizedTimeDuration ,
20
+ Duration ,
21
+ } ;
18
22
19
23
/// The native Rust implementation of `Temporal.PlainDateTime`
20
24
#[ non_exhaustive]
@@ -51,6 +55,40 @@ impl<C: CalendarProtocol> DateTime<C> {
51
55
let iso = IsoDateTime :: from_epoch_nanos ( & instant. nanos , offset) ?;
52
56
Ok ( Self { iso, calendar } )
53
57
}
58
+
59
+ // 5.5.14 AddDurationToOrSubtractDurationFromPlainDateTime ( operation, dateTime, temporalDurationLike, options )
60
+ fn add_or_subtract_duration (
61
+ & self ,
62
+ duration : & Duration ,
63
+ overflow : Option < ArithmeticOverflow > ,
64
+ context : & mut C :: Context ,
65
+ ) -> TemporalResult < Self > {
66
+ // SKIP: 1, 2, 3, 4
67
+ // 1. If operation is subtract, let sign be -1. Otherwise, let sign be 1.
68
+ // 2. Let duration be ? ToTemporalDurationRecord(temporalDurationLike).
69
+ // 3. Set options to ? GetOptionsObject(options).
70
+ // 4. Let calendarRec be ? CreateCalendarMethodsRecord(dateTime.[[Calendar]], « date-add »).
71
+
72
+ // 5. Let norm be NormalizeTimeDuration(sign × duration.[[Hours]], sign × duration.[[Minutes]], sign × duration.[[Seconds]], sign × duration.[[Milliseconds]], sign × duration.[[Microseconds]], sign × duration.[[Nanoseconds]]).
73
+ let norm = NormalizedTimeDuration :: from_time_duration ( duration. time ( ) ) ;
74
+
75
+ // TODO: validate Constrain is default with all the recent changes.
76
+ // 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).
77
+ let result = self . iso . add_date_duration (
78
+ self . calendar ( ) ,
79
+ duration. date ( ) ,
80
+ norm,
81
+ overflow,
82
+ context,
83
+ ) ?;
84
+
85
+ // 7. Assert: IsValidISODate(result.[[Year]], result.[[Month]], result.[[Day]]) is true.
86
+ // 8. Assert: IsValidTime(result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]]) is true.
87
+ assert ! ( result. is_within_limits( ) ) ;
88
+
89
+ // 9. Return ? CreateTemporalDateTime(result.[[Year]], result.[[Month]], result.[[Day]], result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]], dateTime.[[Calendar]]).
90
+ Ok ( Self :: new_unchecked ( result, self . calendar . clone ( ) ) )
91
+ }
54
92
}
55
93
56
94
// ==== Public DateTime API ====
@@ -166,7 +204,6 @@ impl<C: CalendarProtocol> DateTime<C> {
166
204
167
205
// ==== Calendar-derived public API ====
168
206
169
- // TODO: Revert to `DateTime<C>`.
170
207
impl DateTime < ( ) > {
171
208
/// Returns the calendar year value.
172
209
pub fn year ( & self ) -> TemporalResult < i32 > {
@@ -245,6 +282,24 @@ impl DateTime<()> {
245
282
self . calendar
246
283
. in_leap_year ( & CalendarDateLike :: DateTime ( self . clone ( ) ) , & mut ( ) )
247
284
}
285
+
286
+ #[ inline]
287
+ pub fn add (
288
+ & self ,
289
+ duration : & Duration ,
290
+ overflow : Option < ArithmeticOverflow > ,
291
+ ) -> TemporalResult < Self > {
292
+ self . contextual_add ( duration, overflow, & mut ( ) )
293
+ }
294
+
295
+ #[ inline]
296
+ pub fn subtract (
297
+ & self ,
298
+ duration : & Duration ,
299
+ overflow : Option < ArithmeticOverflow > ,
300
+ ) -> TemporalResult < Self > {
301
+ self . contextual_subtract ( duration, overflow, & mut ( ) )
302
+ }
248
303
}
249
304
250
305
impl < C : CalendarProtocol > DateTime < C > {
@@ -355,6 +410,26 @@ impl<C: CalendarProtocol> DateTime<C> {
355
410
this. get_calendar ( )
356
411
. in_leap_year ( & CalendarDateLike :: CustomDateTime ( this. clone ( ) ) , context)
357
412
}
413
+
414
+ #[ inline]
415
+ pub fn contextual_add (
416
+ & self ,
417
+ duration : & Duration ,
418
+ overflow : Option < ArithmeticOverflow > ,
419
+ context : & mut C :: Context ,
420
+ ) -> TemporalResult < Self > {
421
+ self . add_or_subtract_duration ( duration, overflow, context)
422
+ }
423
+
424
+ #[ inline]
425
+ pub fn contextual_subtract (
426
+ & self ,
427
+ duration : & Duration ,
428
+ overflow : Option < ArithmeticOverflow > ,
429
+ context : & mut C :: Context ,
430
+ ) -> TemporalResult < Self > {
431
+ self . add_or_subtract_duration ( & duration. negated ( ) , overflow, context)
432
+ }
358
433
}
359
434
360
435
// ==== Trait impls ====
@@ -408,7 +483,10 @@ impl<C: CalendarProtocol> FromStr for DateTime<C> {
408
483
mod tests {
409
484
use std:: str:: FromStr ;
410
485
411
- use crate :: components:: calendar:: CalendarSlot ;
486
+ use crate :: {
487
+ components:: { calendar:: CalendarSlot , Duration } ,
488
+ iso:: { IsoDate , IsoTime } ,
489
+ } ;
412
490
413
491
use super :: DateTime ;
414
492
@@ -429,20 +507,118 @@ mod tests {
429
507
0 ,
430
508
CalendarSlot :: from_str ( "iso8601" ) . unwrap ( ) ,
431
509
) ;
432
- let positive_limit = DateTime :: < ( ) > :: new (
433
- 275_760 ,
434
- 9 ,
435
- 14 ,
436
- 0 ,
437
- 0 ,
438
- 0 ,
439
- 0 ,
440
- 0 ,
441
- 0 ,
442
- CalendarSlot :: from_str ( "iso8601" ) . unwrap ( ) ,
443
- ) ;
510
+ let positive_limit =
511
+ DateTime :: < ( ) > :: new ( 275_760 , 9 , 14 , 0 , 0 , 0 , 0 , 0 , 0 , CalendarSlot :: default ( ) ) ;
444
512
445
513
assert ! ( negative_limit. is_err( ) ) ;
446
514
assert ! ( positive_limit. is_err( ) ) ;
447
515
}
516
+
517
+ // options-undefined.js
518
+ #[ test]
519
+ fn datetime_add_test ( ) {
520
+ let pdt = DateTime :: < ( ) > :: new (
521
+ 2020 ,
522
+ 1 ,
523
+ 31 ,
524
+ 12 ,
525
+ 34 ,
526
+ 56 ,
527
+ 987 ,
528
+ 654 ,
529
+ 321 ,
530
+ CalendarSlot :: default ( ) ,
531
+ )
532
+ . unwrap ( ) ;
533
+
534
+ let result = pdt. add ( & Duration :: one_month ( 1.0 ) , None ) . unwrap ( ) ;
535
+
536
+ assert_eq ! ( result. month( ) , Ok ( 2 ) ) ;
537
+ assert_eq ! ( result. day( ) , Ok ( 29 ) ) ;
538
+ }
539
+
540
+ // options-undefined.js
541
+ #[ test]
542
+ fn datetime_subtract_test ( ) {
543
+ let pdt = DateTime :: < ( ) > :: new (
544
+ 2000 ,
545
+ 3 ,
546
+ 31 ,
547
+ 12 ,
548
+ 34 ,
549
+ 56 ,
550
+ 987 ,
551
+ 654 ,
552
+ 321 ,
553
+ CalendarSlot :: default ( ) ,
554
+ )
555
+ . unwrap ( ) ;
556
+
557
+ let result = pdt. subtract ( & Duration :: one_month ( 1.0 ) , None ) . unwrap ( ) ;
558
+
559
+ assert_eq ! ( result. month( ) , Ok ( 2 ) ) ;
560
+ assert_eq ! ( result. day( ) , Ok ( 29 ) ) ;
561
+ }
562
+
563
+ // subtract/hour-overflow.js
564
+ #[ test]
565
+ fn datetime_subtract_hour_overflows ( ) {
566
+ let dt = DateTime :: < ( ) > :: new (
567
+ 2019 ,
568
+ 10 ,
569
+ 29 ,
570
+ 10 ,
571
+ 46 ,
572
+ 38 ,
573
+ 271 ,
574
+ 986 ,
575
+ 102 ,
576
+ CalendarSlot :: default ( ) ,
577
+ )
578
+ . unwrap ( ) ;
579
+
580
+ let result = dt. subtract ( & Duration :: hour ( 12.0 ) , None ) . unwrap ( ) ;
581
+
582
+ assert_eq ! (
583
+ result. iso. date,
584
+ IsoDate {
585
+ year: 2019 ,
586
+ month: 10 ,
587
+ day: 28
588
+ }
589
+ ) ;
590
+ assert_eq ! (
591
+ result. iso. time,
592
+ IsoTime {
593
+ hour: 22 ,
594
+ minute: 46 ,
595
+ second: 38 ,
596
+ millisecond: 271 ,
597
+ microsecond: 986 ,
598
+ nanosecond: 102
599
+ }
600
+ ) ;
601
+
602
+ let result = dt. add ( & Duration :: hour ( -12.0 ) , None ) . unwrap ( ) ;
603
+
604
+ assert_eq ! (
605
+ result. iso. date,
606
+ IsoDate {
607
+ year: 2019 ,
608
+ month: 10 ,
609
+ day: 28
610
+ }
611
+ ) ;
612
+ assert_eq ! (
613
+ result. iso. time,
614
+ IsoTime {
615
+ hour: 22 ,
616
+ minute: 46 ,
617
+ second: 38 ,
618
+ millisecond: 271 ,
619
+ microsecond: 986 ,
620
+ nanosecond: 102
621
+ }
622
+ ) ;
623
+ }
448
624
}
0 commit comments