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

zbraniecki / icu4x / 8219362155

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

push

github

web-flow
Bump diplomat (#4671)

And fix some Dart renames

49581 of 65251 relevant lines covered (75.99%)

519628.46 hits per line

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

83.84
/components/datetime/src/any/zoned_datetime.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 crate::{calendar, options::DateTimeFormatterOptions, raw};
6
use alloc::string::String;
7

8
use icu_provider::prelude::*;
9

10
use crate::helpers::size_test;
11
use crate::input::{DateTimeInput, ExtractedDateTimeInput, TimeZoneInput};
12
use crate::provider::{self, calendar::*, date_time::PatternSelector};
13
use crate::time_zone::TimeZoneFormatterOptions;
14
use crate::{DateTimeError, FormattedZonedDateTime};
15
use icu_calendar::any_calendar::{AnyCalendar, AnyCalendarKind};
16
use icu_calendar::provider::{
17
    ChineseCacheV1Marker, DangiCacheV1Marker, JapaneseErasV1Marker, JapaneseExtendedErasV1Marker,
18
    WeekDataV1Marker,
19
};
20
use icu_calendar::{types::Time, DateTime};
21
use icu_decimal::provider::DecimalSymbolsV1Marker;
22
use icu_plurals::provider::OrdinalV1Marker;
23
use writeable::Writeable;
24

25
size_test!(ZonedDateTimeFormatter, zoned_date_time_formatter_size, 6248);
26

27
/// [`ZonedDateTimeFormatter`] is a formatter capable of formatting
28
/// date/times with time zones from any calendar, selected at runtime. For the difference between this and [`TypedZonedDateTimeFormatter`](crate::TypedZonedDateTimeFormatter),
29
/// please read the [crate root docs][crate].
30
///
31
/// This is equivalently the composition of
32
/// [`DateTimeFormatter`](crate::DateTimeFormatter) and [`TimeZoneFormatter`].
33
///
34
/// [`ZonedDateTimeFormatter`] uses data from the [data provider]s, the selected [`DataLocale`], and the
35
/// provided pattern to collect all data necessary to format a datetime with time zones into that locale.
36
///
37
/// The various pattern symbols specified in UTS-35 require different sets of data for formatting.
38
/// As such, `TimeZoneFormatter` will pull in only the resources it needs to format that pattern
39
/// that is derived from the provided [`DateTimeFormatterOptions`].
40
///
41
/// For that reason, one should think of the process of formatting a zoned datetime in two steps:
42
/// first, a computationally heavy construction of [`ZonedDateTimeFormatter`], and then fast formatting
43
/// of the data using the instance.
44
///
45
#[doc = zoned_date_time_formatter_size!()]
46
///
47
/// # Examples
48
///
49
/// Using a GMT time zone:
50
///
51
/// ```
52
/// use icu::calendar::DateTime;
53
/// use icu::datetime::{options::length, ZonedDateTimeFormatter};
54
/// use icu::locid::locale;
55
/// use icu::timezone::CustomTimeZone;
56
/// use writeable::assert_writeable_eq;
57
///
58
/// let options = length::Bag::from_date_time_style(
59
///     length::Date::Medium,
60
///     length::Time::Long,
61
/// );
62
/// let zdtf = ZonedDateTimeFormatter::try_new(
63
///     &locale!("en").into(),
64
///     options.into(),
65
///     Default::default(),
66
/// )
67
/// .expect("Failed to create ZonedDateTimeFormatter instance.");
68
///
69
/// let datetime = DateTime::try_new_iso_datetime(2020, 9, 1, 12, 34, 28)
70
///     .expect("Failed to construct DateTime.");
71
/// let any_datetime = datetime.to_any();
72
///
73
/// let time_zone = CustomTimeZone::utc();
74
///
75
/// assert_writeable_eq!(
76
///     zdtf.format(&any_datetime, &time_zone)
77
///         .expect("Calendars should match"),
78
///     "Sep 1, 2020, 12:34:28 PM GMT"
79
/// );
80
/// ```
81
///
82
/// Using a non-GMT time zone, specified by id:
83
///
84
/// ```
85
/// use icu::calendar::DateTime;
86
/// use icu::datetime::{options::length, ZonedDateTimeFormatter};
87
/// use icu::locid::locale;
88
/// use icu::timezone::{CustomTimeZone, GmtOffset, MetazoneCalculator, ZoneVariant};
89
/// use tinystr::TinyAsciiStr;
90
/// use writeable::assert_writeable_eq;
91
///
92
/// let options = length::Bag::from_date_time_style(
93
///     length::Date::Medium,
94
///     length::Time::Full,
95
/// );
96
/// let zdtf = ZonedDateTimeFormatter::try_new(
97
///     &locale!("en").into(),
98
///     options.into(),
99
///     Default::default(),
100
/// )
101
/// .expect("Failed to create ZonedDateTimeFormatter instance.");
102
///
103
/// // Create a DateTime at September 1, 2020 at 12:34:28 PM
104
/// let datetime = DateTime::try_new_iso_datetime(2020, 9, 1, 12, 34, 28)
105
///     .expect("Failed to construct DateTime.");
106
/// let any_datetime = datetime.to_any();
107
///
108
/// // Create a time zone for America/Chicago at GMT-6:
109
/// let mut time_zone = CustomTimeZone::new_empty();
110
/// time_zone.gmt_offset = "-06:00".parse::<GmtOffset>().ok();
111
/// time_zone.time_zone_id = "uschi".parse::<TinyAsciiStr<8>>().ok().map(Into::into);
112
/// time_zone.zone_variant = Some(ZoneVariant::daylight());
113
///
114
/// // Compute the metazone during `datetime` (September 1, 2020 at 12:34:28 PM):
115
/// let mzc = MetazoneCalculator::new();
116
/// time_zone.maybe_calculate_metazone(&mzc, &datetime);
117
///
118
/// assert_writeable_eq!(
119
///     zdtf
120
///       .format(&any_datetime, &time_zone)
121
///       .expect("Calendars should match"),
122
///     "Sep 1, 2020, 12:34:28 PM Central Daylight Time");
123
/// ```
124
///
125
/// [`TimeZoneFormatter`]: crate::time_zone::TimeZoneFormatter
126
#[derive(Debug)]
×
127
pub struct ZonedDateTimeFormatter(raw::ZonedDateTimeFormatter, AnyCalendar);
×
128

129
impl ZonedDateTimeFormatter {
130
    /// Constructor that takes a selected [`DataLocale`] and a list of [`DateTimeFormatterOptions`] and uses compiled data.
131
    /// It collects all data necessary to format zoned datetime values into the given locale.
132
    ///
133
    /// This method will pick the calendar off of the locale; and if unspecified or unknown will fall back to the default
134
    /// calendar for the locale. See [`AnyCalendarKind`] for a list of supported calendars.
135
    ///
136
    /// ✨ *Enabled with the `compiled_data` and `experimental` Cargo features.*
137
    ///
138
    /// [📚 Help choosing a constructor](icu_provider::constructors)
139
    ///
140
    /// <div class="stab unstable">
141
    /// 🚧 This code is experimental; it may change at any time, in breaking or non-breaking ways,
142
    /// including in SemVer minor releases. It can be enabled with the "experimental" Cargo feature
143
    /// of the icu meta-crate. Use with caution.
144
    /// <a href="https://github.com/unicode-org/icu4x/issues/1317">#1317</a>
145
    /// </div>
146
    ///
147
    /// # Examples
148
    ///
149
    /// ```
150
    /// use icu::calendar::DateTime;
151
    /// use icu::datetime::options::components;
152
    /// use icu::datetime::ZonedDateTimeFormatter;
153
    /// use icu::locid::locale;
154
    /// use icu::timezone::CustomTimeZone;
155
    /// use std::str::FromStr;
156
    /// use writeable::assert_writeable_eq;
157
    ///
158
    /// let mut options = components::Bag::default();
159
    /// options.year = Some(components::Year::Numeric);
160
    /// options.month = Some(components::Month::Long);
161
    /// options.hour = Some(components::Numeric::Numeric);
162
    /// options.minute = Some(components::Numeric::Numeric);
163
    /// options.time_zone_name = Some(components::TimeZoneName::GmtOffset);
164
    ///
165
    /// let zdtf = ZonedDateTimeFormatter::try_new_experimental(
166
    ///     &locale!("en-u-ca-gregory").into(),
167
    ///     options.into(),
168
    ///     Default::default(),
169
    /// )
170
    /// .expect("Construction should succeed");
171
    ///
172
    /// let datetime =
173
    ///     DateTime::try_new_iso_datetime(2021, 04, 08, 16, 12, 37).unwrap();
174
    /// let time_zone = CustomTimeZone::from_str("-07:00").unwrap();
175
    /// let any_datetime = datetime.to_any();
176
    ///
177
    /// assert_writeable_eq!(
178
    ///     zdtf.format(&any_datetime, &time_zone).unwrap(),
179
    ///     "April 2021, 16:12 GMT-07:00"
180
    /// );
181
    /// ```
182
    #[cfg(feature = "experimental")]
183
    #[cfg(feature = "compiled_data")]
184
    pub fn try_new_experimental(
1✔
185
        locale: &DataLocale,
186
        date_time_format_options: DateTimeFormatterOptions,
187
        time_zone_format_options: TimeZoneFormatterOptions,
188
    ) -> Result<Self, DateTimeError> {
189
        let calendar = AnyCalendar::new_for_locale(locale);
1✔
190
        let kind = calendar.kind();
1✔
191

192
        let patterns = PatternSelector::for_options_experimental(
1✔
193
            &crate::provider::Baked,
194
            calendar::load_lengths_for_any_calendar_kind(&crate::provider::Baked, locale, kind)?,
1✔
195
            locale,
196
            &kind.as_bcp47_value(),
1✔
197
            &date_time_format_options,
198
        )?;
1✔
199

200
        Ok(Self(
1✔
201
            raw::ZonedDateTimeFormatter::try_new(
1✔
202
                patterns,
1✔
203
                || {
2✔
204
                    calendar::load_symbols_for_any_calendar_kind(
1✔
205
                        &crate::provider::Baked,
206
                        locale,
1✔
207
                        kind,
1✔
208
                    )
209
                },
1✔
210
                locale,
211
                time_zone_format_options,
1✔
212
            )?,
×
213
            calendar,
1✔
214
        ))
215
    }
1✔
216

217
    #[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::try_new_experimental)]
218
    #[cfg(feature = "experimental")]
219
    #[inline]
220
    #[allow(clippy::too_many_arguments)]
221
    pub fn try_new_experimental_unstable<P>(
222
        provider: &P,
223
        locale: &DataLocale,
224
        date_time_format_options: DateTimeFormatterOptions,
225
        time_zone_format_options: TimeZoneFormatterOptions,
226
    ) -> Result<Self, DateTimeError>
227
    where
228
        P: DataProvider<TimeSymbolsV1Marker>
229
            + DataProvider<TimeLengthsV1Marker>
230
            + DataProvider<crate::provider::calendar::DateSkeletonPatternsV1Marker>
231
            + DataProvider<WeekDataV1Marker>
232
            + DataProvider<provider::time_zones::TimeZoneFormatsV1Marker>
233
            + DataProvider<provider::time_zones::ExemplarCitiesV1Marker>
234
            + DataProvider<provider::time_zones::MetazoneGenericNamesLongV1Marker>
235
            + DataProvider<provider::time_zones::MetazoneGenericNamesShortV1Marker>
236
            + DataProvider<provider::time_zones::MetazoneSpecificNamesLongV1Marker>
237
            + DataProvider<provider::time_zones::MetazoneSpecificNamesShortV1Marker>
238
            + DataProvider<OrdinalV1Marker>
239
            + DataProvider<DecimalSymbolsV1Marker>
240
            + DataProvider<BuddhistDateLengthsV1Marker>
241
            + DataProvider<BuddhistDateSymbolsV1Marker>
242
            + DataProvider<ChineseCacheV1Marker>
243
            + DataProvider<ChineseDateLengthsV1Marker>
244
            + DataProvider<ChineseDateSymbolsV1Marker>
245
            + DataProvider<CopticDateLengthsV1Marker>
246
            + DataProvider<CopticDateSymbolsV1Marker>
247
            + DataProvider<DangiCacheV1Marker>
248
            + DataProvider<DangiDateLengthsV1Marker>
249
            + DataProvider<DangiDateSymbolsV1Marker>
250
            + DataProvider<EthiopianDateLengthsV1Marker>
251
            + DataProvider<EthiopianDateSymbolsV1Marker>
252
            + DataProvider<GregorianDateLengthsV1Marker>
253
            + DataProvider<GregorianDateSymbolsV1Marker>
254
            + DataProvider<HebrewDateLengthsV1Marker>
255
            + DataProvider<HebrewDateSymbolsV1Marker>
256
            + DataProvider<IndianDateLengthsV1Marker>
257
            + DataProvider<IndianDateSymbolsV1Marker>
258
            + DataProvider<IslamicDateLengthsV1Marker>
259
            + DataProvider<IslamicDateSymbolsV1Marker>
260
            + DataProvider<JapaneseDateLengthsV1Marker>
261
            + DataProvider<JapaneseDateSymbolsV1Marker>
262
            + DataProvider<JapaneseErasV1Marker>
263
            + DataProvider<JapaneseExtendedDateLengthsV1Marker>
264
            + DataProvider<JapaneseExtendedDateSymbolsV1Marker>
265
            + DataProvider<JapaneseExtendedErasV1Marker>
266
            + DataProvider<PersianDateLengthsV1Marker>
267
            + DataProvider<PersianDateSymbolsV1Marker>
268
            + DataProvider<RocDateLengthsV1Marker>
269
            + DataProvider<RocDateSymbolsV1Marker>
270
            + ?Sized,
271
    {
272
        let calendar = AnyCalendar::try_new_for_locale_unstable(provider, locale)?;
273
        let kind = calendar.kind();
274

275
        let patterns = PatternSelector::for_options_experimental(
276
            provider,
277
            calendar::load_lengths_for_any_calendar_kind(provider, locale, kind)?,
278
            locale,
279
            &kind.as_bcp47_value(),
280
            &date_time_format_options,
281
        )?;
282

283
        Ok(Self(
284
            raw::ZonedDateTimeFormatter::try_new_unstable(
285
                provider,
286
                patterns,
287
                || calendar::load_symbols_for_any_calendar_kind(provider, locale, kind),
288
                locale,
289
                time_zone_format_options,
290
            )?,
291
            calendar,
292
        ))
293
    }
294

295
    /// Constructor that takes a selected [`DataLocale`] and a list of [`DateTimeFormatterOptions`] and uses compiled data.
296
    /// It collects all data necessary to format zoned datetime values into the given locale.
297
    ///
298
    /// This method will pick the calendar off of the locale; and if unspecified or unknown will fall back to the default
299
    /// calendar for the locale. See [`AnyCalendarKind`] for a list of supported calendars.
300
    ///
301
    /// ✨ *Enabled with the `compiled_data` Cargo feature.*
302
    ///
303
    /// [📚 Help choosing a constructor](icu_provider::constructors)
304
    ///
305
    /// # Examples
306
    ///
307
    /// ```
308
    /// use icu::calendar::DateTime;
309
    /// use icu::datetime::options::length;
310
    /// use icu::datetime::time_zone::TimeZoneFormatterOptions;
311
    /// use icu::datetime::ZonedDateTimeFormatter;
312
    /// use icu::locid::locale;
313
    /// use icu::timezone::CustomTimeZone;
314
    /// use std::str::FromStr;
315
    /// use writeable::assert_writeable_eq;
316
    ///
317
    /// let options = length::Bag::from_date_time_style(
318
    ///     length::Date::Medium,
319
    ///     length::Time::Long,
320
    /// );
321
    /// let locale = locale!("en-u-ca-gregory");
322
    ///
323
    /// let zdtf = ZonedDateTimeFormatter::try_new(
324
    ///     &locale.into(),
325
    ///     options.into(),
326
    ///     TimeZoneFormatterOptions::default(),
327
    /// )
328
    /// .expect("Construction should succeed");
329
    ///
330
    /// let datetime =
331
    ///     DateTime::try_new_iso_datetime(2021, 04, 08, 16, 12, 37).unwrap();
332
    /// let time_zone = CustomTimeZone::from_str("-07:00").unwrap();
333
    /// let any_datetime = datetime.to_any();
334
    ///
335
    /// assert_writeable_eq!(
336
    ///     zdtf.format(&any_datetime, &time_zone).unwrap(),
337
    ///     "Apr 8, 2021, 4:12:37 PM GMT-07:00"
338
    /// );
339
    /// ```
340
    #[cfg(feature = "compiled_data")]
341
    pub fn try_new(
3✔
342
        locale: &DataLocale,
343
        date_time_format_options: DateTimeFormatterOptions,
344
        time_zone_format_options: TimeZoneFormatterOptions,
345
    ) -> Result<Self, DateTimeError> {
346
        let calendar = AnyCalendar::new_for_locale(locale);
3✔
347
        let kind = calendar.kind();
3✔
348

349
        let patterns = PatternSelector::for_options(
3✔
350
            &crate::provider::Baked,
351
            calendar::load_lengths_for_any_calendar_kind(&crate::provider::Baked, locale, kind)?,
3✔
352
            locale,
353
            &date_time_format_options,
354
        )?;
×
355

356
        Ok(Self(
3✔
357
            raw::ZonedDateTimeFormatter::try_new(
3✔
358
                patterns,
3✔
359
                || {
6✔
360
                    calendar::load_symbols_for_any_calendar_kind(
3✔
361
                        &crate::provider::Baked,
362
                        locale,
3✔
363
                        kind,
3✔
364
                    )
365
                },
3✔
366
                locale,
367
                time_zone_format_options,
3✔
368
            )?,
×
369
            calendar,
3✔
370
        ))
371
    }
3✔
372

373
    #[inline]
374
    #[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::try_new)]
375
    #[allow(clippy::too_many_arguments)]
376
    pub fn try_new_unstable<P>(
1✔
377
        provider: &P,
378
        locale: &DataLocale,
379
        date_time_format_options: DateTimeFormatterOptions,
380
        time_zone_format_options: TimeZoneFormatterOptions,
381
    ) -> Result<Self, DateTimeError>
382
    where
383
        P: DataProvider<TimeSymbolsV1Marker>
384
            + DataProvider<TimeLengthsV1Marker>
385
            + DataProvider<WeekDataV1Marker>
386
            + DataProvider<provider::time_zones::TimeZoneFormatsV1Marker>
387
            + DataProvider<provider::time_zones::ExemplarCitiesV1Marker>
388
            + DataProvider<provider::time_zones::MetazoneGenericNamesLongV1Marker>
389
            + DataProvider<provider::time_zones::MetazoneGenericNamesShortV1Marker>
390
            + DataProvider<provider::time_zones::MetazoneSpecificNamesLongV1Marker>
391
            + DataProvider<provider::time_zones::MetazoneSpecificNamesShortV1Marker>
392
            + DataProvider<OrdinalV1Marker>
393
            + DataProvider<DecimalSymbolsV1Marker>
394
            + DataProvider<BuddhistDateLengthsV1Marker>
395
            + DataProvider<BuddhistDateSymbolsV1Marker>
396
            + DataProvider<ChineseCacheV1Marker>
397
            + DataProvider<ChineseDateLengthsV1Marker>
398
            + DataProvider<ChineseDateSymbolsV1Marker>
399
            + DataProvider<CopticDateLengthsV1Marker>
400
            + DataProvider<CopticDateSymbolsV1Marker>
401
            + DataProvider<DangiCacheV1Marker>
402
            + DataProvider<DangiDateLengthsV1Marker>
403
            + DataProvider<DangiDateSymbolsV1Marker>
404
            + DataProvider<EthiopianDateLengthsV1Marker>
405
            + DataProvider<EthiopianDateSymbolsV1Marker>
406
            + DataProvider<GregorianDateLengthsV1Marker>
407
            + DataProvider<GregorianDateSymbolsV1Marker>
408
            + DataProvider<HebrewDateLengthsV1Marker>
409
            + DataProvider<HebrewDateSymbolsV1Marker>
410
            + DataProvider<IndianDateLengthsV1Marker>
411
            + DataProvider<IndianDateSymbolsV1Marker>
412
            + DataProvider<IslamicDateLengthsV1Marker>
413
            + DataProvider<IslamicDateSymbolsV1Marker>
414
            + DataProvider<JapaneseDateLengthsV1Marker>
415
            + DataProvider<JapaneseDateSymbolsV1Marker>
416
            + DataProvider<JapaneseErasV1Marker>
417
            + DataProvider<JapaneseExtendedDateLengthsV1Marker>
418
            + DataProvider<JapaneseExtendedDateSymbolsV1Marker>
419
            + DataProvider<JapaneseExtendedErasV1Marker>
420
            + DataProvider<PersianDateLengthsV1Marker>
421
            + DataProvider<PersianDateSymbolsV1Marker>
422
            + DataProvider<RocDateLengthsV1Marker>
423
            + DataProvider<RocDateSymbolsV1Marker>
424
            + ?Sized,
425
    {
426
        let calendar = AnyCalendar::try_new_for_locale_unstable(provider, locale)?;
1✔
427
        let kind = calendar.kind();
1✔
428

429
        let patterns = PatternSelector::for_options(
1✔
430
            provider,
431
            calendar::load_lengths_for_any_calendar_kind(provider, locale, kind)?,
1✔
432
            locale,
433
            &date_time_format_options,
434
        )?;
×
435

436
        Ok(Self(
1✔
437
            raw::ZonedDateTimeFormatter::try_new_unstable(
1✔
438
                provider,
439
                patterns,
1✔
440
                || calendar::load_symbols_for_any_calendar_kind(provider, locale, kind),
2✔
441
                locale,
442
                time_zone_format_options,
1✔
443
            )?,
×
444
            calendar,
1✔
445
        ))
446
    }
1✔
447

448
    #[doc = icu_provider::gen_any_buffer_unstable_docs!(ANY, Self::try_new)]
449
    #[inline]
450
    pub fn try_new_with_any_provider(
×
451
        provider: &impl AnyProvider,
452
        locale: &DataLocale,
453
        options: DateTimeFormatterOptions,
454
        time_zone_format_options: TimeZoneFormatterOptions,
455
    ) -> Result<Self, DateTimeError> {
456
        let downcasting = provider.as_downcasting();
×
457
        Self::try_new_unstable(&downcasting, locale, options, time_zone_format_options)
×
458
    }
×
459

460
    #[doc = icu_provider::gen_any_buffer_unstable_docs!(BUFFER, Self::try_new)]
461
    #[inline]
462
    #[cfg(feature = "serde")]
463
    pub fn try_new_with_buffer_provider(
1✔
464
        provider: &impl BufferProvider,
465
        locale: &DataLocale,
466
        options: DateTimeFormatterOptions,
467
        time_zone_format_options: TimeZoneFormatterOptions,
468
    ) -> Result<Self, DateTimeError> {
469
        let deserializing = provider.as_deserializing();
1✔
470
        Self::try_new_unstable(&deserializing, locale, options, time_zone_format_options)
1✔
471
    }
1✔
472

473
    /// Takes a [`DateTimeInput`] and a [`TimeZoneInput`] and returns an instance of a [`FormattedZonedDateTime`]
474
    /// that contains all information necessary to display a formatted date and operate on it.
475
    ///
476
    /// This function will fail if the date passed in uses a different calendar than that of the
477
    /// AnyCalendar. Please convert dates before passing them in if necessary. This function
478
    /// will automatically convert and format dates that are associated with the ISO calendar.
479
    #[inline]
480
    pub fn format<'l>(
1✔
481
        &'l self,
482
        date: &impl DateTimeInput<Calendar = AnyCalendar>,
483
        time_zone: &impl TimeZoneInput,
484
    ) -> Result<FormattedZonedDateTime<'l>, DateTimeError> {
485
        if let Some(converted) = self.convert_if_necessary(date)? {
1✔
486
            Ok(self.0.format(&converted, time_zone))
1✔
487
        } else {
488
            Ok(self.0.format(date, time_zone))
×
489
        }
490
    }
1✔
491

492
    /// Takes a [`DateTimeInput`] and a [`TimeZoneInput`] and returns it formatted as a string.
493
    ///
494
    /// This function will fail if the date passed in uses a different calendar than that of the
495
    /// AnyCalendar. Please convert dates before passing them in if necessary. This function
496
    /// will automatically convert and format dates that are associated with the ISO calendar.
497
    #[inline]
498
    pub fn format_to_string(
499
        &self,
500
        date: &impl DateTimeInput<Calendar = AnyCalendar>,
501
        time_zone: &impl TimeZoneInput,
502
    ) -> Result<String, DateTimeError> {
503
        Ok(self.format(date, time_zone)?.write_to_string().into_owned())
504
    }
505

506
    /// Converts a date to the correct calendar if necessary
507
    ///
508
    /// Returns `Err` if the date is not ISO or compatible with the current calendar, returns `Ok(None)`
509
    /// if the date is compatible with the current calendar and doesn't need conversion
510
    fn convert_if_necessary(
1✔
511
        &self,
512
        value: &impl DateTimeInput<Calendar = AnyCalendar>,
513
    ) -> Result<Option<ExtractedDateTimeInput>, DateTimeError> {
514
        let this_calendar = self.1.kind();
1✔
515
        let date_calendar = value.any_calendar_kind();
1✔
516
        if Some(this_calendar) != date_calendar {
1✔
517
            if date_calendar != Some(AnyCalendarKind::Iso) {
1✔
518
                return Err(DateTimeError::MismatchedAnyCalendar(
×
519
                    this_calendar,
520
                    date_calendar,
×
521
                ));
522
            }
523
            let date = value.to_iso();
1✔
524
            let time = Time::new(
1✔
525
                value.hour().unwrap_or_default(),
1✔
526
                value.minute().unwrap_or_default(),
1✔
527
                value.second().unwrap_or_default(),
1✔
528
                value.nanosecond().unwrap_or_default(),
1✔
529
            );
530
            let datetime = DateTime::new(date, time).to_any();
1✔
531
            let converted = self.1.convert_any_datetime(&datetime);
1✔
532
            // FIXME(#2145) this is very hacky, can be improved after we improve ZonedDateTimeInput
533
            let converted = ExtractedDateTimeInput::extract_from(&converted);
1✔
534
            Ok(Some(converted))
1✔
535
        } else {
1✔
536
            Ok(None)
×
537
        }
538
    }
1✔
539
}
540

541
#[test]
542
#[cfg(feature = "serde")]
543
fn buffer_constructor() {
2✔
544
    #![allow(clippy::zero_prefixed_literal)]
545
    use icu::calendar::DateTime;
546
    use icu::datetime::options::length;
547
    use icu::datetime::ZonedDateTimeFormatter;
548
    use icu::locid::locale;
549
    use icu::timezone::CustomTimeZone;
550
    use std::str::FromStr;
551
    use writeable::assert_writeable_eq;
552

553
    let provider = icu_provider_blob::BlobDataProvider::try_new_from_static_blob(include_bytes!(
1✔
554
        "../../tests/data/blob.postcard"
555
    ))
556
    .unwrap();
557

558
    let zdtf = ZonedDateTimeFormatter::try_new_with_buffer_provider(
1✔
559
        &provider,
560
        &locale!("en").into(),
1✔
561
        length::Bag::from_date_time_style(length::Date::Medium, length::Time::Long).into(),
1✔
562
        Default::default(),
1✔
563
    )
564
    .unwrap();
1✔
565

566
    assert_writeable_eq!(
1✔
567
        zdtf.format(
1✔
568
            &DateTime::try_new_iso_datetime(2021, 04, 08, 16, 12, 37)
1✔
569
                .unwrap()
570
                .to_any(),
571
            &CustomTimeZone::from_str("-07:00").unwrap()
1✔
572
        )
573
        .unwrap(),
574
        "Apr 8, 2021, 4:12:37 PM GMT-07:00"
575
    );
576
}
2✔
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