Skip to content

Commit c1aab6a

Browse files
committed
Build out add and subtract datetime functionality
1 parent 97943ff commit c1aab6a

File tree

7 files changed

+257
-63
lines changed

7 files changed

+257
-63
lines changed

src/components/date.rs

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ impl<C: CalendarProtocol> Date<C> {
4545
duration: &Duration,
4646
context: &mut C::Context,
4747
) -> TemporalResult<(Self, f64)> {
48-
let new_date = self.add_date(duration, ArithmeticOverflow::Constrain, context)?;
48+
let new_date = self.add_date(duration, None, context)?;
4949
let days = f64::from(self.days_until(&new_date));
5050
Ok((new_date, days))
5151
}
@@ -56,10 +56,11 @@ impl<C: CalendarProtocol> Date<C> {
5656
pub(crate) fn add_date(
5757
&self,
5858
duration: &Duration,
59-
overflow: ArithmeticOverflow,
59+
overflow: Option<ArithmeticOverflow>,
6060
context: &mut C::Context,
6161
) -> TemporalResult<Self> {
6262
// 2. If options is not present, set options to undefined.
63+
let overflow = overflow.unwrap_or(ArithmeticOverflow::Constrain);
6364
// 3. If duration.[[Years]] ≠ 0, or duration.[[Months]] ≠ 0, or duration.[[Weeks]] ≠ 0, then
6465
if duration.date().years != 0.0
6566
|| duration.date().months != 0.0
@@ -301,11 +302,7 @@ impl<C: CalendarProtocol> Date<C> {
301302
overflow: Option<ArithmeticOverflow>,
302303
context: &mut C::Context,
303304
) -> TemporalResult<Self> {
304-
self.add_date(
305-
duration,
306-
overflow.unwrap_or(ArithmeticOverflow::Constrain),
307-
context,
308-
)
305+
self.add_date(duration, overflow, context)
309306
}
310307

311308
pub fn contextual_subtract(
@@ -314,11 +311,7 @@ impl<C: CalendarProtocol> Date<C> {
314311
overflow: Option<ArithmeticOverflow>,
315312
context: &mut C::Context,
316313
) -> TemporalResult<Self> {
317-
self.add_date(
318-
&duration.negated(),
319-
overflow.unwrap_or(ArithmeticOverflow::Constrain),
320-
context,
321-
)
314+
self.add_date(&duration.negated(), overflow, context)
322315
}
323316

324317
pub fn contextual_until(

src/components/datetime.rs

Lines changed: 191 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ use crate::{
1414
use std::str::FromStr;
1515
use tinystr::TinyAsciiStr;
1616

17-
use super::calendar::{CalendarDateLike, GetCalendarSlot};
17+
use super::{
18+
calendar::{CalendarDateLike, GetCalendarSlot},
19+
duration::normalized::NormalizedTimeDuration,
20+
Duration,
21+
};
1822

1923
/// The native Rust implementation of `Temporal.PlainDateTime`
2024
#[non_exhaustive]
@@ -51,6 +55,40 @@ impl<C: CalendarProtocol> DateTime<C> {
5155
let iso = IsoDateTime::from_epoch_nanos(&instant.nanos, offset)?;
5256
Ok(Self { iso, calendar })
5357
}
58+
59+
// 5.5.14 AddDurationToOrSubtractDurationFromPlainDateTime ( operation, dateTime, temporalDurationLike, options )
60+
fn add_or_subtract_duration(
61+
&self,
62+
duration: &Duration,
63+
overflow: Option<ArithmeticOverflow>,
64+
context: &mut C::Context,
65+
) -> TemporalResult<Self> {
66+
// SKIP: 1, 2, 3, 4
67+
// 1. If operation is subtract, let sign be -1. Otherwise, let sign be 1.
68+
// 2. Let duration be ? ToTemporalDurationRecord(temporalDurationLike).
69+
// 3. Set options to ? GetOptionsObject(options).
70+
// 4. Let calendarRec be ? CreateCalendarMethodsRecord(dateTime.[[Calendar]], « date-add »).
71+
72+
// 5. Let norm be NormalizeTimeDuration(sign × duration.[[Hours]], sign × duration.[[Minutes]], sign × duration.[[Seconds]], sign × duration.[[Milliseconds]], sign × duration.[[Microseconds]], sign × duration.[[Nanoseconds]]).
73+
let norm = NormalizedTimeDuration::from_time_duration(duration.time());
74+
75+
// TODO: validate Constrain is default with all the recent changes.
76+
// 6. Let result be ? AddDateTime(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]], calendarRec, sign × duration.[[Years]], sign × duration.[[Months]], sign × duration.[[Weeks]], sign × duration.[[Days]], norm, options).
77+
let result = self.iso.add_date_duration(
78+
self.calendar(),
79+
duration.date(),
80+
norm,
81+
overflow,
82+
context,
83+
)?;
84+
85+
// 7. Assert: IsValidISODate(result.[[Year]], result.[[Month]], result.[[Day]]) is true.
86+
// 8. Assert: IsValidTime(result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]]) is true.
87+
assert!(result.is_within_limits());
88+
89+
// 9. Return ? CreateTemporalDateTime(result.[[Year]], result.[[Month]], result.[[Day]], result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]], dateTime.[[Calendar]]).
90+
Ok(Self::new_unchecked(result, self.calendar.clone()))
91+
}
5492
}
5593

5694
// ==== Public DateTime API ====
@@ -166,7 +204,6 @@ impl<C: CalendarProtocol> DateTime<C> {
166204

167205
// ==== Calendar-derived public API ====
168206

169-
// TODO: Revert to `DateTime<C>`.
170207
impl DateTime<()> {
171208
/// Returns the calendar year value.
172209
pub fn year(&self) -> TemporalResult<i32> {
@@ -245,6 +282,24 @@ impl DateTime<()> {
245282
self.calendar
246283
.in_leap_year(&CalendarDateLike::DateTime(self.clone()), &mut ())
247284
}
285+
286+
#[inline]
287+
pub fn add(
288+
&self,
289+
duration: &Duration,
290+
overflow: Option<ArithmeticOverflow>,
291+
) -> TemporalResult<Self> {
292+
self.contextual_add(duration, overflow, &mut ())
293+
}
294+
295+
#[inline]
296+
pub fn subtract(
297+
&self,
298+
duration: &Duration,
299+
overflow: Option<ArithmeticOverflow>,
300+
) -> TemporalResult<Self> {
301+
self.contextual_subtract(duration, overflow, &mut ())
302+
}
248303
}
249304

250305
impl<C: CalendarProtocol> DateTime<C> {
@@ -355,6 +410,26 @@ impl<C: CalendarProtocol> DateTime<C> {
355410
this.get_calendar()
356411
.in_leap_year(&CalendarDateLike::CustomDateTime(this.clone()), context)
357412
}
413+
414+
#[inline]
415+
pub fn contextual_add(
416+
&self,
417+
duration: &Duration,
418+
overflow: Option<ArithmeticOverflow>,
419+
context: &mut C::Context,
420+
) -> TemporalResult<Self> {
421+
self.add_or_subtract_duration(duration, overflow, context)
422+
}
423+
424+
#[inline]
425+
pub fn contextual_subtract(
426+
&self,
427+
duration: &Duration,
428+
overflow: Option<ArithmeticOverflow>,
429+
context: &mut C::Context,
430+
) -> TemporalResult<Self> {
431+
self.add_or_subtract_duration(&duration.negated(), overflow, context)
432+
}
358433
}
359434

360435
// ==== Trait impls ====
@@ -408,7 +483,10 @@ impl<C: CalendarProtocol> FromStr for DateTime<C> {
408483
mod tests {
409484
use std::str::FromStr;
410485

411-
use crate::components::calendar::CalendarSlot;
486+
use crate::{
487+
components::{calendar::CalendarSlot, Duration},
488+
iso::{IsoDate, IsoTime},
489+
};
412490

413491
use super::DateTime;
414492

@@ -429,20 +507,118 @@ mod tests {
429507
0,
430508
CalendarSlot::from_str("iso8601").unwrap(),
431509
);
432-
let positive_limit = DateTime::<()>::new(
433-
275_760,
434-
9,
435-
14,
436-
0,
437-
0,
438-
0,
439-
0,
440-
0,
441-
0,
442-
CalendarSlot::from_str("iso8601").unwrap(),
443-
);
510+
let positive_limit =
511+
DateTime::<()>::new(275_760, 9, 14, 0, 0, 0, 0, 0, 0, CalendarSlot::default());
444512

445513
assert!(negative_limit.is_err());
446514
assert!(positive_limit.is_err());
447515
}
516+
517+
// options-undefined.js
518+
#[test]
519+
fn datetime_add_test() {
520+
let pdt = DateTime::<()>::new(
521+
2020,
522+
1,
523+
31,
524+
12,
525+
34,
526+
56,
527+
987,
528+
654,
529+
321,
530+
CalendarSlot::default(),
531+
)
532+
.unwrap();
533+
534+
let result = pdt.add(&Duration::one_month(1.0), None).unwrap();
535+
536+
assert_eq!(result.month(), Ok(2));
537+
assert_eq!(result.day(), Ok(29));
538+
}
539+
540+
// options-undefined.js
541+
#[test]
542+
fn datetime_subtract_test() {
543+
let pdt = DateTime::<()>::new(
544+
2000,
545+
3,
546+
31,
547+
12,
548+
34,
549+
56,
550+
987,
551+
654,
552+
321,
553+
CalendarSlot::default(),
554+
)
555+
.unwrap();
556+
557+
let result = pdt.subtract(&Duration::one_month(1.0), None).unwrap();
558+
559+
assert_eq!(result.month(), Ok(2));
560+
assert_eq!(result.day(), Ok(29));
561+
}
562+
563+
// subtract/hour-overflow.js
564+
#[test]
565+
fn datetime_subtract_hour_overflows() {
566+
let dt = DateTime::<()>::new(
567+
2019,
568+
10,
569+
29,
570+
10,
571+
46,
572+
38,
573+
271,
574+
986,
575+
102,
576+
CalendarSlot::default(),
577+
)
578+
.unwrap();
579+
580+
let result = dt.subtract(&Duration::hour(12.0), None).unwrap();
581+
582+
assert_eq!(
583+
result.iso.date,
584+
IsoDate {
585+
year: 2019,
586+
month: 10,
587+
day: 28
588+
}
589+
);
590+
assert_eq!(
591+
result.iso.time,
592+
IsoTime {
593+
hour: 22,
594+
minute: 46,
595+
second: 38,
596+
millisecond: 271,
597+
microsecond: 986,
598+
nanosecond: 102
599+
}
600+
);
601+
602+
let result = dt.add(&Duration::hour(-12.0), None).unwrap();
603+
604+
assert_eq!(
605+
result.iso.date,
606+
IsoDate {
607+
year: 2019,
608+
month: 10,
609+
day: 28
610+
}
611+
);
612+
assert_eq!(
613+
result.iso.time,
614+
IsoTime {
615+
hour: 22,
616+
minute: 46,
617+
second: 38,
618+
millisecond: 271,
619+
microsecond: 986,
620+
nanosecond: 102
621+
}
622+
);
623+
}
448624
}

src/components/duration.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,16 @@ pub struct Duration {
4747
// - Methods (private/public/feature)
4848
//
4949

50+
#[cfg(test)]
51+
impl Duration {
52+
pub(crate) fn hour(value: f64) -> Self {
53+
Self::new_unchecked(
54+
DateDuration::default(),
55+
TimeDuration::new_unchecked(value, 0.0, 0.0, 0.0, 0.0, 0.0),
56+
)
57+
}
58+
}
59+
5060
// ==== Private Creation methods ====
5161

5262
impl Duration {

src/components/duration/date.rs

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -500,11 +500,7 @@ impl DateDuration {
500500
// i. Let dateAdd be unused.
501501

502502
// e. Let yearsLater be ? AddDate(calendar, plainRelativeTo, yearsDuration, undefined, dateAdd).
503-
let years_later = plain_relative_to.add_date(
504-
&years_duration,
505-
ArithmeticOverflow::Constrain,
506-
context,
507-
)?;
503+
let years_later = plain_relative_to.add_date(&years_duration, None, context)?;
508504

509505
// f. Let yearsMonthsWeeks be ! CreateTemporalDuration(years, months, weeks, 0, 0, 0, 0, 0, 0, 0).
510506
let years_months_weeks = Duration::new_unchecked(
@@ -513,11 +509,8 @@ impl DateDuration {
513509
);
514510

515511
// g. Let yearsMonthsWeeksLater be ? AddDate(calendar, plainRelativeTo, yearsMonthsWeeks, undefined, dateAdd).
516-
let years_months_weeks_later = plain_relative_to.add_date(
517-
&years_months_weeks,
518-
ArithmeticOverflow::Constrain,
519-
context,
520-
)?;
512+
let years_months_weeks_later =
513+
plain_relative_to.add_date(&years_months_weeks, None, context)?;
521514

522515
// h. Let monthsWeeksInDays be DaysUntil(yearsLater, yearsMonthsWeeksLater).
523516
let months_weeks_in_days = years_later.days_until(&years_months_weeks_later);
@@ -609,23 +602,17 @@ impl DateDuration {
609602
// i. Let dateAdd be unused.
610603

611604
// e. Let yearsMonthsLater be ? AddDate(calendar, plainRelativeTo, yearsMonths, undefined, dateAdd).
612-
let years_months_later = plain_relative_to.add_date(
613-
&years_months,
614-
ArithmeticOverflow::Constrain,
615-
context,
616-
)?;
605+
let years_months_later =
606+
plain_relative_to.add_date(&years_months, None, context)?;
617607

618608
// f. Let yearsMonthsWeeks be ! CreateTemporalDuration(years, months, weeks, 0, 0, 0, 0, 0, 0, 0).
619609
let years_months_weeks = Duration::from_date_duration(
620610
&DateDuration::new_unchecked(self.years, self.months, self.weeks, 0.0),
621611
);
622612

623613
// g. Let yearsMonthsWeeksLater be ? AddDate(calendar, plainRelativeTo, yearsMonthsWeeks, undefined, dateAdd).
624-
let years_months_weeks_later = plain_relative_to.add_date(
625-
&years_months_weeks,
626-
ArithmeticOverflow::Constrain,
627-
context,
628-
)?;
614+
let years_months_weeks_later =
615+
plain_relative_to.add_date(&years_months_weeks, None, context)?;
629616

630617
// h. Let weeksInDays be DaysUntil(yearsMonthsLater, yearsMonthsWeeksLater).
631618
let weeks_in_days = years_months_later.days_until(&years_months_weeks_later);

0 commit comments

Comments
 (0)