@@ -21,14 +21,19 @@ use crate::{
2121} ;
2222use crate :: { builtins:: temporal:: options:: get_digits_option, value:: JsVariant } ;
2323use boa_gc:: { Finalize , Trace } ;
24+ use num_traits:: { AsPrimitive , PrimInt } ;
2425use temporal_rs:: {
2526PlainTime as PlainTimeInner ,
2627 options:: {
2728Overflow , RoundingIncrement , RoundingMode , RoundingOptions , ToStringRoundingOptions , Unit ,
2829} ,
2930 partial:: PartialTime ,
31+ primitive:: FiniteF64 ,
3032} ;
3133
34+ #[ cfg( test) ]
35+ mod tests;
36+
3237/// The `Temporal.PlainTime` built-in implementation.
3338///
3439/// More information:
@@ -540,13 +545,19 @@ impl PlainTime {
540545} ;
541546
542547// Steps 5-16 equate to the below
543- let partial =to_partial_time_record ( & partial_object, context) ?;
548+ let partial =to_js_partial_time_record ( & partial_object, context) ?;
544549// 17. Let resolvedOptions be ? GetOptionsObject(options).
545550// 18. Let overflow be ? GetTemporalOverflowOption(resolvedOptions).
546551let options =get_options_object ( args. get_or_undefined ( 1 ) ) ?;
547552let overflow =get_option :: < Overflow > ( & options, js_string ! ( "overflow" ) , context) ?;
548553
549- create_temporal_time ( time. inner . with ( partial, overflow) ?, None , context) . map ( Into :: into)
554+ create_temporal_time (
555+ time. inner
556+ . with ( partial. as_temporal_partial_time ( overflow) ?, overflow) ?,
557+ None ,
558+ context,
559+ )
560+ . map ( Into :: into)
550561}
551562
552563/// 4.3.12 `Temporal.PlainTime.prototype.until ( other [ , options ] )`
@@ -907,12 +918,13 @@ pub(crate) fn to_temporal_time(
907918// e. Set result to ? RegulateTime(result.[[Hour]], result.[[Minute]],
908919// result.[[Second]], result.[[Millisecond]], result.[[Microsecond]],
909920// result.[[Nanosecond]], overflow).
910- let partial =to_partial_time_record ( & object, context) ?;
921+ let partial =to_js_partial_time_record ( & object, context) ?;
911922
912923let options =get_options_object ( options) ?;
913924let overflow =get_option :: < Overflow > ( & options, js_string ! ( "overflow" ) , context) ?;
914925
915- PlainTimeInner :: from_partial ( partial, overflow) . map_err ( Into :: into)
926+ PlainTimeInner :: from_partial ( partial. as_temporal_partial_time ( overflow) ?, overflow)
927+ . map_err ( Into :: into)
916928}
917929// 3. Else,
918930JsVariant :: String ( str) =>{
@@ -934,59 +946,100 @@ pub(crate) fn to_temporal_time(
934946// 4. Return ! CreateTemporalTime(result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]]).
935947}
936948
937- pub ( crate ) fn to_partial_time_record (
949+ /// A `PartialTime` represents partially filled `Time` fields.
950+ #[ derive( Debug , Default , Clone , Copy , PartialEq ) ]
951+ pub struct JsPartialTime {
952+ /// A potentially set `hour` field.
953+ pub hour : Option < FiniteF64 > ,
954+ /// A potentially set `minute` field.
955+ pub minute : Option < FiniteF64 > ,
956+ /// A potentially set `second` field.
957+ pub second : Option < FiniteF64 > ,
958+ /// A potentially set `millisecond` field.
959+ pub millisecond : Option < FiniteF64 > ,
960+ /// A potentially set `microsecond` field.
961+ pub microsecond : Option < FiniteF64 > ,
962+ /// A potentially set `nanosecond` field.
963+ pub nanosecond : Option < FiniteF64 > ,
964+ }
965+
966+ impl JsPartialTime {
967+ fn as_temporal_partial_time ( & self , overflow : Option < Overflow > ) ->JsResult < PartialTime > {
968+ fn check ( value : Option < FiniteF64 > , typ : & ' static str , max : u16 ) ->JsResult < ( ) > {
969+ if let Some ( value) = value
970+ && value. as_inner ( ) . is_sign_negative ( )
971+ {
972+ return Err ( JsNativeError :: range ( )
973+ . with_message ( format ! ( "time value '{typ}' not in 0..{max}: {value}" ) )
974+ . into ( ) ) ;
975+ }
976+ Ok ( ( ) )
977+ }
978+
979+ fn truncate < T > ( value : Option < FiniteF64 > ) ->Option < T >
980+ where
981+ T : PrimInt +AsPrimitive < f64 > ,
982+ f64 : AsPrimitive < T > ,
983+ {
984+ value
985+ . as_ref ( )
986+ . map ( FiniteF64 :: as_integer_with_truncation :: < T > )
987+ }
988+
989+ if overflow ==Some ( Overflow :: Reject ) {
990+ check ( self . hour , "hour" , 23 ) ?;
991+ check ( self . minute , "minute" , 59 ) ?;
992+ check ( self . second , "second" , 59 ) ?;
993+ check ( self . millisecond , "millisecond" , 999 ) ?;
994+ check ( self . microsecond , "microsecond" , 999 ) ?;
995+ check ( self . nanosecond , "nanosecond" , 999 ) ?;
996+ }
997+
998+ Ok ( PartialTime :: new ( )
999+ . with_hour ( truncate ( self . hour ) )
1000+ . with_minute ( truncate ( self . minute ) )
1001+ . with_second ( truncate ( self . second ) )
1002+ . with_millisecond ( truncate ( self . millisecond ) )
1003+ . with_microsecond ( truncate ( self . microsecond ) )
1004+ . with_nanosecond ( truncate ( self . nanosecond ) ) )
1005+ }
1006+ }
1007+
1008+ pub ( crate ) fn to_js_partial_time_record (
9381009partial_object : & JsObject ,
9391010context : & mut Context ,
940- ) ->JsResult < PartialTime > {
1011+ ) ->JsResult < JsPartialTime > {
9411012let hour = partial_object
9421013. get ( js_string ! ( "hour" ) , context) ?
943- . map ( |v|{
944- let finite = v. to_finitef64 ( context) ?;
945- Ok :: < u8 , JsError > ( finite. as_integer_with_truncation :: < u8 > ( ) )
946- } )
1014+ . map ( |v| v. to_finitef64 ( context) )
9471015. transpose ( ) ?;
9481016
9491017let microsecond = partial_object
9501018. get ( js_string ! ( "microsecond" ) , context) ?
951- . map ( |v|{
952- let finite = v. to_finitef64 ( context) ?;
953- Ok :: < u16 , JsError > ( finite. as_integer_with_truncation :: < u16 > ( ) )
954- } )
1019+ . map ( |v| v. to_finitef64 ( context) )
9551020. transpose ( ) ?;
9561021
9571022let millisecond = partial_object
9581023. get ( js_string ! ( "millisecond" ) , context) ?
959- . map ( |v|{
960- let finite = v. to_finitef64 ( context) ?;
961- Ok :: < u16 , JsError > ( finite. as_integer_with_truncation :: < u16 > ( ) )
962- } )
1024+ . map ( |v| v. to_finitef64 ( context) )
9631025. transpose ( ) ?;
9641026
9651027let minute = partial_object
9661028. get ( js_string ! ( "minute" ) , context) ?
967- . map ( |v|{
968- let finite = v. to_finitef64 ( context) ?;
969- Ok :: < u8 , JsError > ( finite. as_integer_with_truncation :: < u8 > ( ) )
970- } )
1029+ . map ( |v| v. to_finitef64 ( context) )
9711030. transpose ( ) ?;
9721031
9731032let nanosecond = partial_object
9741033. get ( js_string ! ( "nanosecond" ) , context) ?
975- . map ( |v|{
976- let finite = v. to_finitef64 ( context) ?;
977- Ok :: < u16 , JsError > ( finite. as_integer_with_truncation :: < u16 > ( ) )
978- } )
1034+ . map ( |v| v. to_finitef64 ( context) )
9791035. transpose ( ) ?;
9801036
9811037let second = partial_object
9821038. get ( js_string ! ( "second" ) , context) ?
983- . map ( |v|{
984- let finite = v. to_finitef64 ( context) ?;
985- Ok :: < u8 , JsError > ( finite. as_integer_with_truncation :: < u8 > ( ) )
986- } )
1039+ . map ( |v| v. to_finitef64 ( context) )
9871040. transpose ( ) ?;
9881041
989- Ok ( PartialTime {
1042+ Ok ( JsPartialTime {
9901043 hour,
9911044 minute,
9921045 second,