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

Add DateTimeFormat #202

Merged
merged 15 commits into from
Oct 2, 2020
17 changes: 9 additions & 8 deletions components/datetime/benches/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use criterion::{criterion_group, criterion_main, Criterion};
use std::fmt::Write;

use icu_datetime::DateTimeFormat;
use icu_datetime::DummyDateTime;
use icu_fs_data_provider::FsDataProvider;

fn datetime_benches(c: &mut Criterion) {
Expand All @@ -18,10 +19,10 @@ fn datetime_benches(c: &mut Criterion) {
group.bench_function("DateTimeFormat/format_to_write", |b| {
b.iter(|| {
for fx in &fxs.0 {
let datetimes: Vec<_> = fx
let datetimes: Vec<DummyDateTime> = fx
.values
.iter()
.map(|value| fixtures::parse_date(value).unwrap())
.map(|value| value.parse().unwrap())
.collect();

for setup in &fx.setups {
Expand All @@ -43,10 +44,10 @@ fn datetime_benches(c: &mut Criterion) {
group.bench_function("DateTimeFormat/format_to_string", |b| {
b.iter(|| {
for fx in &fxs.0 {
let datetimes: Vec<_> = fx
let datetimes: Vec<DummyDateTime> = fx
.values
.iter()
.map(|value| fixtures::parse_date(value).unwrap())
.map(|value| value.parse().unwrap())
.collect();

for setup in &fx.setups {
Expand All @@ -65,10 +66,10 @@ fn datetime_benches(c: &mut Criterion) {
group.bench_function("FormattedDateTime/format", |b| {
b.iter(|| {
for fx in &fxs.0 {
let datetimes: Vec<_> = fx
let datetimes: Vec<DummyDateTime> = fx
.values
.iter()
.map(|value| fixtures::parse_date(value).unwrap())
.map(|value| value.parse().unwrap())
.collect();

for setup in &fx.setups {
Expand All @@ -91,10 +92,10 @@ fn datetime_benches(c: &mut Criterion) {
group.bench_function("FormattedDateTime/to_string", |b| {
b.iter(|| {
for fx in &fxs.0 {
let datetimes: Vec<_> = fx
let datetimes: Vec<DummyDateTime> = fx
.values
.iter()
.map(|value| fixtures::parse_date(value).unwrap())
.map(|value| value.parse().unwrap())
.collect();

for setup in &fx.setups {
Expand Down
21 changes: 0 additions & 21 deletions components/datetime/benches/fixtures/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
pub mod structs;

use icu_datetime::options::{style, DateTimeFormatOptions};
use icu_datetime::DummyDateTime;
use std::fs::File;
use std::io::BufReader;

Expand Down Expand Up @@ -40,23 +39,3 @@ pub fn get_options(input: &structs::TestOptions) -> DateTimeFormatOptions {
};
style.into()
}

#[allow(dead_code)]
pub fn parse_date(input: &str) -> Result<DummyDateTime, std::num::ParseIntError> {
let year: usize = input[0..4].parse()?;
let month: usize = input[5..7].parse()?;
let day: usize = input[8..10].parse()?;
let hour: usize = input[11..13].parse()?;
let minute: usize = input[14..16].parse()?;
let second: usize = input[17..19].parse()?;
let millisecond: usize = input[20..23].parse()?;
Ok(DummyDateTime {
year,
month: month - 1,
day: day - 1,
hour,
minute,
second,
millisecond,
})
}
212 changes: 174 additions & 38 deletions components/datetime/src/date.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,34 @@
use std::convert::{TryFrom, TryInto};
use std::ops::{Add, Sub};
use std::str::FromStr;

#[derive(Debug)]
pub enum DateTimeError {
Parse(std::num::ParseIntError),
Overflow(&'static str),
zbraniecki marked this conversation as resolved.
Show resolved Hide resolved
}

impl From<std::num::ParseIntError> for DateTimeError {
fn from(input: std::num::ParseIntError) -> Self {
Self::Parse(input)
}
}

/// Temporary trait used to represent the input data for `DateTimeFormat`.
///
/// This type represents all data that the formatted needs in order to produced formatted string.
zbraniecki marked this conversation as resolved.
Show resolved Hide resolved
///
/// *Note*: At the moment we support only `gregorian` calendar, and plan to extend support to
/// other calendars in the upcoming releases.
pub trait DateTimeType {
pub trait DateTimeType: FromStr {
fn year(&self) -> usize;
fn month(&self) -> usize;
fn day(&self) -> usize;
fn hour(&self) -> usize;
fn minute(&self) -> usize;
fn second(&self) -> usize;
fn millisecond(&self) -> usize;
fn month(&self) -> Month;
fn day(&self) -> Day;
fn hour(&self) -> Hour;
fn minute(&self) -> Minute;
fn second(&self) -> Second;
}

/// Temporary implementation of `DateTimeType`,
/// which is used in tests, benchmarks and examples of this component.
///
Expand All @@ -21,37 +37,27 @@ pub trait DateTimeType {
/// ```
/// use icu_datetime::DummyDateTime;
///
/// let dt = DummyDateTime::new(2020, 9, 24, 13, 21, 0, 0);
/// let dt = DummyDateTime::try_new(2020, 9, 24, 13, 21, 0)
/// .expect("Failed to construct DateTime.");
/// ```
#[derive(Debug, Default)]
pub struct DummyDateTime {
zbraniecki marked this conversation as resolved.
Show resolved Hide resolved
pub year: usize,
pub month: usize,
pub day: usize,
pub hour: usize,
pub minute: usize,
pub second: usize,
pub millisecond: usize,
pub month: Month,
pub day: Day,
pub hour: Hour,
pub minute: Minute,
pub second: Second,
}

impl DummyDateTime {
/// Constructor for the `DummyDateTime`.
///
/// # Examples
///
/// ```
/// use icu_datetime::DummyDateTime;
///
/// let dt = DummyDateTime::new(2020, 9, 24, 13, 21, 0, 0);
/// ```
pub fn new(
year: usize, // 0-
month: usize, // 0-11
day: usize, // 0-31
hour: usize, // 0-23
minute: usize, // 0-60
second: usize, // 0-60
millisecond: usize, // 0-999
year: usize,
month: Month,
day: Day,
hour: Hour,
minute: Minute,
second: Second,
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: I think you added millisecond at some point, but now it's gone?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, sorry for not explanining. I initially prototyped a larger version of this api to understand the facets that included most of ldml, skeletons, components etc. Then, in result of our agreement to trim it to minimum I removed almost all pieces that weren't strictly needed and yet the review took over 150 comments. Shane called out a number of places where I bits and pieces were left that aren't needed for this PR and I went through a cleanse to remove them all. We don't use milliseconds in styles so I removed it and will bring back when we work on skeletons.
Is that ok with you?

) -> Self {
Self {
year,
Expand All @@ -60,31 +66,161 @@ impl DummyDateTime {
hour,
minute,
second,
millisecond,
}
}

/// Constructor for the `DummyDateTime`.
///
/// # Examples
///
/// ```
/// use icu_datetime::DummyDateTime;
///
/// let dt = DummyDateTime::try_new(2020, 9, 24, 13, 21, 0)
/// .expect("Failed to construct a DateTime");
/// ```
pub fn try_new(
year: usize,
month: usize,
day: usize,
hour: usize,
minute: usize,
second: usize,
) -> Result<Self, DateTimeError> {
Ok(Self {
year,
month: month.try_into()?,
day: day.try_into()?,
hour: hour.try_into()?,
minute: minute.try_into()?,
second: second.try_into()?,
})
}
}

impl DateTimeType for DummyDateTime {
fn year(&self) -> usize {
self.year
}
fn month(&self) -> usize {
fn month(&self) -> Month {
self.month
}
fn day(&self) -> usize {
fn day(&self) -> Day {
self.day
}
fn hour(&self) -> usize {
fn hour(&self) -> Hour {
self.hour
}
fn minute(&self) -> usize {
fn minute(&self) -> Minute {
self.minute
}
fn second(&self) -> usize {
fn second(&self) -> Second {
self.second
}
fn millisecond(&self) -> usize {
self.millisecond
}

impl FromStr for DummyDateTime {
type Err = DateTimeError;

fn from_str(input: &str) -> Result<Self, Self::Err> {
let year: usize = input[0..4].parse()?;
let month: Month = input[5..7].parse()?;
let day: Day = input[8..10].parse()?;
let hour: Hour = input[11..13].parse()?;
let minute: Minute = input[14..16].parse()?;
let second: Second = input[17..19].parse()?;
Ok(DummyDateTime {
year,
month: month - 1,
day: day - 1,
hour,
minute,
second,
})
}
}

macro_rules! dt_unit {
($name:ident, $value:expr) => {
#[derive(Debug, Default, Clone, Copy, PartialEq, Hash)]
pub struct $name(u8);

impl $name {
pub fn new_unchecked(input: u8) -> Self {
Self(input)
}
}

impl FromStr for $name {
type Err = DateTimeError;

fn from_str(input: &str) -> Result<Self, Self::Err> {
let val: u8 = input.parse()?;
if val > $value {
Err(DateTimeError::Overflow("$name must be between 0-$value"))
} else {
Ok(Self(val))
}
}
}

impl TryFrom<u8> for $name {
type Error = DateTimeError;

fn try_from(input: u8) -> Result<Self, Self::Error> {
if input > $value {
Err(DateTimeError::Overflow("$name must be between 0-$value"))
} else {
Ok(Self(input))
}
}
}

impl TryFrom<usize> for $name {
type Error = DateTimeError;

fn try_from(input: usize) -> Result<Self, Self::Error> {
if input > $value {
Err(DateTimeError::Overflow("$name must be between 0-$value"))
} else {
Ok(Self(input as u8))
}
}
}

impl From<$name> for u8 {
fn from(input: $name) -> u8 {
input.0
}
}

impl From<$name> for usize {
fn from(input: $name) -> usize {
input.0 as usize
}
}

impl Add<u8> for $name {
type Output = Self;

fn add(self, other: u8) -> Self {
Self(self.0 + other)
}
}

impl Sub<u8> for $name {
type Output = Self;

fn sub(self, other: u8) -> Self {
Self(self.0 - other)
}
}
};
}

dt_unit!(Month, 12);
dt_unit!(WeekDay, 7);
dt_unit!(Day, 32);
dt_unit!(Hour, 24);
dt_unit!(Minute, 60);
dt_unit!(Second, 60);
6 changes: 0 additions & 6 deletions components/datetime/src/fields.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ impl FieldLength {

#[derive(Debug, PartialEq, Clone, Copy)]
pub enum FieldSymbol {
Era,
Year(Year),
Month(Month),
Day(Day),
Expand All @@ -47,7 +46,6 @@ impl TryFrom<u8> for FieldSymbol {
type Error = Error;
fn try_from(b: u8) -> Result<Self, Self::Error> {
match b {
b'G' => Ok(Self::Era),
b'm' => Ok(Self::Minute),
_ => Year::try_from(b)
.map(Self::Year)
Expand Down Expand Up @@ -214,17 +212,13 @@ impl From<Weekday> for FieldSymbol {
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum DayPeriod {
AmPm,
NoonMidnight,
Flexible,
}

impl TryFrom<u8> for DayPeriod {
type Error = Error;
fn try_from(b: u8) -> Result<Self, Self::Error> {
match b {
b'a' => Ok(Self::AmPm),
b'b' => Ok(Self::NoonMidnight),
b'B' => Ok(Self::Flexible),
_ => Err(Error::Unknown),
}
}
Expand Down
Loading