Skip to content

Commit

Permalink
adding methods for yearMonth and MonthDay (#44)
Browse files Browse the repository at this point in the history
Adds methods to yearMonth and monthDay
  • Loading branch information
jasonwilliams authored Jul 24, 2024
1 parent 0fada43 commit 9a36aa1
Show file tree
Hide file tree
Showing 7 changed files with 194 additions and 12 deletions.
6 changes: 6 additions & 0 deletions src/components/calendar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,10 @@ pub enum CalendarDateLike {
DateTime(DateTime),
/// Represents a `Date`.
Date(Date),
/// Represents a `YearMonth`.
YearMonth(YearMonth),
/// Represents a `MonthDay`.
MonthDay(MonthDay),
}

impl CalendarDateLike {
Expand All @@ -263,6 +267,8 @@ impl CalendarDateLike {
match self {
CalendarDateLike::DateTime(dt) => dt.iso_date(),
CalendarDateLike::Date(d) => d.iso_date(),
CalendarDateLike::YearMonth(ym) => ym.iso_date(),
CalendarDateLike::MonthDay(md) => md.iso_date(),
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/components/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,6 @@ pub use month_day::MonthDay;
pub use time::Time;
#[doc(inline)]
pub use year_month::YearMonth;
pub use year_month::YearMonthFields;
#[doc(inline)]
pub use zoneddatetime::ZonedDateTime;
29 changes: 23 additions & 6 deletions src/components/month_day.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@

use std::str::FromStr;

use tinystr::TinyAsciiStr;

use crate::{
components::calendar::Calendar,
iso::{IsoDate, IsoDateSlots},
options::ArithmeticOverflow,
TemporalError, TemporalResult, TemporalUnwrap,
};

use super::calendar::GetTemporalCalendar;
use super::calendar::{CalendarDateLike, GetTemporalCalendar};

/// The native Rust implementation of `Temporal.PlainMonthDay`
#[non_exhaustive]
Expand All @@ -35,22 +37,30 @@ impl MonthDay {
calendar: Calendar,
overflow: ArithmeticOverflow,
) -> TemporalResult<Self> {
// 1972 is the first leap year in the Unix epoch (needed to cover all dates)
let iso = IsoDate::new(1972, month, day, overflow)?;
Ok(Self::new_unchecked(iso, calendar))
}

/// Returns the `month` value of `MonthDay`.
/// Returns the iso day value of `MonthDay`.
#[inline]
#[must_use]
pub fn iso_day(&self) -> u8 {
self.iso.day
}

// returns the iso month value of `MonthDay`.
#[inline]
#[must_use]
pub fn month(&self) -> u8 {
pub fn iso_month(&self) -> u8 {
self.iso.month
}

/// Returns the `day` value of `MonthDay`.
/// Returns the string identifier for the current calendar used.
#[inline]
#[must_use]
pub fn day(&self) -> u8 {
self.iso.day
pub fn calendar_id(&self) -> String {
self.calendar.identifier()
}

/// Returns a reference to `MonthDay`'s `CalendarSlot`
Expand All @@ -59,6 +69,13 @@ impl MonthDay {
pub fn calendar(&self) -> &Calendar {
&self.calendar
}

/// Returns the `monthCode` value of `MonthDay`.
#[inline]
pub fn month_code(&self) -> TemporalResult<TinyAsciiStr<4>> {
self.calendar
.month_code(&CalendarDateLike::MonthDay(self.clone()))
}
}

impl GetTemporalCalendar for MonthDay {
Expand Down
99 changes: 94 additions & 5 deletions src/components/year_month.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,23 @@

use std::str::FromStr;

use tinystr::TinyAsciiStr;

use crate::{
components::calendar::Calendar,
iso::{IsoDate, IsoDateSlots},
options::ArithmeticOverflow,
utils::pad_iso_year,
TemporalError, TemporalResult, TemporalUnwrap,
};

use super::calendar::GetTemporalCalendar;
use super::{
calendar::{CalendarDateLike, GetTemporalCalendar},
Duration,
};

// Subset of `TemporalFields` representing just the `YearMonthFields`
pub struct YearMonthFields(pub i32, pub u8);

/// The native Rust implementation of `Temporal.YearMonth`.
#[non_exhaustive]
Expand Down Expand Up @@ -41,26 +50,106 @@ impl YearMonth {
Ok(Self::new_unchecked(iso, calendar))
}

/// Returns the `year` value for this `YearMonth`.
/// Returns the iso year value for this `YearMonth`.
#[inline]
#[must_use]
pub fn year(&self) -> i32 {
pub fn iso_year(&self) -> i32 {
self.iso.year
}

/// Returns the `month` value for this `YearMonth`.
/// Returns the padded ISO year string
#[inline]
#[must_use]
pub fn padded_iso_year_string(&self) -> String {
pad_iso_year(self.iso.year)
}

/// Returns the iso month value for this `YearMonth`.
#[inline]
#[must_use]
pub fn month(&self) -> u8 {
pub fn iso_month(&self) -> u8 {
self.iso.month
}

pub fn month_code(&self) -> TemporalResult<TinyAsciiStr<4>> {
self.get_calendar()
.month_code(&CalendarDateLike::YearMonth(self.clone()))
}

#[inline]
#[must_use]
pub fn in_leap_year(&self) -> bool {
self.get_calendar()
.in_leap_year(&CalendarDateLike::YearMonth(self.clone()))
.is_ok_and(|is_leap_year| is_leap_year)
}

pub fn get_days_in_year(&self) -> TemporalResult<u16> {
self.get_calendar()
.days_in_year(&CalendarDateLike::YearMonth(self.clone()))
}

pub fn get_days_in_month(&self) -> TemporalResult<u16> {
self.get_calendar()
.days_in_month(&CalendarDateLike::YearMonth(self.clone()))
}

pub fn get_months_in_year(&self) -> TemporalResult<u16> {
self.get_calendar()
.months_in_year(&CalendarDateLike::YearMonth(self.clone()))
}
}

impl YearMonth {
/// Returns the Calendar value.
#[inline]
#[must_use]
pub fn calendar(&self) -> &Calendar {
&self.calendar
}

/// Returns the string identifier for the current calendar used.
#[inline]
#[must_use]
pub fn calendar_id(&self) -> String {
self.calendar.identifier()
}

pub fn add_duration(
&self,
duration: &Duration,
overflow: ArithmeticOverflow,
) -> TemporalResult<YearMonth> {
self.add_or_subtract_duration(duration, overflow)
}

pub fn subtract_duration(
&self,
duration: &Duration,
overflow: ArithmeticOverflow,
) -> TemporalResult<YearMonth> {
self.add_or_subtract_duration(&duration.negated(), overflow)
}

pub(crate) fn add_or_subtract_duration(
&self,
duration: &Duration,
overflow: ArithmeticOverflow,
) -> TemporalResult<YearMonth> {
let mut fields = YearMonthFields(self.iso_date().year, self.iso_date().month).into();

let mut intermediate_date = self
.get_calendar()
.date_from_fields(&mut fields, overflow)?;

intermediate_date = intermediate_date.add_date(duration, Some(overflow))?;

let mut result_fields =
YearMonthFields(intermediate_date.iso_year(), intermediate_date.iso_month()).into();

self.get_calendar()
.year_month_from_fields(&mut result_fields, overflow)
}
}

impl GetTemporalCalendar for YearMonth {
Expand Down
19 changes: 18 additions & 1 deletion src/fields.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
use core::fmt;
use std::str::FromStr;

use crate::{components::calendar::Calendar, error::TemporalError, iso::IsoDate, TemporalResult};
use crate::{
components::{calendar::Calendar, YearMonthFields},
error::TemporalError,
iso::IsoDate,
TemporalResult,
};

use bitflags::bitflags;
use tinystr::TinyAsciiStr;
Expand Down Expand Up @@ -577,3 +582,15 @@ fn month_code_to_integer(mc: TinyAsciiStr<4>) -> TemporalResult<i32> {
_ => Err(TemporalError::range().with_message("monthCode is not within the valid values.")),
}
}

// Conversion to `TemporalFields`
impl From<YearMonthFields> for TemporalFields {
fn from(value: YearMonthFields) -> Self {
TemporalFields {
bit_map: FieldMap::YEAR | FieldMap::MONTH,
year: Some(value.0),
month: Some(value.1.into()),
..Default::default()
}
}
}
40 changes: 40 additions & 0 deletions src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -689,3 +689,43 @@ impl fmt::Display for TemporalRoundingMode {
.fmt(f)
}
}

/// values for `CalendarName`, whether to show the calendar in toString() methods
/// <https://tc39.es/proposal-temporal/#sec-temporal-gettemporalshowcalendarnameoption>
#[derive(Debug, Clone, Copy)]
pub enum CalendarName {
/// `Auto` option
Auto,
/// `Always` option
Always,
/// `Never` option
Never,
// `Critical` option
Critical,
}

impl fmt::Display for CalendarName {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CalendarName::Auto => "auto",
CalendarName::Always => "always",
CalendarName::Never => "never",
CalendarName::Critical => "critical",
}
.fmt(f)
}
}

impl FromStr for CalendarName {
type Err = TemporalError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"auto" => Ok(Self::Auto),
"always" => Ok(Self::Always),
"never" => Ok(Self::Never),
"critical" => Ok(Self::Critical),
_ => Err(TemporalError::range().with_message("Invalid CalendarName provided.")),
}
}
}
12 changes: 12 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ pub(crate) fn epoch_days_to_epoch_ms(day: i32, time: f64) -> f64 {
f64::from(day).mul_add(f64::from(MS_PER_DAY), time).floor()
}

/// 3.5.11 PadISOYear ( y )
///
/// returns a String representation of y suitable for inclusion in an ISO 8601 string
pub(crate) fn pad_iso_year(year: i32) -> String {
if (0..9999).contains(&year) {
return format!("{:04}", year);
}
let year_sign = if year > 0 { "+" } else { "-" };
let year_string = format!("{:06}", year.abs());
format!("{year_sign}{year_string}",)
}

/// `EpochTimeToDayNumber`
///
/// This equation is the equivalent to `ECMAScript`'s `Date(t)`
Expand Down

0 comments on commit 9a36aa1

Please sign in to comment.