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