3
3
use crate :: {
4
4
components:: { calendar:: Calendar , duration:: TimeDuration , Instant } ,
5
5
iso:: { IsoDate , IsoDateSlots , IsoDateTime , IsoTime } ,
6
- options:: { ArithmeticOverflow , ResolvedRoundingOptions , TemporalUnit } ,
6
+ options:: {
7
+ ArithmeticOverflow , DifferenceOperation , DifferenceSettings , ResolvedRoundingOptions ,
8
+ TemporalUnit ,
9
+ } ,
7
10
parsers:: parse_date_time,
8
- temporal_assert, TemporalError , TemporalResult , TemporalUnwrap ,
11
+ temporal_assert, Sign , TemporalError , TemporalResult , TemporalUnwrap ,
9
12
} ;
10
13
11
14
use std:: { cmp:: Ordering , str:: FromStr } ;
@@ -89,6 +92,42 @@ impl DateTime {
89
92
Ok ( Self :: new_unchecked ( result, self . calendar . clone ( ) ) )
90
93
}
91
94
95
+ /// Difference two `DateTime`s together.
96
+ pub ( crate ) fn diff (
97
+ & self ,
98
+ op : DifferenceOperation ,
99
+ other : & Self ,
100
+ settings : DifferenceSettings ,
101
+ ) -> TemporalResult < Duration > {
102
+ // 3. If ? CalendarEquals(dateTime.[[Calendar]], other.[[Calendar]]) is false, throw a RangeError exception.
103
+ if self . calendar != other. calendar {
104
+ return Err ( TemporalError :: range ( )
105
+ . with_message ( "Calendar must be the same when diffing two DateTimes" ) ) ;
106
+ }
107
+
108
+ // 5. Let settings be ? GetDifferenceSettings(operation, resolvedOptions, datetime, « », "nanosecond", "day").
109
+ let ( sign, options) = ResolvedRoundingOptions :: from_diff_settings (
110
+ settings,
111
+ op,
112
+ TemporalUnit :: Day ,
113
+ TemporalUnit :: Nanosecond ,
114
+ ) ?;
115
+
116
+ // Step 7-8 combined.
117
+ if self . iso == other. iso {
118
+ return Ok ( Duration :: default ( ) ) ;
119
+ }
120
+
121
+ // Step 10-11.
122
+ let ( result, _) = self . diff_dt_with_rounding ( other, options) ?;
123
+
124
+ // Step 12
125
+ match sign {
126
+ Sign :: Positive | Sign :: Zero => Ok ( result) ,
127
+ Sign :: Negative => Ok ( result. negated ( ) ) ,
128
+ }
129
+ }
130
+
92
131
// TODO: Figure out whether to handle resolvedOptions
93
132
// 5.5.12 DifferencePlainDateTimeWithRounding ( y1, mon1, d1, h1, min1, s1, ms1, mus1, ns1, y2, mon2, d2, h2, min2, s2, ms2,
94
133
// mus2, ns2, calendarRec, largestUnit, roundingIncrement, smallestUnit, roundingMode, resolvedOptions )
@@ -381,6 +420,7 @@ impl DateTime {
381
420
}
382
421
383
422
#[ inline]
423
+ /// Adds a `Duration` to the current `DateTime`.
384
424
pub fn add (
385
425
& self ,
386
426
duration : & Duration ,
@@ -390,13 +430,26 @@ impl DateTime {
390
430
}
391
431
392
432
#[ inline]
433
+ /// Subtracts a `Duration` to the current `DateTime`.
393
434
pub fn subtract (
394
435
& self ,
395
436
duration : & Duration ,
396
437
overflow : Option < ArithmeticOverflow > ,
397
438
) -> TemporalResult < Self > {
398
439
self . add_or_subtract_duration ( & duration. negated ( ) , overflow)
399
440
}
441
+
442
+ #[ inline]
443
+ /// Returns a `Duration` representing the period of time from this `DateTime` until the other `DateTime`.
444
+ pub fn until ( & self , other : & Self , settings : DifferenceSettings ) -> TemporalResult < Duration > {
445
+ self . diff ( DifferenceOperation :: Until , other, settings)
446
+ }
447
+
448
+ #[ inline]
449
+ /// Returns a `Duration` representing the period of time from this `DateTime` since the other `DateTime`.
450
+ pub fn since ( & self , other : & Self , settings : DifferenceSettings ) -> TemporalResult < Duration > {
451
+ self . diff ( DifferenceOperation :: Since , other, settings)
452
+ }
400
453
}
401
454
402
455
// ==== Trait impls ====
@@ -464,6 +517,7 @@ mod tests {
464
517
use crate :: {
465
518
components:: { calendar:: Calendar , duration:: DateDuration , Duration } ,
466
519
iso:: { IsoDate , IsoTime } ,
520
+ options:: { DifferenceSettings , RoundingIncrement , TemporalRoundingMode , TemporalUnit } ,
467
521
} ;
468
522
469
523
use super :: DateTime ;
@@ -575,4 +629,61 @@ mod tests {
575
629
}
576
630
) ;
577
631
}
632
+
633
+ fn create_diff_setting (
634
+ smallest : TemporalUnit ,
635
+ increment : u32 ,
636
+ rounding_mode : TemporalRoundingMode ,
637
+ ) -> DifferenceSettings {
638
+ DifferenceSettings {
639
+ largest_unit : None ,
640
+ smallest_unit : Some ( smallest) ,
641
+ increment : Some ( RoundingIncrement :: try_new ( increment) . unwrap ( ) ) ,
642
+ rounding_mode : Some ( rounding_mode) ,
643
+ }
644
+ }
645
+
646
+ #[ test]
647
+ fn dt_until_basic ( ) {
648
+ let earlier =
649
+ DateTime :: new ( 2019 , 1 , 8 , 8 , 22 , 36 , 123 , 456 , 789 , Calendar :: default ( ) ) . unwrap ( ) ;
650
+ let later =
651
+ DateTime :: new ( 2021 , 9 , 7 , 12 , 39 , 40 , 987 , 654 , 321 , Calendar :: default ( ) ) . unwrap ( ) ;
652
+
653
+ let settings = create_diff_setting ( TemporalUnit :: Hour , 3 , TemporalRoundingMode :: HalfExpand ) ;
654
+ let result = earlier. until ( & later, settings) . unwrap ( ) ;
655
+
656
+ assert_eq ! ( result. days( ) , 973.0 ) ;
657
+ assert_eq ! ( result. hours( ) , 3.0 ) ;
658
+
659
+ let settings =
660
+ create_diff_setting ( TemporalUnit :: Minute , 30 , TemporalRoundingMode :: HalfExpand ) ;
661
+ let result = earlier. until ( & later, settings) . unwrap ( ) ;
662
+
663
+ assert_eq ! ( result. days( ) , 973.0 ) ;
664
+ assert_eq ! ( result. hours( ) , 4.0 ) ;
665
+ assert_eq ! ( result. minutes( ) , 30.0 ) ;
666
+ }
667
+
668
+ #[ test]
669
+ fn dt_since_basic ( ) {
670
+ let earlier =
671
+ DateTime :: new ( 2019 , 1 , 8 , 8 , 22 , 36 , 123 , 456 , 789 , Calendar :: default ( ) ) . unwrap ( ) ;
672
+ let later =
673
+ DateTime :: new ( 2021 , 9 , 7 , 12 , 39 , 40 , 987 , 654 , 321 , Calendar :: default ( ) ) . unwrap ( ) ;
674
+
675
+ let settings = create_diff_setting ( TemporalUnit :: Hour , 3 , TemporalRoundingMode :: HalfExpand ) ;
676
+ let result = later. since ( & earlier, settings) . unwrap ( ) ;
677
+
678
+ assert_eq ! ( result. days( ) , 973.0 ) ;
679
+ assert_eq ! ( result. hours( ) , 3.0 ) ;
680
+
681
+ let settings =
682
+ create_diff_setting ( TemporalUnit :: Minute , 30 , TemporalRoundingMode :: HalfExpand ) ;
683
+ let result = later. since ( & earlier, settings) . unwrap ( ) ;
684
+
685
+ assert_eq ! ( result. days( ) , 973.0 ) ;
686
+ assert_eq ! ( result. hours( ) , 4.0 ) ;
687
+ assert_eq ! ( result. minutes( ) , 30.0 ) ;
688
+ }
578
689
}
0 commit comments