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

zbraniecki / icu4x / 9457158389

10 Jun 2024 11:45PM UTC coverage: 75.174% (+0.05%) from 75.121%
9457158389

push

github

web-flow
Add constructing TinyAsciiStr from utf16 (#5025)

Introduces TinyAsciiStr constructors from utf16 and converges on the
consensus from #4931.

---------

Co-authored-by: Robert Bastian <4706271+robertbastian@users.noreply.github.com>

65 of 82 new or added lines in 14 files covered. (79.27%)

3441 existing lines in 141 files now uncovered.

52850 of 70304 relevant lines covered (75.17%)

563298.06 hits per line

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

92.83
/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
#[cfg(feature = "experimental")]
9
use crate::neo_marker::{DateMarkers, NeoGetField, TimeMarkers, TypedDateMarkers, ZoneMarkers};
10
use crate::provider::time_zones::{MetazoneId, TimeZoneBcp47Id};
11
use icu_calendar::any_calendar::AnyCalendarKind;
12
use icu_calendar::week::{RelativeUnit, WeekCalculator};
13
use icu_calendar::Calendar;
14
use icu_calendar::{AsCalendar, Date, DateTime, Iso};
15
use icu_timezone::{CustomTimeZone, GmtOffset, ZoneVariant};
16

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

103
/// A [`DateTimeInput`] type with all of the fields pre-extracted
104
///
105
/// See [`DateTimeInput`] for documentation on individual fields
106
#[derive(Default, Debug, Copy, Clone)]
2✔
107
pub(crate) struct ExtractedDateTimeInput {
108
    year: Option<FormattableYear>,
1✔
109
    month: Option<FormattableMonth>,
1✔
110
    day_of_month: Option<DayOfMonth>,
1✔
111
    iso_weekday: Option<IsoWeekday>,
1✔
112
    day_of_year_info: Option<DayOfYearInfo>,
1✔
113
    any_calendar_kind: Option<AnyCalendarKind>,
1✔
114
    hour: Option<IsoHour>,
1✔
115
    minute: Option<IsoMinute>,
1✔
116
    second: Option<IsoSecond>,
1✔
117
    nanosecond: Option<NanoSecond>,
1✔
118
    time_zone: Option<CustomTimeZone>,
1✔
119
}
120

121
/// A [`TimeZoneInput`] type with all of the fields pre-extracted
122
///
123
/// See [`TimeZoneInput`] for documentation on individual fields
UNCOV
124
#[derive(Debug, Copy, Clone)]
×
125
pub(crate) struct ExtractedTimeZoneInput {
126
    gmt_offset: Option<GmtOffset>,
UNCOV
127
    time_zone_id: Option<TimeZoneBcp47Id>,
×
UNCOV
128
    metazone_id: Option<MetazoneId>,
×
UNCOV
129
    zone_variant: Option<ZoneVariant>,
×
130
}
131

132
impl ExtractedDateTimeInput {
133
    /// Construct given an instance of a [`DateTimeInput`].
134
    pub(crate) fn extract_from<T: DateTimeInput>(input: &T) -> Self {
11✔
135
        Self {
11✔
136
            year: input.year(),
11✔
137
            month: input.month(),
11✔
138
            day_of_month: input.day_of_month(),
11✔
139
            iso_weekday: input.iso_weekday(),
11✔
140
            day_of_year_info: input.day_of_year_info(),
11✔
141
            any_calendar_kind: input.any_calendar_kind(),
11✔
142
            hour: input.hour(),
11✔
143
            minute: input.minute(),
11✔
144
            second: input.second(),
11✔
145
            nanosecond: input.nanosecond(),
11✔
146
            time_zone: None,
11✔
147
        }
148
    }
11✔
149
    /// Construct given an instance of a [`DateTimeInput`].
150
    pub(crate) fn extract_from_date<T: DateInput>(input: &T) -> Self {
1✔
151
        Self {
1✔
152
            year: input.year(),
1✔
153
            month: input.month(),
1✔
154
            day_of_month: input.day_of_month(),
1✔
155
            iso_weekday: input.iso_weekday(),
1✔
156
            day_of_year_info: input.day_of_year_info(),
1✔
157
            any_calendar_kind: input.any_calendar_kind(),
1✔
158
            ..Default::default()
1✔
159
        }
160
    }
1✔
161
    /// Construct given an instance of a [`DateTimeInput`].
UNCOV
162
    pub(crate) fn extract_from_time<T: IsoTimeInput>(input: &T) -> Self {
×
UNCOV
163
        Self {
×
UNCOV
164
            hour: input.hour(),
×
UNCOV
165
            minute: input.minute(),
×
UNCOV
166
            second: input.second(),
×
UNCOV
167
            nanosecond: input.nanosecond(),
×
UNCOV
168
            ..Default::default()
×
169
        }
UNCOV
170
    }
×
171
    /// Construct given neo date input instances.
172
    #[cfg(feature = "experimental")]
173
    pub(crate) fn extract_from_typed_neo_input<C, D, T, Z, I>(input: &I) -> Self
36✔
174
    where
175
        D: TypedDateMarkers<C>,
176
        T: TimeMarkers,
177
        Z: ZoneMarkers,
178
        I: ?Sized
179
            + NeoGetField<D::YearInput>
180
            + NeoGetField<D::MonthInput>
181
            + NeoGetField<D::DayOfMonthInput>
182
            + NeoGetField<D::DayOfWeekInput>
183
            + NeoGetField<D::DayOfYearInput>
184
            + NeoGetField<D::AnyCalendarKindInput>
185
            + NeoGetField<T::HourInput>
186
            + NeoGetField<T::MinuteInput>
187
            + NeoGetField<T::SecondInput>
188
            + NeoGetField<T::NanoSecondInput>
189
            + NeoGetField<Z::TimeZoneInput>,
190
    {
191
        Self {
36✔
192
            year: NeoGetField::<D::YearInput>::get_field(input).into(),
36✔
193
            month: NeoGetField::<D::MonthInput>::get_field(input).into(),
36✔
194
            day_of_month: NeoGetField::<D::DayOfMonthInput>::get_field(input).into(),
36✔
195
            iso_weekday: NeoGetField::<D::DayOfWeekInput>::get_field(input).into(),
36✔
196
            day_of_year_info: NeoGetField::<D::DayOfYearInput>::get_field(input).into(),
36✔
197
            any_calendar_kind: NeoGetField::<D::AnyCalendarKindInput>::get_field(input).into(),
36✔
198
            hour: NeoGetField::<T::HourInput>::get_field(input).into(),
36✔
199
            minute: NeoGetField::<T::MinuteInput>::get_field(input).into(),
36✔
200
            second: NeoGetField::<T::SecondInput>::get_field(input).into(),
36✔
201
            nanosecond: NeoGetField::<T::NanoSecondInput>::get_field(input).into(),
36✔
202
            time_zone: NeoGetField::<Z::TimeZoneInput>::get_field(input).into(),
36✔
203
        }
204
    }
36✔
205
    /// Construct given neo date input instances.
206
    #[cfg(feature = "experimental")]
207
    pub(crate) fn extract_from_any_neo_input<D, T, Z, I>(input: &I) -> Self
1✔
208
    where
209
        D: DateMarkers,
210
        T: TimeMarkers,
211
        Z: ZoneMarkers,
212
        I: ?Sized
213
            + NeoGetField<D::YearInput>
214
            + NeoGetField<D::MonthInput>
215
            + NeoGetField<D::DayOfMonthInput>
216
            + NeoGetField<D::DayOfWeekInput>
217
            + NeoGetField<D::DayOfYearInput>
218
            + NeoGetField<D::AnyCalendarKindInput>
219
            + NeoGetField<T::HourInput>
220
            + NeoGetField<T::MinuteInput>
221
            + NeoGetField<T::SecondInput>
222
            + NeoGetField<T::NanoSecondInput>
223
            + NeoGetField<Z::TimeZoneInput>,
224
    {
225
        Self {
1✔
226
            year: NeoGetField::<D::YearInput>::get_field(input).into(),
1✔
227
            month: NeoGetField::<D::MonthInput>::get_field(input).into(),
1✔
228
            day_of_month: NeoGetField::<D::DayOfMonthInput>::get_field(input).into(),
1✔
229
            iso_weekday: NeoGetField::<D::DayOfWeekInput>::get_field(input).into(),
1✔
230
            day_of_year_info: NeoGetField::<D::DayOfYearInput>::get_field(input).into(),
1✔
231
            any_calendar_kind: NeoGetField::<D::AnyCalendarKindInput>::get_field(input).into(),
1✔
232
            hour: NeoGetField::<T::HourInput>::get_field(input).into(),
1✔
233
            minute: NeoGetField::<T::MinuteInput>::get_field(input).into(),
1✔
234
            second: NeoGetField::<T::SecondInput>::get_field(input).into(),
1✔
235
            nanosecond: NeoGetField::<T::NanoSecondInput>::get_field(input).into(),
1✔
236
            time_zone: NeoGetField::<Z::TimeZoneInput>::get_field(input).into(),
1✔
237
        }
238
    }
1✔
239

240
    pub(crate) fn time_zone(&self) -> Option<CustomTimeZone> {
15✔
241
        self.time_zone
15✔
242
    }
15✔
243
}
244

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

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

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

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

314
impl ExtractedDateTimeInput {
315
    pub(crate) fn week_of_month(
36✔
316
        &self,
317
        calculator: &WeekCalculator,
318
    ) -> Result<WeekOfMonth, &'static str> {
319
        let day_of_month = self.day_of_month().ok_or("day_of_month")?;
36✔
320
        let iso_weekday = self.iso_weekday().ok_or("iso_weekday")?;
36✔
321
        Ok(calculator.week_of_month(day_of_month, iso_weekday))
36✔
322
    }
36✔
323

324
    pub(crate) fn week_of_year(
500✔
325
        &self,
326
        calculator: &WeekCalculator,
327
    ) -> Result<(FormattableYear, WeekOfYear), &'static str> {
328
        let day_of_year_info = self.day_of_year_info().ok_or("day_of_year_info")?;
500✔
329
        let iso_weekday = self.iso_weekday().ok_or("iso_weekday")?;
500✔
330
        let week_of = calculator.week_of_year(day_of_year_info, iso_weekday);
500✔
331
        let year = match week_of.unit {
500✔
UNCOV
332
            RelativeUnit::Previous => day_of_year_info.prev_year,
×
333
            RelativeUnit::Current => self.year().ok_or("year")?,
500✔
UNCOV
334
            RelativeUnit::Next => day_of_year_info.next_year,
×
335
        };
336
        Ok((year, WeekOfYear(week_of.week as u32)))
500✔
337
    }
500✔
338
}
339

340
impl<C: Calendar, A: AsCalendar<Calendar = C>> DateInput for Date<A> {
341
    type Calendar = C;
342
    /// Gets the era and year input.
343
    fn year(&self) -> Option<FormattableYear> {
1✔
344
        Some(self.year())
1✔
345
    }
1✔
346

347
    /// Gets the month input.
348
    fn month(&self) -> Option<FormattableMonth> {
1✔
349
        Some(self.month())
1✔
350
    }
1✔
351

352
    /// Gets the day input.
353
    fn day_of_month(&self) -> Option<DayOfMonth> {
1✔
354
        Some(self.day_of_month())
1✔
355
    }
1✔
356

357
    /// Gets the weekday input.
358
    fn iso_weekday(&self) -> Option<IsoWeekday> {
1✔
359
        Some(self.day_of_week())
1✔
360
    }
1✔
361

362
    /// Gets information on the position of the day within the year.
363
    fn day_of_year_info(&self) -> Option<DayOfYearInfo> {
1✔
364
        Some(self.day_of_year_info())
1✔
365
    }
1✔
366

367
    fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
2✔
368
        self.calendar().any_calendar_kind()
2✔
369
    }
2✔
370

371
    fn to_iso(&self) -> Date<Iso> {
1✔
372
        Date::to_iso(self)
1✔
373
    }
1✔
374
}
375

376
impl<C: Calendar, A: AsCalendar<Calendar = C>> DateInput for DateTime<A> {
377
    type Calendar = C;
378
    /// Gets the era and year input.
379
    fn year(&self) -> Option<FormattableYear> {
10✔
380
        Some(self.date.year())
10✔
381
    }
10✔
382

383
    /// Gets the month input.
384
    fn month(&self) -> Option<FormattableMonth> {
10✔
385
        Some(self.date.month())
10✔
386
    }
10✔
387

388
    /// Gets the day input.
389
    fn day_of_month(&self) -> Option<DayOfMonth> {
10✔
390
        Some(self.date.day_of_month())
10✔
391
    }
10✔
392

393
    /// Gets the weekday input.
394
    fn iso_weekday(&self) -> Option<IsoWeekday> {
10✔
395
        Some(self.date.day_of_week())
10✔
396
    }
10✔
397

398
    /// Gets information on the position of the day within the year.
399
    fn day_of_year_info(&self) -> Option<DayOfYearInfo> {
10✔
400
        Some(self.date.day_of_year_info())
10✔
401
    }
10✔
402

403
    fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
18✔
404
        self.date.calendar().any_calendar_kind()
18✔
405
    }
18✔
406
    fn to_iso(&self) -> Date<Iso> {
8✔
407
        Date::to_iso(&self.date)
8✔
408
    }
8✔
409
}
410

411
impl<A: AsCalendar> IsoTimeInput for DateTime<A> {
412
    /// Gets the hour input.
413
    fn hour(&self) -> Option<IsoHour> {
18✔
414
        Some(self.time.hour)
18✔
415
    }
18✔
416

417
    /// Gets the minute input.
418
    fn minute(&self) -> Option<IsoMinute> {
18✔
419
        Some(self.time.minute)
18✔
420
    }
18✔
421

422
    /// Gets the second input.
423
    fn second(&self) -> Option<IsoSecond> {
18✔
424
        Some(self.time.second)
18✔
425
    }
18✔
426

427
    /// Gets the fractional second input.
428
    fn nanosecond(&self) -> Option<NanoSecond> {
18✔
429
        Some(self.time.nanosecond)
18✔
430
    }
18✔
431
}
432

433
impl TimeZoneInput for CustomTimeZone {
434
    fn gmt_offset(&self) -> Option<GmtOffset> {
435✔
435
        self.gmt_offset
435✔
436
    }
435✔
437

438
    fn time_zone_id(&self) -> Option<TimeZoneBcp47Id> {
358✔
439
        self.time_zone_id
358✔
440
    }
358✔
441

442
    fn metazone_id(&self) -> Option<MetazoneId> {
296✔
443
        self.metazone_id
296✔
444
    }
296✔
445

446
    fn zone_variant(&self) -> Option<ZoneVariant> {
266✔
447
        self.zone_variant
266✔
448
    }
266✔
449
}
450

451
impl IsoTimeInput for Time {
452
    fn hour(&self) -> Option<IsoHour> {
4✔
453
        Some(self.hour)
4✔
454
    }
4✔
455
    fn minute(&self) -> Option<IsoMinute> {
4✔
456
        Some(self.minute)
4✔
457
    }
4✔
458
    fn second(&self) -> Option<IsoSecond> {
4✔
459
        Some(self.second)
4✔
460
    }
4✔
461
    fn nanosecond(&self) -> Option<NanoSecond> {
4✔
462
        Some(self.nanosecond)
4✔
463
    }
4✔
464
}
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