icu_datetime/pattern/mod.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
5//! Lower-level, power-user APIs for formatting datetimes with pattern strings.
6//!
7//! ❗ This module forgoes most internationalization functionality of the datetime crate.
8//! It assumes that the pattern is already localized for the customer's locale. Most clients
9//! should use [`DateTimeFormatter`] instead of directly formatting with patterns.
10//!
11//! [`DateTimeFormatter`]: crate::DateTimeFormatter
12
13mod formatter;
14mod names;
15#[allow(clippy::module_inception)] // the file pattern.rs should contain DateTimePattern
16mod pattern;
17
18pub use crate::error::ErrorField;
19use crate::unchecked::MissingInputFieldKind;
20pub use formatter::DateTimePatternFormatter;
21pub use formatter::FormattedDateTimePattern;
22use icu_calendar::types::MonthCode;
23use icu_calendar::AnyCalendarKind;
24use icu_pattern::SinglePlaceholderPattern;
25pub use names::DateTimeNames;
26pub(crate) use names::DateTimeNamesMetadata;
27pub use names::DayPeriodNameLength;
28pub use names::FixedCalendarDateTimeNames;
29pub use names::MonthNameLength;
30pub(crate) use names::RawDateTimeNames;
31pub(crate) use names::RawDateTimeNamesBorrowed;
32pub(crate) use names::TimeZoneDataPayloadsBorrowed;
33pub use names::WeekdayNameLength;
34pub use names::YearNameLength;
35pub use pattern::DateTimePattern;
36use tinystr::TinyStr16;
37
38#[cfg(doc)]
39use icu_calendar::types::CyclicYear;
40#[cfg(doc)]
41use icu_decimal::DecimalFormatter;
42#[cfg(doc)]
43use writeable::TryWriteable;
44
45pub(crate) enum GetNameForMonthError {
46 InvalidMonthCode,
47 InvalidFieldLength,
48 NotLoaded,
49}
50pub(crate) enum GetNameForWeekdayError {
51 InvalidFieldLength,
52 NotLoaded,
53}
54
55pub(crate) enum GetNameForEraError {
56 InvalidEraCode,
57 InvalidFieldLength,
58 NotLoaded,
59}
60
61pub(crate) enum GetNameForCyclicYearError {
62 InvalidYearNumber { max: u8 },
63 InvalidFieldLength,
64 NotLoaded,
65}
66
67pub(crate) enum GetNameForDayPeriodError {
68 InvalidFieldLength,
69 NotLoaded,
70}
71
72/// Internal enum to represent the kinds of month symbols for interpolation
73pub(crate) enum MonthPlaceholderValue<'a> {
74 PlainString(&'a str),
75 Numeric,
76 NumericPattern(&'a SinglePlaceholderPattern),
77}
78
79/// Error returned from [`FixedCalendarDateTimeNames`]'s pattern load methods.
80#[derive(Debug, Clone, Copy, PartialEq, displaydoc::Display)]
81#[non_exhaustive]
82pub enum PatternLoadError {
83 /// A field conflicts with a previous field.
84 ///
85 /// Fields conflict if they require the same type of data, for example the
86 /// `EEE` and `EEEE` fields (short vs long weekday) conflict, or the `M`
87 /// and `L` (format vs standalone month) conflict.
88 #[displaydoc("A field {field:?} conflicts with a previously loaded field {previous_field:?}.")]
89 ConflictingField {
90 /// The field that was not able to be loaded.
91 field: ErrorField,
92 /// The field that prevented the new field from being loaded.
93 previous_field: ErrorField,
94 },
95 /// The field symbol is not supported in that length.
96 ///
97 /// Some fields, such as `O` are not defined for all lengths (e.g. `OO`).
98 #[displaydoc("The field {0:?} symbol is not supported in that length.")]
99 UnsupportedLength(ErrorField),
100 /// The specific formatter does not support this field.
101 ///
102 /// This happens for example when trying to load a month field
103 /// on a [`FixedCalendarDateTimeNames<Gregorian, ZoneFieldSet>`].
104 #[displaydoc("The specific formatter does not support the field {0:?}.")]
105 FormatterTooSpecific(ErrorField),
106 /// An error arising from the [`data provider`](icu_provider) for loading names.
107 #[displaydoc("Problem loading data for field {1:?}: {0}")]
108 Data(icu_provider::DataError, ErrorField),
109}
110
111impl core::error::Error for PatternLoadError {}
112
113/// Error returned from constructors that map from AnyCalendar to a formatter.
114#[derive(Debug, Clone, Copy, PartialEq, displaydoc::Display)]
115#[displaydoc("The calendar {kind:?} is not supported in DateTimeFormatter")]
116#[non_exhaustive]
117pub struct UnsupportedCalendarError {
118 /// The calendar kind that is not supported.
119 pub kind: AnyCalendarKind,
120}
121
122impl core::error::Error for UnsupportedCalendarError {}
123
124#[non_exhaustive]
125#[derive(Debug, PartialEq, Copy, Clone, displaydoc::Display)]
126/// Error for the [`TryWriteable`] implementation of [`FormattedDateTimePattern`].
127///
128/// There are 3 general conditions for these errors to occur:
129///
130/// 1. Invariants of **unchecked functions** are not upheld
131/// 2. Invariants of **locale data** are not upheld
132/// 3. Invariants of **trait impls** are not upheld (including [scaffolding traits])
133///
134/// It is not always possible to distinguish the source of the errors. Each variant is documented
135/// with rules of thumb for when they might occur.
136///
137/// [unstable scaffolding traits]: crate::scaffold
138pub enum FormattedDateTimePatternError {
139 /// The [`MonthCode`] of the input is not valid for this calendar.
140 ///
141 /// Error conditions:
142 ///
143 /// - **Locale data:** for example, datetime names don't match the formatter's calendar
144 /// - **Trait impls:** for example, the date returns fields for the wrong calendar
145 ///
146 /// The output will contain the raw [`MonthCode`] as a fallback value.
147 #[displaydoc("Invalid month {0:?}")]
148 InvalidMonthCode(MonthCode),
149 /// The era code of the input is not valid for this calendar.
150 ///
151 /// Same error conditions as [`FormattedDateTimePatternError::InvalidMonthCode`].
152 ///
153 /// The output will contain the era code as the fallback.
154 #[displaydoc("Invalid era {0:?}")]
155 InvalidEra(TinyStr16),
156 /// The [`CyclicYear::year`] of the input is not valid for this calendar.
157 ///
158 /// Same error conditions as [`FormattedDateTimePatternError::InvalidMonthCode`].
159 ///
160 /// The output will contain [`CyclicYear::related_iso`] as a fallback value.
161 #[displaydoc("Invalid cyclic year {value} (maximum {max})")]
162 InvalidCyclicYear {
163 /// Value
164 value: u8,
165 /// Max
166 max: u8,
167 },
168
169 /// The localized names for a field have not been loaded.
170 ///
171 /// Error conditions:
172 ///
173 /// - **Unchecked functions:** for example, the pattern in [`with_pattern_unchecked`] contains fields that haven't been loaded
174 /// - **Trait impls:** for example, a custom field set does not include the correct names data
175 ///
176 /// The output will contain fallback values using field identifiers (such as `tue` for `Weekday::Tuesday`,
177 /// `M02` for month 2, etc.).
178 ///
179 /// [`with_pattern_unchecked`]: FixedCalendarDateTimeNames::with_pattern_unchecked
180 #[displaydoc("Names for {0:?} not loaded")]
181 NamesNotLoaded(ErrorField),
182 /// The [`DecimalFormatter`] has not been loaded.
183 ///
184 /// Same error conditions as [`FormattedDateTimePatternError::NamesNotLoaded`].
185 ///
186 /// The output will contain fallback values using Latin numerals.
187 #[displaydoc("DecimalFormatter not loaded")]
188 DecimalFormatterNotLoaded,
189
190 /// An input field (such as "hour" or "month") is missing.
191 ///
192 /// Error conditions:
193 ///
194 /// - **Unchecked functions:** for example, the pattern in [`with_pattern_unchecked`] contains fields that aren't in the fieldset
195 /// - **Trait impls:** for example, a custom field set does not require the correct fields
196 ///
197 /// The output will contain the string `{X}` instead, where `X` is the symbol for which the input is missing.
198 ///
199 /// [`with_pattern_unchecked`]: FixedCalendarDateTimeNames::with_pattern_unchecked
200 #[displaydoc("Incomplete input, missing value for {0:?}")]
201 MissingInputField(MissingInputFieldKind),
202
203 /// The pattern contains a field symbol for which formatting is unsupported.
204 ///
205 /// Error conditions:
206 ///
207 /// - **Unchecked functions:** for example, the pattern in [`with_pattern_unchecked`] contains an unsupported field
208 ///
209 /// The output will contain the string `{unsupported:X}`, where `X` is the symbol of the unsupported field.
210 ///
211 /// [`with_pattern_unchecked`]: FixedCalendarDateTimeNames::with_pattern_unchecked
212 #[displaydoc("Unsupported field {0:?}")]
213 UnsupportedField(ErrorField),
214 /// The pattern contains a field that has a valid symbol but invalid length.
215 ///
216 /// Same error conditions as [`FormattedDateTimePatternError::UnsupportedField`].
217 ///
218 /// The output will contain fallback values similar to [`FormattedDateTimePatternError::NamesNotLoaded`].
219 #[displaydoc("Field length for {0:?} is invalid")]
220 UnsupportedLength(ErrorField),
221}