@@ -4,16 +4,22 @@ use crate::{
44 components:: { timezone:: TimeZoneProvider , PlainDateTime , PlainTime } ,
55 iso:: { IsoDateTime , IsoTime } ,
66 options:: {
7- ArithmeticOverflow , RelativeTo , ResolvedRoundingOptions , RoundingOptions , TemporalUnit ,
7+ ArithmeticOverflow , RelativeTo , ResolvedRoundingOptions , RoundingIncrement ,
8+ RoundingOptions , TemporalUnit , ToStringRoundingOptions ,
89 } ,
10+ parsers:: { FormattableDuration , Precision } ,
911 primitive:: FiniteF64 ,
1012 temporal_assert, Sign , TemporalError , TemporalResult ,
1113} ;
1214use alloc:: format;
15+ use alloc:: string:: String ;
1316use alloc:: vec;
1417use alloc:: vec:: Vec ;
1518use core:: str:: FromStr ;
16- use ixdtf:: parsers:: { records:: TimeDurationRecord , IsoDurationParser } ;
19+ use ixdtf:: parsers:: {
20+ records:: { DateDurationRecord , DurationParseRecord , Sign as IxdtfSign , TimeDurationRecord } ,
21+ IsoDurationParser ,
22+ } ;
1723use normalized:: NormalizedDurationRecord ;
1824use num_traits:: AsPrimitive ;
1925
@@ -26,7 +32,6 @@ mod date;
2632pub ( crate ) mod normalized;
2733mod time;
2834
29- #[ cfg( feature = "experimental" ) ]
3035#[ cfg( test) ]
3136mod tests;
3237
@@ -82,6 +87,16 @@ pub struct Duration {
8287 time : TimeDuration ,
8388}
8489
90+ impl core:: fmt:: Display for Duration {
91+ fn fmt ( & self , f : & mut core:: fmt:: Formatter < ' _ > ) -> core:: fmt:: Result {
92+ f. write_str (
93+ & self
94+ . to_temporal_string ( ToStringRoundingOptions :: default ( ) )
95+ . expect ( "Duration must return a valid string with default options." ) ,
96+ )
97+ }
98+ }
99+
85100// NOTE(nekevss): Structure of the below is going to be a little convoluted,
86101// but intended to section everything based on the below
87102//
@@ -128,13 +143,18 @@ impl Duration {
128143 duration_record. normalized_time_duration ( ) ,
129144 largest_unit,
130145 ) ?;
131- let date = DateDuration :: new (
146+ Self :: new (
132147 duration_record. date ( ) . years ,
133148 duration_record. date ( ) . months ,
134149 duration_record. date ( ) . weeks ,
135150 duration_record. date ( ) . days . checked_add ( & overflow_day) ?,
136- ) ?;
137- Ok ( Self :: new_unchecked ( date, time) )
151+ time. hours ,
152+ time. minutes ,
153+ time. seconds ,
154+ time. milliseconds ,
155+ time. microseconds ,
156+ time. nanoseconds ,
157+ )
138158 }
139159
140160 /// Returns the a `Vec` of the fields values.
@@ -618,6 +638,98 @@ impl Duration {
618638 }
619639 }
620640 }
641+
642+ pub fn to_temporal_string ( & self , options : ToStringRoundingOptions ) -> TemporalResult < String > {
643+ if options. smallest_unit == Some ( TemporalUnit :: Hour )
644+ || options. smallest_unit == Some ( TemporalUnit :: Minute )
645+ {
646+ return Err ( TemporalError :: range ( ) . with_message (
647+ "string rounding options cannot have hour or minute smallest unit." ,
648+ ) ) ;
649+ }
650+
651+ let resolved_options = options. resolve ( ) ?;
652+ if resolved_options. smallest_unit == TemporalUnit :: Nanosecond
653+ && resolved_options. increment == RoundingIncrement :: ONE
654+ {
655+ let duration = duration_to_formattable ( self , resolved_options. precision ) ?;
656+ return Ok ( duration. to_string ( ) ) ;
657+ }
658+
659+ let rounding_options = ResolvedRoundingOptions :: from_to_string_options ( & resolved_options) ;
660+
661+ // 11. Let largestUnit be DefaultTemporalLargestUnit(duration).
662+ let largest = self . default_largest_unit ( ) ;
663+ // 12. Let internalDuration be ToInternalDurationRecord(duration).
664+ let norm = NormalizedDurationRecord :: new (
665+ self . date ,
666+ NormalizedTimeDuration :: from_time_duration ( & self . time ) ,
667+ ) ?;
668+ // 13. Let timeDuration be ? RoundTimeDuration(internalDuration.[[Time]], precision.[[Increment]], precision.[[Unit]], roundingMode).
669+ let ( rounded, _) = norm
670+ . normalized_time_duration ( )
671+ . round ( FiniteF64 :: default ( ) , rounding_options) ?;
672+ // 14. Set internalDuration to CombineDateAndTimeDuration(internalDuration.[[Date]], timeDuration).
673+ let norm = NormalizedDurationRecord :: new ( norm. date ( ) , rounded. normalized_time_duration ( ) ) ?;
674+ // 15. Let roundedLargestUnit be LargerOfTwoTemporalUnits(largestUnit, second).
675+ let rounded_largest = largest. max ( TemporalUnit :: Second ) ;
676+ // 16. Let roundedDuration be ? TemporalDurationFromInternal(internalDuration, roundedLargestUnit).
677+ let rounded = Self :: from_normalized ( norm, rounded_largest) ?;
678+
679+ // 17. Return TemporalDurationToString(roundedDuration, precision.[[Precision]]).
680+ Ok ( duration_to_formattable ( & rounded, resolved_options. precision ) ?. to_string ( ) )
681+ }
682+ }
683+
684+ pub fn duration_to_formattable (
685+ duration : & Duration ,
686+ precision : Precision ,
687+ ) -> TemporalResult < FormattableDuration > {
688+ let sign = duration. sign ( ) ;
689+ let sign = if sign == Sign :: Negative {
690+ IxdtfSign :: Negative
691+ } else {
692+ IxdtfSign :: Positive
693+ } ;
694+ let duration = duration. abs ( ) ;
695+ let date = duration. years ( ) . 0 + duration. months ( ) . 0 + duration. weeks ( ) . 0 + duration. days ( ) . 0 ;
696+ let date = if date != 0.0 {
697+ Some ( DateDurationRecord {
698+ years : duration. years ( ) . 0 as u32 ,
699+ months : duration. months ( ) . 0 as u32 ,
700+ weeks : duration. weeks ( ) . 0 as u32 ,
701+ days : duration. days ( ) . 0 as u64 ,
702+ } )
703+ } else {
704+ None
705+ } ;
706+
707+ let hours = duration. hours ( ) . abs ( ) ;
708+ let minutes = duration. minutes ( ) . abs ( ) ;
709+
710+ let time = NormalizedTimeDuration :: from_time_duration ( & TimeDuration :: new_unchecked (
711+ FiniteF64 :: default ( ) ,
712+ FiniteF64 :: default ( ) ,
713+ duration. seconds ( ) ,
714+ duration. milliseconds ( ) ,
715+ duration. microseconds ( ) ,
716+ duration. nanoseconds ( ) ,
717+ ) ) ;
718+
719+ let seconds = time. seconds ( ) . unsigned_abs ( ) ;
720+ let subseconds = time. subseconds ( ) . unsigned_abs ( ) ;
721+
722+ let time = Some ( TimeDurationRecord :: Seconds {
723+ hours : hours. 0 as u64 ,
724+ minutes : minutes. 0 as u64 ,
725+ seconds,
726+ fraction : subseconds,
727+ } ) ;
728+
729+ Ok ( FormattableDuration {
730+ precision,
731+ duration : DurationParseRecord { sign, date, time } ,
732+ } )
621733}
622734
623735#[ cfg( feature = "experimental" ) ]
@@ -636,6 +748,8 @@ impl Duration {
636748
637749// TODO: Update, optimize, and fix the below. is_valid_duration should probably be generic over a T.
638750
751+ const TWO_POWER_FIFTY_THREE : i128 = 9_007_199_254_740_992 ;
752+
639753// NOTE: Can FiniteF64 optimize the duration_validation
640754/// Utility function to check whether the `Duration` fields are valid.
641755#[ inline]
@@ -701,21 +815,18 @@ pub(crate) fn is_valid_duration(
701815 // in C++ with an implementation of core::remquo() with sufficient bits in the quotient.
702816 // String manipulation will also give an exact result, since the multiplication is by a power of 10.
703817 // Seconds part
704- let normalized_seconds = days. 0 . mul_add (
705- 86_400.0 ,
706- hours . 0 . mul_add ( 3600.0 , minutes. 0 . mul_add ( 60.0 , seconds . 0 ) ) ,
707- ) ;
818+ let normalized_seconds = ( days. 0 as i128 * 86_400 )
819+ + ( hours . 0 as i128 ) * 3600
820+ + minutes. 0 as i128 * 60
821+ + seconds . 0 as i128 ;
708822 // Subseconds part
709- let normalized_subseconds_parts = milliseconds. 0 . mul_add (
710- 10e-3 ,
711- microseconds
712- . 0
713- . mul_add ( 10e-6 , nanoseconds. 0 . mul_add ( 10e-9 , 0.0 ) ) ,
714- ) ;
823+ let normalized_subseconds_parts = ( milliseconds. 0 as i128 / 1_000 )
824+ + ( microseconds. 0 as i128 / 1_000_000 )
825+ + ( nanoseconds. 0 as i128 / 1_000_000_000 ) ;
715826
716827 let normalized_seconds = normalized_seconds + normalized_subseconds_parts;
717828 // 8. If abs(normalizedSeconds) ≥ 2**53, return false.
718- if normalized_seconds. abs ( ) >= 2e53 {
829+ if normalized_seconds. abs ( ) >= TWO_POWER_FIFTY_THREE {
719830 return false ;
720831 }
721832
@@ -786,7 +897,7 @@ impl FromStr for Duration {
786897 let nanoseconds = rem. rem_euclid ( 1_000 ) ;
787898
788899 (
789- f64 :: from ( hours) ,
900+ hours as f64 ,
790901 minutes as f64 ,
791902 seconds as f64 ,
792903 milliseconds as f64 ,
@@ -810,8 +921,8 @@ impl FromStr for Duration {
810921 let nanoseconds = rem. rem_euclid ( 1_000 ) ;
811922
812923 (
813- f64 :: from ( hours) ,
814- f64 :: from ( minutes) ,
924+ hours as f64 ,
925+ minutes as f64 ,
815926 seconds as f64 ,
816927 milliseconds as f64 ,
817928 microseconds as f64 ,
@@ -832,9 +943,9 @@ impl FromStr for Duration {
832943 let nanoseconds = rem. rem_euclid ( 1_000 ) ;
833944
834945 (
835- f64 :: from ( hours) ,
836- f64 :: from ( minutes) ,
837- f64 :: from ( seconds) ,
946+ hours as f64 ,
947+ minutes as f64 ,
948+ seconds as f64 ,
838949 milliseconds as f64 ,
839950 microseconds as f64 ,
840951 nanoseconds as f64 ,
@@ -855,7 +966,7 @@ impl FromStr for Duration {
855966 FiniteF64 :: from ( years) . copysign ( sign) ,
856967 FiniteF64 :: from ( months) . copysign ( sign) ,
857968 FiniteF64 :: from ( weeks) . copysign ( sign) ,
858- FiniteF64 :: from ( days) . copysign ( sign) ,
969+ FiniteF64 :: try_from ( days) ? . copysign ( sign) ,
859970 FiniteF64 :: try_from ( hours) ?. copysign ( sign) ,
860971 FiniteF64 :: try_from ( minutes) ?. copysign ( sign) ,
861972 FiniteF64 :: try_from ( seconds) ?. copysign ( sign) ,
0 commit comments