Skip to content

Commit

Permalink
Make NoonMidnight dependent on granularity of pattern's time.
Browse files Browse the repository at this point in the history
- Adds capability for a pattern to compute its most granular time.
  - e.g. `h:mm:ss` is `Seconds`.
  - e.g. `h` is `Hours`.
  - e.g. `E, dd/MM/y` is `None`.
- Patterns containing `b` the `NoonMidnight` pattern item will now
  display noon or midnight only if the displayed time falls on the hour.
  - This means that `12:00:00` is always noon-compatible.
  - However, `12:05:15` is noon-compatible only if the display pattern
    does not contain the minutes or seconds.
  • Loading branch information
nordzilla committed Jan 25, 2021
1 parent aa2f30f commit 770936d
Show file tree
Hide file tree
Showing 4 changed files with 347 additions and 77 deletions.
18 changes: 16 additions & 2 deletions components/datetime/src/format/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/master/LICENSE ).

use crate::date::{self, DateTimeType};
use crate::error::DateTimeFormatError;
use crate::fields::{self, FieldLength, FieldSymbol};
use crate::pattern::{Pattern, PatternItem};
use crate::provider::DateTimeDates;
use crate::{
date::{self, DateTimeType},
pattern::TimeGranularity,
};
use icu_provider::structs;
use std::fmt;
use writeable::Writeable;
Expand Down Expand Up @@ -107,6 +110,17 @@ where
T: DateTimeType,
W: fmt::Write + ?Sized,
{
// A formatted time is only noon- or midnight-compatible if the
// most granular time being displayed lands exactly on the hour.
// e.g. 12:05:15 is noon-compatible if displaying only the hour,
// but not noon-compatible if displaying the minutes or seconds.
let noon_midnight_compatible = match pattern.most_granular_time() {
None | Some(TimeGranularity::Hours) => true,
Some(TimeGranularity::Minutes) => u8::from(date_time.minute()) == 0,
Some(TimeGranularity::Seconds) => {
u8::from(date_time.minute()) + u8::from(date_time.second()) == 0
}
};
for item in &pattern.0 {
match item {
PatternItem::Field(field) => match field.symbol {
Expand Down Expand Up @@ -160,7 +174,7 @@ where
period,
field.length,
date_time.hour(),
date_time.minute(),
noon_midnight_compatible,
);
w.write_str(symbol)?
}
Expand Down
31 changes: 31 additions & 0 deletions components/datetime/src/pattern/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,28 @@ pub enum PatternItem {
Literal(String),
}

/// The granularity of time represented by `PatternItem`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum TimeGranularity {
Hours,
Minutes,
Seconds,
}

impl PatternItem {
fn time_granularity(&self) -> Option<TimeGranularity> {
match self {
Self::Field(field) => match field.symbol {
fields::FieldSymbol::Hour(_) => Some(TimeGranularity::Hours),
fields::FieldSymbol::Minute => Some(TimeGranularity::Minutes),
fields::FieldSymbol::Second(_) => Some(TimeGranularity::Seconds),
_ => None,
},
_ => None,
}
}
}

impl From<(FieldSymbol, FieldLength)> for PatternItem {
fn from(input: (FieldSymbol, FieldLength)) -> Self {
Self::Field(Field {
Expand Down Expand Up @@ -62,6 +84,15 @@ impl Pattern {
.parse_placeholders(vec![time, date])
.map(Self)
}

/// Returns the maximum time granularity contained in the pattern,
/// or `None` if the pattern does not contain time.
/// e.g. The maximum granularity for `h:mm` is `Minutes`.
/// e.g. The maximum granularity for `h:mm:ss` is `Seconds`.
/// e.g. The maximum granularity for `E, dd/MM/y` is `None`.
pub fn most_granular_time(&self) -> Option<TimeGranularity> {
self.0.iter().flat_map(PatternItem::time_granularity).max()
}
}

impl FromIterator<PatternItem> for Pattern {
Expand Down
10 changes: 5 additions & 5 deletions components/datetime/src/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ pub trait DateTimeDates {
day_period: fields::DayPeriod,
length: fields::FieldLength,
hour: date::Hour,
minute: date::Minute,
noon_midnight_compatible: bool,
) -> &Cow<str>;
}

Expand Down Expand Up @@ -181,7 +181,7 @@ impl DateTimeDates for structs::dates::gregory::DatesV1 {
day_period: fields::DayPeriod,
length: fields::FieldLength,
hour: date::Hour,
minute: date::Minute,
noon_midnight_compatible: bool,
) -> &Cow<str> {
use fields::{DayPeriod::NoonMidnight, FieldLength};
let widths = &self.symbols.day_periods.format;
Expand All @@ -190,9 +190,9 @@ impl DateTimeDates for structs::dates::gregory::DatesV1 {
FieldLength::Narrow => &widths.narrow,
_ => &widths.abbreviated,
};
match (day_period, u8::from(hour), u8::from(minute)) {
(NoonMidnight, 0, 0) => symbols.midnight.as_ref().unwrap_or(&symbols.am),
(NoonMidnight, 12, 0) => symbols.noon.as_ref().unwrap_or(&symbols.pm),
match (day_period, u8::from(hour), noon_midnight_compatible) {
(NoonMidnight, 00, true) => symbols.midnight.as_ref().unwrap_or(&symbols.am),
(NoonMidnight, 12, true) => symbols.noon.as_ref().unwrap_or(&symbols.pm),
(_, hour, _) => {
if hour < 12 {
&symbols.am
Expand Down
Loading

0 comments on commit 770936d

Please sign in to comment.