Skip to content

Commit

Permalink
Add initial calendars crate (#827)
Browse files Browse the repository at this point in the history
* Add initial calendars crate

* rm serde

* review fixes

* fmt

* Move iso constructors to Date

* review fixes

* rm where

* scope Debug bound

* construct_unchecked -> from_raw

* fmt

* add Default

* fix clippy

* +errors
  • Loading branch information
Manishearth authored Jun 26, 2021
1 parent 085c816 commit 67bd340
Show file tree
Hide file tree
Showing 9 changed files with 618 additions and 0 deletions.
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ members = [
"components/plurals",
"components/uniset",
"experimental/bies",
"experimental/calendar",
"experimental/provider_ppucd",
"experimental/provider_static",
"experimental/segmenter",
Expand Down
29 changes: 29 additions & 0 deletions experimental/calendar/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# This file is part of ICU4X. For terms of use, please see the file
# called LICENSE at the top level of the ICU4X source tree
# (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

[package]
name = "icu_calendar"
description = "API for supporting various types of calendars"
version = "0.2.0"
authors = ["The ICU4X Project Developers"]
edition = "2018"
readme = "README.md"
repository = "https://github.com/unicode-org/icu4x"
license-file = "../../LICENSE"
categories = ["internationalization"]
# Keep this in sync with other crates unless there are exceptions
include = [
"src/**/*",
"examples/**/*",
"benches/**/*",
"tests/**/*",
"Cargo.toml",
"README.md"
]

[package.metadata.docs.rs]
all-features = true

[package.metadata.cargo-all-features]
skip_optional_dependencies = true
46 changes: 46 additions & 0 deletions experimental/calendar/src/calendar.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

use crate::{Date, DateDuration, DurationUnit, Iso};

/// A calendar implementation
///
/// Only implementors of [`Calendar`] should care about these methods, in general users of
/// these calendars should use the methods on [`Date`] instead.
///
/// Individual [`Calendar`] implementations may have inherent utility methods
/// allowing for direct construction, etc.
pub trait Calendar {
type DateInner: PartialEq + Eq + Clone;
/// Construct the date from an ISO date
fn date_from_iso(&self, iso: Date<Iso>) -> Self::DateInner;
fn date_to_iso(&self, date: &Self::DateInner) -> Date<Iso>;
// fn validate_date(&self, e: Era, y: Year, m: MonthCode, d: Day) -> bool;
// // similar validators for YearMonth, etc

// fn is_leap<A: AsCalendar<Calendar = Self>>(&self, date: &Date<A>) -> bool;
fn months_in_year(&self, date: &Self::DateInner) -> u8;
fn days_in_year(&self, date: &Self::DateInner) -> u32;
fn days_in_month(&self, date: &Self::DateInner) -> u8;
/// Monday is 1, Sunday is 7, according to ISO
fn day_of_week(&self, date: &Self::DateInner) -> u8 {
self.date_to_iso(date).day_of_week()
}
// fn week_of_year(&self, date: &Self::DateInner) -> u8;

/// Add `offset` to `date`
fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>);

/// Calculate `date2 - date` as a duration
fn until(
&self,
date1: &Self::DateInner,
date2: &Self::DateInner,
largest_unit: DurationUnit,
smallest_unit: DurationUnit,
) -> DateDuration<Self>;

fn debug_name() -> &'static str;
// fn since(&self, from: &Date<Self>, to: &Date<Self>) -> Duration<Self>, Error;
}
144 changes: 144 additions & 0 deletions experimental/calendar/src/date.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

use crate::{Calendar, DateDuration, DurationUnit, Iso};
use std::fmt;

pub trait AsCalendar {
type Calendar: Calendar;
fn as_calendar(&self) -> &Self::Calendar;
}

impl<C: Calendar> AsCalendar for C {
type Calendar = C;
#[inline]
fn as_calendar(&self) -> &Self {
self
}
}

pub struct Date<A: AsCalendar> {
inner: <A::Calendar as Calendar>::DateInner,
calendar: A,
}

impl<A: AsCalendar> Date<A> {
/// Construct a date from an ISO date and some calendar representation
#[inline]
pub fn new_from_iso(iso: Date<Iso>, calendar: A) -> Self {
let inner = calendar.as_calendar().date_from_iso(iso);
Date { inner, calendar }
}

#[inline]
pub fn to_iso(&self) -> Date<Iso> {
self.calendar.as_calendar().date_to_iso(self.inner())
}

/// The number of months in the year of this date
#[inline]
pub fn months_in_year(&self) -> u8 {
self.calendar.as_calendar().months_in_year(self.inner())
}

/// The number of days in the year of this date
#[inline]
pub fn days_in_year(&self) -> u32 {
self.calendar.as_calendar().days_in_year(self.inner())
}

/// The number of days in the month of this date
#[inline]
pub fn days_in_month(&self) -> u8 {
self.calendar.as_calendar().days_in_month(self.inner())
}

/// The day of the week for this date
///
/// Monday is 1, Sunday is 7, according to ISO
#[inline]
pub fn day_of_week(&self) -> u8 {
self.calendar.as_calendar().day_of_week(self.inner())
}

/// Add a `duration` to this date, mutating it
#[inline]
pub fn add(&mut self, duration: DateDuration<A::Calendar>) {
self.calendar
.as_calendar()
.offset_date(&mut self.inner, duration)
}

/// Add a `duration` to this date, returning the new one
#[inline]
pub fn added(mut self, duration: DateDuration<A::Calendar>) -> Self {
self.add(duration);
self
}

/// Calculating the duration between `other - self`
#[inline]
pub fn until<B: AsCalendar<Calendar = A::Calendar>>(
&self,
other: &Date<B>,
largest_unit: DurationUnit,
smallest_unit: DurationUnit,
) -> DateDuration<A::Calendar> {
self.calendar
.as_calendar()
.until(self.inner(), other.inner(), largest_unit, smallest_unit)
}

/// Construct a date from raw values for a given calendar. This does not check any
/// invariants for the date and calendar, and should only be called by calendar implementations.
///
/// Calling this outside of calendar implementations is sound, but calendar implementations are not
/// expected to do anything sensible with such invalid dates.
#[inline]
pub fn from_raw(inner: <A::Calendar as Calendar>::DateInner, calendar: A) -> Self {
Self { inner, calendar }
}

/// Get the inner date implementation. Should not be called outside of calendar implementations
#[inline]
pub fn inner(&self) -> &<A::Calendar as Calendar>::DateInner {
&self.inner
}
}

impl<C, A, B> PartialEq<Date<B>> for Date<A>
where
C: Calendar,
A: AsCalendar<Calendar = C>,
B: AsCalendar<Calendar = C>,
{
fn eq(&self, other: &Date<B>) -> bool {
self.inner.eq(&other.inner)
}
}

impl<A: AsCalendar> Eq for Date<A> {}

impl<A: AsCalendar> fmt::Debug for Date<A>
where
<A::Calendar as Calendar>::DateInner: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(
f,
"Date({:?}, for calendar {})",
self.inner,
A::Calendar::debug_name()
)
}
}

impl<A: AsCalendar + Clone> Clone for Date<A> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
calendar: self.calendar.clone(),
}
}
}
60 changes: 60 additions & 0 deletions experimental/calendar/src/duration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

use crate::Calendar;
use std::fmt;
use std::marker::PhantomData;

#[derive(Copy, Clone, Eq, PartialEq)]
/// A duration between two dates
pub struct DateDuration<C: Calendar + ?Sized> {
pub years: i32,
pub months: i32,
pub weeks: i32,
pub days: i32,
pub marker: PhantomData<C>,
}

#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum DurationUnit {
Years,
Months,
Weeks,
Days,
}

impl<C: Calendar + ?Sized> Default for DateDuration<C> {
fn default() -> Self {
Self {
years: 0,
months: 0,
weeks: 0,
days: 0,
marker: PhantomData,
}
}
}

impl<C: Calendar + ?Sized> DateDuration<C> {
pub fn new(years: i32, months: i32, weeks: i32, days: i32) -> Self {
DateDuration {
years,
months,
weeks,
days,
marker: PhantomData,
}
}
}

impl<C: Calendar> fmt::Debug for DateDuration<C> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_struct("DateDuration")
.field("years", &self.years)
.field("months", &self.months)
.field("weeks", &self.weeks)
.field("days", &self.days)
.finish()
}
}
8 changes: 8 additions & 0 deletions experimental/calendar/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Error {
OutOfRange,
}
Loading

0 comments on commit 67bd340

Please sign in to comment.