Skip to content

Commit

Permalink
Add from_partial methods to PlainTime, PlainDate, and `PlainDat…
Browse files Browse the repository at this point in the history
…eTime` (#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.
  • Loading branch information
nekevss authored Nov 4, 2024
1 parent 49c2f59 commit 345ad54
Show file tree
Hide file tree
Showing 4 changed files with 189 additions and 2 deletions.
37 changes: 37 additions & 0 deletions src/components/date.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,43 @@ impl PlainDate {
Ok(Self::new_unchecked(iso, calendar))
}

/// Create a `PlainDate` from a `PartialDate`
///
/// ```rust
/// use temporal_rs::{PlainDate, partial::PartialDate};
///
/// let partial = PartialDate {
/// year: Some(2000),
/// month: Some(13),
/// day: Some(2),
/// ..Default::default()
/// };
///
/// let date = PlainDate::from_partial(partial, None, None).unwrap();
///
/// assert_eq!(date.year().unwrap(), 2000);
/// assert_eq!(date.month().unwrap(), 12);
/// assert_eq!(date.day().unwrap(), 2);
/// assert_eq!(date.calendar().identifier(), "iso8601");
///
/// ```
#[inline]
pub fn from_partial(
partial: PartialDate,
calendar: Option<Calendar>,
overflow: Option<ArithmeticOverflow>,
) -> TemporalResult<Self> {
let year_check =
partial.year.is_some() || (partial.era.is_some() && partial.era_year.is_some());
let month_check = partial.month.is_some() || partial.month_code.is_some();
if !year_check || !month_check || partial.day.is_none() {
return Err(TemporalError::range().with_message("Invalid PlainDate fields provided."));
}
let calendar = calendar.unwrap_or_default();
let overflow = overflow.unwrap_or_default();
calendar.date_from_partial(&partial, overflow)
}

/// Creates a date time with values from a `PartialDate`.
pub fn with(
&self,
Expand Down
76 changes: 75 additions & 1 deletion src/components/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,10 @@ impl PlainDateTime {
Self { iso, calendar }
}

// TODO: Potentially deprecate and remove.
/// Utility function for validating `IsoDate`s
#[inline]
#[must_use]
/// Utility function for validating `IsoDate`s
fn validate_iso(iso: IsoDate) -> bool {
IsoDateTime::new_unchecked(iso, IsoTime::noon()).is_within_limits()
}
Expand Down Expand Up @@ -321,7 +322,80 @@ impl PlainDateTime {
))
}

/// Creates a `DateTime` from a `PartialDateTime`.
///
/// ```rust
/// use temporal_rs::{PlainDateTime, partial::{PartialDateTime, PartialTime, PartialDate}};
///
/// let date = PartialDate {
/// year: Some(2000),
/// month: Some(13),
/// day: Some(2),
/// ..Default::default()
/// };
///
/// let time = PartialTime {
/// hour: Some(4),
/// minute: Some(25),
/// ..Default::default()
/// };
///
/// let partial = PartialDateTime { date, time };
///
/// let date = PlainDateTime::from_partial(partial, None, None).unwrap();
///
/// assert_eq!(date.year().unwrap(), 2000);
/// assert_eq!(date.month().unwrap(), 12);
/// assert_eq!(date.day().unwrap(), 2);
/// assert_eq!(date.calendar().identifier(), "iso8601");
/// assert_eq!(date.hour(), 4);
/// assert_eq!(date.minute(), 25);
/// assert_eq!(date.second(), 0);
/// assert_eq!(date.millisecond(), 0);
///
/// ```
pub fn from_partial(
partial: PartialDateTime,
calendar: Option<Calendar>,
overflow: Option<ArithmeticOverflow>,
) -> TemporalResult<Self> {
let date = PlainDate::from_partial(partial.date, calendar, overflow)?;
let time = PlainTime::from_partial(partial.time, overflow)?;
Self::from_date_and_time(date, time)
}

/// Creates a new `DateTime` with the fields of a `PartialDateTime`.
///
/// ```rust
/// use temporal_rs::{Calendar, PlainDateTime, partial::{PartialDateTime, PartialTime, PartialDate}};
///
/// let initial = PlainDateTime::try_new(2000, 12, 2, 0,0,0,0,0,0, Calendar::default()).unwrap();
///
/// let date = PartialDate {
/// month: Some(5),
/// ..Default::default()
/// };
///
/// let time = PartialTime {
/// hour: Some(4),
/// second: Some(30),
/// ..Default::default()
/// };
///
/// let partial = PartialDateTime { date, time };
///
/// let date = initial.with(partial, None).unwrap();
///
/// assert_eq!(date.year().unwrap(), 2000);
/// assert_eq!(date.month().unwrap(), 5);
/// assert_eq!(date.day().unwrap(), 2);
/// assert_eq!(date.calendar().identifier(), "iso8601");
/// assert_eq!(date.hour(), 4);
/// assert_eq!(date.minute(), 0);
/// assert_eq!(date.second(), 30);
/// assert_eq!(date.millisecond(), 0);
///
/// ```
#[inline]
pub fn with(
&self,
Expand Down
75 changes: 75 additions & 0 deletions src/components/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,15 @@ impl PlainTime {

impl PlainTime {
/// Creates a new `PlainTime`, constraining any field into a valid range.
///
/// ```rust
/// use temporal_rs::PlainTime;
///
/// let time = PlainTime::new(23, 59, 59, 999, 999, 999).unwrap();
///
/// let constrained_time = PlainTime::new(24, 59, 59, 999, 999, 999).unwrap();
/// assert_eq!(time, constrained_time);
/// ```
pub fn new(
hour: i32,
minute: i32,
Expand All @@ -179,6 +188,15 @@ impl PlainTime {
}

/// Creates a new `PlainTime`, rejecting any field that is not in a valid range.
///
/// ```rust
/// use temporal_rs::PlainTime;
///
/// let time = PlainTime::try_new(23, 59, 59, 999, 999, 999).unwrap();
///
/// let invalid_time = PlainTime::try_new(24, 59, 59, 999, 999, 999);
/// assert!(invalid_time.is_err());
/// ```
pub fn try_new(
hour: i32,
minute: i32,
Expand Down Expand Up @@ -221,11 +239,68 @@ impl PlainTime {
Ok(Self::new_unchecked(time))
}

/// Creates a new `PlainTime` from a `PartialTime`.
///
/// ```rust
/// use temporal_rs::{partial::PartialTime, PlainTime};
///
/// let partial_time = PartialTime {
/// hour: Some(22),
/// ..Default::default()
/// };
///
/// let time = PlainTime::from_partial(partial_time, None).unwrap();
///
/// assert_eq!(time.hour(), 22);
/// assert_eq!(time.minute(), 0);
/// assert_eq!(time.second(), 0);
/// assert_eq!(time.millisecond(), 0);
/// assert_eq!(time.microsecond(), 0);
/// assert_eq!(time.nanosecond(), 0);
///
/// ```
pub fn from_partial(
partial: PartialTime,
overflow: Option<ArithmeticOverflow>,
) -> TemporalResult<Self> {
// NOTE: 4.5.12 ToTemporalTimeRecord requires one field to be set.
if partial.is_empty() {
return Err(TemporalError::r#type().with_message("PartialTime cannot be empty."));
}

let overflow = overflow.unwrap_or_default();
let iso = IsoTime::default().with(partial, overflow)?;
Ok(Self::new_unchecked(iso))
}

/// Creates a new `PlainTime` using the current `PlainTime` fields as a fallback.
///
/// ```rust
/// use temporal_rs::{partial::PartialTime, PlainTime};
///
/// let partial_time = PartialTime {
/// hour: Some(22),
/// ..Default::default()
/// };
///
/// let initial = PlainTime::try_new(15, 30, 12, 123, 456, 789).unwrap();
///
/// let time = initial.with(partial_time, None).unwrap();
///
/// assert_eq!(time.hour(), 22);
/// assert_eq!(time.minute(), 30);
/// assert_eq!(time.second(), 12);
/// assert_eq!(time.millisecond(), 123);
/// assert_eq!(time.microsecond(), 456);
/// assert_eq!(time.nanosecond(), 789);
///
/// ```
pub fn with(
&self,
partial: PartialTime,
overflow: Option<ArithmeticOverflow>,
) -> TemporalResult<Self> {
// NOTE: 4.5.12 ToTemporalTimeRecord requires one field to be set.
if partial.is_empty() {
return Err(TemporalError::r#type().with_message("PartialTime cannot be empty."));
}
Expand Down
3 changes: 2 additions & 1 deletion src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,9 +390,10 @@ impl fmt::Display for TemporalUnit {
/// `ArithmeticOverflow` can also be used as an
/// assignment overflow and consists of the "constrain"
/// and "reject" options.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub enum ArithmeticOverflow {
/// Constrain option
#[default]
Constrain,
/// Constrain option
Reject,
Expand Down

0 comments on commit 345ad54

Please sign in to comment.