@@ -4,16 +4,22 @@ use crate::{
44 components:: { timezone:: TzProvider , 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
@@ -83,6 +88,16 @@ pub struct Duration {
8388 time : TimeDuration ,
8489}
8590
91+ impl core:: fmt:: Display for Duration {
92+ fn fmt ( & self , f : & mut core:: fmt:: Formatter < ' _ > ) -> core:: fmt:: Result {
93+ f. write_str (
94+ & self
95+ . to_temporal_string ( ToStringRoundingOptions :: default ( ) )
96+ . expect ( "Duration must return a valid string with default options." ) ,
97+ )
98+ }
99+ }
100+
86101// NOTE(nekevss): Structure of the below is going to be a little convoluted,
87102// but intended to section everything based on the below
88103//
@@ -619,6 +634,97 @@ impl Duration {
619634 }
620635 }
621636 }
637+
638+ pub fn to_temporal_string ( & self , options : ToStringRoundingOptions ) -> TemporalResult < String > {
639+ if options. smallest_unit == Some ( TemporalUnit :: Hour )
640+ || options. smallest_unit == Some ( TemporalUnit :: Minute )
641+ {
642+ return Err ( TemporalError :: range ( ) . with_message (
643+ "string rounding options cannot have hour or minute smallest unit." ,
644+ ) ) ;
645+ }
646+
647+ let resolved_options = options. resolve ( ) ?;
648+ if resolved_options. smallest_unit == TemporalUnit :: Nanosecond
649+ && resolved_options. increment == RoundingIncrement :: ONE
650+ {
651+ let duration = duration_to_formattable ( self , resolved_options. precision ) ?;
652+ return Ok ( duration. to_string ( ) ) ;
653+ }
654+
655+ let rounding_options = ResolvedRoundingOptions :: from_to_string_options ( & resolved_options) ;
656+
657+ // 11. Let largestUnit be DefaultTemporalLargestUnit(duration).
658+ let largest = self . default_largest_unit ( ) ;
659+ // 12. Let internalDuration be ToInternalDurationRecord(duration).
660+ let norm = NormalizedDurationRecord :: new (
661+ self . date ,
662+ NormalizedTimeDuration :: from_time_duration ( & self . time ) ,
663+ ) ?;
664+ // 13. Let timeDuration be ? RoundTimeDuration(internalDuration.[[Time]], precision.[[Increment]], precision.[[Unit]], roundingMode).
665+ let ( rounded, _) = norm
666+ . normalized_time_duration ( )
667+ . round ( FiniteF64 :: default ( ) , rounding_options) ?;
668+ // 14. Set internalDuration to CombineDateAndTimeDuration(internalDuration.[[Date]], timeDuration).
669+ let norm = NormalizedDurationRecord :: new ( norm. date ( ) , rounded. normalized_time_duration ( ) ) ?;
670+ // 15. Let roundedLargestUnit be LargerOfTwoTemporalUnits(largestUnit, second).
671+ let rounded_largest = largest. max ( TemporalUnit :: Second ) ;
672+ // 16. Let roundedDuration be ? TemporalDurationFromInternal(internalDuration, roundedLargestUnit).
673+ let rounded = Self :: from_normalized ( norm, rounded_largest) ?;
674+
675+ // 17. Return TemporalDurationToString(roundedDuration, precision.[[Precision]]).
676+ Ok ( duration_to_formattable ( & rounded, resolved_options. precision ) ?. to_string ( ) )
677+ }
678+ }
679+
680+ pub fn duration_to_formattable (
681+ duration : & Duration ,
682+ precision : Precision ,
683+ ) -> TemporalResult < FormattableDuration > {
684+ let sign = duration. sign ( ) ;
685+ let sign = if sign == Sign :: Negative {
686+ IxdtfSign :: Negative
687+ } else {
688+ IxdtfSign :: Positive
689+ } ;
690+ let date = duration. years ( ) . 0 + duration. months ( ) . 0 + duration. weeks ( ) . 0 + duration. days ( ) . 0 ;
691+ let date = if date != 0.0 {
692+ Some ( DateDurationRecord {
693+ years : duration. years ( ) . 0 as u32 ,
694+ months : duration. months ( ) . 0 as u32 ,
695+ weeks : duration. weeks ( ) . 0 as u32 ,
696+ days : duration. days ( ) . 0 as u32 ,
697+ } )
698+ } else {
699+ None
700+ } ;
701+
702+ let hours = duration. hours ( ) . abs ( ) ;
703+ let minutes = duration. minutes ( ) . abs ( ) ;
704+
705+ let time = NormalizedTimeDuration :: from_time_duration ( & TimeDuration :: new_unchecked (
706+ FiniteF64 :: default ( ) ,
707+ FiniteF64 :: default ( ) ,
708+ duration. seconds ( ) ,
709+ duration. milliseconds ( ) ,
710+ duration. microseconds ( ) ,
711+ duration. nanoseconds ( ) ,
712+ ) ) ;
713+
714+ let seconds = time. seconds ( ) . unsigned_abs ( ) as u32 ;
715+ let subseconds = time. subseconds ( ) . unsigned_abs ( ) ;
716+
717+ let time = Some ( TimeDurationRecord :: Seconds {
718+ hours : hours. 0 as u32 ,
719+ minutes : minutes. 0 as u32 ,
720+ seconds,
721+ fraction : subseconds,
722+ } ) ;
723+
724+ Ok ( FormattableDuration {
725+ precision,
726+ duration : DurationParseRecord { sign, date, time } ,
727+ } )
622728}
623729
624730#[ cfg( feature = "experimental" ) ]
0 commit comments