Skip to content

Commit 345ad54

Browse files
authored
Add from_partial methods to PlainTime, PlainDate, and PlainDateTime (#106)
This PR primarily adds `from_partial` methods to `PlainTime`, `PlainDate`, and `PlainDateTime`. This also begins to add doc tests to some high visibility methods and also sets the Constrain option of ArithmeticOverflow as the default.
1 parent 49c2f59 commit 345ad54

File tree

4 files changed

+189
-2
lines changed

4 files changed

+189
-2
lines changed

src/components/date.rs

+37
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,43 @@ impl PlainDate {
327327
Ok(Self::new_unchecked(iso, calendar))
328328
}
329329

330+
/// Create a `PlainDate` from a `PartialDate`
331+
///
332+
/// ```rust
333+
/// use temporal_rs::{PlainDate, partial::PartialDate};
334+
///
335+
/// let partial = PartialDate {
336+
/// year: Some(2000),
337+
/// month: Some(13),
338+
/// day: Some(2),
339+
/// ..Default::default()
340+
/// };
341+
///
342+
/// let date = PlainDate::from_partial(partial, None, None).unwrap();
343+
///
344+
/// assert_eq!(date.year().unwrap(), 2000);
345+
/// assert_eq!(date.month().unwrap(), 12);
346+
/// assert_eq!(date.day().unwrap(), 2);
347+
/// assert_eq!(date.calendar().identifier(), "iso8601");
348+
///
349+
/// ```
350+
#[inline]
351+
pub fn from_partial(
352+
partial: PartialDate,
353+
calendar: Option<Calendar>,
354+
overflow: Option<ArithmeticOverflow>,
355+
) -> TemporalResult<Self> {
356+
let year_check =
357+
partial.year.is_some() || (partial.era.is_some() && partial.era_year.is_some());
358+
let month_check = partial.month.is_some() || partial.month_code.is_some();
359+
if !year_check || !month_check || partial.day.is_none() {
360+
return Err(TemporalError::range().with_message("Invalid PlainDate fields provided."));
361+
}
362+
let calendar = calendar.unwrap_or_default();
363+
let overflow = overflow.unwrap_or_default();
364+
calendar.date_from_partial(&partial, overflow)
365+
}
366+
330367
/// Creates a date time with values from a `PartialDate`.
331368
pub fn with(
332369
&self,

src/components/datetime.rs

+75-1
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,10 @@ impl PlainDateTime {
6060
Self { iso, calendar }
6161
}
6262

63+
// TODO: Potentially deprecate and remove.
64+
/// Utility function for validating `IsoDate`s
6365
#[inline]
6466
#[must_use]
65-
/// Utility function for validating `IsoDate`s
6667
fn validate_iso(iso: IsoDate) -> bool {
6768
IsoDateTime::new_unchecked(iso, IsoTime::noon()).is_within_limits()
6869
}
@@ -321,7 +322,80 @@ impl PlainDateTime {
321322
))
322323
}
323324

325+
/// Creates a `DateTime` from a `PartialDateTime`.
326+
///
327+
/// ```rust
328+
/// use temporal_rs::{PlainDateTime, partial::{PartialDateTime, PartialTime, PartialDate}};
329+
///
330+
/// let date = PartialDate {
331+
/// year: Some(2000),
332+
/// month: Some(13),
333+
/// day: Some(2),
334+
/// ..Default::default()
335+
/// };
336+
///
337+
/// let time = PartialTime {
338+
/// hour: Some(4),
339+
/// minute: Some(25),
340+
/// ..Default::default()
341+
/// };
342+
///
343+
/// let partial = PartialDateTime { date, time };
344+
///
345+
/// let date = PlainDateTime::from_partial(partial, None, None).unwrap();
346+
///
347+
/// assert_eq!(date.year().unwrap(), 2000);
348+
/// assert_eq!(date.month().unwrap(), 12);
349+
/// assert_eq!(date.day().unwrap(), 2);
350+
/// assert_eq!(date.calendar().identifier(), "iso8601");
351+
/// assert_eq!(date.hour(), 4);
352+
/// assert_eq!(date.minute(), 25);
353+
/// assert_eq!(date.second(), 0);
354+
/// assert_eq!(date.millisecond(), 0);
355+
///
356+
/// ```
357+
pub fn from_partial(
358+
partial: PartialDateTime,
359+
calendar: Option<Calendar>,
360+
overflow: Option<ArithmeticOverflow>,
361+
) -> TemporalResult<Self> {
362+
let date = PlainDate::from_partial(partial.date, calendar, overflow)?;
363+
let time = PlainTime::from_partial(partial.time, overflow)?;
364+
Self::from_date_and_time(date, time)
365+
}
366+
324367
/// Creates a new `DateTime` with the fields of a `PartialDateTime`.
368+
///
369+
/// ```rust
370+
/// use temporal_rs::{Calendar, PlainDateTime, partial::{PartialDateTime, PartialTime, PartialDate}};
371+
///
372+
/// let initial = PlainDateTime::try_new(2000, 12, 2, 0,0,0,0,0,0, Calendar::default()).unwrap();
373+
///
374+
/// let date = PartialDate {
375+
/// month: Some(5),
376+
/// ..Default::default()
377+
/// };
378+
///
379+
/// let time = PartialTime {
380+
/// hour: Some(4),
381+
/// second: Some(30),
382+
/// ..Default::default()
383+
/// };
384+
///
385+
/// let partial = PartialDateTime { date, time };
386+
///
387+
/// let date = initial.with(partial, None).unwrap();
388+
///
389+
/// assert_eq!(date.year().unwrap(), 2000);
390+
/// assert_eq!(date.month().unwrap(), 5);
391+
/// assert_eq!(date.day().unwrap(), 2);
392+
/// assert_eq!(date.calendar().identifier(), "iso8601");
393+
/// assert_eq!(date.hour(), 4);
394+
/// assert_eq!(date.minute(), 0);
395+
/// assert_eq!(date.second(), 30);
396+
/// assert_eq!(date.millisecond(), 0);
397+
///
398+
/// ```
325399
#[inline]
326400
pub fn with(
327401
&self,

src/components/time.rs

+75
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,15 @@ impl PlainTime {
159159

160160
impl PlainTime {
161161
/// Creates a new `PlainTime`, constraining any field into a valid range.
162+
///
163+
/// ```rust
164+
/// use temporal_rs::PlainTime;
165+
///
166+
/// let time = PlainTime::new(23, 59, 59, 999, 999, 999).unwrap();
167+
///
168+
/// let constrained_time = PlainTime::new(24, 59, 59, 999, 999, 999).unwrap();
169+
/// assert_eq!(time, constrained_time);
170+
/// ```
162171
pub fn new(
163172
hour: i32,
164173
minute: i32,
@@ -179,6 +188,15 @@ impl PlainTime {
179188
}
180189

181190
/// Creates a new `PlainTime`, rejecting any field that is not in a valid range.
191+
///
192+
/// ```rust
193+
/// use temporal_rs::PlainTime;
194+
///
195+
/// let time = PlainTime::try_new(23, 59, 59, 999, 999, 999).unwrap();
196+
///
197+
/// let invalid_time = PlainTime::try_new(24, 59, 59, 999, 999, 999);
198+
/// assert!(invalid_time.is_err());
199+
/// ```
182200
pub fn try_new(
183201
hour: i32,
184202
minute: i32,
@@ -221,11 +239,68 @@ impl PlainTime {
221239
Ok(Self::new_unchecked(time))
222240
}
223241

242+
/// Creates a new `PlainTime` from a `PartialTime`.
243+
///
244+
/// ```rust
245+
/// use temporal_rs::{partial::PartialTime, PlainTime};
246+
///
247+
/// let partial_time = PartialTime {
248+
/// hour: Some(22),
249+
/// ..Default::default()
250+
/// };
251+
///
252+
/// let time = PlainTime::from_partial(partial_time, None).unwrap();
253+
///
254+
/// assert_eq!(time.hour(), 22);
255+
/// assert_eq!(time.minute(), 0);
256+
/// assert_eq!(time.second(), 0);
257+
/// assert_eq!(time.millisecond(), 0);
258+
/// assert_eq!(time.microsecond(), 0);
259+
/// assert_eq!(time.nanosecond(), 0);
260+
///
261+
/// ```
262+
pub fn from_partial(
263+
partial: PartialTime,
264+
overflow: Option<ArithmeticOverflow>,
265+
) -> TemporalResult<Self> {
266+
// NOTE: 4.5.12 ToTemporalTimeRecord requires one field to be set.
267+
if partial.is_empty() {
268+
return Err(TemporalError::r#type().with_message("PartialTime cannot be empty."));
269+
}
270+
271+
let overflow = overflow.unwrap_or_default();
272+
let iso = IsoTime::default().with(partial, overflow)?;
273+
Ok(Self::new_unchecked(iso))
274+
}
275+
276+
/// Creates a new `PlainTime` using the current `PlainTime` fields as a fallback.
277+
///
278+
/// ```rust
279+
/// use temporal_rs::{partial::PartialTime, PlainTime};
280+
///
281+
/// let partial_time = PartialTime {
282+
/// hour: Some(22),
283+
/// ..Default::default()
284+
/// };
285+
///
286+
/// let initial = PlainTime::try_new(15, 30, 12, 123, 456, 789).unwrap();
287+
///
288+
/// let time = initial.with(partial_time, None).unwrap();
289+
///
290+
/// assert_eq!(time.hour(), 22);
291+
/// assert_eq!(time.minute(), 30);
292+
/// assert_eq!(time.second(), 12);
293+
/// assert_eq!(time.millisecond(), 123);
294+
/// assert_eq!(time.microsecond(), 456);
295+
/// assert_eq!(time.nanosecond(), 789);
296+
///
297+
/// ```
224298
pub fn with(
225299
&self,
226300
partial: PartialTime,
227301
overflow: Option<ArithmeticOverflow>,
228302
) -> TemporalResult<Self> {
303+
// NOTE: 4.5.12 ToTemporalTimeRecord requires one field to be set.
229304
if partial.is_empty() {
230305
return Err(TemporalError::r#type().with_message("PartialTime cannot be empty."));
231306
}

src/options.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -390,9 +390,10 @@ impl fmt::Display for TemporalUnit {
390390
/// `ArithmeticOverflow` can also be used as an
391391
/// assignment overflow and consists of the "constrain"
392392
/// and "reject" options.
393-
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
393+
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
394394
pub enum ArithmeticOverflow {
395395
/// Constrain option
396+
#[default]
396397
Constrain,
397398
/// Constrain option
398399
Reject,

0 commit comments

Comments
 (0)