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

zbraniecki / icu4x / 9014530096

08 May 2024 07:27PM UTC coverage: 76.402% (+0.2%) from 76.234%
9014530096

push

github

web-flow
Add missing std pointer-like impls for DataProvider, DynamicDataProvider (#4880)

0 of 3 new or added lines in 1 file covered. (0.0%)

3218 existing lines in 167 files now uncovered.

53328 of 69799 relevant lines covered (76.4%)

504343.42 hits per line

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

89.66
/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
///
99
/// # Examples
100
///
101
/// If the trait does not return all required fields, an error output will occur:
102
///
103
/// ```
104
/// use icu::calendar::*;
105
/// use icu::calendar::types::*;
106
/// use icu::datetime::input::*;
107
/// use icu::datetime::{DateTimeWriteError, TypedDateTimeNames};
108
/// use icu::datetime::fields::{Field, FieldLength, FieldSymbol, Weekday};
109
/// use icu::datetime::neo_pattern::DateTimePattern;
110
/// use icu::locid::locale;
111
/// use writeable::assert_try_writeable_eq;
112
///
113
/// struct Empty;
114
///
115
/// impl DateInput for Empty {
116
///     type Calendar = Gregorian;
117
///     fn year(&self) -> Option<FormattableYear> { None }
118
///     fn month(&self) -> Option<FormattableMonth> { None }
119
///     fn day_of_month(&self) -> Option<DayOfMonth> { None }
120
///     fn iso_weekday(&self) -> Option<IsoWeekday> { None }
121
///     fn day_of_year_info(&self) -> Option<DayOfYearInfo> { None }
122
///     fn any_calendar_kind(&self) -> Option<AnyCalendarKind> { None }
123
///     fn to_iso(&self) -> icu::calendar::Date<Iso> { todo!() }
124
/// }
125
///
126
/// impl IsoTimeInput for Empty {
127
///     fn hour(&self) -> Option<IsoHour> { None }
128
///     fn minute(&self) -> Option<IsoMinute> { None }
129
///     fn second(&self) -> Option<IsoSecond> { None }
130
///     fn nanosecond(&self) -> Option<NanoSecond> { None }
131
/// }
132
///
133
/// // Create an instance that can format abbreviated month, weekday, and day period names:
134
/// let mut names: TypedDateTimeNames<Gregorian> =
135
///     TypedDateTimeNames::try_new(&locale!("en").into()).unwrap();
136
///
137
/// // Create a pattern from a pattern string:
138
/// let pattern_str = "'It is:' E MMM d y G 'at' h:mm:ssSSS a";
139
/// let pattern: DateTimePattern = pattern_str.parse().unwrap();
140
///
141
/// // The pattern string contains lots of symbols, but our DateTimeInput is empty!
142
/// let mut buffer = String::new();
143
/// // Missing data is filled in on a best-effort basis, and an error is signaled.
144
/// assert_try_writeable_eq!(
145
///     names.with_pattern(&pattern).format(&Empty),
146
///     "It is: {E} {M} {d} {y} {G} at {h}:{m}:{s}{S} {a}",
147
///     Err(DateTimeWriteError::MissingInputField("iso_weekday"))
148
/// );
149
/// ```
150
pub trait DateTimeInput: DateInput + IsoTimeInput {}
151

152
impl<T> DateTimeInput for T where T: DateInput + IsoTimeInput {}
153

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

159
    /// The week of the month.
160
    ///
161
    /// For example, January 1, 2021 is part of the first week of January.
162
    fn week_of_month(&self) -> Result<WeekOfMonth, CalendarError>;
163

164
    /// The week number of the year and the corresponding year.
165
    ///
166
    /// For example, December 31, 2020 is part of the first week of 2021.
167
    fn week_of_year(&self) -> Result<(FormattableYear, WeekOfYear), CalendarError>;
168

169
    /// The day of week in this month.
170
    ///
171
    /// For example, July 8, 2020 is the 2nd Wednesday of July.
172
    fn day_of_week_in_month(&self) -> Result<DayOfWeekInMonth, CalendarError>;
173

174
    /// TODO(#487): Implement flexible day periods.
175
    fn flexible_day_period(&self);
176
}
177

178
/// A [`DateTimeInput`] type with all of the fields pre-extracted
179
///
180
/// See [`DateTimeInput`] for documentation on individual fields
181
#[derive(Default, Debug, Copy, Clone)]
4✔
182
pub(crate) struct ExtractedDateTimeInput {
183
    year: Option<FormattableYear>,
2✔
184
    month: Option<FormattableMonth>,
2✔
185
    day_of_month: Option<DayOfMonth>,
2✔
186
    iso_weekday: Option<IsoWeekday>,
2✔
187
    day_of_year_info: Option<DayOfYearInfo>,
2✔
188
    any_calendar_kind: Option<AnyCalendarKind>,
2✔
189
    hour: Option<IsoHour>,
2✔
190
    minute: Option<IsoMinute>,
2✔
191
    second: Option<IsoSecond>,
2✔
192
    nanosecond: Option<NanoSecond>,
2✔
193
}
194

195
/// A [`TimeZoneInput`] type with all of the fields pre-extracted
196
///
197
/// See [`TimeZoneInput`] for documentation on individual fields
UNCOV
198
#[derive(Debug, Copy, Clone)]
×
199
pub(crate) struct ExtractedTimeZoneInput {
UNCOV
200
    gmt_offset: Option<GmtOffset>,
×
UNCOV
201
    time_zone_id: Option<TimeZoneBcp47Id>,
×
UNCOV
202
    metazone_id: Option<MetazoneId>,
×
UNCOV
203
    zone_variant: Option<ZoneVariant>,
×
204
}
205

206
impl ExtractedDateTimeInput {
207
    /// Construct given an instance of a [`DateTimeInput`].
208
    pub(crate) fn extract_from<T: DateTimeInput>(input: &T) -> Self {
47✔
209
        Self {
47✔
210
            year: input.year(),
47✔
211
            month: input.month(),
47✔
212
            day_of_month: input.day_of_month(),
47✔
213
            iso_weekday: input.iso_weekday(),
47✔
214
            day_of_year_info: input.day_of_year_info(),
47✔
215
            any_calendar_kind: input.any_calendar_kind(),
47✔
216
            hour: input.hour(),
47✔
217
            minute: input.minute(),
47✔
218
            second: input.second(),
47✔
219
            nanosecond: input.nanosecond(),
47✔
220
        }
221
    }
47✔
222
    /// Construct given an instance of a [`DateTimeInput`].
223
    pub(crate) fn extract_from_date<T: DateInput>(input: &T) -> Self {
2✔
224
        Self {
2✔
225
            year: input.year(),
2✔
226
            month: input.month(),
2✔
227
            day_of_month: input.day_of_month(),
2✔
228
            iso_weekday: input.iso_weekday(),
2✔
229
            day_of_year_info: input.day_of_year_info(),
2✔
230
            any_calendar_kind: input.any_calendar_kind(),
2✔
231
            ..Default::default()
2✔
232
        }
233
    }
2✔
234
    /// Construct given an instance of a [`DateTimeInput`].
UNCOV
235
    pub(crate) fn extract_from_time<T: IsoTimeInput>(input: &T) -> Self {
×
UNCOV
236
        Self {
×
UNCOV
237
            hour: input.hour(),
×
UNCOV
238
            minute: input.minute(),
×
UNCOV
239
            second: input.second(),
×
UNCOV
240
            nanosecond: input.nanosecond(),
×
UNCOV
241
            ..Default::default()
×
242
        }
UNCOV
243
    }
×
244
}
245

246
impl ExtractedTimeZoneInput {
247
    /// Construct given an instance of a [`ZonedDateTimeInput`].
248
    pub(crate) fn extract_from<T: TimeZoneInput>(input: &T) -> Self {
1✔
249
        Self {
1✔
250
            gmt_offset: input.gmt_offset(),
1✔
251
            time_zone_id: input.time_zone_id(),
1✔
252
            metazone_id: input.metazone_id(),
1✔
253
            zone_variant: input.zone_variant(),
1✔
254
        }
255
    }
1✔
256
}
257

258
impl DateInput for ExtractedDateTimeInput {
259
    /// This actually doesn't matter, by the time we use this
260
    /// it's purely internal raw code where calendars are irrelevant
261
    type Calendar = icu_calendar::any_calendar::AnyCalendar;
262
    fn year(&self) -> Option<FormattableYear> {
4,946✔
263
        self.year
4,946✔
264
    }
4,946✔
265
    fn month(&self) -> Option<FormattableMonth> {
2,825✔
266
        self.month
2,825✔
267
    }
2,825✔
268
    fn day_of_month(&self) -> Option<DayOfMonth> {
2,727✔
269
        self.day_of_month
2,727✔
270
    }
2,727✔
271
    fn iso_weekday(&self) -> Option<IsoWeekday> {
1,831✔
272
        self.iso_weekday
1,831✔
273
    }
1,831✔
274
    fn day_of_year_info(&self) -> Option<DayOfYearInfo> {
505✔
275
        self.day_of_year_info
505✔
276
    }
505✔
277
    fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
167✔
278
        self.any_calendar_kind
167✔
279
    }
167✔
UNCOV
280
    fn to_iso(&self) -> Date<Iso> {
×
UNCOV
281
        unreachable!("ExtractedDateTimeInput should never be directly passed to DateTimeFormatter")
×
282
    }
283
}
284

285
impl IsoTimeInput for ExtractedDateTimeInput {
286
    fn hour(&self) -> Option<IsoHour> {
4,418✔
287
        self.hour
4,418✔
288
    }
4,418✔
289
    fn minute(&self) -> Option<IsoMinute> {
4,263✔
290
        self.minute
4,263✔
291
    }
4,263✔
292
    fn second(&self) -> Option<IsoSecond> {
3,450✔
293
        self.second
3,450✔
294
    }
3,450✔
295
    fn nanosecond(&self) -> Option<NanoSecond> {
1,913✔
296
        self.nanosecond
1,913✔
297
    }
1,913✔
298
}
299

300
impl TimeZoneInput for ExtractedTimeZoneInput {
301
    fn gmt_offset(&self) -> Option<GmtOffset> {
625✔
302
        self.gmt_offset
625✔
303
    }
625✔
304
    fn time_zone_id(&self) -> Option<TimeZoneBcp47Id> {
325✔
305
        self.time_zone_id
325✔
306
    }
325✔
307
    fn metazone_id(&self) -> Option<MetazoneId> {
277✔
308
        self.metazone_id
277✔
309
    }
277✔
310
    fn zone_variant(&self) -> Option<ZoneVariant> {
246✔
311
        self.zone_variant
246✔
312
    }
246✔
313
}
314

315
pub(crate) enum ExtractedDateTimeInputWeekCalculatorError {
316
    Missing(&'static str),
317
}
318

319
impl ExtractedDateTimeInput {
320
    pub(crate) fn week_of_month(
36✔
321
        &self,
322
        calculator: &WeekCalculator,
323
    ) -> Result<WeekOfMonth, ExtractedDateTimeInputWeekCalculatorError> {
324
        let day_of_month =
325
            self.day_of_month()
72✔
326
                .ok_or(ExtractedDateTimeInputWeekCalculatorError::Missing(
36✔
327
                    "day_of_month",
UNCOV
328
                ))?;
×
329
        let iso_weekday =
330
            self.iso_weekday()
72✔
331
                .ok_or(ExtractedDateTimeInputWeekCalculatorError::Missing(
36✔
332
                    "iso_weekday",
UNCOV
333
                ))?;
×
334
        Ok(calculator.week_of_month(day_of_month, iso_weekday))
36✔
335
    }
36✔
336

337
    pub(crate) fn week_of_year(
500✔
338
        &self,
339
        calculator: &WeekCalculator,
340
    ) -> Result<(FormattableYear, WeekOfYear), ExtractedDateTimeInputWeekCalculatorError> {
341
        let day_of_year_info =
342
            self.day_of_year_info()
1,000✔
343
                .ok_or(ExtractedDateTimeInputWeekCalculatorError::Missing(
500✔
344
                    "day_of_year_info",
UNCOV
345
                ))?;
×
346
        let iso_weekday =
347
            self.iso_weekday()
1,000✔
348
                .ok_or(ExtractedDateTimeInputWeekCalculatorError::Missing(
500✔
349
                    "iso_weekday",
UNCOV
350
                ))?;
×
351
        // We don't have any calendars with < 14 days per year, and it's unlikely we'll add one
352
        debug_assert!(day_of_year_info.day_of_year >= icu_calendar::week::MIN_UNIT_DAYS);
500✔
353
        debug_assert!(day_of_year_info.days_in_prev_year >= icu_calendar::week::MIN_UNIT_DAYS);
500✔
354
        #[allow(clippy::unwrap_used)]
355
        let week_of = calculator
500✔
356
            .week_of_year(day_of_year_info, iso_weekday)
500✔
357
            .unwrap();
358
        let year = match week_of.unit {
500✔
UNCOV
359
            RelativeUnit::Previous => day_of_year_info.prev_year,
×
360
            RelativeUnit::Current => self
1,000✔
361
                .year()
362
                .ok_or(ExtractedDateTimeInputWeekCalculatorError::Missing("year"))?,
500✔
UNCOV
363
            RelativeUnit::Next => day_of_year_info.next_year,
×
364
        };
365
        Ok((year, WeekOfYear(week_of.week as u32)))
500✔
366
    }
500✔
367
}
368

369
impl<C: Calendar, A: AsCalendar<Calendar = C>> DateInput for Date<A> {
370
    type Calendar = C;
371
    /// Gets the era and year input.
372
    fn year(&self) -> Option<FormattableYear> {
2✔
373
        Some(self.year())
2✔
374
    }
2✔
375

376
    /// Gets the month input.
377
    fn month(&self) -> Option<FormattableMonth> {
2✔
378
        Some(self.month())
2✔
379
    }
2✔
380

381
    /// Gets the day input.
382
    fn day_of_month(&self) -> Option<DayOfMonth> {
2✔
383
        Some(self.day_of_month())
2✔
384
    }
2✔
385

386
    /// Gets the weekday input.
387
    fn iso_weekday(&self) -> Option<IsoWeekday> {
2✔
388
        Some(self.day_of_week())
2✔
389
    }
2✔
390

391
    /// Gets information on the position of the day within the year.
392
    fn day_of_year_info(&self) -> Option<DayOfYearInfo> {
2✔
393
        Some(self.day_of_year_info())
2✔
394
    }
2✔
395

396
    fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
4✔
397
        self.calendar().any_calendar_kind()
4✔
398
    }
4✔
399

400
    fn to_iso(&self) -> Date<Iso> {
1✔
401
        Date::to_iso(self)
1✔
402
    }
1✔
403
}
404

405
impl<C: Calendar, A: AsCalendar<Calendar = C>> DateInput for DateTime<A> {
406
    type Calendar = C;
407
    /// Gets the era and year input.
408
    fn year(&self) -> Option<FormattableYear> {
46✔
409
        Some(self.date.year())
46✔
410
    }
46✔
411

412
    /// Gets the month input.
413
    fn month(&self) -> Option<FormattableMonth> {
46✔
414
        Some(self.date.month())
46✔
415
    }
46✔
416

417
    /// Gets the day input.
418
    fn day_of_month(&self) -> Option<DayOfMonth> {
45✔
419
        Some(self.date.day_of_month())
45✔
420
    }
45✔
421

422
    /// Gets the weekday input.
423
    fn iso_weekday(&self) -> Option<IsoWeekday> {
45✔
424
        Some(self.date.day_of_week())
45✔
425
    }
45✔
426

427
    /// Gets information on the position of the day within the year.
428
    fn day_of_year_info(&self) -> Option<DayOfYearInfo> {
46✔
429
        Some(self.date.day_of_year_info())
46✔
430
    }
46✔
431

432
    fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
53✔
433
        self.date.calendar().any_calendar_kind()
53✔
434
    }
53✔
435
    fn to_iso(&self) -> Date<Iso> {
8✔
436
        Date::to_iso(&self.date)
8✔
437
    }
8✔
438
}
439

440
impl<A: AsCalendar> IsoTimeInput for DateTime<A> {
441
    /// Gets the hour input.
442
    fn hour(&self) -> Option<IsoHour> {
53✔
443
        Some(self.time.hour)
53✔
444
    }
53✔
445

446
    /// Gets the minute input.
447
    fn minute(&self) -> Option<IsoMinute> {
53✔
448
        Some(self.time.minute)
53✔
449
    }
53✔
450

451
    /// Gets the second input.
452
    fn second(&self) -> Option<IsoSecond> {
54✔
453
        Some(self.time.second)
54✔
454
    }
54✔
455

456
    /// Gets the fractional second input.
457
    fn nanosecond(&self) -> Option<NanoSecond> {
54✔
458
        Some(self.time.nanosecond)
54✔
459
    }
54✔
460
}
461

462
impl TimeZoneInput for CustomTimeZone {
463
    fn gmt_offset(&self) -> Option<GmtOffset> {
435✔
464
        self.gmt_offset
435✔
465
    }
435✔
466

467
    fn time_zone_id(&self) -> Option<TimeZoneBcp47Id> {
358✔
468
        self.time_zone_id
358✔
469
    }
358✔
470

471
    fn metazone_id(&self) -> Option<MetazoneId> {
296✔
472
        self.metazone_id
296✔
473
    }
296✔
474

475
    fn zone_variant(&self) -> Option<ZoneVariant> {
266✔
476
        self.zone_variant
266✔
477
    }
266✔
478
}
479

480
impl IsoTimeInput for Time {
481
    fn hour(&self) -> Option<IsoHour> {
6✔
482
        Some(self.hour)
6✔
483
    }
6✔
484
    fn minute(&self) -> Option<IsoMinute> {
6✔
485
        Some(self.minute)
6✔
486
    }
6✔
487
    fn second(&self) -> Option<IsoSecond> {
6✔
488
        Some(self.second)
6✔
489
    }
6✔
490
    fn nanosecond(&self) -> Option<NanoSecond> {
6✔
491
        Some(self.nanosecond)
6✔
492
    }
6✔
493
}
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