@@ -8,7 +8,7 @@ use crate::{
8
8
RoundingOptions , TemporalUnit ,
9
9
} ,
10
10
parsers:: parse_date_time,
11
- temporal_assert, Sign , TemporalError , TemporalResult , TemporalUnwrap ,
11
+ temporal_assert, Sign , TemporalError , TemporalFields , TemporalResult , TemporalUnwrap ,
12
12
} ;
13
13
14
14
use num_traits:: AsPrimitive ;
@@ -18,9 +18,16 @@ use tinystr::TinyAsciiStr;
18
18
use super :: {
19
19
calendar:: { CalendarDateLike , GetTemporalCalendar } ,
20
20
duration:: normalized:: { NormalizedTimeDuration , RelativeRoundResult } ,
21
- Date , Duration , Time ,
21
+ Date , Duration , PartialDate , PartialTime , Time ,
22
22
} ;
23
23
24
+ /// A partial DateTime record
25
+ #[ derive( Debug , Default , Copy , Clone ) ]
26
+ pub struct PartialDateTime {
27
+ date : PartialDate ,
28
+ time : PartialTime ,
29
+ }
30
+
24
31
/// The native Rust implementation of `Temporal.PlainDateTime`
25
32
#[ non_exhaustive]
26
33
#[ derive( Debug , Default , Clone , PartialEq , Eq ) ]
@@ -243,6 +250,35 @@ impl DateTime {
243
250
) )
244
251
}
245
252
253
+ /// Creates a new `DateTime` with the fields of a `PartialDateTime`.
254
+ #[ inline]
255
+ pub fn with (
256
+ & self ,
257
+ partial_datetime : PartialDateTime ,
258
+ overflow : Option < ArithmeticOverflow > ,
259
+ ) -> TemporalResult < Self > {
260
+ // Determine the Date from the provided fields.
261
+ let fields = TemporalFields :: from ( self ) ;
262
+ let partial_fields = TemporalFields :: from ( partial_datetime. date ) ;
263
+
264
+ let mut merge_result = fields. merge_fields ( & partial_fields, self . calendar ( ) ) ?;
265
+
266
+ let result_date = self . calendar . date_from_fields (
267
+ & mut merge_result,
268
+ overflow. unwrap_or ( ArithmeticOverflow :: Constrain ) ,
269
+ ) ?;
270
+
271
+ // Determine the `Time` based off the partial values.
272
+ let time = self . iso . time . with (
273
+ partial_datetime. time ,
274
+ overflow. unwrap_or ( ArithmeticOverflow :: Constrain ) ,
275
+ ) ?;
276
+
277
+ let iso_datetime = IsoDateTime :: new ( result_date. iso , time) ?;
278
+
279
+ Ok ( Self :: new_unchecked ( iso_datetime, self . calendar ( ) . clone ( ) ) )
280
+ }
281
+
246
282
/// Creates a new `DateTime` from the current `DateTime` and the provided `Time`.
247
283
pub fn with_time ( & self , time : Time ) -> TemporalResult < Self > {
248
284
Self :: new (
@@ -540,17 +576,35 @@ impl FromStr for DateTime {
540
576
mod tests {
541
577
use std:: str:: FromStr ;
542
578
579
+ use tinystr:: { tinystr, TinyAsciiStr } ;
580
+
543
581
use crate :: {
544
- components:: { calendar:: Calendar , duration:: DateDuration , Duration } ,
545
- iso:: { IsoDate , IsoTime } ,
582
+ components:: {
583
+ calendar:: Calendar , duration:: DateDuration , DateTime , Duration , MonthCode , PartialDate ,
584
+ PartialDateTime , PartialTime ,
585
+ } ,
546
586
options:: {
547
587
DifferenceSettings , RoundingIncrement , RoundingOptions , TemporalRoundingMode ,
548
588
TemporalUnit ,
549
589
} ,
550
590
primitive:: FiniteF64 ,
551
591
} ;
552
592
553
- use super :: DateTime ;
593
+ fn assert_datetime (
594
+ dt : DateTime ,
595
+ fields : ( i32 , u8 , TinyAsciiStr < 4 > , u8 , u8 , u8 , u8 , u16 , u16 , u16 ) ,
596
+ ) {
597
+ assert_eq ! ( dt. year( ) . unwrap( ) , fields. 0 ) ;
598
+ assert_eq ! ( dt. month( ) . unwrap( ) , fields. 1 ) ;
599
+ assert_eq ! ( dt. month_code( ) . unwrap( ) , fields. 2 ) ;
600
+ assert_eq ! ( dt. day( ) . unwrap( ) , fields. 3 ) ;
601
+ assert_eq ! ( dt. hour( ) , fields. 4 ) ;
602
+ assert_eq ! ( dt. minute( ) , fields. 5 ) ;
603
+ assert_eq ! ( dt. second( ) , fields. 6 ) ;
604
+ assert_eq ! ( dt. millisecond( ) , fields. 7 ) ;
605
+ assert_eq ! ( dt. microsecond( ) , fields. 8 ) ;
606
+ assert_eq ! ( dt. nanosecond( ) , fields. 9 ) ;
607
+ }
554
608
555
609
#[ test]
556
610
#[ allow( clippy:: float_cmp) ]
@@ -575,6 +629,152 @@ mod tests {
575
629
assert ! ( positive_limit. is_err( ) ) ;
576
630
}
577
631
632
+ #[ test]
633
+ fn basic_with_test ( ) {
634
+ let pdt =
635
+ DateTime :: new ( 1976 , 11 , 18 , 15 , 23 , 30 , 123 , 456 , 789 , Calendar :: default ( ) ) . unwrap ( ) ;
636
+
637
+ // Test year
638
+ let partial = PartialDateTime {
639
+ date : PartialDate {
640
+ year : Some ( 2019 ) ,
641
+ ..Default :: default ( )
642
+ } ,
643
+ time : PartialTime :: default ( ) ,
644
+ } ;
645
+ let result = pdt. with ( partial, None ) . unwrap ( ) ;
646
+ assert_datetime (
647
+ result,
648
+ ( 2019 , 11 , tinystr ! ( 4 , "M11" ) , 18 , 15 , 23 , 30 , 123 , 456 , 789 ) ,
649
+ ) ;
650
+
651
+ // Test month
652
+ let partial = PartialDateTime {
653
+ date : PartialDate {
654
+ month : Some ( 5 ) ,
655
+ ..Default :: default ( )
656
+ } ,
657
+ time : PartialTime :: default ( ) ,
658
+ } ;
659
+ let result = pdt. with ( partial, None ) . unwrap ( ) ;
660
+ assert_datetime (
661
+ result,
662
+ ( 1976 , 5 , tinystr ! ( 4 , "M05" ) , 18 , 15 , 23 , 30 , 123 , 456 , 789 ) ,
663
+ ) ;
664
+
665
+ // Test monthCode
666
+ let partial = PartialDateTime {
667
+ date : PartialDate {
668
+ month_code : Some ( MonthCode :: Five ) ,
669
+ ..Default :: default ( )
670
+ } ,
671
+ time : PartialTime :: default ( ) ,
672
+ } ;
673
+ let result = pdt. with ( partial, None ) . unwrap ( ) ;
674
+ assert_datetime (
675
+ result,
676
+ ( 1976 , 5 , tinystr ! ( 4 , "M05" ) , 18 , 15 , 23 , 30 , 123 , 456 , 789 ) ,
677
+ ) ;
678
+
679
+ // Test day
680
+ let partial = PartialDateTime {
681
+ date : PartialDate {
682
+ day : Some ( 5 ) ,
683
+ ..Default :: default ( )
684
+ } ,
685
+ time : PartialTime :: default ( ) ,
686
+ } ;
687
+ let result = pdt. with ( partial, None ) . unwrap ( ) ;
688
+ assert_datetime (
689
+ result,
690
+ ( 1976 , 11 , tinystr ! ( 4 , "M11" ) , 5 , 15 , 23 , 30 , 123 , 456 , 789 ) ,
691
+ ) ;
692
+
693
+ // Test hour
694
+ let partial = PartialDateTime {
695
+ date : PartialDate :: default ( ) ,
696
+ time : PartialTime {
697
+ hour : Some ( 5 ) ,
698
+ ..Default :: default ( )
699
+ } ,
700
+ } ;
701
+ let result = pdt. with ( partial, None ) . unwrap ( ) ;
702
+ assert_datetime (
703
+ result,
704
+ ( 1976 , 11 , tinystr ! ( 4 , "M11" ) , 18 , 5 , 23 , 30 , 123 , 456 , 789 ) ,
705
+ ) ;
706
+
707
+ // Test minute
708
+ let partial = PartialDateTime {
709
+ date : PartialDate :: default ( ) ,
710
+ time : PartialTime {
711
+ minute : Some ( 5 ) ,
712
+ ..Default :: default ( )
713
+ } ,
714
+ } ;
715
+ let result = pdt. with ( partial, None ) . unwrap ( ) ;
716
+ assert_datetime (
717
+ result,
718
+ ( 1976 , 11 , tinystr ! ( 4 , "M11" ) , 18 , 15 , 5 , 30 , 123 , 456 , 789 ) ,
719
+ ) ;
720
+
721
+ // Test second
722
+ let partial = PartialDateTime {
723
+ date : PartialDate :: default ( ) ,
724
+ time : PartialTime {
725
+ second : Some ( 5 ) ,
726
+ ..Default :: default ( )
727
+ } ,
728
+ } ;
729
+ let result = pdt. with ( partial, None ) . unwrap ( ) ;
730
+ assert_datetime (
731
+ result,
732
+ ( 1976 , 11 , tinystr ! ( 4 , "M11" ) , 18 , 15 , 23 , 5 , 123 , 456 , 789 ) ,
733
+ ) ;
734
+
735
+ // Test second
736
+ let partial = PartialDateTime {
737
+ date : PartialDate :: default ( ) ,
738
+ time : PartialTime {
739
+ millisecond : Some ( 5 ) ,
740
+ ..Default :: default ( )
741
+ } ,
742
+ } ;
743
+ let result = pdt. with ( partial, None ) . unwrap ( ) ;
744
+ assert_datetime (
745
+ result,
746
+ ( 1976 , 11 , tinystr ! ( 4 , "M11" ) , 18 , 15 , 23 , 30 , 5 , 456 , 789 ) ,
747
+ ) ;
748
+
749
+ // Test second
750
+ let partial = PartialDateTime {
751
+ date : PartialDate :: default ( ) ,
752
+ time : PartialTime {
753
+ microsecond : Some ( 5 ) ,
754
+ ..Default :: default ( )
755
+ } ,
756
+ } ;
757
+ let result = pdt. with ( partial, None ) . unwrap ( ) ;
758
+ assert_datetime (
759
+ result,
760
+ ( 1976 , 11 , tinystr ! ( 4 , "M11" ) , 18 , 15 , 23 , 30 , 123 , 5 , 789 ) ,
761
+ ) ;
762
+
763
+ // Test second
764
+ let partial = PartialDateTime {
765
+ date : PartialDate :: default ( ) ,
766
+ time : PartialTime {
767
+ nanosecond : Some ( 5 ) ,
768
+ ..Default :: default ( )
769
+ } ,
770
+ } ;
771
+ let result = pdt. with ( partial, None ) . unwrap ( ) ;
772
+ assert_datetime (
773
+ result,
774
+ ( 1976 , 11 , tinystr ! ( 4 , "M11" ) , 18 , 15 , 23 , 30 , 123 , 456 , 5 ) ,
775
+ ) ;
776
+ }
777
+
578
778
// options-undefined.js
579
779
#[ test]
580
780
fn datetime_add_test ( ) {
@@ -632,47 +832,15 @@ mod tests {
632
832
DateTime :: new ( 2019 , 10 , 29 , 10 , 46 , 38 , 271 , 986 , 102 , Calendar :: default ( ) ) . unwrap ( ) ;
633
833
634
834
let result = dt. subtract ( & Duration :: hour ( FiniteF64 ( 12.0 ) ) , None ) . unwrap ( ) ;
635
-
636
- assert_eq ! (
637
- result. iso. date,
638
- IsoDate {
639
- year: 2019 ,
640
- month: 10 ,
641
- day: 28
642
- }
643
- ) ;
644
- assert_eq ! (
645
- result. iso. time,
646
- IsoTime {
647
- hour: 22 ,
648
- minute: 46 ,
649
- second: 38 ,
650
- millisecond: 271 ,
651
- microsecond: 986 ,
652
- nanosecond: 102
653
- }
835
+ assert_datetime (
836
+ result,
837
+ ( 2019 , 10 , tinystr ! ( 4 , "M10" ) , 28 , 22 , 46 , 38 , 271 , 986 , 102 ) ,
654
838
) ;
655
839
656
840
let result = dt. add ( & Duration :: hour ( FiniteF64 ( -12.0 ) ) , None ) . unwrap ( ) ;
657
-
658
- assert_eq ! (
659
- result. iso. date,
660
- IsoDate {
661
- year: 2019 ,
662
- month: 10 ,
663
- day: 28
664
- }
665
- ) ;
666
- assert_eq ! (
667
- result. iso. time,
668
- IsoTime {
669
- hour: 22 ,
670
- minute: 46 ,
671
- second: 38 ,
672
- millisecond: 271 ,
673
- microsecond: 986 ,
674
- nanosecond: 102
675
- }
841
+ assert_datetime (
842
+ result,
843
+ ( 2019 , 10 , tinystr ! ( 4 , "M10" ) , 28 , 22 , 46 , 38 , 271 , 986 , 102 ) ,
676
844
) ;
677
845
}
678
846
0 commit comments