icu_datetime/pattern/
pattern.rs

1// This file is part of ICU4X. For terms of use, please see the file
2// called LICENSE at the top level of the ICU4X source tree
3// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4
5use core::str::FromStr;
6
7use writeable::{impl_display_with_writeable, Writeable};
8
9use crate::provider::pattern::{runtime, PatternError, PatternItem};
10use crate::size_test_macro::size_test;
11
12size_test!(DateTimePattern, date_time_pattern_size, 32);
13
14/// A pattern for formatting a datetime in a calendar.
15///
16/// ❗ This type forgoes most internationalization functionality of the datetime crate.
17/// It assumes that the pattern is already localized for the customer's locale. Most clients
18/// should use [`DateTimeFormatter`] instead of directly formatting with patterns.
19///
20/// There are two ways to make one of these:
21///
22/// 1. From a custom pattern string: [`DateTimePattern::try_from_pattern_str`]
23/// 2. From a formatted datetime: [`FormattedDateTime::pattern`]
24///
25/// Things you can do with one of these:
26///
27/// 1. Use it to directly format a datetime via [`FixedCalendarDateTimeNames`]
28/// 2. Convert it to a string pattern via [`Writeable`]
29/// 3. Get the resolved components
30#[doc = date_time_pattern_size!()]
31///
32/// # Examples
33///
34/// Create a pattern from a custom string and compare it to one from data,
35/// then check the resolved components:
36///
37/// ```
38/// use icu::calendar::Gregorian;
39/// use icu::datetime::fieldsets::YMD;
40/// use icu::datetime::input::Date;
41/// use icu::datetime::pattern::DateTimePattern;
42/// use icu::datetime::provider::fields::components;
43/// use icu::datetime::FixedCalendarDateTimeFormatter;
44/// use icu::locale::locale;
45/// use writeable::assert_writeable_eq;
46///
47/// // Create the pattern from a string:
48/// let pattern_str = "d MMM y";
49/// let custom_pattern =
50///     DateTimePattern::try_from_pattern_str(pattern_str).unwrap();
51/// assert_writeable_eq!(custom_pattern, pattern_str);
52///
53/// // Load data that resolves to the same pattern:
54/// let data_pattern = FixedCalendarDateTimeFormatter::<Gregorian, _>::try_new(
55///     locale!("es-MX").into(),
56///     YMD::medium(),
57/// )
58/// .unwrap()
59/// // The pattern can depend on the datetime being formatted.
60/// .format(&Date::try_new_gregorian(2024, 1, 1).unwrap())
61/// .pattern();
62/// assert_writeable_eq!(data_pattern, pattern_str);
63/// assert_eq!(custom_pattern, data_pattern);
64///
65/// // Check the resolved components:
66/// let mut expected_components_bag = components::Bag::default();
67/// expected_components_bag.year = Some(components::Year::Numeric);
68/// expected_components_bag.month = Some(components::Month::Short);
69/// expected_components_bag.day = Some(components::Day::NumericDayOfMonth);
70/// let actual_components_bag = components::Bag::from(&data_pattern);
71/// assert_eq!(actual_components_bag, expected_components_bag);
72/// ```
73///
74/// Check the hour cycle of a resolved pattern:
75///
76/// ```
77/// use icu::datetime::fieldsets::T;
78/// use icu::datetime::input::Time;
79/// use icu::datetime::pattern::DateTimePattern;
80/// use icu::datetime::provider::fields::components;
81/// use icu::datetime::NoCalendarFormatter;
82/// use icu::locale::locale;
83/// use icu::locale::preferences::extensions::unicode::keywords::HourCycle;
84/// use writeable::assert_writeable_eq;
85///
86/// let pattern =
87///     NoCalendarFormatter::try_new(locale!("es-MX").into(), T::medium())
88///         .unwrap()
89///         // The pattern can depend on the datetime being formatted.
90///         .format(&Time::try_new(12, 0, 0, 0).unwrap())
91///         .pattern();
92///
93/// assert_writeable_eq!(pattern, "hh:mm:ss a");
94///
95/// // Get the hour cycle from the resolved components:
96/// let components = components::Bag::from(&pattern);
97/// assert_eq!(components.hour_cycle, Some(HourCycle::H12));
98/// ```
99///
100/// [`DateTimeFormatter`]: crate::DateTimeFormatter
101/// [`FormattedDateTime::pattern`]: crate::FormattedDateTime::pattern
102/// [`FixedCalendarDateTimeNames`]: crate::pattern::FixedCalendarDateTimeNames
103#[derive(Debug)]
104pub struct DateTimePattern {
105    pattern: runtime::Pattern<'static>,
106}
107
108impl DateTimePattern {
109    /// Creates a [`DateTimePattern`] from a pattern string.
110    ///
111    /// For more details on the syntax, see UTS 35:
112    /// <https://unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns>
113    pub fn try_from_pattern_str(pattern_str: &str) -> Result<Self, PatternError> {
114        let pattern = runtime::Pattern::from_str(pattern_str)?;
115        Ok(Self { pattern })
116    }
117
118    pub(crate) fn iter_items(&self) -> impl Iterator<Item = PatternItem> + '_ {
119        self.pattern.items.iter()
120    }
121
122    pub(crate) fn as_borrowed(&self) -> DateTimePatternBorrowed {
123        DateTimePatternBorrowed(&self.pattern)
124    }
125}
126
127impl<'a> From<runtime::Pattern<'a>> for DateTimePattern {
128    fn from(pattern: runtime::Pattern<'a>) -> Self {
129        Self {
130            pattern: pattern.into_owned(),
131        }
132    }
133}
134
135impl<'a> From<runtime::PatternBorrowed<'a>> for DateTimePattern {
136    fn from(pattern: runtime::PatternBorrowed<'a>) -> Self {
137        Self {
138            pattern: pattern.as_pattern().into_owned(),
139        }
140    }
141}
142
143impl From<DateTimePattern> for runtime::Pattern<'_> {
144    fn from(value: DateTimePattern) -> Self {
145        value.pattern
146    }
147}
148
149impl FromStr for DateTimePattern {
150    type Err = PatternError;
151    fn from_str(s: &str) -> Result<Self, Self::Err> {
152        Self::try_from_pattern_str(s)
153    }
154}
155
156// Check equality on the borrowed variant since it flattens the difference
157// between the three `Single` pattern variants, which is not public-facing
158impl PartialEq for DateTimePattern {
159    fn eq(&self, other: &Self) -> bool {
160        self.as_borrowed().eq(&other.as_borrowed())
161    }
162}
163
164impl Eq for DateTimePattern {}
165
166impl Writeable for DateTimePattern {
167    fn write_to<W: core::fmt::Write + ?Sized>(&self, sink: &mut W) -> core::fmt::Result {
168        self.pattern.write_to(sink)
169    }
170}
171
172impl_display_with_writeable!(DateTimePattern);
173
174// Not clear if this needs to be public. For now, make it private.
175#[derive(Debug, Copy, Clone, PartialEq, Eq)]
176pub(crate) struct DateTimePatternBorrowed<'a>(pub(crate) &'a runtime::Pattern<'a>);