Skip to content

Commit

Permalink
Add support for the Gregorian Calendar availableFormats (#480)
Browse files Browse the repository at this point in the history
  • Loading branch information
gregtatum authored Mar 29, 2021
1 parent 30a3909 commit 19642c3
Show file tree
Hide file tree
Showing 38 changed files with 2,081 additions and 103 deletions.
5 changes: 5 additions & 0 deletions Cargo.lock

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

5 changes: 4 additions & 1 deletion components/datetime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ skip_optional_dependencies = true
icu_locid = { version = "0.1", path = "../locid" }
icu_provider = { version = "0.1", path = "../provider" }
writeable = { version = "0.2", path = "../../utils/writeable" }
litemap = { version = "0.1.1", path = "../../utils/litemap" }
tinystr = { version = "0.4.1" }
serde = { version = "1.0", features = ["derive"], optional = true }
smallvec = "1.4"

[dev-dependencies]
criterion = "0.3"
Expand All @@ -38,14 +40,15 @@ icu_testdata = { version = "0.1", path = "../../resources/testdata" }
icu_locid_macros = { version = "0.1", path = "../locid/macros" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
bincode = "1.3"

[lib]
bench = false # This option is required for Benchmark CI

[features]
default = ["provider_serde"]
bench = []
provider_serde = ["serde"]
provider_serde = ["serde", "litemap/serde"]
serialize_none = []

[[bench]]
Expand Down
13 changes: 10 additions & 3 deletions components/datetime/src/fields/length.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,21 @@
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

use std::convert::TryFrom;
use std::{
cmp::{Ord, PartialOrd},
convert::TryFrom,
};

#[derive(Debug)]
#[derive(Debug, PartialEq)]
pub enum LengthError {
TooLong,
}

#[derive(Debug, PartialEq, Clone, Copy)]
#[derive(Debug, Eq, PartialEq, Clone, Copy, Ord, PartialOrd)]
#[cfg_attr(
feature = "provider_serde",
derive(serde::Serialize, serde::Deserialize)
)]
pub enum FieldLength {
One = 1,
TwoDigit = 2,
Expand Down
26 changes: 20 additions & 6 deletions components/datetime/src/fields/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,40 @@
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

mod length;
mod symbols;
pub(crate) mod symbols;

pub use length::FieldLength;
pub use length::{FieldLength, LengthError};
pub use symbols::*;

use std::convert::{TryFrom, TryInto};
use std::{
cmp::{Ord, PartialOrd},
convert::{TryFrom, TryInto},
fmt,
};

#[derive(Debug)]
pub enum Error {
TooLong(FieldSymbol),
}

#[derive(Debug, PartialEq, Clone, Copy)]
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::TooLong(symbol) => write!(f, "field {:?} is too long", symbol),
}
}
}

#[derive(Debug, Eq, PartialEq, Clone, Copy, Ord, PartialOrd)]
#[cfg_attr(
feature = "provider_serde",
derive(serde::Serialize, serde::Deserialize)
)]
pub struct Field {
pub symbol: FieldSymbol,
pub length: FieldLength,
}

impl Field {}

impl From<(FieldSymbol, FieldLength)> for Field {
fn from(input: (FieldSymbol, FieldLength)) -> Self {
Self {
Expand Down
143 changes: 133 additions & 10 deletions components/datetime/src/fields/symbols.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,21 @@
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

use std::convert::TryFrom;
use std::{cmp::Ordering, convert::TryFrom};

#[derive(Debug)]
#[derive(Debug, PartialEq)]
pub enum SymbolError {
/// Unknown field symbol
Unknown(u8),
/// Invalid character for a field symbol
Invalid(char),
}

#[derive(Debug, PartialEq, Clone, Copy)]
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
#[cfg_attr(
feature = "provider_serde",
derive(serde::Serialize, serde::Deserialize)
)]
pub enum FieldSymbol {
Year(Year),
Month(Month),
Expand All @@ -24,6 +28,43 @@ pub enum FieldSymbol {
Second(Second),
}

impl FieldSymbol {
/// Skeletons are a Vec<Field>, and represent the Fields that can be used to match to a
/// specific pattern. The order of the Vec does not affect the Pattern that is output.
/// However, it's more performant when matching these fields, and it's more deterministic
/// when serializing them to present them in a consistent order.
///
/// This ordering is taken by the order of the fields listed in the [UTS 35 Date Field Symbol Table]
/// (https://unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table), and are generally
/// ordered most significant to least significant.
///
fn get_canonical_order(&self) -> u8 {
match self {
FieldSymbol::Year(Year::Calendar) => 0,
FieldSymbol::Year(Year::WeekOf) => 1,
FieldSymbol::Month(Month::Format) => 2,
FieldSymbol::Month(Month::StandAlone) => 3,
FieldSymbol::Day(Day::DayOfMonth) => 4,
FieldSymbol::Day(Day::DayOfYear) => 5,
FieldSymbol::Day(Day::DayOfWeekInMonth) => 6,
FieldSymbol::Day(Day::ModifiedJulianDay) => 7,
FieldSymbol::Weekday(Weekday::Format) => 8,
FieldSymbol::Weekday(Weekday::Local) => 9,
FieldSymbol::Weekday(Weekday::StandAlone) => 10,
FieldSymbol::DayPeriod(DayPeriod::AmPm) => 11,
FieldSymbol::DayPeriod(DayPeriod::NoonMidnight) => 12,
FieldSymbol::Hour(Hour::H11) => 13,
FieldSymbol::Hour(Hour::H12) => 14,
FieldSymbol::Hour(Hour::H23) => 15,
FieldSymbol::Hour(Hour::H24) => 16,
FieldSymbol::Minute => 17,
FieldSymbol::Second(Second::Second) => 18,
FieldSymbol::Second(Second::FractionalSecond) => 19,
FieldSymbol::Second(Second::Millisecond) => 20,
}
}
}

impl TryFrom<u8> for FieldSymbol {
type Error = SymbolError;
fn try_from(b: u8) -> Result<Self, Self::Error> {
Expand Down Expand Up @@ -53,7 +94,65 @@ impl TryFrom<char> for FieldSymbol {
}
}

#[derive(Debug, PartialEq, Clone, Copy)]
impl From<FieldSymbol> for char {
fn from(symbol: FieldSymbol) -> Self {
match symbol {
FieldSymbol::Year(year) => match year {
Year::Calendar => 'y',
Year::WeekOf => 'Y',
},
FieldSymbol::Month(month) => match month {
Month::Format => 'M',
Month::StandAlone => 'L',
},
FieldSymbol::Day(day) => match day {
Day::DayOfMonth => 'd',
Day::DayOfYear => 'D',
Day::DayOfWeekInMonth => 'F',
Day::ModifiedJulianDay => 'g',
},
FieldSymbol::Weekday(weekday) => match weekday {
Weekday::Format => 'E',
Weekday::Local => 'e',
Weekday::StandAlone => 'c',
},
FieldSymbol::DayPeriod(dayperiod) => match dayperiod {
DayPeriod::AmPm => 'a',
DayPeriod::NoonMidnight => 'b',
},
FieldSymbol::Hour(hour) => match hour {
Hour::H11 => 'K',
Hour::H12 => 'h',
Hour::H23 => 'H',
Hour::H24 => 'k',
},
FieldSymbol::Minute => 'm',
FieldSymbol::Second(second) => match second {
Second::Second => 's',
Second::FractionalSecond => 'S',
Second::Millisecond => 'A',
},
}
}
}

impl PartialOrd for FieldSymbol {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}

impl Ord for FieldSymbol {
fn cmp(&self, other: &Self) -> Ordering {
self.get_canonical_order().cmp(&other.get_canonical_order())
}
}

#[derive(Debug, Eq, PartialEq, Clone, Copy)]
#[cfg_attr(
feature = "provider_serde",
derive(serde::Serialize, serde::Deserialize)
)]
pub enum Year {
Calendar,
WeekOf,
Expand All @@ -76,7 +175,11 @@ impl From<Year> for FieldSymbol {
}
}

#[derive(Debug, PartialEq, Clone, Copy)]
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
#[cfg_attr(
feature = "provider_serde",
derive(serde::Serialize, serde::Deserialize)
)]
pub enum Month {
Format,
StandAlone,
Expand All @@ -99,7 +202,11 @@ impl From<Month> for FieldSymbol {
}
}

#[derive(Debug, PartialEq, Clone, Copy)]
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
#[cfg_attr(
feature = "provider_serde",
derive(serde::Serialize, serde::Deserialize)
)]
pub enum Day {
DayOfMonth,
DayOfYear,
Expand All @@ -126,7 +233,11 @@ impl From<Day> for FieldSymbol {
}
}

#[derive(Debug, PartialEq, Clone, Copy)]
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
#[cfg_attr(
feature = "provider_serde",
derive(serde::Serialize, serde::Deserialize)
)]
pub enum Hour {
H11,
H12,
Expand All @@ -153,7 +264,11 @@ impl From<Hour> for FieldSymbol {
}
}

#[derive(Debug, PartialEq, Clone, Copy)]
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
#[cfg_attr(
feature = "provider_serde",
derive(serde::Serialize, serde::Deserialize)
)]
pub enum Second {
Second,
FractionalSecond,
Expand All @@ -178,7 +293,11 @@ impl From<Second> for FieldSymbol {
}
}

#[derive(Debug, PartialEq, Clone, Copy)]
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
#[cfg_attr(
feature = "provider_serde",
derive(serde::Serialize, serde::Deserialize)
)]
pub enum Weekday {
Format,
Local,
Expand All @@ -203,7 +322,11 @@ impl From<Weekday> for FieldSymbol {
}
}

#[derive(Debug, PartialEq, Clone, Copy)]
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
#[cfg_attr(
feature = "provider_serde",
derive(serde::Serialize, serde::Deserialize)
)]
pub enum DayPeriod {
AmPm,
NoonMidnight,
Expand Down
1 change: 1 addition & 0 deletions components/datetime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ pub mod options;
#[doc(hidden)]
pub mod pattern;
pub mod provider;
pub mod skeleton;

use crate::provider::helpers::DateTimePatterns;
use date::DateTimeInput;
Expand Down
17 changes: 17 additions & 0 deletions components/datetime/src/pattern/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

use crate::fields;
use std::fmt;

#[derive(Debug, PartialEq)]
pub enum Error {
Expand All @@ -12,6 +13,22 @@ pub enum Error {
UnclosedPlaceholder,
}

/// These strings follow the recommendations for the serde::de::Unexpected::Other type.
/// https://docs.serde.rs/serde/de/enum.Unexpected.html#variant.Other
///
/// Serde will generate an error such as:
/// "invalid value: unclosed literal in pattern, expected a valid UTS 35 pattern string at line 1 column 12"
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::FieldTooLong(symbol) => write!(f, "{:?} field too long in pattern", symbol),
Error::UnknownSubstitution(ch) => write!(f, "unknown substitution {} in pattern", ch),
Error::UnclosedLiteral => write!(f, "unclosed literal in pattern"),
Error::UnclosedPlaceholder => write!(f, "unclosed placeholder in pattern"),
}
}
}

impl From<fields::Error> for Error {
fn from(input: fields::Error) -> Self {
match input {
Expand Down
Loading

0 comments on commit 19642c3

Please sign in to comment.