Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add some missing trait implementations #81

Merged
merged 2 commits into from
Jul 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/components/calendar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ impl Calendar {
) -> TemporalResult<Duration> {
if self.is_iso() {
let date_duration = one.iso.diff_iso_date(&two.iso, largest_unit)?;
return Ok(Duration::from_date_duration(&date_duration));
return Ok(Duration::from(date_duration));
}

Err(TemporalError::range().with_message("Not yet implemented."))
Expand Down
14 changes: 11 additions & 3 deletions src/components/date.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ impl Date {

if largest_unit == TemporalUnit::Day {
let days = self.days_until(other);
return Ok(Duration::from_date_duration(&DateDuration::new(
return Ok(Duration::from(DateDuration::new(
0f64,
0f64,
0f64,
Expand Down Expand Up @@ -169,7 +169,7 @@ impl Date {

let sign = f64::from(sign as i8);
// 13. Return ! CreateTemporalDuration(sign × duration.[[Years]], sign × duration.[[Months]], sign × duration.[[Weeks]], sign × duration.[[Days]], 0, 0, 0, 0, 0, 0).
Ok(Duration::from_date_duration(&DateDuration::new(
Ok(Duration::from(DateDuration::new(
date_duration.years * sign,
date_duration.months * sign,
date_duration.weeks * sign,
Expand Down Expand Up @@ -383,6 +383,8 @@ impl Date {
}
}

// ==== Trait impls ====

impl GetTemporalCalendar for Date {
fn get_calendar(&self) -> Calendar {
self.calendar.clone()
Expand All @@ -396,7 +398,13 @@ impl IsoDateSlots for Date {
}
}

// ==== Trait impls ====
impl From<DateTime> for Date {
fn from(value: DateTime) -> Self {
Date::new_unchecked(value.iso.date, value.calendar().clone())
}
}

// TODO: impl From<ZonedDateTime> for Date

impl FromStr for Date {
type Err = TemporalError;
Expand Down
15 changes: 12 additions & 3 deletions src/components/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use tinystr::TinyAsciiStr;
use super::{
calendar::{CalendarDateLike, GetTemporalCalendar},
duration::normalized::{NormalizedTimeDuration, RelativeRoundResult},
Duration,
Date, Duration,
};

/// The native Rust implementation of `Temporal.PlainDateTime`
Expand Down Expand Up @@ -381,6 +381,15 @@ impl IsoDateSlots for DateTime {
}
}

impl From<Date> for DateTime {
fn from(value: Date) -> Self {
DateTime::new_unchecked(
IsoDateTime::new_unchecked(value.iso, IsoTime::default()),
value.calendar().clone(),
)
}
}

impl FromStr for DateTime {
type Err = TemporalError;

Expand Down Expand Up @@ -458,7 +467,7 @@ mod tests {

let result = pdt
.add(
&Duration::from_date_duration(&DateDuration::new(0.0, 1.0, 0.0, 0.0).unwrap()),
&Duration::from(DateDuration::new(0.0, 1.0, 0.0, 0.0).unwrap()),
None,
)
.unwrap();
Expand All @@ -475,7 +484,7 @@ mod tests {

let result = pdt
.subtract(
&Duration::from_date_duration(&DateDuration::new(0.0, 1.0, 0.0, 0.0).unwrap()),
&Duration::from(DateDuration::new(0.0, 1.0, 0.0, 0.0).unwrap()),
None,
)
.unwrap();
Expand Down
21 changes: 10 additions & 11 deletions src/components/duration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,15 +156,6 @@ impl Duration {
}
}

/// Creates a `Duration` from only a `DateDuration`.
#[must_use]
pub fn from_date_duration(date: &DateDuration) -> Self {
Self {
date: *date,
time: TimeDuration::default(),
}
}

/// Creates a `Duration` from a provided a day and a `TimeDuration`.
///
/// Note: `TimeDuration` records can store a day value to deal with overflow.
Expand Down Expand Up @@ -562,8 +553,7 @@ impl Duration {
)?;

// c. Let targetDate be ? AddDate(calendarRec, plainRelativeTo, dateDuration).
let target_date =
plain_date.add_date(&Duration::from_date_duration(&date_duration), None)?;
let target_date = plain_date.add_date(&Duration::from(date_duration), None)?;

let plain_dt = DateTime::new_unchecked(
IsoDateTime::new(plain_date.iso, IsoTime::default())?,
Expand Down Expand Up @@ -735,6 +725,15 @@ impl From<TimeDuration> for Duration {
}
}

impl From<DateDuration> for Duration {
fn from(value: DateDuration) -> Self {
Self {
date: value,
time: TimeDuration::default(),
}
}
}

// ==== FromStr trait impl ====

impl FromStr for Duration {
Expand Down
23 changes: 7 additions & 16 deletions src/components/duration/date.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,8 @@ impl DateDuration {
if smallest_unit == TemporalUnit::Week {
// i. Assert: days = 0.
// ii. Let yearsMonthsDuration be ! CreateTemporalDuration(years, months, 0, 0, 0, 0, 0, 0, 0, 0).
let years_months = Duration::from_date_duration(&Self::new_unchecked(
self.years,
self.months,
0.0,
0.0,
));
let years_months =
Duration::from(Self::new_unchecked(self.years, self.months, 0.0, 0.0));

// iii. Let later be ? AddDate(calendarRec, plainRelativeTo, yearsMonthsDuration).
let later = plain_relative.calendar().date_add(
Expand All @@ -104,7 +100,7 @@ impl DateDuration {
}

// b. Let yearsMonthsWeeksDaysDuration be ! CreateTemporalDuration(years, months, weeks, days, 0, 0, 0, 0, 0, 0).
let years_months_weeks = Duration::from_date_duration(self);
let years_months_weeks = Duration::from(*self);

// c. Let later be ? AddDate(calendarRec, plainRelativeTo, yearsMonthsWeeksDaysDuration).
let later = plain_relative.calendar().date_add(
Expand All @@ -131,12 +127,8 @@ impl DateDuration {
}

// c. Let monthsWeeksDaysDuration be ! CreateTemporalDuration(0, months, weeks, days, 0, 0, 0, 0, 0, 0).
let months_weeks_days = Duration::from_date_duration(&Self::new_unchecked(
0.0,
self.months,
self.weeks,
self.days,
));
let months_weeks_days =
Duration::from(Self::new_unchecked(0.0, self.months, self.weeks, self.days));

// d. Let later be ? AddDate(calendarRec, plainRelativeTo, monthsWeeksDaysDuration).
let later = plain_relative.calendar().date_add(
Expand All @@ -159,9 +151,8 @@ impl DateDuration {
// 13. Assert: years = 0.
// 14. Assert: months = 0.
// 15. Let weeksDaysDuration be ! CreateTemporalDuration(0, 0, weeks, days, 0, 0, 0, 0, 0, 0).
let weeks_days = Duration::from_date_duration(&Self::new_unchecked(
0.0, 0.0, self.weeks, self.days,
));
let weeks_days =
Duration::from(Self::new_unchecked(0.0, 0.0, self.weeks, self.days));

// 16. Let later be ? AddDate(calendarRec, plainRelativeTo, weeksDaysDuration).
let later = plain_relative.calendar().date_add(
Expand Down
3 changes: 1 addition & 2 deletions src/components/duration/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -422,8 +422,7 @@ fn basic_negative_expand_rounding() {
// test262/test/built-ins/Temporal/Duration/prototype/round/roundingincrement-non-integer.js
#[test]
fn rounding_increment_non_integer() {
let test_duration =
Duration::from_date_duration(&DateDuration::new(0.0, 0.0, 0.0, 1.0).unwrap());
let test_duration = Duration::from(DateDuration::new(0.0, 0.0, 0.0, 1.0).unwrap());
let binding = Date::new(
2000,
1,
Expand Down
59 changes: 58 additions & 1 deletion src/components/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ use crate::{
ArithmeticOverflow, DifferenceOperation, DifferenceSettings, ResolvedRoundingOptions,
RoundingIncrement, TemporalRoundingMode, TemporalUnit,
},
parsers::parse_time,
TemporalError, TemporalResult,
};

use super::duration::normalized::NormalizedTimeDuration;
use super::{duration::normalized::NormalizedTimeDuration, DateTime};

use std::str::FromStr;

/// The native Rust implementation of `Temporal.PlainTime`.
#[non_exhaustive]
Expand Down Expand Up @@ -268,6 +271,33 @@ impl Time {
}
}

impl From<DateTime> for Time {
fn from(value: DateTime) -> Self {
Time::new_unchecked(value.iso.time)
}
}

impl FromStr for Time {
type Err = TemporalError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let result = parse_time(s)?;

let (millisecond, rem) = (result.nanosecond / 1_000_000, result.nanosecond % 1_000_000);
let (microsecond, nanosecond) = (rem / 1_000, rem % 1_000);

Time::new(
result.hour.into(),
result.minute.into(),
result.second.into(),
millisecond as i32,
microsecond as i32,
nanosecond as i32,
ArithmeticOverflow::Reject,
)
}
}

// ==== Test land ====

#[cfg(test)]
Expand All @@ -277,6 +307,7 @@ mod tests {
iso::IsoTime,
options::{ArithmeticOverflow, DifferenceSettings, TemporalUnit},
};
use num_traits::FromPrimitive;

use super::Time;

Expand All @@ -296,6 +327,32 @@ mod tests {
);
}

#[test]
fn from_str_cast_sanity_test() {
let max = u32::MAX;
let (millisecond, rem) = (max / 1_000_000, max % 1_000_000);
let (microsecond, nanosecond) = (rem / 1_000, rem % 1_000);

assert!(i32::from_u32(millisecond).is_some());
assert!(i32::from_u32(microsecond).is_some());
assert!(i32::from_u32(nanosecond).is_some());
}

#[test]
fn basic_parse_time() {
let result = "T12:05:24-05:00[u-ca=iso8601]".parse::<Time>();
assert_time(result.unwrap(), (12, 5, 24, 0, 0, 0));

let result = "T12:05:24.123456789-05:00[u-ca=iso8601]".parse::<Time>();
assert_time(result.unwrap(), (12, 5, 24, 123, 456, 789));

let result = "2024-05-04 12:05:24.123456789-05:00[u-ca=iso8601]".parse::<Time>();
assert_time(result.unwrap(), (12, 5, 24, 123, 456, 789));

let result = "2024-05-04 12:05:24.123456789-05:00[u-ca=iso8601]".parse::<Time>();
assert_time(result.unwrap(), (12, 5, 24, 123, 456, 789));
}

#[test]
fn time_round_millisecond() {
let base = Time::new_unchecked(IsoTime::new_unchecked(3, 34, 56, 987, 654, 321));
Expand Down
21 changes: 19 additions & 2 deletions src/parsers.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
//! This module implements Temporal Date/Time parsing functionality.

use crate::{TemporalError, TemporalResult};
use crate::{TemporalError, TemporalResult, TemporalUnwrap};

use ixdtf::parsers::{
records::{Annotation, IxdtfParseRecord},
records::{Annotation, IxdtfParseRecord, TimeRecord},
IxdtfParser,
};

Expand Down Expand Up @@ -125,4 +125,21 @@ pub(crate) fn parse_month_day(source: &str) -> TemporalResult<IxdtfParseRecord>
}
}

pub(crate) fn parse_time(source: &str) -> TemporalResult<TimeRecord> {
let time_record = IxdtfParser::new(source).parse_time();

let time_err = match time_record {
Ok(time) => return time.time.temporal_unwrap(),
Err(e) => TemporalError::range().with_message(format!("{e}")),
};

let dt_parse = parse_ixdtf(source, ParseVariant::DateTime);

match dt_parse {
Ok(dt) if dt.time.is_some() => Ok(dt.time.temporal_unwrap()?),
// Format and return the error from parsing Time.
_ => Err(time_err),
}
}

// TODO: ParseTemporalTimeString, ParseTimeZoneString, ParseZonedDateTimeString