Browse Source

Fix `Date` for dynamic timezones (#2877)

* Fix `Date` for dynamic timezones

* Fix test
gh-readonly-queue/main/pr-2877-b0ddf5eed00a53281d67fc7d846233fc0d99ce9c
José Julián Espina 2 years ago committed by GitHub
parent
commit
3af82222eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 245
      boa_engine/src/builtins/date/mod.rs
  2. 11
      boa_engine/src/builtins/date/tests.rs
  3. 18
      boa_engine/src/builtins/date/utils.rs
  4. 23
      boa_engine/src/context/hooks.rs

245
boa_engine/src/builtins/date/mod.rs

@ -206,12 +206,11 @@ impl BuiltInConstructor for Date {
// a. Let now be the time value (UTC) identifying the current time. // a. Let now be the time value (UTC) identifying the current time.
// b. Return ToDateString(now). // b. Return ToDateString(now).
return Ok(JsValue::new( return Ok(JsValue::new(
DateTime::<FixedOffset>::from_utc( context
context.host_hooks().utc_now(), .host_hooks()
context.host_hooks().tz_offset(), .local_from_utc(context.host_hooks().utc_now())
) .format("%a %b %d %Y %H:%M:%S GMT%:z")
.format("%a %b %d %Y %H:%M:%S GMT%:z") .to_string(),
.to_string(),
)); ));
} }
// 2. Let numberOfArgs be the number of elements in values. // 2. Let numberOfArgs be the number of elements in values.
@ -269,14 +268,7 @@ impl BuiltInConstructor for Date {
// Separating this into its own function to simplify the logic. // Separating this into its own function to simplify the logic.
let dt = Self::construct_date(args, context)? let dt = Self::construct_date(args, context)?
.and_then(|dt| { .and_then(|dt| context.host_hooks().local_from_naive_local(dt).earliest());
context
.host_hooks()
.tz_offset()
.from_local_datetime(&dt)
.earliest()
})
.map(|dt| dt.naive_utc());
Self(dt.map(|dt| dt.timestamp_millis())) Self(dt.map(|dt| dt.timestamp_millis()))
} }
@ -469,14 +461,13 @@ impl Date {
let t = this_time_value(this)?; let t = this_time_value(this)?;
// 2. If t is NaN, return NaN. // 2. If t is NaN, return NaN.
let mut t = some_or_nan!(t.and_then(NaiveDateTime::from_timestamp_millis)); let t = some_or_nan!(t
if LOCAL { .and_then(NaiveDateTime::from_timestamp_millis)
t = context .map(|dt| if LOCAL {
.host_hooks() context.host_hooks().local_from_utc(dt).naive_local()
.tz_offset() } else {
.from_utc_datetime(&t) dt
.naive_local(); }));
}
// 3. Return DateFromTime(LocalTime(t)). // 3. Return DateFromTime(LocalTime(t)).
Ok(JsValue::new(t.day())) Ok(JsValue::new(t.day()))
@ -499,14 +490,13 @@ impl Date {
let t = this_time_value(this)?; let t = this_time_value(this)?;
// 2. If t is NaN, return NaN. // 2. If t is NaN, return NaN.
let mut t = some_or_nan!(t.and_then(NaiveDateTime::from_timestamp_millis)); let t = some_or_nan!(t
if LOCAL { .and_then(NaiveDateTime::from_timestamp_millis)
t = context .map(|dt| if LOCAL {
.host_hooks() context.host_hooks().local_from_utc(dt).naive_local()
.tz_offset() } else {
.from_utc_datetime(&t) dt
.naive_local(); }));
}
// 3. Return WeekDay(LocalTime(t)). // 3. Return WeekDay(LocalTime(t)).
Ok(JsValue::new(t.weekday().num_days_from_sunday())) Ok(JsValue::new(t.weekday().num_days_from_sunday()))
@ -535,7 +525,7 @@ impl Date {
let t = some_or_nan!(t.and_then(NaiveDateTime::from_timestamp_millis)); let t = some_or_nan!(t.and_then(NaiveDateTime::from_timestamp_millis));
// 3. Return YearFromTime(LocalTime(t)) - 1900𝔽. // 3. Return YearFromTime(LocalTime(t)) - 1900𝔽.
let local = context.host_hooks().tz_offset().from_utc_datetime(&t); let local = context.host_hooks().local_from_utc(t);
Ok(JsValue::from(local.year() - 1900)) Ok(JsValue::from(local.year() - 1900))
} }
@ -555,14 +545,13 @@ impl Date {
let t = this_time_value(this)?; let t = this_time_value(this)?;
// 2. If t is NaN, return NaN. // 2. If t is NaN, return NaN.
let mut t = some_or_nan!(t.and_then(NaiveDateTime::from_timestamp_millis)); let t = some_or_nan!(t
if LOCAL { .and_then(NaiveDateTime::from_timestamp_millis)
t = context .map(|dt| if LOCAL {
.host_hooks() context.host_hooks().local_from_utc(dt).naive_local()
.tz_offset() } else {
.from_utc_datetime(&t) dt
.naive_local(); }));
}
// 3. Return YearFromTime(LocalTime(t)). // 3. Return YearFromTime(LocalTime(t)).
Ok(JsValue::new(t.year())) Ok(JsValue::new(t.year()))
@ -584,14 +573,13 @@ impl Date {
let t = this_time_value(this)?; let t = this_time_value(this)?;
// 2. If t is NaN, return NaN. // 2. If t is NaN, return NaN.
let mut t = some_or_nan!(t.and_then(NaiveDateTime::from_timestamp_millis)); let t = some_or_nan!(t
if LOCAL { .and_then(NaiveDateTime::from_timestamp_millis)
t = context .map(|dt| if LOCAL {
.host_hooks() context.host_hooks().local_from_utc(dt).naive_local()
.tz_offset() } else {
.from_utc_datetime(&t) dt
.naive_local(); }));
}
// 3. Return HourFromTime(LocalTime(t)). // 3. Return HourFromTime(LocalTime(t)).
Ok(JsValue::new(t.hour())) Ok(JsValue::new(t.hour()))
@ -613,14 +601,13 @@ impl Date {
let t = this_time_value(this)?; let t = this_time_value(this)?;
// 2. If t is NaN, return NaN. // 2. If t is NaN, return NaN.
let mut t = some_or_nan!(t.and_then(NaiveDateTime::from_timestamp_millis)); let t = some_or_nan!(t
if LOCAL { .and_then(NaiveDateTime::from_timestamp_millis)
t = context .map(|dt| if LOCAL {
.host_hooks() context.host_hooks().local_from_utc(dt).naive_local()
.tz_offset() } else {
.from_utc_datetime(&t) dt
.naive_local(); }));
}
// 3. Return msFromTime(LocalTime(t)). // 3. Return msFromTime(LocalTime(t)).
Ok(JsValue::new(t.timestamp_subsec_millis())) Ok(JsValue::new(t.timestamp_subsec_millis()))
@ -642,14 +629,13 @@ impl Date {
let t = this_time_value(this)?; let t = this_time_value(this)?;
// 2. If t is NaN, return NaN. // 2. If t is NaN, return NaN.
let mut t = some_or_nan!(t.and_then(NaiveDateTime::from_timestamp_millis)); let t = some_or_nan!(t
if LOCAL { .and_then(NaiveDateTime::from_timestamp_millis)
t = context .map(|dt| if LOCAL {
.host_hooks() context.host_hooks().local_from_utc(dt).naive_local()
.tz_offset() } else {
.from_utc_datetime(&t) dt
.naive_local(); }));
}
// 3. Return MinFromTime(LocalTime(t)). // 3. Return MinFromTime(LocalTime(t)).
Ok(JsValue::new(t.minute())) Ok(JsValue::new(t.minute()))
@ -672,14 +658,13 @@ impl Date {
let t = this_time_value(this)?; let t = this_time_value(this)?;
// 2. If t is NaN, return NaN. // 2. If t is NaN, return NaN.
let mut t = some_or_nan!(t.and_then(NaiveDateTime::from_timestamp_millis)); let t = some_or_nan!(t
if LOCAL { .and_then(NaiveDateTime::from_timestamp_millis)
t = context .map(|dt| if LOCAL {
.host_hooks() context.host_hooks().local_from_utc(dt).naive_local()
.tz_offset() } else {
.from_utc_datetime(&t) dt
.naive_local(); }));
}
// 3. Return MonthFromTime(LocalTime(t)). // 3. Return MonthFromTime(LocalTime(t)).
Ok(JsValue::new(t.month0())) Ok(JsValue::new(t.month0()))
@ -701,14 +686,13 @@ impl Date {
let t = this_time_value(this)?; let t = this_time_value(this)?;
// 2. If t is NaN, return NaN. // 2. If t is NaN, return NaN.
let mut t = some_or_nan!(t.and_then(NaiveDateTime::from_timestamp_millis)); let t = some_or_nan!(t
if LOCAL { .and_then(NaiveDateTime::from_timestamp_millis)
t = context .map(|dt| if LOCAL {
.host_hooks() context.host_hooks().local_from_utc(dt).naive_local()
.tz_offset() } else {
.from_utc_datetime(&t) dt
.naive_local(); }));
}
// 3. Return SecFromTime(LocalTime(t)) // 3. Return SecFromTime(LocalTime(t))
Ok(JsValue::new(t.second())) Ok(JsValue::new(t.second()))
@ -751,11 +735,16 @@ impl Date {
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
// 1. Let t be ? thisTimeValue(this value). // 1. Let t be ? thisTimeValue(this value).
// 2. If t is NaN, return NaN. // 2. If t is NaN, return NaN.
some_or_nan!(this_time_value(this)?); let t = some_or_nan!(this_time_value(this)?.and_then(NaiveDateTime::from_timestamp_millis));
// 3. Return (t - LocalTime(t)) / msPerMinute. // 3. Return (t - LocalTime(t)) / msPerMinute.
Ok(JsValue::from( Ok(JsValue::from(
-context.host_hooks().tz_offset().local_minus_utc() / 60, -context
.host_hooks()
.local_from_utc(t)
.offset()
.local_minus_utc()
/ 60,
)) ))
} }
@ -784,13 +773,13 @@ impl Date {
// 4. Set t to LocalTime(t). // 4. Set t to LocalTime(t).
// 5. Let newDate be MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), dt), TimeWithinDay(t)). // 5. Let newDate be MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), dt), TimeWithinDay(t)).
// 6. Let u be TimeClip(UTC(newDate)). // 6. Let u be TimeClip(UTC(newDate)).
let datetime = replace_params( let datetime = replace_params::<LOCAL>(
datetime, datetime,
DateParameters { DateParameters {
date: Some(date), date: Some(date),
..Default::default() ..Default::default()
}, },
LOCAL.then(|| context.host_hooks().tz_offset()), &*context.host_hooks(),
); );
// 7. Set the [[DateValue]] internal slot of this Date object to u. // 7. Set the [[DateValue]] internal slot of this Date object to u.
@ -817,21 +806,20 @@ impl Date {
get_mut_date!(let t = this); get_mut_date!(let t = this);
// 2. If t is NaN, set t to +0𝔽; otherwise, set t to LocalTime(t). // 2. If t is NaN, set t to +0𝔽; otherwise, set t to LocalTime(t).
let datetime = match t.0.and_then(NaiveDateTime::from_timestamp_millis) { let datetime =
Some(dt) => dt, t.0.and_then(NaiveDateTime::from_timestamp_millis)
None if LOCAL => { .or_else(|| {
let Some(datetime) = context.host_hooks().tz_offset() if LOCAL {
.from_local_datetime(&NaiveDateTime::default()) context
.earliest() .host_hooks()
.as_ref() .local_from_naive_local(NaiveDateTime::default())
.map(DateTime::naive_utc) else { .earliest()
*t = Self::new(None); .map(|dt| dt.naive_utc())
return Ok(t.as_value()) } else {
}; Some(NaiveDateTime::default())
datetime }
} });
None => NaiveDateTime::default(), let datetime = some_or_nan!(datetime);
};
// 3. Let y be ? ToNumber(year). // 3. Let y be ? ToNumber(year).
let year = args.get_or_undefined(0).to_integer_or_nan(context)?; let year = args.get_or_undefined(0).to_integer_or_nan(context)?;
@ -850,7 +838,7 @@ impl Date {
// 6. Let newDate be MakeDate(MakeDay(y, m, dt), TimeWithinDay(t)). // 6. Let newDate be MakeDate(MakeDay(y, m, dt), TimeWithinDay(t)).
// 7. Let u be TimeClip(UTC(newDate)). // 7. Let u be TimeClip(UTC(newDate)).
let datetime = replace_params( let datetime = replace_params::<LOCAL>(
datetime.timestamp_millis(), datetime.timestamp_millis(),
DateParameters { DateParameters {
year: Some(year), year: Some(year),
@ -858,7 +846,7 @@ impl Date {
date, date,
..Default::default() ..Default::default()
}, },
LOCAL.then(|| context.host_hooks().tz_offset()), &*context.host_hooks(),
); );
// 8. Set the [[DateValue]] internal slot of this Date object to u. // 8. Set the [[DateValue]] internal slot of this Date object to u.
@ -915,7 +903,7 @@ impl Date {
// 10. If ms is not present, let milli be msFromTime(t). // 10. If ms is not present, let milli be msFromTime(t).
// 11. Let date be MakeDate(Day(t), MakeTime(h, m, s, milli)). // 11. Let date be MakeDate(Day(t), MakeTime(h, m, s, milli)).
// 12. Let u be TimeClip(UTC(date)). // 12. Let u be TimeClip(UTC(date)).
let datetime = replace_params( let datetime = replace_params::<LOCAL>(
datetime, datetime,
DateParameters { DateParameters {
hour: Some(hour), hour: Some(hour),
@ -924,7 +912,7 @@ impl Date {
millisecond, millisecond,
..Default::default() ..Default::default()
}, },
LOCAL.then(|| context.host_hooks().tz_offset()), &*context.host_hooks(),
); );
// 13. Set the [[DateValue]] internal slot of this Date object to u. // 13. Set the [[DateValue]] internal slot of this Date object to u.
@ -959,13 +947,13 @@ impl Date {
// 4. Set t to LocalTime(t). // 4. Set t to LocalTime(t).
// 5. Let time be MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms). // 5. Let time be MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms).
// 6. Let u be TimeClip(UTC(MakeDate(Day(t), time))). // 6. Let u be TimeClip(UTC(MakeDate(Day(t), time))).
let datetime = replace_params( let datetime = replace_params::<LOCAL>(
datetime, datetime,
DateParameters { DateParameters {
millisecond: Some(ms), millisecond: Some(ms),
..Default::default() ..Default::default()
}, },
LOCAL.then(|| context.host_hooks().tz_offset()), &*context.host_hooks(),
); );
// 7. Set the [[DateValue]] internal slot of this Date object to u. // 7. Set the [[DateValue]] internal slot of this Date object to u.
@ -1013,7 +1001,7 @@ impl Date {
// 8. If ms is not present, let milli be msFromTime(t). // 8. If ms is not present, let milli be msFromTime(t).
// 9. Let date be MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli)). // 9. Let date be MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli)).
// 10. Let u be TimeClip(UTC(date)). // 10. Let u be TimeClip(UTC(date)).
let datetime = replace_params( let datetime = replace_params::<LOCAL>(
datetime, datetime,
DateParameters { DateParameters {
minute: Some(minute), minute: Some(minute),
@ -1021,7 +1009,7 @@ impl Date {
millisecond, millisecond,
..Default::default() ..Default::default()
}, },
LOCAL.then(|| context.host_hooks().tz_offset()), &*context.host_hooks(),
); );
// 11. Set the [[DateValue]] internal slot of this Date object to u. // 11. Set the [[DateValue]] internal slot of this Date object to u.
@ -1063,14 +1051,14 @@ impl Date {
// 6. If date is not present, let dt be DateFromTime(t). // 6. If date is not present, let dt be DateFromTime(t).
// 7. Let newDate be MakeDate(MakeDay(YearFromTime(t), m, dt), TimeWithinDay(t)). // 7. Let newDate be MakeDate(MakeDay(YearFromTime(t), m, dt), TimeWithinDay(t)).
// 8. Let u be TimeClip(UTC(newDate)). // 8. Let u be TimeClip(UTC(newDate)).
let datetime = replace_params( let datetime = replace_params::<LOCAL>(
datetime, datetime,
DateParameters { DateParameters {
month: Some(month), month: Some(month),
date, date,
..Default::default() ..Default::default()
}, },
LOCAL.then(|| context.host_hooks().tz_offset()), &*context.host_hooks(),
); );
// 9. Set the [[DateValue]] internal slot of this Date object to u. // 9. Set the [[DateValue]] internal slot of this Date object to u.
@ -1111,14 +1099,14 @@ impl Date {
// 6. If ms is not present, let milli be msFromTime(t). // 6. If ms is not present, let milli be msFromTime(t).
// 7. Let date be MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli)). // 7. Let date be MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli)).
// 8. Let u be TimeClip(UTC(date)). // 8. Let u be TimeClip(UTC(date)).
let datetime = replace_params( let datetime = replace_params::<LOCAL>(
datetime, datetime,
DateParameters { DateParameters {
second: Some(second), second: Some(second),
millisecond, millisecond,
..Default::default() ..Default::default()
}, },
LOCAL.then(|| context.host_hooks().tz_offset()), &*context.host_hooks(),
); );
// 9. Set the [[DateValue]] internal slot of this Date object to u. // 9. Set the [[DateValue]] internal slot of this Date object to u.
@ -1155,16 +1143,16 @@ impl Date {
let year = args.get_or_undefined(0).to_integer_or_nan(context)?; let year = args.get_or_undefined(0).to_integer_or_nan(context)?;
// 3. If t is NaN, set t to +0𝔽; otherwise, set t to LocalTime(t). // 3. If t is NaN, set t to +0𝔽; otherwise, set t to LocalTime(t).
let Some(datetime) = t.0.and_then(NaiveDateTime::from_timestamp_millis).or_else(|| { let datetime =
context.host_hooks().tz_offset() t.0.and_then(NaiveDateTime::from_timestamp_millis)
.from_local_datetime(&NaiveDateTime::default()) .or_else(|| {
.earliest() context
.as_ref() .host_hooks()
.map(DateTime::naive_utc) .local_from_naive_local(NaiveDateTime::default())
}) else { .earliest()
*t = Self::new(None); .map(|dt| dt.naive_utc())
return Ok(t.as_value()); });
}; let datetime = some_or_nan!(datetime);
// 4. If y is NaN, then // 4. If y is NaN, then
let Some(mut year) = year.as_integer() else { let Some(mut year) = year.as_integer() else {
@ -1183,13 +1171,13 @@ impl Date {
// 8. Let d be MakeDay(yyyy, MonthFromTime(t), DateFromTime(t)). // 8. Let d be MakeDay(yyyy, MonthFromTime(t), DateFromTime(t)).
// 9. Let date be UTC(MakeDate(d, TimeWithinDay(t))). // 9. Let date be UTC(MakeDate(d, TimeWithinDay(t))).
let datetime = replace_params( let datetime = replace_params::<true>(
datetime.timestamp_millis(), datetime.timestamp_millis(),
DateParameters { DateParameters {
year: Some(IntegerOrNan::Integer(year)), year: Some(IntegerOrNan::Integer(year)),
..Default::default() ..Default::default()
}, },
Some(context.host_hooks().tz_offset()), &*context.host_hooks(),
); );
// 10. Set the [[DateValue]] internal slot of this Date object to TimeClip(date). // 10. Set the [[DateValue]] internal slot of this Date object to TimeClip(date).
@ -1257,8 +1245,7 @@ impl Date {
// 5. Return DateString(t). // 5. Return DateString(t).
Ok(context Ok(context
.host_hooks() .host_hooks()
.tz_offset() .local_from_utc(tv)
.from_utc_datetime(&tv)
.format("%a %b %d %Y") .format("%a %b %d %Y")
.to_string() .to_string()
.into()) .into())
@ -1395,13 +1382,12 @@ impl Date {
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
// 1. Let tv be ? thisTimeValue(this value). // 1. Let tv be ? thisTimeValue(this value).
// 2. Return ToDateString(tv). // 2. Return ToDateString(tv).
let Some(t) = this_time_value(this)?.and_then(NaiveDateTime::from_timestamp_millis) else { let Some(tv) = this_time_value(this)?.and_then(NaiveDateTime::from_timestamp_millis) else {
return Ok(js_string!("Invalid Date").into()); return Ok(js_string!("Invalid Date").into());
}; };
Ok(context Ok(context
.host_hooks() .host_hooks()
.tz_offset() .local_from_utc(tv)
.from_utc_datetime(&t)
.format("%a %b %d %Y %H:%M:%S GMT%z") .format("%a %b %d %Y %H:%M:%S GMT%z")
.to_string() .to_string()
.into()) .into())
@ -1424,7 +1410,7 @@ impl Date {
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
// 1. Let O be this Date object. // 1. Let O be this Date object.
// 2. Let tv be ? thisTimeValue(O). // 2. Let tv be ? thisTimeValue(O).
let Some(t) = this_time_value(this)?.and_then(NaiveDateTime::from_timestamp_millis) else { let Some(tv) = this_time_value(this)?.and_then(NaiveDateTime::from_timestamp_millis) else {
// 3. If tv is NaN, return "Invalid Date". // 3. If tv is NaN, return "Invalid Date".
return Ok(js_string!("Invalid Date").into()); return Ok(js_string!("Invalid Date").into());
}; };
@ -1433,8 +1419,7 @@ impl Date {
// 5. Return the string-concatenation of TimeString(t) and TimeZoneString(tv). // 5. Return the string-concatenation of TimeString(t) and TimeZoneString(tv).
Ok(context Ok(context
.host_hooks() .host_hooks()
.tz_offset() .local_from_utc(tv)
.from_utc_datetime(&t)
.format("%H:%M:%S GMT%z") .format("%H:%M:%S GMT%z")
.to_string() .to_string()
.into()) .into())

11
boa_engine/src/builtins/date/tests.rs

@ -258,7 +258,16 @@ fn date_proto_get_timezone_offset() {
"new Date('1975-08-19T23:15:30+07:00').getTimezoneOffset()", "new Date('1975-08-19T23:15:30+07:00').getTimezoneOffset()",
{ {
// The value of now().offset() depends on the host machine, so we have to replicate the method code here. // The value of now().offset() depends on the host machine, so we have to replicate the method code here.
let offset_seconds = chrono::Local::now().offset().local_minus_utc(); let dt = Local
.from_local_datetime(
&NaiveDate::from_ymd_opt(1975, 8, 19)
.unwrap()
.and_hms_opt(23, 15, 30)
.unwrap(),
)
.earliest()
.unwrap();
let offset_seconds = dt.offset().local_minus_utc();
-offset_seconds / 60 -offset_seconds / 60
}, },
), ),

18
boa_engine/src/builtins/date/utils.rs

@ -1,6 +1,6 @@
use chrono::{Datelike, FixedOffset, NaiveDateTime, TimeZone, Timelike}; use chrono::{Datelike, NaiveDateTime, Timelike};
use crate::value::IntegerOrNan; use crate::{context::HostHooks, value::IntegerOrNan};
/// The absolute maximum value of a timestamp /// The absolute maximum value of a timestamp
pub(super) const MAX_TIMESTAMP: i64 = 864 * 10i64.pow(13); pub(super) const MAX_TIMESTAMP: i64 = 864 * 10i64.pow(13);
@ -133,10 +133,10 @@ pub(super) struct DateParameters {
} }
/// Replaces some (or all) parameters of `date` with the specified parameters /// Replaces some (or all) parameters of `date` with the specified parameters
pub(super) fn replace_params( pub(super) fn replace_params<const LOCAL: bool>(
datetime: i64, datetime: i64,
params: DateParameters, params: DateParameters,
offset: Option<FixedOffset>, hooks: &dyn HostHooks,
) -> Option<i64> { ) -> Option<i64> {
let datetime = NaiveDateTime::from_timestamp_millis(datetime)?; let datetime = NaiveDateTime::from_timestamp_millis(datetime)?;
let DateParameters { let DateParameters {
@ -149,8 +149,8 @@ pub(super) fn replace_params(
millisecond, millisecond,
} = params; } = params;
let datetime = if let Some(offset) = offset { let datetime = if LOCAL {
offset.from_utc_datetime(&datetime).naive_local() hooks.local_from_utc(datetime).naive_local()
} else { } else {
datetime datetime
}; };
@ -188,9 +188,9 @@ pub(super) fn replace_params(
let new_time = make_time(hour, minute, second, millisecond)?; let new_time = make_time(hour, minute, second, millisecond)?;
let mut ts = make_date(new_day, new_time)?; let mut ts = make_date(new_day, new_time)?;
if let Some(offset) = offset { if LOCAL {
ts = offset ts = hooks
.from_local_datetime(&NaiveDateTime::from_timestamp_millis(ts)?) .local_from_naive_local(NaiveDateTime::from_timestamp_millis(ts)?)
.earliest()? .earliest()?
.naive_utc() .naive_utc()
.timestamp_millis(); .timestamp_millis();

23
boa_engine/src/context/hooks.rs

@ -5,7 +5,7 @@ use crate::{
realm::Realm, realm::Realm,
Context, JsResult, JsValue, Context, JsResult, JsValue,
}; };
use chrono::{FixedOffset, Local, NaiveDateTime, Utc}; use chrono::{DateTime, FixedOffset, Local, LocalResult, NaiveDateTime, TimeZone, Utc};
use super::intrinsics::Intrinsics; use super::intrinsics::Intrinsics;
@ -182,14 +182,27 @@ pub trait HostHooks {
Utc::now().naive_utc() Utc::now().naive_utc()
} }
/// Gets the current timezone as a fixed offset from UTC. /// Converts the naive datetime `utc` to the corresponding local datetime.
/// ///
/// Defaults to using [`Local::now`] on all targets, which can cause panics if your platform /// Defaults to using [`Local`] on all targets, which can cause panics if your platform
/// doesn't support [`SystemTime::now`][time]. /// doesn't support [`SystemTime::now`][time].
/// ///
/// [time]: std::time::SystemTime::now /// [time]: std::time::SystemTime::now
fn tz_offset(&self) -> FixedOffset { fn local_from_utc(&self, utc: NaiveDateTime) -> DateTime<FixedOffset> {
*Local::now().offset() let offset = Local.offset_from_utc_datetime(&utc);
DateTime::from_utc(utc, offset)
}
/// Converts the naive local datetime `local` to a local timezone datetime.
///
/// Defaults to using [`Local`] on all targets, which can cause panics if your platform
/// doesn't support [`SystemTime::now`][time].
///
/// [time]: std::time::SystemTime::now
fn local_from_naive_local(&self, local: NaiveDateTime) -> LocalResult<DateTime<FixedOffset>> {
Local
.offset_from_local_datetime(&local)
.map(|offset| DateTime::from_local(local, offset))
} }
} }

Loading…
Cancel
Save