Skip to content

Commit

Permalink
Merge
Browse files Browse the repository at this point in the history
  • Loading branch information
robertbastian committed Feb 24, 2022
2 parents dd9516f + 00dd18c commit f19d849
Show file tree
Hide file tree
Showing 195 changed files with 2,068 additions and 281 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ The design goals of `ICU4X` are:
* Availability and ease of use in multiple programming languages
* Written by i18n experts to encourage best practices

***Stay informed!*** Join our public, low-traffic mailing list: [icu4x-announce@unicode.org](https://corp.unicode.org/mailman/listinfo/icu4x-announce). *Note: After subscribing, check your spam folder for a confirmation.*
***Stay informed!*** Join our public, low-traffic mailing list: [icu4x-announce@unicode.org](https://groups.google.com/u/1/a/unicode.org/g/icu4x-announce). *Note: After subscribing, check your spam folder for a confirmation.*

## Documentation

Expand Down
14 changes: 14 additions & 0 deletions components/calendar/src/arithmetic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ pub mod week_of {
pub const MIN_UNIT_DAYS: u16 = 14;

/// Information about how a given calendar assigns weeks to a year or month.
#[derive(Clone, Copy, Debug)]
#[cfg_attr(
feature = "provider_serde",
derive(serde::Serialize, serde::Deserialize)
)]
pub struct CalendarInfo {
/// The first day of a week.
pub first_weekday: IsoWeekday,
Expand All @@ -23,6 +28,15 @@ pub mod week_of {
}
}

impl Default for CalendarInfo {
fn default() -> Self {
Self {
first_weekday: IsoWeekday::Monday,
min_week_days: 1,
}
}
}

/// Returns the weekday that's `num_days` after `weekday`.
fn add_to_weekday(weekday: IsoWeekday, num_days: i32) -> IsoWeekday {
let new_weekday = (7 + (weekday as i32) + (num_days % 7)) % 7;
Expand Down
4 changes: 4 additions & 0 deletions components/calendar/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,10 @@ impl FromStr for GmtOffset {
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[allow(missing_docs)] // The weekday variants should be self-obvious.
#[repr(i8)]
#[cfg_attr(
feature = "provider_serde",
derive(serde::Serialize, serde::Deserialize)
)]
pub enum IsoWeekday {
Monday = 1,
Tuesday,
Expand Down
2 changes: 1 addition & 1 deletion components/datetime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ bench = false # This option is required for Benchmark CI
std = ["icu_provider/std", "icu_locid/std", "icu_calendar/std"]
default = ["provider_serde"]
bench = []
provider_serde = ["serde", "litemap/serde_serialize", "smallvec/serde", "litemap/serde", "zerovec/serde", "tinystr/serde", "icu_provider/serde"]
provider_serde = ["serde", "litemap/serde_serialize", "smallvec/serde", "litemap/serde", "zerovec/serde", "tinystr/serde", "icu_calendar/provider_serde", "icu_provider/serde"]
provider_transform_internals = ["std"]

[[bench]]
Expand Down
58 changes: 34 additions & 24 deletions components/datetime/src/date.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ pub trait LocalizedDateTimeInput<T: DateTimeInput> {

pub(crate) struct DateTimeInputWithLocale<'data, T: DateTimeInput> {
data: &'data T,
calendar: week_of::CalendarInfo,
calendar: Option<&'data week_of::CalendarInfo>,
}

fn compute_week_of_year<T: DateInput>(
Expand Down Expand Up @@ -164,33 +164,27 @@ fn week_of_year<T: DateInput>(
}

impl<'data, T: DateTimeInput> DateTimeInputWithLocale<'data, T> {
pub fn new(data: &'data T, _locale: &Locale) -> Self {
Self {
data,
// TODO(#488): Implement week calculations.
calendar: week_of::CalendarInfo {
first_weekday: IsoWeekday::Monday,
min_week_days: 4,
},
}
pub fn new(
data: &'data T,
calendar: Option<&'data week_of::CalendarInfo>,
_locale: &Locale,
) -> Self {
Self { data, calendar }
}
}

pub(crate) struct ZonedDateTimeInputWithLocale<'data, T: ZonedDateTimeInput> {
data: &'data T,
calendar: week_of::CalendarInfo,
calendar: Option<&'data week_of::CalendarInfo>,
}

impl<'data, T: ZonedDateTimeInput> ZonedDateTimeInputWithLocale<'data, T> {
pub fn new(data: &'data T, _locale: &Locale) -> Self {
Self {
data,
// TODO(#488): Implement week calculations.
calendar: week_of::CalendarInfo {
first_weekday: IsoWeekday::Monday,
min_week_days: 4,
},
}
pub fn new(
data: &'data T,
calendar: Option<&'data week_of::CalendarInfo>,
_locale: &Locale,
) -> Self {
Self { data, calendar }
}
}

Expand All @@ -200,15 +194,23 @@ impl<'data, T: DateTimeInput> LocalizedDateTimeInput<T> for DateTimeInputWithLoc
}

fn year_week(&self) -> Result<Year, DateTimeError> {
year_week(self.data, &self.calendar)
year_week(
self.data,
self.calendar
.expect("calendar must be provided when using week of methods"),
)
}

fn week_of_month(&self) -> WeekOfMonth {
todo!("#488")
}

fn week_of_year(&self) -> Result<WeekOfYear, DateTimeError> {
week_of_year(self.data, &self.calendar)
week_of_year(
self.data,
self.calendar
.expect("calendar must be provided when using week of methods"),
)
}

fn flexible_day_period(&self) {
Expand All @@ -224,15 +226,23 @@ impl<'data, T: ZonedDateTimeInput> LocalizedDateTimeInput<T>
}

fn year_week(&self) -> Result<Year, DateTimeError> {
year_week(self.data, &self.calendar)
year_week(
self.data,
self.calendar
.expect("calendar must be provided when using week of methods"),
)
}

fn week_of_month(&self) -> WeekOfMonth {
todo!("#488")
}

fn week_of_year(&self) -> Result<WeekOfYear, DateTimeError> {
week_of_year(self.data, &self.calendar)
week_of_year(
self.data,
self.calendar
.expect("calendar must be provided when using week of methods"),
)
}

fn flexible_day_period(&self) {
Expand Down
4 changes: 3 additions & 1 deletion components/datetime/src/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use crate::{
options::{components, DateTimeFormatOptions},
provider::calendar::{DatePatternsV1Marker, DateSkeletonPatternsV1Marker, DateSymbolsV1Marker},
provider::week_data::WeekDataV1Marker,
raw,
};
use alloc::string::String;
Expand Down Expand Up @@ -96,7 +97,8 @@ impl<C: CldrCalendar> DateTimeFormat<C> {
D: ResourceProvider<DateSymbolsV1Marker>
+ ResourceProvider<DatePatternsV1Marker>
+ ResourceProvider<DateSkeletonPatternsV1Marker>
+ ResourceProvider<OrdinalV1Marker>,
+ ResourceProvider<OrdinalV1Marker>
+ ResourceProvider<WeekDataV1Marker>,
{
Ok(Self(
raw::DateTimeFormat::try_new(locale, data_provider, options, C::IDENTIFIER)?,
Expand Down
102 changes: 66 additions & 36 deletions components/datetime/src/format/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::pattern::{
use crate::provider;
use crate::provider::calendar::patterns::PatternPluralsFromPatternsV1Marker;
use crate::provider::date_time::DateTimeSymbols;
use crate::provider::week_data::WeekDataV1;

use alloc::string::ToString;
use core::fmt;
Expand Down Expand Up @@ -55,6 +56,7 @@ where
pub(crate) patterns: &'l DataPayload<PatternPluralsFromPatternsV1Marker>,
pub(crate) symbols: Option<&'l provider::calendar::DateSymbolsV1<'l>>,
pub(crate) datetime: &'l T,
pub(crate) week_data: Option<&'l WeekDataV1>,
pub(crate) locale: &'l Locale,
pub(crate) ordinal_rules: Option<&'l PluralRules>,
}
Expand All @@ -68,6 +70,7 @@ where
&self.patterns.get().0,
self.symbols,
self.datetime,
self.week_data,
self.ordinal_rules,
self.locale,
sink,
Expand Down Expand Up @@ -130,6 +133,7 @@ pub fn write_pattern_plurals<T, W>(
patterns: &PatternPlurals,
symbols: Option<&provider::calendar::DateSymbolsV1>,
datetime: &T,
week_data: Option<&WeekDataV1>,
ordinal_rules: Option<&PluralRules>,
locale: &Locale,
w: &mut W,
Expand All @@ -138,7 +142,7 @@ where
T: DateTimeInput,
W: fmt::Write + ?Sized,
{
let loc_datetime = DateTimeInputWithLocale::new(datetime, locale);
let loc_datetime = DateTimeInputWithLocale::new(datetime, week_data.map(|d| &d.0), locale);
let pattern = patterns.select(&loc_datetime, ordinal_rules)?;
write_pattern(pattern, symbols, &loc_datetime, w)
}
Expand Down Expand Up @@ -298,58 +302,84 @@ where
Ok(())
}

// This function determins whether the struct will load symbols data.
// Keep it in sync with the `write_field` use of symbols.
pub fn analyze_pattern(pattern: &Pattern, supports_time_zones: bool) -> Result<bool, Field> {
let fields = pattern.items.iter().filter_map(|p| match p {
PatternItem::Field(field) => Some(field),
_ => None,
});
/// What data is required to format a given pattern.
#[derive(Default)]
pub struct RequiredData {
// DateSymbolsV1 is required.
pub symbols_data: bool,
// WeekDataV1 is required.
pub week_data: bool,
}

let mut requires_symbols = false;
impl RequiredData {
// Checks if formatting `pattern` would require us to load data & if so adds
// them to this struct. Returns true if requirements are saturated and would
// not change by any further calls.
// Keep it in sync with the `write_field` use of symbols.
fn add_requirements_from_pattern(
&mut self,
pattern: &Pattern,
supports_time_zones: bool,
) -> Result<bool, Field> {
let fields = pattern.items.iter().filter_map(|p| match p {
PatternItem::Field(field) => Some(field),
_ => None,
});

for field in fields {
if !requires_symbols {
requires_symbols = match field.symbol {
FieldSymbol::Era => true,
FieldSymbol::Month(_) => {
!matches!(field.length, FieldLength::One | FieldLength::TwoDigit)
for field in fields {
if !self.symbols_data {
self.symbols_data = match field.symbol {
FieldSymbol::Era => true,
FieldSymbol::Month(_) => {
!matches!(field.length, FieldLength::One | FieldLength::TwoDigit)
}
FieldSymbol::Weekday(_) | FieldSymbol::DayPeriod(_) => true,
_ => false,
}
FieldSymbol::Weekday(_) | FieldSymbol::DayPeriod(_) => true,
_ => false,
}
}
if !self.week_data {
self.week_data = matches!(
field.symbol,
FieldSymbol::Year(Year::WeekOf) | FieldSymbol::Week(_)
)
}

if supports_time_zones {
if requires_symbols {
// If we require time zones, and symbols, we know all
// we need to return already.
break;
if supports_time_zones {
if self.symbols_data && self.week_data {
// If we support time zones, and require everything else, we
// know all we need to return already.
return Ok(true);
}
} else if matches!(field.symbol, FieldSymbol::TimeZone(_)) {
// If we don't support time zones, and encountered a time zone
// field, error out.
return Err(field);
}
} else if matches!(field.symbol, FieldSymbol::TimeZone(_)) {
// If we don't support time zones, and encountered a time zone
// field, error out.
return Err(field);
}
}

Ok(requires_symbols)
Ok(false)
}
}

// This function determines whether any patterns will load symbols data.
// Determines what optional data needs to be loaded to format `patterns`.
pub fn analyze_patterns(
patterns: &PatternPlurals,
supports_time_zones: bool,
) -> Result<bool, Field> {
patterns.patterns_iter().try_fold(false, |a, pattern| {
analyze_pattern(pattern, supports_time_zones).map(|b| a || b)
})
) -> Result<RequiredData, Field> {
let mut required = RequiredData::default();
for pattern in patterns.patterns_iter() {
if required.add_requirements_from_pattern(pattern, supports_time_zones)? {
// We can bail early if everything is required & we don't need to
// validate the absence of TimeZones.
break;
}
}
Ok(required)
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
#[cfg(feature = "provider_serde")]
fn test_basic() {
Expand All @@ -373,7 +403,7 @@ mod tests {
let datetime =
DateTime::new_gregorian_datetime_from_integers(2020, 8, 1, 12, 34, 28).unwrap();
let mut sink = String::new();
let loc_datetime = DateTimeInputWithLocale::new(&datetime, &"und".parse().unwrap());
let loc_datetime = DateTimeInputWithLocale::new(&datetime, None, &"und".parse().unwrap());
write_pattern(&pattern, Some(data.get()), &loc_datetime, &mut sink).unwrap();
println!("{}", sink);
}
Expand Down
10 changes: 9 additions & 1 deletion components/datetime/src/format/zoned_datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,15 @@ where
{
let locale = &zoned_datetime_format.datetime_format.locale;
let patterns = &zoned_datetime_format.datetime_format.patterns;
let loc_datetime = ZonedDateTimeInputWithLocale::new(zoned_datetime, locale);
let loc_datetime = ZonedDateTimeInputWithLocale::new(
zoned_datetime,
zoned_datetime_format
.datetime_format
.week_data
.as_ref()
.map(|d| &d.get().0),
locale,
);

let pattern = patterns.get().0.select(
&loc_datetime,
Expand Down
3 changes: 3 additions & 0 deletions components/datetime/src/provider/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,8 @@ pub mod calendar;
/// Data providers for time zones.
pub mod time_zones;

/// Provider for week data.
pub mod week_data;

/// Traits for managing data needed by [`DateTimeFormat`](crate::DateTimeFormat).
pub(crate) mod date_time;
15 changes: 15 additions & 0 deletions components/datetime/src/provider/week_data.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// 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 icu_provider::prelude::*;

/// An ICU4X mapping to a subset of CLDR weekData.
/// See CLDR-JSON's weekData.json for more context.
#[icu_provider::data_struct(WeekDataV1Marker = "datetime/week_data@1")]
#[derive(Clone, Copy, Default)]
#[cfg_attr(
feature = "provider_serde",
derive(serde::Serialize, serde::Deserialize)
)]
pub struct WeekDataV1(pub icu_calendar::arithmetic::week_of::CalendarInfo);
Loading

0 comments on commit f19d849

Please sign in to comment.