• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

zbraniecki / icu4x / 8219362155

08 Mar 2024 01:21PM UTC coverage: 75.985% (+3.0%) from 73.009%
8219362155

push

github

web-flow
Bump diplomat (#4671)

And fix some Dart renames

49581 of 65251 relevant lines covered (75.99%)

519628.46 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.43
/components/datetime/src/input.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
//! A collection of utilities for representing and working with dates as an input to
6
//! formatting operations.
7

8
use crate::provider::time_zones::{MetazoneId, TimeZoneBcp47Id};
9
use icu_calendar::any_calendar::AnyCalendarKind;
10
use icu_calendar::week::{RelativeUnit, WeekCalculator};
11
use icu_calendar::Calendar;
12
use icu_calendar::{AsCalendar, Date, DateTime, Iso};
13
use icu_timezone::{CustomTimeZone, GmtOffset, ZoneVariant};
14

15
// TODO(#2630) fix up imports to directly import from icu_calendar
16
pub(crate) use icu_calendar::types::{
17
    DayOfMonth, DayOfWeekInMonth, DayOfYearInfo, FormattableMonth, FormattableYear, IsoHour,
18
    IsoMinute, IsoSecond, IsoWeekday, NanoSecond, Time, WeekOfMonth, WeekOfYear,
19
};
20
pub(crate) use icu_calendar::CalendarError;
21

22
/// Representation of a formattable calendar date. Supports dates in any calendar system that uses
23
/// solar days indexed by an era, year, month, and day.
24
///
25
/// All fields are optional. If a field is not present but is required when formatting, an error
26
/// result will be returned from the formatter.
27
///
28
/// All data represented in [`DateInput`] should be locale-agnostic.
29
pub trait DateInput {
30
    /// The calendar this date relates to
31
    type Calendar: Calendar;
32
    /// Gets the era and year input.
33
    fn year(&self) -> Option<FormattableYear>;
34

35
    /// Gets the month input.
36
    fn month(&self) -> Option<FormattableMonth>;
37

38
    /// Gets the day input.
39
    fn day_of_month(&self) -> Option<DayOfMonth>;
40

41
    /// Gets the weekday input.
42
    fn iso_weekday(&self) -> Option<IsoWeekday>;
43

44
    /// Gets information on the position of the day within the year.
45
    fn day_of_year_info(&self) -> Option<DayOfYearInfo>;
46

47
    /// Gets the kind of calendar this date is for, if associated with [`AnyCalendar`]
48
    /// In most cases you'll probably want to return [`AnyCalendarKind::Iso`].
49
    ///
50
    /// [`AnyCalendar`]: icu_calendar::any_calendar::AnyCalendar
51
    fn any_calendar_kind(&self) -> Option<AnyCalendarKind>;
52

53
    /// Converts date to ISO
54
    fn to_iso(&self) -> Date<Iso>;
55
}
56

57
/// Representation of a time of day according to ISO-8601 conventions. Always indexed from
58
/// midnight, regardless of calendar system.
59
///
60
/// All fields are optional. If a field is not present but is required when formatting, an error
61
/// result will be returned from the formatter.
62
///
63
/// All data represented in [`IsoTimeInput`] should be locale-agnostic.
64
pub trait IsoTimeInput {
65
    /// Gets the hour input.
66
    fn hour(&self) -> Option<IsoHour>;
67

68
    /// Gets the minute input.
69
    fn minute(&self) -> Option<IsoMinute>;
70

71
    /// Gets the second input.
72
    fn second(&self) -> Option<IsoSecond>;
73

74
    /// Gets the nanosecond input.
75
    fn nanosecond(&self) -> Option<NanoSecond>;
76
}
77

78
/// Representation of a formattable time zone.
79
///
80
/// Only the [`GmtOffset`] is required, since it is the final format fallback.
81
///
82
/// All data represented in [`TimeZoneInput`] should be locale-agnostic.
83
pub trait TimeZoneInput {
84
    /// The GMT offset in Nanoseconds.
85
    fn gmt_offset(&self) -> Option<GmtOffset>;
86

87
    /// The IANA time-zone identifier.
88
    fn time_zone_id(&self) -> Option<TimeZoneBcp47Id>;
89

90
    /// The metazone identifier.
91
    fn metazone_id(&self) -> Option<MetazoneId>;
92

93
    /// The time variant (e.g. "daylight", "standard")
94
    fn zone_variant(&self) -> Option<ZoneVariant>;
95
}
96

97
/// A combination of a formattable calendar date and ISO time.
98
pub trait DateTimeInput: DateInput + IsoTimeInput {}
99

100
impl<T> DateTimeInput for T where T: DateInput + IsoTimeInput {}
101

102
/// A formattable calendar date and ISO time that takes the locale into account.
103
pub trait LocalizedDateTimeInput<T: DateTimeInput> {
104
    /// A reference to this instance's [`DateTimeInput`].
105
    fn datetime(&self) -> &T;
106

107
    /// The week of the month.
108
    ///
109
    /// For example, January 1, 2021 is part of the first week of January.
110
    fn week_of_month(&self) -> Result<WeekOfMonth, CalendarError>;
111

112
    /// The week number of the year and the corresponding year.
113
    ///
114
    /// For example, December 31, 2020 is part of the first week of 2021.
115
    fn week_of_year(&self) -> Result<(FormattableYear, WeekOfYear), CalendarError>;
116

117
    /// The day of week in this month.
118
    ///
119
    /// For example, July 8, 2020 is the 2nd Wednesday of July.
120
    fn day_of_week_in_month(&self) -> Result<DayOfWeekInMonth, CalendarError>;
121

122
    /// TODO(#487): Implement flexible day periods.
123
    fn flexible_day_period(&self);
124
}
125

126
pub(crate) struct DateTimeInputWithWeekConfig<'data, T: DateTimeInput> {
127
    data: &'data T,
128
    calendar: Option<&'data WeekCalculator>,
129
}
130

131
/// A [`DateTimeInput`] type with all of the fields pre-extracted
132
///
133
/// See [`DateTimeInput`] for documentation on individual fields
134
#[derive(Default, Debug, Copy, Clone)]
4✔
135
pub(crate) struct ExtractedDateTimeInput {
136
    year: Option<FormattableYear>,
2✔
137
    month: Option<FormattableMonth>,
2✔
138
    day_of_month: Option<DayOfMonth>,
2✔
139
    iso_weekday: Option<IsoWeekday>,
2✔
140
    day_of_year_info: Option<DayOfYearInfo>,
2✔
141
    any_calendar_kind: Option<AnyCalendarKind>,
2✔
142
    hour: Option<IsoHour>,
2✔
143
    minute: Option<IsoMinute>,
2✔
144
    second: Option<IsoSecond>,
2✔
145
    nanosecond: Option<NanoSecond>,
2✔
146
}
147

148
/// A [`TimeZoneInput`] type with all of the fields pre-extracted
149
///
150
/// See [`TimeZoneInput`] for documentation on individual fields
151
#[derive(Debug, Copy, Clone)]
×
152
pub(crate) struct ExtractedTimeZoneInput {
153
    gmt_offset: Option<GmtOffset>,
×
154
    time_zone_id: Option<TimeZoneBcp47Id>,
×
155
    metazone_id: Option<MetazoneId>,
×
156
    zone_variant: Option<ZoneVariant>,
×
157
}
158

159
impl ExtractedDateTimeInput {
160
    /// Construct given an instance of a [`DateTimeInput`].
161
    pub(crate) fn extract_from<T: DateTimeInput>(input: &T) -> Self {
46✔
162
        Self {
46✔
163
            year: input.year(),
46✔
164
            month: input.month(),
46✔
165
            day_of_month: input.day_of_month(),
46✔
166
            iso_weekday: input.iso_weekday(),
46✔
167
            day_of_year_info: input.day_of_year_info(),
46✔
168
            any_calendar_kind: input.any_calendar_kind(),
46✔
169
            hour: input.hour(),
46✔
170
            minute: input.minute(),
46✔
171
            second: input.second(),
46✔
172
            nanosecond: input.nanosecond(),
46✔
173
        }
174
    }
46✔
175
    /// Construct given an instance of a [`DateTimeInput`].
176
    pub(crate) fn extract_from_date<T: DateInput>(input: &T) -> Self {
2✔
177
        Self {
2✔
178
            year: input.year(),
2✔
179
            month: input.month(),
2✔
180
            day_of_month: input.day_of_month(),
2✔
181
            iso_weekday: input.iso_weekday(),
2✔
182
            day_of_year_info: input.day_of_year_info(),
2✔
183
            any_calendar_kind: input.any_calendar_kind(),
2✔
184
            ..Default::default()
2✔
185
        }
186
    }
2✔
187
    /// Construct given an instance of a [`DateTimeInput`].
188
    pub(crate) fn extract_from_time<T: IsoTimeInput>(input: &T) -> Self {
×
189
        Self {
×
190
            hour: input.hour(),
×
191
            minute: input.minute(),
×
192
            second: input.second(),
×
193
            nanosecond: input.nanosecond(),
×
194
            ..Default::default()
×
195
        }
196
    }
×
197
}
198

199
impl ExtractedTimeZoneInput {
200
    /// Construct given an instance of a [`ZonedDateTimeInput`].
201
    pub(crate) fn extract_from<T: TimeZoneInput>(input: &T) -> Self {
1✔
202
        Self {
1✔
203
            gmt_offset: input.gmt_offset(),
1✔
204
            time_zone_id: input.time_zone_id(),
1✔
205
            metazone_id: input.metazone_id(),
1✔
206
            zone_variant: input.zone_variant(),
1✔
207
        }
208
    }
1✔
209
}
210

211
impl DateInput for ExtractedDateTimeInput {
212
    /// This actually doesn't matter, by the time we use this
213
    /// it's purely internal raw code where calendars are irrelevant
214
    type Calendar = icu_calendar::any_calendar::AnyCalendar;
215
    fn year(&self) -> Option<FormattableYear> {
2,503✔
216
        self.year
2,503✔
217
    }
2,503✔
218
    fn month(&self) -> Option<FormattableMonth> {
1,442✔
219
        self.month
1,442✔
220
    }
1,442✔
221
    fn day_of_month(&self) -> Option<DayOfMonth> {
1,393✔
222
        self.day_of_month
1,393✔
223
    }
1,393✔
224
    fn iso_weekday(&self) -> Option<IsoWeekday> {
915✔
225
        self.iso_weekday
915✔
226
    }
915✔
227
    fn day_of_year_info(&self) -> Option<DayOfYearInfo> {
239✔
228
        self.day_of_year_info
239✔
229
    }
239✔
230
    fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
86✔
231
        self.any_calendar_kind
86✔
232
    }
86✔
233
    fn to_iso(&self) -> Date<Iso> {
×
234
        unreachable!("ExtractedDateTimeInput should never be directly passed to DateTimeFormatter")
×
235
    }
236
}
237

238
impl IsoTimeInput for ExtractedDateTimeInput {
239
    fn hour(&self) -> Option<IsoHour> {
2,245✔
240
        self.hour
2,245✔
241
    }
2,245✔
242
    fn minute(&self) -> Option<IsoMinute> {
2,169✔
243
        self.minute
2,169✔
244
    }
2,169✔
245
    fn second(&self) -> Option<IsoSecond> {
1,753✔
246
        self.second
1,753✔
247
    }
1,753✔
248
    fn nanosecond(&self) -> Option<NanoSecond> {
973✔
249
        self.nanosecond
973✔
250
    }
973✔
251
}
252

253
impl TimeZoneInput for ExtractedTimeZoneInput {
254
    fn gmt_offset(&self) -> Option<GmtOffset> {
958✔
255
        self.gmt_offset
958✔
256
    }
958✔
257
    fn time_zone_id(&self) -> Option<TimeZoneBcp47Id> {
163✔
258
        self.time_zone_id
163✔
259
    }
163✔
260
    fn metazone_id(&self) -> Option<MetazoneId> {
139✔
261
        self.metazone_id
139✔
262
    }
139✔
263
    fn zone_variant(&self) -> Option<ZoneVariant> {
123✔
264
        self.zone_variant
123✔
265
    }
123✔
266
}
267

268
impl<'data, T: DateTimeInput> DateTimeInputWithWeekConfig<'data, T> {
269
    pub(crate) fn new(data: &'data T, calendar: Option<&'data WeekCalculator>) -> Self {
2,959✔
270
        Self { data, calendar }
2,959✔
271
    }
2,959✔
272
}
273

274
impl<'data, T: DateTimeInput> LocalizedDateTimeInput<T> for DateTimeInputWithWeekConfig<'data, T> {
275
    fn datetime(&self) -> &T {
12,842✔
276
        self.data
12,842✔
277
    }
12,842✔
278

279
    fn week_of_month(&self) -> Result<WeekOfMonth, CalendarError> {
18✔
280
        let config = self.calendar.ok_or(CalendarError::MissingCalendar)?;
18✔
281
        let day_of_month = self
36✔
282
            .data
283
            .day_of_month()
284
            .ok_or(CalendarError::MissingInput("DateTimeInput::day_of_month"))?;
18✔
285
        let iso_weekday = self
36✔
286
            .data
287
            .iso_weekday()
288
            .ok_or(CalendarError::MissingInput("DateTimeInput::iso_weekday"))?;
18✔
289
        Ok(config.week_of_month(day_of_month, iso_weekday))
18✔
290
    }
18✔
291

292
    fn week_of_year(&self) -> Result<(FormattableYear, WeekOfYear), CalendarError> {
234✔
293
        let config = self.calendar.ok_or(CalendarError::MissingCalendar)?;
234✔
294
        let day_of_year_info = self
468✔
295
            .data
296
            .day_of_year_info()
297
            .ok_or(CalendarError::MissingInput(
234✔
298
                "DateTimeInput::day_of_year_info",
299
            ))?;
×
300
        let iso_weekday = self
468✔
301
            .data
302
            .iso_weekday()
303
            .ok_or(CalendarError::MissingInput("DateTimeInput::iso_weekday"))?;
234✔
304
        let week_of = config.week_of_year(day_of_year_info, iso_weekday)?;
234✔
305
        let year = match week_of.unit {
234✔
306
            RelativeUnit::Previous => day_of_year_info.prev_year,
×
307
            RelativeUnit::Current => self
468✔
308
                .data
309
                .year()
310
                .ok_or(CalendarError::MissingInput("DateTimeInput::year"))?,
234✔
311
            RelativeUnit::Next => day_of_year_info.next_year,
×
312
        };
313
        Ok((year, WeekOfYear(week_of.week as u32)))
234✔
314
    }
234✔
315

316
    fn day_of_week_in_month(&self) -> Result<DayOfWeekInMonth, CalendarError> {
9✔
317
        let day_of_month = self
18✔
318
            .data
319
            .day_of_month()
320
            .ok_or(CalendarError::MissingInput("DateTimeInput::day_of_month"))?;
9✔
321
        Ok(day_of_month.into())
9✔
322
    }
9✔
323

324
    fn flexible_day_period(&self) {
325
        todo!("#487")
326
    }
327
}
328

329
impl<C: Calendar, A: AsCalendar<Calendar = C>> DateInput for Date<A> {
330
    type Calendar = C;
331
    /// Gets the era and year input.
332
    fn year(&self) -> Option<FormattableYear> {
2✔
333
        Some(self.year())
2✔
334
    }
2✔
335

336
    /// Gets the month input.
337
    fn month(&self) -> Option<FormattableMonth> {
2✔
338
        Some(self.month())
2✔
339
    }
2✔
340

341
    /// Gets the day input.
342
    fn day_of_month(&self) -> Option<DayOfMonth> {
2✔
343
        Some(self.day_of_month())
2✔
344
    }
2✔
345

346
    /// Gets the weekday input.
347
    fn iso_weekday(&self) -> Option<IsoWeekday> {
2✔
348
        Some(self.day_of_week())
2✔
349
    }
2✔
350

351
    /// Gets information on the position of the day within the year.
352
    fn day_of_year_info(&self) -> Option<DayOfYearInfo> {
2✔
353
        Some(self.day_of_year_info())
2✔
354
    }
2✔
355

356
    fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
4✔
357
        self.calendar().any_calendar_kind()
4✔
358
    }
4✔
359

360
    fn to_iso(&self) -> Date<Iso> {
1✔
361
        Date::to_iso(self)
1✔
362
    }
1✔
363
}
364

365
impl<C: Calendar, A: AsCalendar<Calendar = C>> DateInput for DateTime<A> {
366
    type Calendar = C;
367
    /// Gets the era and year input.
368
    fn year(&self) -> Option<FormattableYear> {
45✔
369
        Some(self.date.year())
45✔
370
    }
45✔
371

372
    /// Gets the month input.
373
    fn month(&self) -> Option<FormattableMonth> {
46✔
374
        Some(self.date.month())
46✔
375
    }
46✔
376

377
    /// Gets the day input.
378
    fn day_of_month(&self) -> Option<DayOfMonth> {
45✔
379
        Some(self.date.day_of_month())
45✔
380
    }
45✔
381

382
    /// Gets the weekday input.
383
    fn iso_weekday(&self) -> Option<IsoWeekday> {
45✔
384
        Some(self.date.day_of_week())
45✔
385
    }
45✔
386

387
    /// Gets information on the position of the day within the year.
388
    fn day_of_year_info(&self) -> Option<DayOfYearInfo> {
45✔
389
        Some(self.date.day_of_year_info())
45✔
390
    }
45✔
391

392
    fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
52✔
393
        self.date.calendar().any_calendar_kind()
52✔
394
    }
52✔
395
    fn to_iso(&self) -> Date<Iso> {
8✔
396
        Date::to_iso(&self.date)
8✔
397
    }
8✔
398
}
399

400
impl<A: AsCalendar> IsoTimeInput for DateTime<A> {
401
    /// Gets the hour input.
402
    fn hour(&self) -> Option<IsoHour> {
52✔
403
        Some(self.time.hour)
52✔
404
    }
52✔
405

406
    /// Gets the minute input.
407
    fn minute(&self) -> Option<IsoMinute> {
53✔
408
        Some(self.time.minute)
53✔
409
    }
53✔
410

411
    /// Gets the second input.
412
    fn second(&self) -> Option<IsoSecond> {
53✔
413
        Some(self.time.second)
53✔
414
    }
53✔
415

416
    /// Gets the fractional second input.
417
    fn nanosecond(&self) -> Option<NanoSecond> {
53✔
418
        Some(self.time.nanosecond)
53✔
419
    }
53✔
420
}
421

422
impl TimeZoneInput for CustomTimeZone {
423
    fn gmt_offset(&self) -> Option<GmtOffset> {
573✔
424
        self.gmt_offset
573✔
425
    }
573✔
426

427
    fn time_zone_id(&self) -> Option<TimeZoneBcp47Id> {
247✔
428
        self.time_zone_id
247✔
429
    }
247✔
430

431
    fn metazone_id(&self) -> Option<MetazoneId> {
215✔
432
        self.metazone_id
215✔
433
    }
215✔
434

435
    fn zone_variant(&self) -> Option<ZoneVariant> {
200✔
436
        self.zone_variant
200✔
437
    }
200✔
438
}
439

440
impl IsoTimeInput for Time {
441
    fn hour(&self) -> Option<IsoHour> {
5✔
442
        Some(self.hour)
5✔
443
    }
5✔
444
    fn minute(&self) -> Option<IsoMinute> {
5✔
445
        Some(self.minute)
5✔
446
    }
5✔
447
    fn second(&self) -> Option<IsoSecond> {
5✔
448
        Some(self.second)
5✔
449
    }
5✔
450
    fn nanosecond(&self) -> Option<NanoSecond> {
5✔
451
        Some(self.nanosecond)
5✔
452
    }
5✔
453
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc