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

zbraniecki / icu4x / 11904027177

19 Nov 2024 12:33AM UTC coverage: 75.477% (+0.3%) from 75.174%
11904027177

push

github

web-flow
Move DateTimePattern into pattern module (#5834)

#1317

Also removes `NeoNeverMarker` and fixes #5689

258 of 319 new or added lines in 6 files covered. (80.88%)

6967 existing lines in 278 files now uncovered.

54522 of 72237 relevant lines covered (75.48%)

655305.49 hits per line

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

89.76
/components/datetime/src/pattern/formatter.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
use super::names::RawDateTimeNamesBorrowed;
6
use super::pattern::DateTimePatternBorrowed;
7
use crate::format::datetime::try_write_pattern_items;
8
use crate::input::ExtractedInput;
9
use crate::scaffold::*;
10
use crate::scaffold::{
11
    AllInputMarkers, DateInputMarkers, DateTimeMarkers, GetField, IsInCalendar, TimeMarkers,
12
    TypedDateDataMarkers, ZoneMarkers,
13
};
14
use crate::DateTimeWriteError;
15
use core::fmt;
16
use core::marker::PhantomData;
17
use writeable::TryWriteable;
18

19
/// A formatter for a specific [`DateTimePattern`].
20
///
21
/// Create one of these via factory methods on [`TypedDateTimeNames`].
22
///
23
/// [`DateTimePattern`]: super::DateTimePattern
24
/// [`TypedDateTimeNames`]: super::TypedDateTimeNames
25
#[derive(Debug, Copy, Clone)]
26
pub struct DateTimePatternFormatter<'a, C: CldrCalendar, R> {
27
    inner: RawDateTimePatternFormatter<'a>,
28
    _calendar: PhantomData<C>,
29
    _marker: PhantomData<R>,
30
}
31

NEW
32
#[derive(Debug, Copy, Clone)]
×
33
pub(crate) struct RawDateTimePatternFormatter<'a> {
NEW
34
    pattern: DateTimePatternBorrowed<'a>,
×
NEW
35
    names: RawDateTimeNamesBorrowed<'a>,
×
36
}
37

38
impl<'a, C: CldrCalendar, R> DateTimePatternFormatter<'a, C, R> {
39
    pub(crate) fn new(
36✔
40
        pattern: DateTimePatternBorrowed<'a>,
41
        names: RawDateTimeNamesBorrowed<'a>,
42
    ) -> Self {
43
        Self {
36✔
44
            inner: RawDateTimePatternFormatter { pattern, names },
36✔
45
            _calendar: PhantomData,
46
            _marker: PhantomData,
47
        }
48
    }
36✔
49
}
50

51
impl<'a, C: CldrCalendar, R: DateTimeMarkers> DateTimePatternFormatter<'a, C, R>
52
where
53
    R::D: TypedDateDataMarkers<C> + DateInputMarkers,
54
    R::T: TimeMarkers,
55
    R::Z: ZoneMarkers,
56
{
57
    /// Formats a date and time of day.
58
    ///
59
    /// For an example, see [`TypedDateTimeNames`](super::TypedDateTimeNames).
60
    pub fn format<I>(&self, datetime: &I) -> FormattedDateTimePattern<'a>
36✔
61
    where
62
        I: ?Sized + IsInCalendar<C> + AllInputMarkers<R>,
63
    {
64
        FormattedDateTimePattern {
36✔
65
            pattern: self.inner.pattern,
36✔
66
            input: ExtractedInput::extract_from_neo_input::<R::D, R::T, R::Z, I>(datetime),
36✔
67
            names: self.inner.names,
36✔
68
        }
69
    }
36✔
70

71
    /// Formats a date without a time of day.
72
    ///
73
    /// # Examples
74
    ///
75
    /// ```
76
    /// use icu::calendar::Date;
77
    /// use icu::calendar::Gregorian;
78
    /// use icu::datetime::fields;
79
    /// use icu::datetime::fields::FieldLength;
80
    /// use icu::datetime::pattern::DateTimePattern;
81
    /// use icu::datetime::pattern::TypedDateTimeNames;
82
    /// use icu::locale::locale;
83
    /// use writeable::assert_try_writeable_eq;
84
    ///
85
    /// // Create an instance that can format wide month and era names:
86
    /// let mut names: TypedDateTimeNames<Gregorian> =
87
    ///     TypedDateTimeNames::try_new(&locale!("en-GB").into()).unwrap();
88
    /// names
89
    ///     .include_month_names(fields::Month::Format, FieldLength::Four)
90
    ///     .unwrap()
91
    ///     .include_year_names(FieldLength::Four)
92
    ///     .unwrap();
93
    ///
94
    /// // Create a pattern from a pattern string:
95
    /// let pattern_str = "'The date is:' MMMM d, y GGGG";
96
    /// let pattern: DateTimePattern = pattern_str.parse().unwrap();
97
    ///
98
    /// // Test it with some different dates:
99
    /// // Note: extended year -50 is year 51 BCE
100
    /// let date_bce = Date::try_new_gregorian(-50, 3, 15).unwrap();
101
    /// let date_ce = Date::try_new_gregorian(1700, 11, 20).unwrap();
102
    /// assert_try_writeable_eq!(
103
    ///     names.with_pattern(&pattern).format_date(&date_bce),
104
    ///     "The date is: March 15, 51 Before Christ"
105
    /// );
106
    /// assert_try_writeable_eq!(
107
    ///     names.with_pattern(&pattern).format_date(&date_ce),
108
    ///     "The date is: November 20, 1700 Anno Domini"
109
    /// );
110
    /// ```
111
    pub fn format_date<I>(&self, datetime: &'a I) -> FormattedDateTimePattern<'a>
112
    where
113
        I: ?Sized
114
            + IsInCalendar<C>
115
            + GetField<<R::D as DateInputMarkers>::YearInput>
116
            + GetField<<R::D as DateInputMarkers>::MonthInput>
117
            + GetField<<R::D as DateInputMarkers>::DayOfMonthInput>
118
            + GetField<<R::D as DateInputMarkers>::DayOfWeekInput>
119
            + GetField<<R::D as DateInputMarkers>::DayOfYearInput>
120
            + GetField<()>,
121
    {
122
        FormattedDateTimePattern {
123
            pattern: self.inner.pattern,
124
            input: ExtractedInput::extract_from_neo_input::<R::D, (), (), I>(datetime),
125
            names: self.inner.names,
126
        }
127
    }
128

129
    /// Formats a time of day without a date.
130
    ///
131
    /// # Examples
132
    ///
133
    /// ```
134
    /// use icu::calendar::Gregorian;
135
    /// use icu::calendar::Time;
136
    /// use icu::datetime::fields::FieldLength;
137
    /// use icu::datetime::pattern::DateTimePattern;
138
    /// use icu::datetime::pattern::TypedDateTimeNames;
139
    /// use icu::locale::locale;
140
    /// use writeable::assert_try_writeable_eq;
141
    ///
142
    /// // Create an instance that can format abbreviated day periods:
143
    /// let mut names: TypedDateTimeNames<Gregorian> =
144
    ///     TypedDateTimeNames::try_new(&locale!("en-US").into()).unwrap();
145
    /// names
146
    ///     .include_day_period_names(FieldLength::Three)
147
    ///     .unwrap();
148
    ///
149
    /// // Create a pattern from a pattern string:
150
    /// let pattern_str = "'The time is:' h:mm b";
151
    /// let pattern: DateTimePattern = pattern_str.parse().unwrap();
152
    ///
153
    /// // Test it with different times of day:
154
    /// let time_am = Time::try_new(11, 4, 14, 0).unwrap();
155
    /// let time_pm = Time::try_new(13, 41, 28, 0).unwrap();
156
    /// let time_noon = Time::try_new(12, 0, 0, 0).unwrap();
157
    /// let time_midnight = Time::try_new(0, 0, 0, 0).unwrap();
158
    /// assert_try_writeable_eq!(
159
    ///     names.with_pattern(&pattern).format_time(&time_am),
160
    ///     "The time is: 11:04 AM"
161
    /// );
162
    /// assert_try_writeable_eq!(
163
    ///     names.with_pattern(&pattern).format_time(&time_pm),
164
    ///     "The time is: 1:41 PM"
165
    /// );
166
    /// assert_try_writeable_eq!(
167
    ///     names.with_pattern(&pattern).format_time(&time_noon),
168
    ///     "The time is: 12:00 noon"
169
    /// );
170
    /// assert_try_writeable_eq!(
171
    ///     names.with_pattern(&pattern).format_time(&time_midnight),
172
    ///     "The time is: 12:00 midnight"
173
    /// );
174
    /// ```
175
    pub fn format_time<I>(&self, datetime: &'a I) -> FormattedDateTimePattern<'a>
176
    where
177
        I: ?Sized
178
            + IsInCalendar<C>
179
            + GetField<<R::T as TimeMarkers>::HourInput>
180
            + GetField<<R::T as TimeMarkers>::MinuteInput>
181
            + GetField<<R::T as TimeMarkers>::SecondInput>
182
            + GetField<<R::T as TimeMarkers>::NanoSecondInput>
183
            + GetField<()>,
184
    {
185
        FormattedDateTimePattern {
186
            pattern: self.inner.pattern,
187
            input: ExtractedInput::extract_from_neo_input::<(), R::T, (), I>(datetime),
188
            names: self.inner.names,
189
        }
190
    }
191

192
    /// Formats a timezone without a date or time.
193
    ///
194
    /// # Examples
195
    ///
196
    /// ```
197
    /// use icu::calendar::Gregorian;
198
    /// use icu::datetime::pattern::DateTimePattern;
199
    /// use icu::datetime::fieldset::dynamic::ZoneFieldSet;
200
    /// use icu::datetime::pattern::TypedDateTimeNames;
201
    /// use icu::locale::locale;
202
    /// use icu::timezone::IxdtfParser;
203
    /// use writeable::assert_try_writeable_eq;
204
    ///
205
    /// let mut london_winter = IxdtfParser::new().try_from_str(
206
    ///     "2024-01-01T00:00:00+00:00[Europe/London]",
207
    /// )
208
    /// .unwrap()
209
    /// .to_calendar(Gregorian);
210
    /// let mut london_summer = IxdtfParser::new().try_from_str(
211
    ///     "2024-07-01T00:00:00+01:00[Europe/London]",
212
    /// )
213
    /// .unwrap()
214
    /// .to_calendar(Gregorian);
215
    ///
216
    /// let mut names =
217
    ///     TypedDateTimeNames::<Gregorian, ZoneFieldSet>::try_new(
218
    ///         &locale!("en-GB").into(),
219
    ///     )
220
    ///     .unwrap();
221
    ///
222
    /// names.include_time_zone_essentials().unwrap();
223
    /// names.include_time_zone_specific_short_names().unwrap();
224
    ///
225
    /// // Create a pattern with symbol `z`:
226
    /// let pattern_str = "'Your time zone is:' z";
227
    /// let pattern: DateTimePattern = pattern_str.parse().unwrap();
228
    ///
229
    /// assert_try_writeable_eq!(
230
    ///     names.with_pattern(&pattern).format_timezone(&london_winter),
231
    ///     "Your time zone is: GMT",
232
    /// );
233
    /// assert_try_writeable_eq!(
234
    ///     names.with_pattern(&pattern).format_timezone(&london_summer),
235
    ///     "Your time zone is: BST",
236
    /// );
237
    /// ```
238
    pub fn format_timezone<I>(&self, datetime: &'a I) -> FormattedDateTimePattern<'a>
239
    where
240
        I: ?Sized
241
            + IsInCalendar<C>
242
            + GetField<<R::Z as ZoneMarkers>::TimeZoneIdInput>
243
            + GetField<<R::Z as ZoneMarkers>::TimeZoneOffsetInput>
244
            + GetField<<R::Z as ZoneMarkers>::TimeZoneVariantInput>
245
            + GetField<<R::Z as ZoneMarkers>::TimeZoneLocalTimeInput>
246
            + GetField<()>,
247
    {
248
        FormattedDateTimePattern {
249
            pattern: self.inner.pattern,
250
            input: ExtractedInput::extract_from_neo_input::<(), (), R::Z, I>(datetime),
251
            names: self.inner.names,
252
        }
253
    }
254
}
255

256
/// A pattern that has been interpolated and implements [`TryWriteable`].
NEW
257
#[derive(Debug)]
×
258
pub struct FormattedDateTimePattern<'a> {
NEW
259
    pattern: DateTimePatternBorrowed<'a>,
×
NEW
260
    input: ExtractedInput,
×
NEW
261
    names: RawDateTimeNamesBorrowed<'a>,
×
262
}
263

264
impl TryWriteable for FormattedDateTimePattern<'_> {
265
    type Error = DateTimeWriteError;
266
    fn try_write_to_parts<S: writeable::PartsWrite + ?Sized>(
349✔
267
        &self,
268
        sink: &mut S,
269
    ) -> Result<Result<(), Self::Error>, fmt::Error> {
270
        try_write_pattern_items(
349✔
271
            self.pattern.0.as_borrowed().metadata,
349✔
272
            self.pattern.0.as_borrowed().items.iter(),
349✔
273
            &self.input,
349✔
274
            &self.names,
275
            self.names.fixed_decimal_formatter,
349✔
276
            sink,
277
        )
278
    }
349✔
279

280
    // TODO(#489): Implement writeable_length_hint
281
}
282

283
#[cfg(test)]
284
#[cfg(feature = "compiled_data")]
285
mod tests {
286
    use super::super::*;
287
    use crate::fields::{self, FieldLength};
288
    use icu_calendar::{DateTime, Gregorian};
289
    use icu_locale_core::locale;
290
    use writeable::assert_try_writeable_eq;
291

292
    #[test]
293
    fn test_basic_pattern_formatting() {
2✔
294
        let locale = locale!("en").into();
1✔
295
        let mut names: TypedDateTimeNames<Gregorian> =
296
            TypedDateTimeNames::try_new(&locale).unwrap();
1✔
297
        names
3✔
298
            .load_month_names(
299
                &crate::provider::Baked,
300
                fields::Month::Format,
3✔
301
                fields::FieldLength::Four,
3✔
302
            )
303
            .unwrap()
304
            .load_weekday_names(
305
                &crate::provider::Baked,
306
                fields::Weekday::Format,
1✔
307
                fields::FieldLength::Three,
1✔
308
            )
309
            .unwrap()
310
            .load_year_names(&crate::provider::Baked, FieldLength::Five)
1✔
311
            .unwrap()
312
            .load_day_period_names(&crate::provider::Baked, FieldLength::Three)
1✔
313
            .unwrap();
314
        let pattern: DateTimePattern = "'It is' E, MMMM d, y GGGGG 'at' hh:mm a'!'"
1✔
315
            .parse()
316
            .unwrap();
317
        let datetime = DateTime::try_new_gregorian(2023, 10, 25, 15, 0, 55).unwrap();
3✔
318
        let formatted_pattern = names.with_pattern(&pattern).format(&datetime);
1✔
319

320
        assert_try_writeable_eq!(
2✔
321
            formatted_pattern,
322
            "It is Wed, October 25, 2023 A at 03:00 PM!",
323
            Ok(()),
324
        );
325
    }
2✔
326

327
    #[test]
328
    fn test_era_coverage() {
2✔
329
        let locale = locale!("uk").into();
1✔
NEW
330
        #[derive(Debug)]
×
331
        struct TestCase {
332
            pattern: &'static str,
NEW
333
            field_length: FieldLength,
×
NEW
334
            expected: &'static str,
×
335
        }
336
        let cases = [
1✔
337
            TestCase {
1✔
338
                pattern: "<G>",
339
                field_length: FieldLength::Three,
1✔
340
                expected: "<н. е.>",
341
            },
342
            TestCase {
1✔
343
                pattern: "<GG>",
344
                field_length: FieldLength::Three,
1✔
345
                expected: "<н. е.>",
346
            },
347
            TestCase {
1✔
348
                pattern: "<GGG>",
349
                field_length: FieldLength::Three,
1✔
350
                expected: "<н. е.>",
351
            },
352
            TestCase {
1✔
353
                pattern: "<GGGG>",
354
                field_length: FieldLength::Four,
1✔
355
                expected: "<нашої ери>",
356
            },
357
            TestCase {
1✔
358
                pattern: "<GGGGG>",
359
                field_length: FieldLength::Five,
1✔
360
                expected: "<н.е.>",
361
            },
362
        ];
363
        for cas in cases {
1✔
364
            let TestCase {
365
                pattern,
5✔
366
                field_length,
5✔
367
                expected,
5✔
368
            } = cas;
369
            let mut names: TypedDateTimeNames<Gregorian> =
370
                TypedDateTimeNames::try_new(&locale).unwrap();
5✔
371
            names
15✔
372
                .load_year_names(&crate::provider::Baked, field_length)
373
                .unwrap();
374
            let pattern: DateTimePattern = pattern.parse().unwrap();
5✔
375
            let datetime = DateTime::try_new_gregorian(2023, 11, 17, 13, 41, 28).unwrap();
15✔
376
            let formatted_pattern = names.with_pattern(&pattern).format(&datetime);
5✔
377

378
            assert_try_writeable_eq!(formatted_pattern, expected, Ok(()), "{cas:?}");
10✔
379
        }
14✔
380
    }
10✔
381

382
    #[test]
383
    fn test_month_coverage() {
2✔
384
        // Ukrainian has different values for format and standalone
385
        let locale = locale!("uk").into();
1✔
NEW
386
        #[derive(Debug)]
×
387
        struct TestCase {
388
            pattern: &'static str,
NEW
389
            field_symbol: fields::Month,
×
NEW
390
            field_length: FieldLength,
×
NEW
391
            expected: &'static str,
×
392
        }
393
        let cases = [
1✔
394
            // 'M' and 'MM' are numeric
395
            TestCase {
1✔
396
                pattern: "<MMM>",
397
                field_symbol: fields::Month::Format,
1✔
398
                field_length: FieldLength::Three,
1✔
399
                expected: "<лист.>",
400
            },
401
            TestCase {
1✔
402
                pattern: "<MMMM>",
403
                field_symbol: fields::Month::Format,
1✔
404
                field_length: FieldLength::Four,
1✔
405
                expected: "<листопада>",
406
            },
407
            TestCase {
1✔
408
                pattern: "<MMMMM>",
409
                field_symbol: fields::Month::Format,
1✔
410
                field_length: FieldLength::Five,
1✔
411
                expected: "<л>",
412
            },
413
            // 'L' and 'LL' are numeric
414
            TestCase {
1✔
415
                pattern: "<LLL>",
416
                field_symbol: fields::Month::StandAlone,
1✔
417
                field_length: FieldLength::Three,
1✔
418
                expected: "<лист.>",
419
            },
420
            TestCase {
1✔
421
                pattern: "<LLLL>",
422
                field_symbol: fields::Month::StandAlone,
1✔
423
                field_length: FieldLength::Four,
1✔
424
                expected: "<листопад>",
425
            },
426
            TestCase {
1✔
427
                pattern: "<LLLLL>",
428
                field_symbol: fields::Month::StandAlone,
1✔
429
                field_length: FieldLength::Five,
1✔
430
                expected: "<Л>",
431
            },
432
        ];
433
        for cas in cases {
1✔
434
            let TestCase {
435
                pattern,
6✔
436
                field_symbol,
6✔
437
                field_length,
6✔
438
                expected,
6✔
439
            } = cas;
440
            let mut names: TypedDateTimeNames<Gregorian> =
441
                TypedDateTimeNames::try_new(&locale).unwrap();
6✔
442
            names
18✔
443
                .load_month_names(&crate::provider::Baked, field_symbol, field_length)
444
                .unwrap();
445
            let pattern: DateTimePattern = pattern.parse().unwrap();
6✔
446
            let datetime = DateTime::try_new_gregorian(2023, 11, 17, 13, 41, 28).unwrap();
18✔
447
            let formatted_pattern = names.with_pattern(&pattern).format(&datetime);
6✔
448

449
            assert_try_writeable_eq!(formatted_pattern, expected, Ok(()), "{cas:?}");
12✔
450
        }
17✔
451
    }
12✔
452

453
    #[test]
454
    fn test_weekday_coverage() {
2✔
455
        let locale = locale!("uk").into();
1✔
NEW
456
        #[derive(Debug)]
×
457
        struct TestCase {
458
            pattern: &'static str,
NEW
459
            field_symbol: fields::Weekday,
×
NEW
460
            field_length: FieldLength,
×
NEW
461
            expected: &'static str,
×
462
        }
463
        let cases = [
1✔
464
            TestCase {
1✔
465
                pattern: "<E>",
466
                field_symbol: fields::Weekday::Format,
1✔
467
                field_length: FieldLength::Three,
1✔
468
                expected: "<пт>",
469
            },
470
            TestCase {
1✔
471
                pattern: "<EE>",
472
                field_symbol: fields::Weekday::Format,
1✔
473
                field_length: FieldLength::Three,
1✔
474
                expected: "<пт>",
475
            },
476
            TestCase {
1✔
477
                pattern: "<EEE>",
478
                field_symbol: fields::Weekday::Format,
1✔
479
                field_length: FieldLength::Three,
1✔
480
                expected: "<пт>",
481
            },
482
            TestCase {
1✔
483
                pattern: "<EEEE>",
484
                field_symbol: fields::Weekday::Format,
1✔
485
                field_length: FieldLength::Four,
1✔
486
                expected: "<пʼятниця>",
487
            },
488
            TestCase {
1✔
489
                pattern: "<EEEEE>",
490
                field_symbol: fields::Weekday::Format,
1✔
491
                field_length: FieldLength::Five,
1✔
492
                expected: "<П>",
493
            },
494
            TestCase {
1✔
495
                pattern: "<EEEEEE>",
496
                field_symbol: fields::Weekday::Format,
1✔
497
                field_length: FieldLength::Six,
1✔
498
                expected: "<пт>",
499
            },
500
            // 'e' and 'ee' are numeric
501
            TestCase {
1✔
502
                pattern: "<eee>",
503
                field_symbol: fields::Weekday::Format,
1✔
504
                field_length: FieldLength::Three,
1✔
505
                expected: "<пт>",
506
            },
507
            TestCase {
1✔
508
                pattern: "<eeee>",
509
                field_symbol: fields::Weekday::Format,
1✔
510
                field_length: FieldLength::Four,
1✔
511
                expected: "<пʼятниця>",
512
            },
513
            TestCase {
1✔
514
                pattern: "<eeeee>",
515
                field_symbol: fields::Weekday::Format,
1✔
516
                field_length: FieldLength::Five,
1✔
517
                expected: "<П>",
518
            },
519
            TestCase {
1✔
520
                pattern: "<eeeeee>",
521
                field_symbol: fields::Weekday::Format,
1✔
522
                field_length: FieldLength::Six,
1✔
523
                expected: "<пт>",
524
            },
525
            // 'c' and 'cc' are numeric
526
            TestCase {
1✔
527
                pattern: "<ccc>",
528
                field_symbol: fields::Weekday::StandAlone,
1✔
529
                field_length: FieldLength::Three,
1✔
530
                expected: "<пт>",
531
            },
532
            TestCase {
1✔
533
                pattern: "<cccc>",
534
                field_symbol: fields::Weekday::StandAlone,
1✔
535
                field_length: FieldLength::Four,
1✔
536
                expected: "<пʼятниця>",
537
            },
538
            TestCase {
1✔
539
                pattern: "<ccccc>",
540
                field_symbol: fields::Weekday::StandAlone,
1✔
541
                field_length: FieldLength::Five,
1✔
542
                expected: "<П>",
543
            },
544
            TestCase {
1✔
545
                pattern: "<cccccc>",
546
                field_symbol: fields::Weekday::StandAlone,
1✔
547
                field_length: FieldLength::Six,
1✔
548
                expected: "<пт>",
549
            },
550
        ];
551
        for cas in cases {
1✔
552
            let TestCase {
553
                pattern,
14✔
554
                field_symbol,
14✔
555
                field_length,
14✔
556
                expected,
14✔
557
            } = cas;
558
            let mut names: TypedDateTimeNames<Gregorian> =
559
                TypedDateTimeNames::try_new(&locale).unwrap();
14✔
560
            names
42✔
561
                .load_weekday_names(&crate::provider::Baked, field_symbol, field_length)
562
                .unwrap();
563
            let pattern: DateTimePattern = pattern.parse().unwrap();
14✔
564
            let datetime = DateTime::try_new_gregorian(2023, 11, 17, 13, 41, 28).unwrap();
42✔
565
            let formatted_pattern = names.with_pattern(&pattern).format(&datetime);
14✔
566

567
            assert_try_writeable_eq!(formatted_pattern, expected, Ok(()), "{cas:?}");
28✔
568
        }
41✔
569
    }
28✔
570

571
    #[test]
572
    fn test_dayperiod_coverage() {
2✔
573
        // Thai has different values for different lengths of day periods
574
        // TODO(#487): Support flexible day periods, too
575
        let locale = locale!("th").into();
1✔
NEW
576
        #[derive(Debug)]
×
577
        struct TestCase {
578
            pattern: &'static str,
NEW
579
            field_length: FieldLength,
×
NEW
580
            expected: &'static str,
×
581
        }
582
        let cases = [
1✔
583
            TestCase {
1✔
584
                pattern: "<a>",
585
                field_length: FieldLength::Three,
1✔
586
                expected: "<PM>",
587
            },
588
            TestCase {
1✔
589
                pattern: "<aa>",
590
                field_length: FieldLength::Three,
1✔
591
                expected: "<PM>",
592
            },
593
            TestCase {
1✔
594
                pattern: "<aaa>",
595
                field_length: FieldLength::Three,
1✔
596
                expected: "<PM>",
597
            },
598
            TestCase {
1✔
599
                pattern: "<aaaa>",
600
                field_length: FieldLength::Four,
1✔
601
                expected: "<หลังเที่ยง>",
602
            },
603
            TestCase {
1✔
604
                pattern: "<aaaaa>",
605
                field_length: FieldLength::Five,
1✔
606
                expected: "<p>",
607
            },
608
            TestCase {
1✔
609
                pattern: "<b>",
610
                field_length: FieldLength::Three,
1✔
611
                expected: "<PM>",
612
            },
613
            TestCase {
1✔
614
                pattern: "<bb>",
615
                field_length: FieldLength::Three,
1✔
616
                expected: "<PM>",
617
            },
618
            TestCase {
1✔
619
                pattern: "<bbb>",
620
                field_length: FieldLength::Three,
1✔
621
                expected: "<PM>",
622
            },
623
            TestCase {
1✔
624
                pattern: "<bbbb>",
625
                field_length: FieldLength::Four,
1✔
626
                expected: "<หลังเที่ยง>",
627
            },
628
            TestCase {
1✔
629
                pattern: "<bbbbb>",
630
                field_length: FieldLength::Five,
1✔
631
                expected: "<p>",
632
            },
633
        ];
634
        for cas in cases {
1✔
635
            let TestCase {
636
                pattern,
10✔
637
                field_length,
10✔
638
                expected,
10✔
639
            } = cas;
640
            let mut names: TypedDateTimeNames<Gregorian> =
641
                TypedDateTimeNames::try_new(&locale).unwrap();
10✔
642
            names
30✔
643
                .load_day_period_names(&crate::provider::Baked, field_length)
644
                .unwrap();
645
            let pattern: DateTimePattern = pattern.parse().unwrap();
10✔
646
            let datetime = DateTime::try_new_gregorian(2023, 11, 17, 13, 41, 28).unwrap();
30✔
647
            let formatted_pattern = names.with_pattern(&pattern).format(&datetime);
10✔
648

649
            assert_try_writeable_eq!(formatted_pattern, expected, Ok(()), "{cas:?}");
20✔
650
        }
29✔
651
    }
20✔
652
}
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