Skip to content

Commit

Permalink
Showing 13 changed files with 129 additions and 6 deletions.
48 changes: 44 additions & 4 deletions components/datetime/src/date.rs
Original file line number Diff line number Diff line change
@@ -2,21 +2,32 @@
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

//! A collection of utilities for representing and working with dates as an input to
//! formatting operations.
use core::convert::TryFrom;
use core::ops::{Add, Sub};
use core::str::FromStr;
use displaydoc::Display;
use icu_locid::Locale;
use tinystr::TinyStr8;

/// A list of possible error outcomes for working with various inputs to DateTime inputs
/// and operations.
#[derive(Display, Debug)]
pub enum DateTimeError {
/// An input could not be parsed.
#[displaydoc("{0}")]
Parse(core::num::ParseIntError),
/// An input overflowed its range.
#[allow(missing_docs)] // TODO(#686) - Add missing docs.
#[displaydoc("{field} must be between 0-{max}")]
Overflow { field: &'static str, max: usize },
#[allow(missing_docs)] // TODO(#686) - Add missing docs.
#[displaydoc("{field} must be between {min}-0")]
/// An input underflowed its range.
Underflow { field: &'static str, min: isize },
/// The time zone offset was invalid.
#[displaydoc("Failed to parse time-zone offset")]
InvalidTimeZoneOffset,
}
@@ -249,11 +260,18 @@ pub struct Month {
pub code: MonthCode,
}

/// A struct containing various details about the position of the day within a year. It is returned
// by the [`day_of_year_info()`](trait.DateInput.html#tymethod.day_of_year_info) method of the
// [`DateInput`] trait.
#[derive(Clone, Debug, PartialEq)]
pub struct DayOfYearInfo {
/// The current day of the year, 1-based.
pub day_of_year: u32,
/// The number of days in a year.
pub days_in_year: u32,
/// The previous year.
pub prev_year: Year,
/// The next year.
pub next_year: Year,
}

@@ -270,6 +288,7 @@ pub struct DayOfYearInfo {
/// assert_eq!(7, IsoWeekday::Sunday as usize);
/// ```
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[allow(missing_docs)] // The weekday variants should be self-obvious.
#[repr(i8)]
pub enum IsoWeekday {
Monday = 1,
@@ -319,11 +338,13 @@ pub struct WeekOfYear(pub u32);
/// unit is bounded by a range. The traits implemented here will return a Result on
/// whether or not the unit is in range from the given input.
macro_rules! dt_unit {
($name:ident, $value:expr) => {
($name:ident, $value:expr, $docs:expr) => {
#[doc=$docs]
#[derive(Debug, Default, Clone, Copy, PartialEq, Hash)]
pub struct $name(u8);

impl $name {
/// Do not validate the numeric input for this component.
pub const fn new_unchecked(input: u8) -> Self {
Self(input)
}
@@ -405,17 +426,34 @@ macro_rules! dt_unit {
};
}

dt_unit!(IsoHour, 24);
dt_unit!(
IsoHour,
24,
"An ISO-8601 hour component, for use with the [`IsoTimeInput`]."
);

dt_unit!(IsoMinute, 60);
dt_unit!(
IsoMinute,
60,
"An ISO-8601 minute component, for use with the [`IsoTimeInput`]."
);

dt_unit!(IsoSecond, 61);
dt_unit!(
IsoSecond,
61,
"An ISO-8601 second component, for use with the [`IsoTimeInput`]."
);

// TODO(#485): Improve FractionalSecond.
/// A placeholder for fractional seconds support. See [Issue #485](https://github.com/unicode-org/icu4x/issues/485)
/// for tracking the support of this feature.
#[derive(Clone, Debug, PartialEq)]
pub enum FractionalSecond {
/// The millisecond component of the fractional second.
Millisecond(u16),
/// The microsecond component of the fractional second.
Microsecond(u32),
/// The nanosecond component of the fractional second.
Nanosecond(u32),
}

@@ -424,6 +462,8 @@ pub enum FractionalSecond {
pub struct GmtOffset(i32);

impl GmtOffset {
/// Attempt to create a [`GmtOffset`] from a seconds input. It returns an error when the seconds
/// overflows or underflows.
pub fn try_new(seconds: i32) -> Result<Self, DateTimeError> {
// Valid range is from GMT-12 to GMT+14 in seconds.
if seconds < -(12 * 60 * 60) {
3 changes: 3 additions & 0 deletions components/datetime/src/datetime.rs
Original file line number Diff line number Diff line change
@@ -2,6 +2,9 @@
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

//! The collection of code that is needed for handling formatting operations for DateTimes.
//! Central to this is the [`DateTimeFormat`].
use crate::{
format::datetime,
options::DateTimeFormatOptions,
3 changes: 3 additions & 0 deletions components/datetime/src/format/zoned_datetime.rs
Original file line number Diff line number Diff line change
@@ -2,6 +2,8 @@
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

//! A collection of code for formatting DateTimes with time zones.
use crate::date::{LocalizedDateTimeInput, ZonedDateTimeInputWithLocale};
use crate::error::DateTimeFormatError as Error;
use crate::fields::{self, FieldSymbol};
@@ -13,6 +15,7 @@ use writeable::Writeable;
use super::datetime;
use super::time_zone;

#[allow(missing_docs)] // TODO(#686) - Add missing docs.
pub struct FormattedZonedDateTime<'l, 'data, T>
where
T: ZonedDateTimeInput,
4 changes: 4 additions & 0 deletions components/datetime/src/lib.rs
Original file line number Diff line number Diff line change
@@ -2,6 +2,8 @@
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

#![warn(missing_docs)]

//! `icu_datetime` is one of the [`ICU4X`] components.
//!
//! This API provides necessary functionality for formatting date and time to user readable textual representation.
@@ -88,7 +90,9 @@ pub mod provider;
#[doc(hidden)]
pub mod skeleton;
// TODO(#622) make the time_zone module public once TimeZoneFormat is public.
#[allow(missing_docs)] // TODO(#686) - Add missing docs.
pub(crate) mod time_zone;
#[allow(missing_docs)] // TODO(#686) - Add missing docs.
pub mod zoned_datetime;

pub use datetime::DateTimeFormat;
8 changes: 8 additions & 0 deletions components/datetime/src/mock/mod.rs
Original file line number Diff line number Diff line change
@@ -2,6 +2,14 @@
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

//! A collection of temporary structs and utilities to input data for tests, benchmarks,
//! and examples.
/// Temporary DateTime input utilities.
pub mod datetime;

/// Temporary time zone input utilities.
pub mod time_zone;

/// Temporary zoned DateTime input utilities.
pub mod zoned_datetime;
27 changes: 27 additions & 0 deletions components/datetime/src/options/components.rs
Original file line number Diff line number Diff line change
@@ -84,18 +84,28 @@ use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Bag {
/// Include the era, such as "AD" or "CE".
pub era: Option<Text>,
/// Include the year, such as "1970" or "70".
pub year: Option<Numeric>,
/// Include the month, such as "April" or "Apr".
pub month: Option<Month>,
/// Include the day, such as "07" or "7".
pub day: Option<Numeric>,
/// Include the weekday, such as "Wednesday" or "Wed".
pub weekday: Option<Text>,

/// Include the hour such as "2" or "14".
pub hour: Option<Numeric>,
/// Include the minute such as "3" or "03".
pub minute: Option<Numeric>,
/// Include the second such as "3" or "03".
pub second: Option<Numeric>,

/// Include the time zone, such as "GMT+05:00".
pub time_zone_name: Option<TimeZoneName>,

/// Adjust the preferences for the date, such as setting the hour cycle.
pub preferences: Option<preferences::Bag>,
}

@@ -303,46 +313,63 @@ impl Default for Bag {
}
}

/// A numeric component for the `components::`[`Bag`]. It is used for the year, day, hour, minute,
/// and second.
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Numeric {
/// Display the numeric value. For instance in a year this would be "1970".
#[cfg_attr(feature = "serde", serde(rename = "numeric"))]
Numeric,
/// Display the two digit value. For instance in a year this would be "70".
#[cfg_attr(feature = "serde", serde(rename = "two-digit"))]
TwoDigit,
}

/// A text component for the `components::`[`Bag`]. It is used for the era and weekday.
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Text {
/// Display the long form of the text, such as "Wednesday" for the weekday.
#[cfg_attr(feature = "serde", serde(rename = "long"))]
Long,
/// Display the short form of the text, such as "Wed" for the weekday.
#[cfg_attr(feature = "serde", serde(rename = "short"))]
Short,
/// Display the narrow form of the text, such as "W" for the weekday.
#[cfg_attr(feature = "serde", serde(rename = "narrow"))]
Narrow,
}

/// Options for displaying a Month for the `components::`[`Bag`].
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Month {
/// The numeric value of the month, such as "4".
#[cfg_attr(feature = "serde", serde(rename = "numeric"))]
Numeric,
/// The two-digit value of the month, such as "04".
#[cfg_attr(feature = "serde", serde(rename = "two-digit"))]
TwoDigit,
/// The two-digit value of the month, such as "April".
#[cfg_attr(feature = "serde", serde(rename = "long"))]
Long,
/// The short value of the month, such as "Apr".
#[cfg_attr(feature = "serde", serde(rename = "short"))]
Short,
/// The narrow value of the month, such as "A".
#[cfg_attr(feature = "serde", serde(rename = "narrow"))]
Narrow,
}

/// Options for displaying a time zone for the `components::`[`Bag`].
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum TimeZoneName {
#[allow(missing_docs)] // TODO(#686) - Add missing docs.
#[cfg_attr(feature = "serde", serde(rename = "long"))]
Long,
#[allow(missing_docs)] // TODO(#686) - Add missing docs.
#[cfg_attr(feature = "serde", serde(rename = "short"))]
Short,
}
3 changes: 3 additions & 0 deletions components/datetime/src/options/length.rs
Original file line number Diff line number Diff line change
@@ -80,8 +80,11 @@ use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Bag {
/// Configure the date part of the datetime.
pub date: Option<Date>,
/// Configure the time part of the datetime.
pub time: Option<Time>,
/// Configure the preferences for the datetime, such as the hour cycle.
pub preferences: Option<preferences::Bag>,
}

4 changes: 4 additions & 0 deletions components/datetime/src/options/preferences.rs
Original file line number Diff line number Diff line change
@@ -43,6 +43,9 @@ use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Bag {
/// The hour cycle can be adjusts according to user preferences, for instance at the OS-level.
/// That preference can be applied here to change the hour cycle from the default for the
/// given locale.
#[cfg_attr(feature = "serde", serde(rename = "hourCycle"))]
pub hour_cycle: Option<HourCycle>,
}
@@ -102,6 +105,7 @@ pub enum HourCycle {
}

impl HourCycle {
/// Convert the HourCycle preference to a field.
pub fn field(self) -> fields::Hour {
match self {
Self::H11 => fields::Hour::H11,
2 changes: 1 addition & 1 deletion components/datetime/src/pattern/error.rs
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ use crate::fields;
use displaydoc::Display;

/// These strings follow the recommendations for the serde::de::Unexpected::Other type.
/// https://docs.serde.rs/serde/de/enum.Unexpected.html#variant.Other
/// <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"
2 changes: 2 additions & 0 deletions components/datetime/src/provider/gregory.rs
Original file line number Diff line number Diff line change
@@ -2,6 +2,8 @@
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

#![allow(missing_docs)] // TODO(#686) - Add missing docs.

use crate::pattern;
use alloc::borrow::Cow;
use icu_provider::yoke::{self, *};
27 changes: 27 additions & 0 deletions components/datetime/src/provider/mod.rs
Original file line number Diff line number Diff line change
@@ -6,24 +6,51 @@
//!
//! Read more about data providers: [`icu_provider`]
#[cfg(doc)]
use icu_provider::prelude::ResourceKey;

/// Data providers for the Gregorian Calendar.
pub mod gregory;

pub(crate) mod helpers;

/// Data providers for time zones.
pub mod time_zones;

/// A collection of [`ResourceKey`] structs for DateTime providers.
pub mod key {
#[cfg(doc)]
use crate::provider::{gregory, time_zones};

use icu_provider::{resource_key, ResourceKey};

/// A [`ResourceKey`] to [`gregory::DatePatternsV1`].
pub const GREGORY_DATE_PATTERNS_V1: ResourceKey =
resource_key!(DateTime, "gregory_patterns", 1);

/// A [`ResourceKey`] to [`gregory::DateSymbolsV1`]
pub const GREGORY_DATE_SYMBOLS_V1: ResourceKey = resource_key!(DateTime, "gregory_symbols", 1);

/// A [`ResourceKey`] to [`time_zones::TimeZoneFormatsV1`].
pub const TIMEZONE_FORMATS_V1: ResourceKey = resource_key!(TimeZone, "formats", 1);

/// A [`ResourceKey`] to [`time_zones::ExemplarCitiesV1`].
pub const TIMEZONE_EXEMPLAR_CITIES_V1: ResourceKey =
resource_key!(TimeZone, "exemplar_cities", 1);

/// A [`ResourceKey`] to [`time_zones::MetaZoneGenericNamesLongV1`].
pub const TIMEZONE_GENERIC_NAMES_LONG_V1: ResourceKey =
resource_key!(TimeZone, "generic_long", 1);

/// A [`ResourceKey`] to [`time_zones::MetaZoneGenericNamesShortV1`].
pub const TIMEZONE_GENERIC_NAMES_SHORT_V1: ResourceKey =
resource_key!(TimeZone, "generic_short", 1);

/// A [`ResourceKey`] to [`time_zones::MetaZoneSpecificNamesLongV1`].
pub const TIMEZONE_SPECIFIC_NAMES_LONG_V1: ResourceKey =
resource_key!(TimeZone, "specific_long", 1);

/// A [`ResourceKey`] to [`time_zones::MetaZoneSpecificNamesShortV1`].
pub const TIMEZONE_SPECIFIC_NAMES_SHORT_V1: ResourceKey =
resource_key!(TimeZone, "specific_short", 1);
}
2 changes: 2 additions & 0 deletions components/datetime/src/provider/time_zones.rs
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@ use tinystr::TinyStr8;
macro_rules! map_access {
($outer: ty[$key: ty] => $inner: ty: $lt: lifetime) => {
impl<$lt> $outer {
/// Get the data from the key.
pub fn get<Q: ?Sized>(&self, key: &Q) -> Option<&$inner>
where
Q: Ord,
@@ -20,6 +21,7 @@ macro_rules! map_access {
self.0.get(key)
}

/// Check if the underlying data is empty.
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
2 changes: 1 addition & 1 deletion components/datetime/src/skeleton.rs
Original file line number Diff line number Diff line change
@@ -286,7 +286,7 @@ impl<'a> From<(&'a SkeletonV1, &'a PatternV1)> for AvailableFormatPattern<'a> {
}

/// These strings follow the recommendations for the serde::de::Unexpected::Other type.
/// https://docs.serde.rs/serde/de/enum.Unexpected.html#variant.Other
/// <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"

0 comments on commit 26b1be1

Please sign in to comment.