@ -12,7 +12,14 @@ use crate::{
use chrono ::{ prelude ::* , Duration , LocalResult } ;
use std ::fmt ::Display ;
const NANOS_IN_MS : f64 = 1_000_000 f64 ;
/// The number of nanoseconds in a millisecond.
const NANOS_PER_MS : i64 = 1_000_000 ;
/// The number of milliseconds in an hour.
const MILLIS_PER_HOUR : i64 = 3_600_000 ;
/// The number of milliseconds in a minute.
const MILLIS_PER_MINUTE : i64 = 60_000 ;
/// The number of milliseconds in a second.
const MILLIS_PER_SECOND : i64 = 1000 ;
#[ inline ]
fn is_zero_or_normal_opt ( value : Option < f64 > ) -> bool {
@ -191,6 +198,22 @@ impl Date {
/// The amount of arguments this function object takes.
pub ( crate ) const LENGTH : usize = 7 ;
/// Check if the time (number of miliseconds) is in the expected range.
/// Returns None if the time is not in the range, otherwise returns the time itself in option.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-timeclip
#[ inline ]
pub fn time_clip ( time : f64 ) -> Option < f64 > {
if time . abs ( ) > 8.64e15 {
None
} else {
Some ( time )
}
}
/// Converts the `Date` to a local `DateTime`.
///
/// If the `Date` is invalid (i.e. NAN), this function will return `None`.
@ -224,11 +247,13 @@ impl Date {
millisecond : Option < f64 > ,
) {
#[ inline ]
fn num_days_in ( year : i32 , month : u32 ) -> i32 {
fn num_days_in ( year : i32 , month : u32 ) -> Option < u32 > {
let month = month + 1 ; // zero-based for calculations
NaiveDate ::from_ymd (
Some (
NaiveDate ::from_ymd_opt (
match month {
12 = > year + 1 ,
12 = > year . checked_add ( 1 ) ? ,
_ = > year ,
} ,
match month {
@ -236,41 +261,50 @@ impl Date {
_ = > month + 1 ,
} ,
1 ,
) ?
. signed_duration_since ( NaiveDate ::from_ymd_opt ( year , month , 1 ) ? )
. num_days ( ) as u32 ,
)
. signed_duration_since ( NaiveDate ::from_ymd ( year , month , 1 ) )
. num_days ( ) as i32
}
#[ inline ]
fn fix_month ( year : & mut i32 , month : & mut i32 ) {
* year + = * month / 12 ;
* month = if * month < 0 {
* year - = 1 ;
11 + ( * month + 1 ) % 12
fn fix_month ( year : i32 , month : i32 ) -> Option < ( i32 , u32 ) > {
let year = year . checked_add ( month / 12 ) ? ;
if month < 0 {
let year = year . checked_sub ( 1 ) ? ;
let month = ( 11 + ( month + 1 ) % 12 ) as u32 ;
Some ( ( year , month ) )
} else {
* month % 12
let month = ( month % 12 ) as u32 ;
Some ( ( year , month ) )
}
}
#[ inline ]
fn fix_day ( year : & mut i32 , month : & mut i32 , day : & mut i32 ) {
fix_month ( year , month ) ;
fn fix_day ( mut year : i32 , mut month : i32 , mut day : i32 ) -> Option < ( i32 , u32 , u32 ) > {
loop {
if * day < 0 {
* month - = 1 ;
fix_month ( year , month ) ;
* day + = num_days_in ( * year , * month as u32 ) ;
if day < 0 {
let ( fixed_year , fixed_month ) = fix_month ( year , month . checked_sub ( 1 ) ? ) ? ;
year = fixed_year ;
month = fixed_month as i32 ;
day + = num_days_in ( fixed_year , fixed_month ) ? as i32 ;
} else {
let num_days = num_days_in ( * year , * month as u32 ) ;
if * day > = num_days {
* day - = num_days_in ( * year , * month as u32 ) ;
* month + = 1 ;
fix_month ( year , month ) ;
let ( fixed_year , fixed_month ) = fix_month ( year , month ) ? ;
let num_days = num_days_in ( fixed_year , fixed_month ) ? as i32 ;
if day > = num_days {
day - = num_days ;
month = month . checked_add ( 1 ) ? ;
} else {
break ;
}
}
}
let ( fixed_year , fixed_month ) = fix_month ( year , month ) ? ;
Some ( ( fixed_year , fixed_month , day as u32 ) )
}
// If any of the args are infinity or NaN, return an invalid date.
@ -286,22 +320,29 @@ impl Date {
} ;
self . 0 = naive . and_then ( | naive | {
let mut year = year . unwrap_or_else ( | | naive . year ( ) as f64 ) as i32 ;
let mut month = month . unwrap_or_else ( | | naive . month0 ( ) as f64 ) as i32 ;
let mut day = day . unwrap_or_else ( | | naive . day ( ) as f64 ) as i32 - 1 ;
let year = year . unwrap_or_else ( | | naive . year ( ) as f64 ) as i32 ;
let month = month . unwrap_or_else ( | | naive . month0 ( ) as f64 ) as i32 ;
let day = ( day . unwrap_or_else ( | | naive . day ( ) as f64 ) as i32 ) . checked_sub ( 1 ) ? ;
let hour = hour . unwrap_or_else ( | | naive . hour ( ) as f64 ) as i64 ;
let minute = minute . unwrap_or_else ( | | naive . minute ( ) as f64 ) as i64 ;
let second = second . unwrap_or_else ( | | naive . second ( ) as f64 ) as i64 ;
let millisecond =
millisecond . unwrap_or_else ( | | naive . nanosecond ( ) as f64 / NANOS_IN_MS ) as i64 ;
let millisecond = millisecond
. unwrap_or_else ( | | naive . nanosecond ( ) as f64 / NANOS_PER_MS as f64 )
as i64 ;
let ( year , month , day ) = fix_day ( year , month , day ) ? ;
fix_day ( & mut year , & mut month , & mut day ) ;
let duration_hour = Duration ::milliseconds ( hour . checked_mul ( MILLIS_PER_HOUR ) ? ) ;
let duration_minute = Duration ::milliseconds ( minute . checked_mul ( MILLIS_PER_MINUTE ) ? ) ;
let duration_second = Duration ::milliseconds ( second . checked_mul ( MILLIS_PER_SECOND ) ? ) ;
let duration_milisecond = Duration ::milliseconds ( millisecond ) ;
let duration = Duration ::hours ( hour )
+ Duration ::minutes ( minute )
+ Duration ::seconds ( second )
+ Duration ::milliseconds ( millisecond ) ;
NaiveDate ::from_ymd_opt ( year , month as u32 + 1 , day as u32 + 1 )
let duration = duration_hour
. checked_add ( & duration_minute ) ?
. checked_add ( & duration_second ) ?
. checked_add ( & duration_milisecond ) ? ;
NaiveDate ::from_ymd_opt ( year , month + 1 , day + 1 )
. and_then ( | dt | dt . and_hms ( 0 , 0 , 0 ) . checked_add_signed ( duration ) )
. and_then ( | dt | {
if utc {
@ -310,6 +351,7 @@ impl Date {
ignore_ambiguity ( Local . from_local_datetime ( & dt ) ) . map ( | dt | dt . naive_utc ( ) )
}
} )
. filter ( | dt | Self ::time_clip ( dt . timestamp_millis ( ) as f64 ) . is_some ( ) )
} ) ;
}
@ -415,6 +457,7 @@ impl Date {
} ,
} ;
let tv = tv . filter ( | time | Self ::time_clip ( time . timestamp_millis ( ) as f64 ) . is_some ( ) ) ;
let date = Date ( tv ) ;
this . set_data ( ObjectData ::Date ( date ) ) ;
Ok ( this . clone ( ) )
@ -477,7 +520,8 @@ impl Date {
let final_date = NaiveDate ::from_ymd_opt ( year , month + 1 , day )
. and_then ( | naive_date | naive_date . and_hms_milli_opt ( hour , min , sec , milli ) )
. and_then ( | local | ignore_ambiguity ( Local . from_local_datetime ( & local ) ) )
. map ( | local | local . naive_utc ( ) ) ;
. map ( | local | local . naive_utc ( ) )
. filter ( | time | Self ::time_clip ( time . timestamp_millis ( ) as f64 ) . is_some ( ) ) ;
let date = Date ( final_date ) ;
this . set_data ( ObjectData ::Date ( date ) ) ;
@ -557,7 +601,7 @@ impl Date {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getMilliseconds
pub fn get_milliseconds ( & self ) -> f64 {
self . to_local ( )
. map_or ( f64 ::NAN , | dt | dt . nanosecond ( ) as f64 / NANOS_IN_MS )
. map_or ( f64 ::NAN , | dt | dt . nanosecond ( ) as f64 / NANOS_PER_MS as f64 )
}
/// `Date.prototype.getMinutes()`
@ -724,7 +768,7 @@ impl Date {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getUTCMilliseconds
pub fn get_utc_milliseconds ( & self ) -> f64 {
self . to_utc ( )
. map_or ( f64 ::NAN , | dt | dt . nanosecond ( ) as f64 / NANOS_IN_MS )
. map_or ( f64 ::NAN , | dt | dt . nanosecond ( ) as f64 / NANOS_PER_MS as f64 )
}
/// `Date.prototype.getUTCMinutes()`
@ -1352,9 +1396,8 @@ impl Date {
NaiveDate ::from_ymd_opt ( year , month + 1 , day )
. and_then ( | f | f . and_hms_milli_opt ( hour , min , sec , milli ) )
. map_or ( Ok ( Value ::number ( f64 ::NAN ) ) , | f | {
Ok ( Value ::number ( f . timestamp_millis ( ) as f64 ) )
} )
. and_then ( | f | Self ::time_clip ( f . timestamp_millis ( ) as f64 ) )
. map_or ( Ok ( Value ::number ( f64 ::NAN ) ) , | time | Ok ( Value ::number ( time ) ) )
}
}