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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
//! `mf2-fn-datetime` — the MessageFormat 2 date/time functions of mf2-two:
//! `:datetime`, `:date`, `:time`, and the handler that formats unannotated
//! date/time values. The semantics — operands, options, errors, time zones
//! — are here, once; a [`Backend`] only turns the result, a [`Plan`], into
//! text:
//!
//! | Feature | [`DefaultBackend`] (the statics') |
//! |---|---|
//! | none | [`Neutral`], a deterministic locale-independent stub (ISO 8601 pieces) |
//! | `datetime-icu` | `icu::Icu`: ICU4X over the catalog's `icu.blob` LOCALE entry, on client and server alike (narrower variants: `Icu<GregorianOnly, NoZones>` …) |
//! | `datetime-intl` | `Intl` on `wasm32-unknown-unknown`: `Host::format_date_time` (the browser's `Intl.DateTimeFormat` through `mf2-host-web`'s `INTL_HOST`); elsewhere `Icu` over ICU4X's compiled data |
//!
//! ```
//! use mf2_fn_datetime::{DateTimeFunction, Neutral};
//! use mf2_runtime::{Function, Registry};
//!
//! // `DATETIME`, `DATE`, `TIME`, `DATES` are these over the default backend.
//! static DATETIME: DateTimeFunction<Neutral> = DateTimeFunction::datetime(Neutral);
//! static DATES: DateTimeFunction<Neutral> = DateTimeFunction::unannotated(Neutral);
//! static FUNCTIONS: [(&str, &dyn Function); 1] = [("datetime", &DATETIME)];
//! static REGISTRY: Registry = Registry::new(&FUNCTIONS).with_dates(&DATES);
//! ```
//!
//! A message formatted with that registry: the `mf2` facade's front page,
//! which reaches this crate as `mf2::fn_datetime`.
//!
//! # The handlers
//!
//! | Static | Registry name | What |
//! |---|---|---|
//! | [`DATETIME`] | `datetime` | date and time; `dateFields` (`year-month-day`), `dateLength` (`medium`), `timePrecision` (`minute`), `timeZoneStyle` |
//! | [`DATE`] | `date` | the date; `fields` (`year-month-day`), `length` (`medium`) |
//! | [`TIME`] | `time` | the time; `precision` (`minute`), `timeZoneStyle` |
//! | [`DATES`] | — (`Registry::with_dates`) | an unannotated date/time, as `:datetime` with its defaults |
//!
//! All three functions take the override options `timeZone` and `calendar`;
//! `:datetime` and `:time` also `hour12`. Closed world: a registry
//! names only the handlers its corpus uses, and an unused one is never
//! linked. A handler over another backend: [`DateTimeFunction::datetime`]
//! and its siblings.
//!
//! # Semantics, and the choices the spec leaves open
//!
//! * **Operands.** A date/time value — an [`Arg::DateTime`], or what a
//! date/time function resolved, with its options — or an application
//! value's `CustomValue::as_date_time`, or a string that is a *date/time
//! literal value*: a literal, a string argument or an application value's
//! `as_str` ([`parse_literal`]). A literal must match the spec's regular
//! expression as a whole and name a day that exists; the spec's MAY —
//! other ISO 8601 forms — is not taken, so what parses is exactly the
//! regex (`-00:00` is offset 0). Without a time it is 00:00:00, without
//! an offset floating. Anything else (numbers, booleans, a fallback
//! value, no operand) is *Bad Operand* and a fallback value.
//! * **A `:date` value as a `:time` operand** (and the reverse, which the
//! spec says MAY be a *Bad Operand*): accepted. A resolved value keeps the
//! whole date/time of its operand, so `{$d :time}` over `$d = {|…T15:04:06|
//! :date}` shows 15:04; a date literal's time is 00:00.
//! * **Options.** Each function takes exactly the options datetime.md lists
//! for it; any other option is ignored (so `hour12` and `timeZoneStyle`
//! on `:date`). The non-override options must be literals: a variable is
//! *Bad Option* and the option is ignored. A value that is not one the
//! option takes is *Bad Option*, ignored. Values compare exactly
//! (case-sensitive). An override option may come from a variable whose
//! value is a string (or an application value with `as_str`); any other
//! value is *Bad Option*.
//! * **Override options.** `timeZone`: `input`, `UTC`, an RFC 3339
//! `time-numoffset` (`±hh:mm`, hour 00–23), or an RFC 9557
//! `time-zone-name` ([`mf2_runtime::is_zone_name`]; any well-formed name —
//! whether a zone of that name exists is the host's knowledge). `hour12`:
//! `true` / `false`. `calendar`: a well-formed `uvalue`, `3*8alphanum
//! *("-" 3*8alphanum)` in its BCP 47 spelling (UTS 35 also admits `_`,
//! which `Intl.DateTimeFormat` rejects); whether the calendar is known is
//! the backend's to report. They are inherited from a date/time operand
//! (an argument may carry them too) and the expression's own take
//! priority; the operand's other options are not inherited: over `.local
//! $d = {|2006-01-02| :date length=long}`, `{$d}` formats `$d` as it was
//! resolved (long), `{$d :date}` is a new `:date` (medium). A `:date`
//! value keeps an `hour12` it inherited, for a later `:time`.
//! * **Resolved value.** A [`Value::DateTime`] whose `options` hold what
//! resolved: `date` for `:datetime` and `:date`, `time` for `:datetime`
//! and `:time`, `time_zone_style`, `hour12`, `calendar`, and `time_zone`
//! — the zone the value is now in (`None`: the formatting context's). Not
//! selectable: a selector on it is *Bad Selector* (the runtime's). Part
//! kind `datetime`, direction `Ltr` for the neutral backend.
//! * **Time zones** (`zone`'s `place`). The default of `timeZone` is the
//! formatting context's zone ([`FnContext::time_zone`]). `input` is the
//! operand's own zone; on a floating operand it is *Bad Operand* and the
//! context's zone is used (and recorded, so a later expression inheriting
//! it does not report it again). A floating value takes the target zone
//! without conversion; a value with an offset or a zone is converted to
//! a different target: to UTC or an offset by arithmetic, to a named zone
//! through [`Host::zone_offset`]. A floating wall time placed in a named
//! zone gets its instant from a bracketing search over `zone_offset`
//! (java.time's / Temporal's `compatible` choice: in an overlap the
//! earlier instant, in a gap the wall time moves forward by the gap).
//! * **No zone data** (`Host::zone_offset` answers `None`, the default).
//! Converting a value with an offset to a named zone, or a value in a
//! named zone whose offset is unknown to any other zone, cannot be done:
//! *Bad Option* and a fallback value, the alternative the specification
//! allows —
//! not a wall time shown in the wrong zone, and not *Unsupported
//! Operation*, since the conversion itself is what the spec asks for. This
//! includes the formatting context's default zone, which is the
//! `timeZone` option's resolved value when the expression sets none: a
//! context in a named zone needs a host with zone data for instants.
//! Placing a *floating* value in a named
//! zone needs no conversion, so it is no error: the wall time shows, and
//! the zone's offset stays unknown (the neutral backend then names the
//! zone for `timeZoneStyle`). A converted value past `Date`'s year limit
//! is *Bad Operand*.
//! * **Unannotated** date/time values ([`DATES`], `Registry::with_dates`):
//! formatted as `:datetime` with no options (its own override options
//! apply); if that resolution reports an error, the placeholder is a
//! fallback value with that error.
//!
//! Client-path code: `no_std`, `forbid(unsafe_code)`, no `core::fmt` use,
//! no panicking operation, no allocation —
//! the semantics, the neutral and the `Intl` backends. The ICU4X backend
//! allocates (the blob's provider) and links ICU4X's own `core::fmt` and
//! panic paths: that is `datetime-icu`'s cost in client size.
//!
//!
//! # The user guide
//!
//! Getting started, call sites, delivery modes, switching language,
//! accessibility, migrating from `leptos-fluent`, and what 1.x promises
//! (`versioning.md`): the user guide is the `docs/` directory of the
//! mf2-two repository. An application reaches this crate through
//! [`mf2`](https://docs.rs/mf2), as `mf2::fn_datetime` (feature `fn-datetime`).
//!
//! [`Arg::DateTime`]: mf2_runtime::Arg::DateTime
//! [`Value::DateTime`]: mf2_runtime::Value::DateTime
//! [`FnContext::time_zone`]: mf2_runtime::FnContext::time_zone
//! [`Host::zone_offset`]: mf2_runtime::Host::zone_offset
// docs.rs (`cargo xtask docs-rs`): each feature-gated item says which features it needs.
extern crate alloc;
pub use ;
// The build's reading of a literal's options (`mf2-locale-data`'s `icu.blob`).
pub use literal_options;
pub use Intl;
pub use parse_literal;
pub use Neutral;
pub use ;
/// The backend the statics format with: [`icu::Icu`] with `datetime-icu`
/// (every calendar, zone styles, the catalog's `icu.blob`); with
/// `datetime-intl` alone, [`Intl`] in the browser (`wasm32-unknown-unknown`)
/// and `Icu` over compiled data elsewhere; [`Neutral`] with neither.
pub type DefaultBackend = Icu;
/// The backend the statics format with (see the `datetime-icu` build).
pub type DefaultBackend = Intl;
/// The backend the statics format with (see the `datetime-icu` build).
pub type DefaultBackend = Icu;
/// The backend the statics format with (see the `datetime-icu` build).
pub type DefaultBackend = Neutral;
const DEFAULT_BACKEND: DefaultBackend = NEW;
const DEFAULT_BACKEND: DefaultBackend = Intl;
const DEFAULT_BACKEND: DefaultBackend = Neutral;
/// `:datetime`.
pub static DATETIME: DateTimeFunction = datetime;
/// `:date`.
pub static DATE: DateTimeFunction = date;
/// `:time`.
pub static TIME: DateTimeFunction = time;
/// Unannotated date/time values, for `Registry::with_dates`: as
/// `:datetime` with its defaults.
pub static DATES: DateTimeFunction = unannotated;