Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding methods for yearMonth and MonthDay #44

Merged
merged 17 commits into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
27 changes: 22 additions & 5 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 `day` value of `MonthDay`.
#[inline]
#[must_use]
pub fn day(&self) -> u8 {
jasonwilliams marked this conversation as resolved.
Show resolved Hide resolved
self.iso.day
}

// returns the `month` value of `MonthDay`.
#[inline]
#[must_use]
pub fn 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
89 changes: 88 additions & 1 deletion 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::{self, 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);
Comment on lines +20 to +21
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to expose the fields as public?
Also, I think this should be a proper struct with named fields for clarity


/// The native Rust implementation of `Temporal.YearMonth`.
#[non_exhaustive]
Expand Down Expand Up @@ -48,19 +57,97 @@ impl YearMonth {
self.iso.year
}

/// Returns the padded ISO year string
#[inline]
#[must_use]
pub fn padded_iso_year_string(&self) -> String {
jasonwilliams marked this conversation as resolved.
Show resolved Hide resolved
pad_iso_year(self.iso.year)
}

/// Returns the `month` value for this `YearMonth`.
#[inline]
#[must_use]
pub fn 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 {
utils::mathematical_in_leap_year(utils::epoch_time_for_year(self.iso.year)) == 1
jasonwilliams marked this conversation as resolved.
Show resolved Hide resolved
}

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