Skip to content

Commit 51bc30a

Browse files
authored
Add PartialTime and PartialDateTime with corresponding with methods. (#92)
This continues the work on implementing `with` methods with the approach from #89 by adding the same type of records to `DateTime` and `Time`.
1 parent a7fc946 commit 51bc30a

File tree

6 files changed

+295
-81
lines changed

6 files changed

+295
-81
lines changed

src/components/date.rs

Lines changed: 12 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -28,40 +28,18 @@ use super::{
2828
/// A partial Date that may or may not be complete.
2929
#[derive(Debug, Default, Clone, Copy)]
3030
pub struct PartialDate {
31-
pub(crate) year: Option<i32>,
32-
pub(crate) month: Option<i32>,
33-
pub(crate) month_code: Option<MonthCode>,
34-
pub(crate) day: Option<i32>,
35-
pub(crate) era: Option<TinyAsciiStr<16>>,
36-
pub(crate) era_year: Option<i32>,
37-
}
38-
39-
impl PartialDate {
40-
/// Create a new `PartialDate`
41-
pub fn new(
42-
year: Option<i32>,
43-
month: Option<i32>,
44-
month_code: Option<MonthCode>,
45-
day: Option<i32>,
46-
era: Option<TinyAsciiStr<16>>,
47-
era_year: Option<i32>,
48-
) -> TemporalResult<Self> {
49-
if !(day.is_some()
50-
&& (month.is_some() || month_code.is_some())
51-
&& (year.is_some() || (era.is_some() && era_year.is_some())))
52-
{
53-
return Err(TemporalError::r#type()
54-
.with_message("A partial date must have at least one defined field."));
55-
}
56-
Ok(Self {
57-
year,
58-
month,
59-
month_code,
60-
day,
61-
era,
62-
era_year,
63-
})
64-
}
31+
// A potentially set `year` field.
32+
pub year: Option<i32>,
33+
// A potentially set `month` field.
34+
pub month: Option<i32>,
35+
// A potentially set `month_code` field.
36+
pub month_code: Option<MonthCode>,
37+
// A potentially set `day` field.
38+
pub day: Option<i32>,
39+
// A potentially set `era` field.
40+
pub era: Option<TinyAsciiStr<16>>,
41+
// A potentially set `era_year` field.
42+
pub era_year: Option<i32>,
6543
}
6644

6745
/// The native Rust implementation of `Temporal.PlainDate`.

src/components/datetime.rs

Lines changed: 211 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::{
88
RoundingOptions, TemporalUnit,
99
},
1010
parsers::parse_date_time,
11-
temporal_assert, Sign, TemporalError, TemporalResult, TemporalUnwrap,
11+
temporal_assert, Sign, TemporalError, TemporalFields, TemporalResult, TemporalUnwrap,
1212
};
1313

1414
use num_traits::AsPrimitive;
@@ -18,9 +18,16 @@ use tinystr::TinyAsciiStr;
1818
use super::{
1919
calendar::{CalendarDateLike, GetTemporalCalendar},
2020
duration::normalized::{NormalizedTimeDuration, RelativeRoundResult},
21-
Date, Duration, Time,
21+
Date, Duration, PartialDate, PartialTime, Time,
2222
};
2323

24+
/// A partial DateTime record
25+
#[derive(Debug, Default, Copy, Clone)]
26+
pub struct PartialDateTime {
27+
date: PartialDate,
28+
time: PartialTime,
29+
}
30+
2431
/// The native Rust implementation of `Temporal.PlainDateTime`
2532
#[non_exhaustive]
2633
#[derive(Debug, Default, Clone, PartialEq, Eq)]
@@ -243,6 +250,35 @@ impl DateTime {
243250
))
244251
}
245252

253+
/// Creates a new `DateTime` with the fields of a `PartialDateTime`.
254+
#[inline]
255+
pub fn with(
256+
&self,
257+
partial_datetime: PartialDateTime,
258+
overflow: Option<ArithmeticOverflow>,
259+
) -> TemporalResult<Self> {
260+
// Determine the Date from the provided fields.
261+
let fields = TemporalFields::from(self);
262+
let partial_fields = TemporalFields::from(partial_datetime.date);
263+
264+
let mut merge_result = fields.merge_fields(&partial_fields, self.calendar())?;
265+
266+
let result_date = self.calendar.date_from_fields(
267+
&mut merge_result,
268+
overflow.unwrap_or(ArithmeticOverflow::Constrain),
269+
)?;
270+
271+
// Determine the `Time` based off the partial values.
272+
let time = self.iso.time.with(
273+
partial_datetime.time,
274+
overflow.unwrap_or(ArithmeticOverflow::Constrain),
275+
)?;
276+
277+
let iso_datetime = IsoDateTime::new(result_date.iso, time)?;
278+
279+
Ok(Self::new_unchecked(iso_datetime, self.calendar().clone()))
280+
}
281+
246282
/// Creates a new `DateTime` from the current `DateTime` and the provided `Time`.
247283
pub fn with_time(&self, time: Time) -> TemporalResult<Self> {
248284
Self::new(
@@ -540,17 +576,35 @@ impl FromStr for DateTime {
540576
mod tests {
541577
use std::str::FromStr;
542578

579+
use tinystr::{tinystr, TinyAsciiStr};
580+
543581
use crate::{
544-
components::{calendar::Calendar, duration::DateDuration, Duration},
545-
iso::{IsoDate, IsoTime},
582+
components::{
583+
calendar::Calendar, duration::DateDuration, DateTime, Duration, MonthCode, PartialDate,
584+
PartialDateTime, PartialTime,
585+
},
546586
options::{
547587
DifferenceSettings, RoundingIncrement, RoundingOptions, TemporalRoundingMode,
548588
TemporalUnit,
549589
},
550590
primitive::FiniteF64,
551591
};
552592

553-
use super::DateTime;
593+
fn assert_datetime(
594+
dt: DateTime,
595+
fields: (i32, u8, TinyAsciiStr<4>, u8, u8, u8, u8, u16, u16, u16),
596+
) {
597+
assert_eq!(dt.year().unwrap(), fields.0);
598+
assert_eq!(dt.month().unwrap(), fields.1);
599+
assert_eq!(dt.month_code().unwrap(), fields.2);
600+
assert_eq!(dt.day().unwrap(), fields.3);
601+
assert_eq!(dt.hour(), fields.4);
602+
assert_eq!(dt.minute(), fields.5);
603+
assert_eq!(dt.second(), fields.6);
604+
assert_eq!(dt.millisecond(), fields.7);
605+
assert_eq!(dt.microsecond(), fields.8);
606+
assert_eq!(dt.nanosecond(), fields.9);
607+
}
554608

555609
#[test]
556610
#[allow(clippy::float_cmp)]
@@ -575,6 +629,152 @@ mod tests {
575629
assert!(positive_limit.is_err());
576630
}
577631

632+
#[test]
633+
fn basic_with_test() {
634+
let pdt =
635+
DateTime::new(1976, 11, 18, 15, 23, 30, 123, 456, 789, Calendar::default()).unwrap();
636+
637+
// Test year
638+
let partial = PartialDateTime {
639+
date: PartialDate {
640+
year: Some(2019),
641+
..Default::default()
642+
},
643+
time: PartialTime::default(),
644+
};
645+
let result = pdt.with(partial, None).unwrap();
646+
assert_datetime(
647+
result,
648+
(2019, 11, tinystr!(4, "M11"), 18, 15, 23, 30, 123, 456, 789),
649+
);
650+
651+
// Test month
652+
let partial = PartialDateTime {
653+
date: PartialDate {
654+
month: Some(5),
655+
..Default::default()
656+
},
657+
time: PartialTime::default(),
658+
};
659+
let result = pdt.with(partial, None).unwrap();
660+
assert_datetime(
661+
result,
662+
(1976, 5, tinystr!(4, "M05"), 18, 15, 23, 30, 123, 456, 789),
663+
);
664+
665+
// Test monthCode
666+
let partial = PartialDateTime {
667+
date: PartialDate {
668+
month_code: Some(MonthCode::Five),
669+
..Default::default()
670+
},
671+
time: PartialTime::default(),
672+
};
673+
let result = pdt.with(partial, None).unwrap();
674+
assert_datetime(
675+
result,
676+
(1976, 5, tinystr!(4, "M05"), 18, 15, 23, 30, 123, 456, 789),
677+
);
678+
679+
// Test day
680+
let partial = PartialDateTime {
681+
date: PartialDate {
682+
day: Some(5),
683+
..Default::default()
684+
},
685+
time: PartialTime::default(),
686+
};
687+
let result = pdt.with(partial, None).unwrap();
688+
assert_datetime(
689+
result,
690+
(1976, 11, tinystr!(4, "M11"), 5, 15, 23, 30, 123, 456, 789),
691+
);
692+
693+
// Test hour
694+
let partial = PartialDateTime {
695+
date: PartialDate::default(),
696+
time: PartialTime {
697+
hour: Some(5),
698+
..Default::default()
699+
},
700+
};
701+
let result = pdt.with(partial, None).unwrap();
702+
assert_datetime(
703+
result,
704+
(1976, 11, tinystr!(4, "M11"), 18, 5, 23, 30, 123, 456, 789),
705+
);
706+
707+
// Test minute
708+
let partial = PartialDateTime {
709+
date: PartialDate::default(),
710+
time: PartialTime {
711+
minute: Some(5),
712+
..Default::default()
713+
},
714+
};
715+
let result = pdt.with(partial, None).unwrap();
716+
assert_datetime(
717+
result,
718+
(1976, 11, tinystr!(4, "M11"), 18, 15, 5, 30, 123, 456, 789),
719+
);
720+
721+
// Test second
722+
let partial = PartialDateTime {
723+
date: PartialDate::default(),
724+
time: PartialTime {
725+
second: Some(5),
726+
..Default::default()
727+
},
728+
};
729+
let result = pdt.with(partial, None).unwrap();
730+
assert_datetime(
731+
result,
732+
(1976, 11, tinystr!(4, "M11"), 18, 15, 23, 5, 123, 456, 789),
733+
);
734+
735+
// Test second
736+
let partial = PartialDateTime {
737+
date: PartialDate::default(),
738+
time: PartialTime {
739+
millisecond: Some(5),
740+
..Default::default()
741+
},
742+
};
743+
let result = pdt.with(partial, None).unwrap();
744+
assert_datetime(
745+
result,
746+
(1976, 11, tinystr!(4, "M11"), 18, 15, 23, 30, 5, 456, 789),
747+
);
748+
749+
// Test second
750+
let partial = PartialDateTime {
751+
date: PartialDate::default(),
752+
time: PartialTime {
753+
microsecond: Some(5),
754+
..Default::default()
755+
},
756+
};
757+
let result = pdt.with(partial, None).unwrap();
758+
assert_datetime(
759+
result,
760+
(1976, 11, tinystr!(4, "M11"), 18, 15, 23, 30, 123, 5, 789),
761+
);
762+
763+
// Test second
764+
let partial = PartialDateTime {
765+
date: PartialDate::default(),
766+
time: PartialTime {
767+
nanosecond: Some(5),
768+
..Default::default()
769+
},
770+
};
771+
let result = pdt.with(partial, None).unwrap();
772+
assert_datetime(
773+
result,
774+
(1976, 11, tinystr!(4, "M11"), 18, 15, 23, 30, 123, 456, 5),
775+
);
776+
}
777+
578778
// options-undefined.js
579779
#[test]
580780
fn datetime_add_test() {
@@ -632,47 +832,15 @@ mod tests {
632832
DateTime::new(2019, 10, 29, 10, 46, 38, 271, 986, 102, Calendar::default()).unwrap();
633833

634834
let result = dt.subtract(&Duration::hour(FiniteF64(12.0)), None).unwrap();
635-
636-
assert_eq!(
637-
result.iso.date,
638-
IsoDate {
639-
year: 2019,
640-
month: 10,
641-
day: 28
642-
}
643-
);
644-
assert_eq!(
645-
result.iso.time,
646-
IsoTime {
647-
hour: 22,
648-
minute: 46,
649-
second: 38,
650-
millisecond: 271,
651-
microsecond: 986,
652-
nanosecond: 102
653-
}
835+
assert_datetime(
836+
result,
837+
(2019, 10, tinystr!(4, "M10"), 28, 22, 46, 38, 271, 986, 102),
654838
);
655839

656840
let result = dt.add(&Duration::hour(FiniteF64(-12.0)), None).unwrap();
657-
658-
assert_eq!(
659-
result.iso.date,
660-
IsoDate {
661-
year: 2019,
662-
month: 10,
663-
day: 28
664-
}
665-
);
666-
assert_eq!(
667-
result.iso.time,
668-
IsoTime {
669-
hour: 22,
670-
minute: 46,
671-
second: 38,
672-
millisecond: 271,
673-
microsecond: 986,
674-
nanosecond: 102
675-
}
841+
assert_datetime(
842+
result,
843+
(2019, 10, tinystr!(4, "M10"), 28, 22, 46, 38, 271, 986, 102),
676844
);
677845
}
678846

src/components/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,15 @@ use std::str::FromStr;
3333
#[doc(inline)]
3434
pub use date::{Date, PartialDate};
3535
#[doc(inline)]
36-
pub use datetime::DateTime;
36+
pub use datetime::{DateTime, PartialDateTime};
3737
#[doc(inline)]
3838
pub use duration::Duration;
3939
#[doc(inline)]
4040
pub use instant::Instant;
4141
#[doc(inline)]
4242
pub use month_day::MonthDay;
4343
#[doc(inline)]
44-
pub use time::Time;
44+
pub use time::{PartialTime, Time};
4545
#[doc(inline)]
4646
pub use year_month::YearMonth;
4747
pub use year_month::YearMonthFields;

0 commit comments

Comments
 (0)