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

zbraniecki / icu4x / 6815798908

09 Nov 2023 05:17PM UTC coverage: 72.607% (-2.4%) from 75.01%
6815798908

push

github

web-flow
Implement `Any/BufferProvider` for some smart pointers (#4255)

Allows storing them as a `Box<dyn Any/BufferProvider>` without using a
wrapper type that implements the trait.

44281 of 60987 relevant lines covered (72.61%)

201375.86 hits per line

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

89.22
/components/datetime/src/format/datetime.rs
1
// This file is part of ICU4X. For terms of use, please see the file
1✔
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 crate::error::DateTimeError as Error;
6
use crate::fields::{self, Field, FieldLength, FieldSymbol, Second, Week, Year};
7
use crate::input::{
8
    DateTimeInput, DateTimeInputWithWeekConfig, ExtractedDateTimeInput, LocalizedDateTimeInput,
9
};
10
use crate::pattern::{
11
    runtime::{Pattern, PatternPlurals},
12
    PatternItem,
13
};
14
use crate::provider;
15
use crate::provider::calendar::patterns::PatternPluralsFromPatternsV1Marker;
16
use crate::provider::date_time::{DateSymbols, TimeSymbols};
17

18
use core::fmt;
19
use fixed_decimal::FixedDecimal;
20
use icu_calendar::week::WeekCalculator;
21
use icu_calendar::AnyCalendarKind;
22
use icu_decimal::FixedDecimalFormatter;
23
use icu_plurals::PluralRules;
24
use icu_provider::DataPayload;
25
use writeable::Writeable;
26

27
/// [`FormattedDateTime`] is a intermediate structure which can be retrieved as
28
/// an output from [`TypedDateTimeFormatter`](crate::TypedDateTimeFormatter).
29
///
30
/// The structure contains all the information needed to display formatted value,
31
/// and it will also contain additional methods allowing the user to introspect
32
/// and even manipulate the formatted data.
33
///
34
/// # Examples
35
///
36
/// ```no_run
37
/// use icu::calendar::{DateTime, Gregorian};
38
/// use icu::datetime::TypedDateTimeFormatter;
39
/// use icu::locid::locale;
40
/// let dtf = TypedDateTimeFormatter::<Gregorian>::try_new(
41
///     &locale!("en").into(),
42
///     Default::default(),
43
/// )
44
/// .expect("Failed to create TypedDateTimeFormatter instance.");
45
///
46
/// let datetime = DateTime::try_new_gregorian_datetime(2020, 9, 1, 12, 34, 28)
47
///     .expect("Failed to construct DateTime.");
48
///
49
/// let formatted_date = dtf.format(&datetime);
50
///
51
/// let _ = format!("Date: {}", formatted_date);
52
/// ```
53
#[derive(Debug)]
×
54
pub struct FormattedDateTime<'l> {
55
    pub(crate) patterns: &'l DataPayload<PatternPluralsFromPatternsV1Marker>,
×
56
    pub(crate) date_symbols: Option<&'l provider::calendar::DateSymbolsV1<'l>>,
×
57
    pub(crate) time_symbols: Option<&'l provider::calendar::TimeSymbolsV1<'l>>,
×
58
    pub(crate) datetime: ExtractedDateTimeInput,
×
59
    pub(crate) week_data: Option<&'l WeekCalculator>,
×
60
    pub(crate) ordinal_rules: Option<&'l PluralRules>,
×
61
    pub(crate) fixed_decimal_format: &'l FixedDecimalFormatter,
×
62
}
63

64
impl<'l> Writeable for FormattedDateTime<'l> {
65
    fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
1,415✔
66
        write_pattern_plurals(
1,415✔
67
            &self.patterns.get().0,
1,415✔
68
            self.date_symbols,
1,415✔
69
            self.time_symbols,
1,415✔
70
            &self.datetime,
1,415✔
71
            self.week_data,
1,415✔
72
            self.ordinal_rules,
1,415✔
73
            self.fixed_decimal_format,
1,415✔
74
            sink,
75
        )
76
        .map_err(|_| core::fmt::Error)
×
77
    }
1,415✔
78

79
    // TODO(#489): Implement writeable_length_hint
80
}
81

82
impl<'l> fmt::Display for FormattedDateTime<'l> {
83
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
700✔
84
        self.write_to(f)
700✔
85
    }
700✔
86
}
87

88
// Apply length to input number and write to result using fixed_decimal_format.
89
fn format_number<W>(
3,723✔
90
    result: &mut W,
91
    fixed_decimal_format: &FixedDecimalFormatter,
92
    mut num: FixedDecimal,
93
    length: FieldLength,
94
) -> fmt::Result
95
where
96
    W: fmt::Write + ?Sized,
97
{
98
    match length {
3,723✔
99
        FieldLength::One | FieldLength::NumericOverride(_) => {}
100
        FieldLength::TwoDigit => {
101
            num.pad_start(2);
1,752✔
102
            num.set_max_position(2);
1,752✔
103
        }
104
        FieldLength::Abbreviated => {
105
            num.pad_start(3);
5✔
106
        }
107
        FieldLength::Wide => {
108
            num.pad_start(4);
5✔
109
        }
110
        FieldLength::Narrow => {
111
            num.pad_start(5);
×
112
        }
113
        FieldLength::Six => {
114
            num.pad_start(6);
×
115
        }
116
        FieldLength::Fixed(p) => {
×
117
            num.pad_start(p as i16);
×
118
            num.set_max_position(p as i16);
×
119
        }
120
    }
121

122
    let formatted = fixed_decimal_format.format(&num);
3,723✔
123
    formatted.write_to(result)
3,722✔
124
}
3,721✔
125

126
fn write_pattern<T, W>(
1,416✔
127
    pattern: &crate::pattern::runtime::Pattern,
128
    date_symbols: Option<&provider::calendar::DateSymbolsV1>,
129
    time_symbols: Option<&provider::calendar::TimeSymbolsV1>,
130
    loc_datetime: &impl LocalizedDateTimeInput<T>,
131
    fixed_decimal_format: &FixedDecimalFormatter,
132
    w: &mut W,
133
) -> Result<(), Error>
134
where
135
    T: DateTimeInput,
136
    W: fmt::Write + ?Sized,
137
{
138
    let mut iter = pattern.items.iter().peekable();
1,416✔
139
    loop {
140
        match iter.next() {
11,728✔
141
            Some(PatternItem::Field(field)) => write_field(
10,428✔
142
                pattern,
143
                field,
5,214✔
144
                iter.peek(),
5,214✔
145
                date_symbols,
146
                time_symbols,
147
                loc_datetime,
148
                fixed_decimal_format,
149
                w,
150
            )?,
×
151
            Some(PatternItem::Literal(ch)) => w.write_char(ch)?,
5,098✔
152
            None => break,
153
        }
154
    }
155
    Ok(())
1,416✔
156
}
1,416✔
157

158
#[allow(clippy::too_many_arguments)]
159
pub fn write_pattern_plurals<T, W>(
1,415✔
160
    patterns: &PatternPlurals,
161
    date_symbols: Option<&provider::calendar::DateSymbolsV1>,
162
    time_symbols: Option<&provider::calendar::TimeSymbolsV1>,
163
    datetime: &T,
164
    week_data: Option<&WeekCalculator>,
165
    ordinal_rules: Option<&PluralRules>,
166
    fixed_decimal_format: &FixedDecimalFormatter,
167
    w: &mut W,
168
) -> Result<(), Error>
169
where
170
    T: DateTimeInput,
171
    W: fmt::Write + ?Sized,
172
{
173
    let loc_datetime = DateTimeInputWithWeekConfig::new(datetime, week_data);
1,415✔
174
    let pattern = patterns.select(&loc_datetime, ordinal_rules)?;
1,415✔
175
    write_pattern(
1,415✔
176
        pattern,
177
        date_symbols,
178
        time_symbols,
179
        &loc_datetime,
180
        fixed_decimal_format,
181
        w,
182
    )
183
}
1,415✔
184

185
const CHINESE_CYCLIC_YEARS: [&str; 60] = [
186
    "甲子", "乙丑", "丙寅", "丁卯", "戊辰", "己巳", "庚午", "辛未", "壬申", "癸酉", "甲戌", "乙亥",
187
    "丙子", "丁丑", "戊寅", "己卯", "庚辰", "辛巳", "壬午", "癸未", "甲申", "乙酉", "丙戌", "丁亥",
188
    "戊子", "己丑", "庚寅", "辛卯", "壬辰", "癸巳", "甲午", "乙未", "丙申", "丁酉", "戊戌", "己亥",
189
    "庚子", "辛丑", "壬寅", "癸卯", "甲辰", "乙巳", "丙午", "丁未", "戊申", "己酉", "庚戌", "辛亥",
190
    "壬子", "癸丑", "甲寅", "乙卯", "丙辰", "丁巳", "戊午", "己未", "庚申", "辛酉", "壬戌", "癸亥",
191
];
192
const DANGI_CYCLIC_YEARS: [&str; 60] = [
193
    "갑자", "을축", "병인", "정묘", "무진", "기사", "경오", "신미", "임신", "계유", "갑술", "을해",
194
    "병자", "정축", "무인", "기묘", "경진", "신사", "임오", "계미", "갑신", "을유", "병술", "정해",
195
    "무자", "기축", "경인", "신묘", "임진", "계사", "갑오", "을미", "병신", "정유", "무술", "기해",
196
    "경자", "신축", "임인", "계묘", "갑진", "을사", "병오", "정미", "무신", "기유", "경술", "신해",
197
    "임자", "계축", "갑인", "을묘", "병진", "정사", "무오", "기미", "경신", "신유", "임술", "계해",
198
];
199

200
const CHINESE_LEAP_PREFIX: &str = "閏";
201
const DANGI_LEAP_PREFIX: &str = "윤";
202
const PLACEHOLDER_LEAP_PREFIX: &str = "(leap)";
203
// This function assumes that the correct decision has been
204
// made regarding availability of symbols in the caller.
205
//
206
// When modifying the list of fields using symbols,
207
// update the matching query in `analyze_pattern` function.
208
#[allow(clippy::too_many_arguments)]
209
pub(super) fn write_field<T, W>(
5,632✔
210
    pattern: &crate::pattern::runtime::Pattern,
211
    field: fields::Field,
212
    next_item: Option<&PatternItem>,
213
    date_symbols: Option<&crate::provider::calendar::DateSymbolsV1>,
214
    time_symbols: Option<&crate::provider::calendar::TimeSymbolsV1>,
215
    datetime: &impl LocalizedDateTimeInput<T>,
216
    fixed_decimal_format: &FixedDecimalFormatter,
217
    w: &mut W,
218
) -> Result<(), Error>
219
where
220
    T: DateTimeInput,
221
    W: fmt::Write + ?Sized,
222
{
223
    match field.symbol {
5,632✔
224
        FieldSymbol::Era => {
225
            let era = datetime
868✔
226
                .datetime()
227
                .year()
228
                .ok_or(Error::MissingInputField(Some("year")))?
434✔
229
                .era;
230
            let symbol = date_symbols
434✔
231
                .ok_or(Error::MissingDateSymbols)?
434✔
232
                .get_symbol_for_era(field.length, &era);
434✔
233
            w.write_str(symbol)?
434✔
234
        }
235
        FieldSymbol::Year(year) => match year {
915✔
236
            Year::Calendar => format_number(
773✔
237
                w,
238
                fixed_decimal_format,
239
                FixedDecimal::from(
773✔
240
                    datetime
1,546✔
241
                        .datetime()
242
                        .year()
243
                        .ok_or(Error::MissingInputField(Some("year")))?
773✔
244
                        .number,
245
                ),
246
                field.length,
773✔
247
            )?,
248
            Year::WeekOf => format_number(
58✔
249
                w,
250
                fixed_decimal_format,
251
                FixedDecimal::from(datetime.week_of_year()?.0.number),
58✔
252
                field.length,
58✔
253
            )?,
254
            Year::Cyclic => {
255
                let datetime = datetime.datetime();
42✔
256
                let cyclic = datetime
84✔
257
                    .year()
258
                    .ok_or(Error::MissingInputField(Some("year")))?
42✔
259
                    .cyclic
260
                    .ok_or(Error::MissingInputField(Some("cyclic")))?;
42✔
261
                // TODO(#3761): This is a hack, we should use actual data for cyclic years
262
                let cyclics = match datetime.any_calendar_kind() {
42✔
263
                    Some(AnyCalendarKind::Dangi) => &DANGI_CYCLIC_YEARS,
18✔
264
                    _ => &CHINESE_CYCLIC_YEARS, /* for now assume all other calendars use the stem-branch model */
24✔
265
                };
266
                let cyclic_str = cyclics.get(usize::from(cyclic.get()) - 1).ok_or(
84✔
267
                    icu_calendar::CalendarError::Overflow {
42✔
268
                        field: "cyclic",
269
                        max: 60,
270
                    },
271
                )?;
×
272
                w.write_str(cyclic_str)?;
42✔
273
            }
274
            Year::RelatedIso => {
275
                format_number(
42✔
276
                    w,
277
                    fixed_decimal_format,
278
                    FixedDecimal::from(
42✔
279
                        datetime
84✔
280
                            .datetime()
281
                            .year()
282
                            .ok_or(Error::MissingInputField(Some("year")))?
42✔
283
                            .related_iso
284
                            .ok_or(Error::MissingInputField(Some("related_iso")))?,
42✔
285
                    ),
286
                    field.length,
42✔
287
                )?;
288
            }
289
        },
290
        FieldSymbol::Month(month) => match field.length {
744✔
291
            FieldLength::One | FieldLength::TwoDigit => format_number(
177✔
292
                w,
293
                fixed_decimal_format,
294
                FixedDecimal::from(
177✔
295
                    datetime
354✔
296
                        .datetime()
297
                        .month()
298
                        .ok_or(Error::MissingInputField(Some("month")))?
177✔
299
                        .ordinal,
300
                ),
301
                field.length,
177✔
302
            )?,
303
            length => {
567✔
304
                let datetime = datetime.datetime();
567✔
305
                let code = datetime
1,134✔
306
                    .month()
307
                    .ok_or(Error::MissingInputField(Some("month")))?
567✔
308
                    .code;
309

310
                let (symbol, is_leap) = date_symbols
567✔
311
                    .ok_or(Error::MissingDateSymbols)?
567✔
312
                    .get_symbol_for_month(month, length, code)?;
567✔
313

314
                // FIXME (#3766) this should be using actual data for leap months
315
                if is_leap {
567✔
316
                    let leap_str = match datetime.any_calendar_kind() {
12✔
317
                        Some(AnyCalendarKind::Chinese) => CHINESE_LEAP_PREFIX,
12✔
318
                        Some(AnyCalendarKind::Dangi) => DANGI_LEAP_PREFIX,
×
319
                        _ => PLACEHOLDER_LEAP_PREFIX,
×
320
                    };
321
                    w.write_str(leap_str)?;
12✔
322
                }
323
                w.write_str(symbol)?;
567✔
324
            }
325
        },
326
        FieldSymbol::Week(week) => match week {
82✔
327
            Week::WeekOfYear => format_number(
70✔
328
                w,
329
                fixed_decimal_format,
330
                FixedDecimal::from(datetime.week_of_year()?.1 .0),
70✔
331
                field.length,
70✔
332
            )?,
333
            Week::WeekOfMonth => format_number(
12✔
334
                w,
335
                fixed_decimal_format,
336
                FixedDecimal::from(datetime.week_of_month()?.0),
12✔
337
                field.length,
12✔
338
            )?,
339
        },
340
        FieldSymbol::Weekday(weekday) => {
346✔
341
            let dow = datetime
692✔
342
                .datetime()
343
                .iso_weekday()
344
                .ok_or(Error::MissingInputField(Some("iso_weekday")))?;
346✔
345
            let symbol = date_symbols
346✔
346
                .ok_or(Error::MissingDateSymbols)?
346✔
347
                .get_symbol_for_weekday(weekday, field.length, dow)?;
346✔
348
            w.write_str(symbol)?
346✔
349
        }
350
        symbol @ FieldSymbol::Day(day) => format_number(
1,422✔
351
            w,
352
            fixed_decimal_format,
353
            FixedDecimal::from(match day {
1,422✔
354
                fields::Day::DayOfMonth => {
355
                    datetime
1,410✔
356
                        .datetime()
357
                        .day_of_month()
358
                        .ok_or(Error::MissingInputField(Some("day_of_month")))?
705✔
359
                        .0
360
                }
705✔
361
                fields::Day::DayOfWeekInMonth => datetime.day_of_week_in_month()?.0,
6✔
362
                _ => return Err(Error::UnsupportedField(symbol)),
×
363
            }),
364
            field.length,
711✔
365
        )?,
366
        FieldSymbol::Hour(hour) => {
722✔
367
            let h = usize::from(
722✔
368
                datetime
1,444✔
369
                    .datetime()
370
                    .hour()
371
                    .ok_or(Error::MissingInputField(Some("hour")))?,
722✔
372
            ) as isize;
373
            let value = match hour {
722✔
374
                fields::Hour::H11 => h % 12,
34✔
375
                fields::Hour::H12 => {
376
                    let v = h % 12;
273✔
377
                    if v == 0 {
273✔
378
                        12
170✔
379
                    } else {
380
                        v
103✔
381
                    }
382
                }
383
                fields::Hour::H23 => h,
381✔
384
                fields::Hour::H24 => {
385
                    if h == 0 {
34✔
386
                        24
34✔
387
                    } else {
388
                        h
×
389
                    }
390
                }
391
            };
392
            format_number(
722✔
393
                w,
394
                fixed_decimal_format,
395
                FixedDecimal::from(value),
722✔
396
                field.length,
722✔
397
            )?
398
        }
399
        FieldSymbol::Minute => format_number(
672✔
400
            w,
401
            fixed_decimal_format,
402
            FixedDecimal::from(usize::from(
672✔
403
                datetime
1,344✔
404
                    .datetime()
405
                    .minute()
406
                    .ok_or(Error::MissingInputField(Some("minute")))?,
672✔
407
            )),
408
            field.length,
672✔
409
        )?,
410
        FieldSymbol::Second(Second::Second) => {
411
            let mut seconds = FixedDecimal::from(usize::from(
465✔
412
                datetime
930✔
413
                    .datetime()
414
                    .second()
415
                    .ok_or(Error::MissingInputField(Some("second")))?,
465✔
416
            ));
417
            if let Some(PatternItem::Field(next_field)) = next_item {
465✔
418
                if let FieldSymbol::Second(Second::FractionalSecond) = next_field.symbol {
36✔
419
                    let mut fraction = FixedDecimal::from(usize::from(
18✔
420
                        datetime
18✔
421
                            .datetime()
422
                            .nanosecond()
423
                            .ok_or(Error::MissingInputField(Some("nanosecond")))?,
18✔
424
                    ));
18✔
425

426
                    // We only support fixed field length for fractional seconds.
427
                    let precision = match next_field.length {
18✔
428
                        FieldLength::Fixed(p) => p,
18✔
429
                        _ => {
430
                            return Err(Error::Pattern(
×
431
                                crate::pattern::PatternError::FieldLengthInvalid(
×
432
                                    FieldSymbol::Second(Second::FractionalSecond),
×
433
                                ),
434
                            ));
435
                        }
436
                    };
437

438
                    // We store fractional seconds as nanoseconds, convert to seconds.
439
                    fraction.multiply_pow10(-9);
18✔
440

441
                    seconds
18✔
442
                        .concatenate_end(fraction)
18✔
443
                        .map_err(|_| Error::FixedDecimal)?;
×
444
                    seconds.pad_end(-(precision as i16));
18✔
445
                }
18✔
446
            }
447
            format_number(w, fixed_decimal_format, seconds, field.length)?
465✔
448
        }
465✔
449
        FieldSymbol::Second(Second::FractionalSecond) => {
450
            // Formatting of fractional seconds is handled when formatting seconds.
451
        }
452
        field @ FieldSymbol::Second(Second::Millisecond) => {
×
453
            return Err(Error::UnsupportedField(field))
×
454
        }
455
        FieldSymbol::DayPeriod(period) => {
523✔
456
            let symbol = time_symbols
523✔
457
                .ok_or(Error::MissingTimeSymbols)?
523✔
458
                .get_symbol_for_day_period(
459
                    period,
460
                    field.length,
523✔
461
                    datetime
1,046✔
462
                        .datetime()
463
                        .hour()
464
                        .ok_or(Error::MissingInputField(Some("hour")))?,
523✔
465
                    pattern.time_granularity.is_top_of_hour(
1,046✔
466
                        datetime.datetime().minute().map(u8::from).unwrap_or(0),
523✔
467
                        datetime.datetime().second().map(u8::from).unwrap_or(0),
523✔
468
                        datetime.datetime().nanosecond().map(u32::from).unwrap_or(0),
523✔
469
                    ),
470
                )?;
×
471
            w.write_str(symbol)?
523✔
472
        }
473
        field @ FieldSymbol::TimeZone(_) => return Err(Error::UnsupportedField(field)),
×
474
    };
475
    Ok(())
5,632✔
476
}
5,632✔
477

478
/// What data is required to format a given pattern.
479
#[derive(Default)]
1,402✔
480
pub struct RequiredData {
481
    // DateSymbolsV1 is required.
482
    pub date_symbols_data: bool,
701✔
483
    // TimeSymbolsV1 is required.
484
    pub time_symbols_data: bool,
701✔
485
    // WeekDataV1 is required.
486
    pub week_data: bool,
701✔
487
}
488

489
impl RequiredData {
490
    // Checks if formatting `pattern` would require us to load data & if so adds
491
    // them to this struct. Returns true if requirements are saturated and would
492
    // not change by any further calls.
493
    // Keep it in sync with the `write_field` use of symbols.
494
    fn add_requirements_from_pattern(
1,272✔
495
        &mut self,
496
        pattern: &Pattern,
497
        supports_time_zones: bool,
498
    ) -> Result<bool, Field> {
499
        let fields = pattern.items.iter().filter_map(|p| match p {
5,837✔
500
            PatternItem::Field(field) => Some(field),
2,309✔
501
            _ => None,
2,256✔
502
        });
4,565✔
503

504
        for field in fields {
3,581✔
505
            if !self.date_symbols_data {
4,095✔
506
                self.date_symbols_data = match field.symbol {
2,442✔
507
                    FieldSymbol::Era => true,
60✔
508
                    FieldSymbol::Month(_) => {
509
                        !matches!(field.length, FieldLength::One | FieldLength::TwoDigit)
182✔
510
                    }
511
                    FieldSymbol::Weekday(_) => true,
110✔
512
                    _ => false,
869✔
513
                }
514
            }
515
            if !self.time_symbols_data {
5,152✔
516
                self.time_symbols_data = matches!(field.symbol, FieldSymbol::DayPeriod(_));
2,278✔
517
            }
518

519
            if !self.week_data {
5,117✔
520
                self.week_data = matches!(
2,807✔
521
                    field.symbol,
2,807✔
522
                    FieldSymbol::Year(Year::WeekOf) | FieldSymbol::Week(_)
523
                )
524
            }
525

526
            if supports_time_zones {
2,310✔
527
                if self.date_symbols_data && self.time_symbols_data && self.week_data {
346✔
528
                    // If we support time zones, and require everything else, we
529
                    // know all we need to return already.
530
                    return Ok(true);
×
531
                }
532
            } else if matches!(field.symbol, FieldSymbol::TimeZone(_)) {
1,964✔
533
                // If we don't support time zones, and encountered a time zone
534
                // field, error out.
535
                return Err(field);
1✔
536
            }
537
        }
538

539
        Ok(false)
707✔
540
    }
708✔
541
}
542

543
// Determines what optional data needs to be loaded to format `patterns`.
544
pub fn analyze_patterns(
701✔
545
    patterns: &PatternPlurals,
546
    supports_time_zones: bool,
547
) -> Result<RequiredData, Field> {
548
    let mut required = RequiredData::default();
701✔
549
    for pattern in patterns.patterns_iter() {
1,408✔
550
        if required.add_requirements_from_pattern(pattern, supports_time_zones)? {
708✔
551
            // We can bail early if everything is required & we don't need to
552
            // validate the absence of TimeZones.
553
            break;
554
        }
555
    }
556
    Ok(required)
700✔
557
}
701✔
558

559
#[cfg(test)]
560
#[allow(unused_imports)]
561
mod tests {
562
    use super::*;
563
    use icu_decimal::options::{FixedDecimalFormatterOptions, GroupingStrategy};
564
    use icu_locid::Locale;
565

566
    #[test]
567
    fn test_mixed_calendar_eras() {
2✔
568
        use icu::calendar::japanese::JapaneseExtended;
569
        use icu::calendar::Date;
570
        use icu::datetime::options::length;
571
        use icu::datetime::DateFormatter;
572

573
        let locale: Locale = "en-u-ca-japanese".parse().unwrap();
1✔
574
        let dtf = DateFormatter::try_new_with_length(&locale.into(), length::Date::Medium)
1✔
575
            .expect("DateTimeFormat construction succeeds");
1✔
576

577
        let date = Date::try_new_gregorian_date(1800, 9, 1).expect("Failed to construct Date.");
1✔
578
        let date = date
1✔
579
            .to_calendar(JapaneseExtended::new())
1✔
580
            .into_japanese_date()
581
            .to_any();
1✔
582

583
        writeable::assert_writeable_eq!(dtf.format(&date).unwrap(), "Sep 1, 12 kansei-1789")
1✔
584
    }
2✔
585

586
    #[test]
587
    #[cfg(feature = "serde")]
588
    fn test_basic() {
2✔
589
        use crate::provider::calendar::{GregorianDateSymbolsV1Marker, TimeSymbolsV1Marker};
590
        use icu_calendar::DateTime;
591
        use icu_provider::prelude::*;
592

593
        let locale = "en-u-ca-gregory".parse::<Locale>().unwrap().into();
1✔
594
        let req = DataRequest {
1✔
595
            locale: &locale,
596
            metadata: Default::default(),
1✔
597
        };
598
        let date_data: DataPayload<GregorianDateSymbolsV1Marker> = crate::provider::Baked
1✔
599
            .load(req)
1✔
600
            .unwrap()
601
            .take_payload()
602
            .unwrap();
603
        let time_data: DataPayload<TimeSymbolsV1Marker> = crate::provider::Baked
1✔
604
            .load(req)
1✔
605
            .unwrap()
606
            .take_payload()
607
            .unwrap();
608
        let pattern = "MMM".parse().unwrap();
1✔
609
        let datetime = DateTime::try_new_gregorian_datetime(2020, 8, 1, 12, 34, 28).unwrap();
1✔
610
        let fixed_decimal_format =
611
            FixedDecimalFormatter::try_new(&locale, Default::default()).unwrap();
1✔
612

613
        let mut sink = String::new();
1✔
614
        let loc_datetime = DateTimeInputWithWeekConfig::new(&datetime, None);
1✔
615
        write_pattern(
1✔
616
            &pattern,
617
            Some(date_data.get()),
1✔
618
            Some(time_data.get()),
1✔
619
            &loc_datetime,
620
            &fixed_decimal_format,
621
            &mut sink,
622
        )
623
        .unwrap();
624
        println!("{sink}");
1✔
625
    }
2✔
626

627
    #[test]
628
    fn test_format_number() {
2✔
629
        let values = &[2, 20, 201, 2017, 20173];
1✔
630
        let samples = &[
1✔
631
            (FieldLength::One, ["2", "20", "201", "2017", "20173"]),
632
            (FieldLength::TwoDigit, ["02", "20", "01", "17", "73"]),
633
            (
634
                FieldLength::Abbreviated,
635
                ["002", "020", "201", "2017", "20173"],
636
            ),
637
            (FieldLength::Wide, ["0002", "0020", "0201", "2017", "20173"]),
638
        ];
639

640
        let mut fixed_decimal_format_options = FixedDecimalFormatterOptions::default();
1✔
641
        fixed_decimal_format_options.grouping_strategy = GroupingStrategy::Never;
1✔
642
        let fixed_decimal_format = FixedDecimalFormatter::try_new(
1✔
643
            &icu_locid::locale!("en").into(),
1✔
644
            fixed_decimal_format_options,
1✔
645
        )
646
        .unwrap();
1✔
647

648
        for (length, expected) in samples {
5✔
649
            for (value, expected) in values.iter().zip(expected) {
4✔
650
                let mut s = String::new();
20✔
651
                format_number(
20✔
652
                    &mut s,
653
                    &fixed_decimal_format,
654
                    FixedDecimal::from(*value),
20✔
655
                    *length,
20✔
656
                )
657
                .unwrap();
658
                assert_eq!(s, *expected);
20✔
659
            }
20✔
660
        }
661
    }
32✔
662
}
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

© 2025 Coveralls, Inc