Coverage Report

Created: 2026-08-13 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/time-0.3.55/src/utc_offset.rs
Line
Count
Source
1
//! The [`UtcOffset`] struct and its associated `impl`s.
2
3
#[cfg(feature = "formatting")]
4
use alloc::string::String;
5
use core::cmp::Ordering;
6
use core::fmt;
7
use core::hash::{Hash, Hasher};
8
use core::mem::MaybeUninit;
9
use core::ops::Neg;
10
#[cfg(feature = "formatting")]
11
use std::io;
12
13
use deranged::{ri8, ri32, ru8};
14
use powerfmt::smart_display::{FormatterOptions, Metadata, SmartDisplay};
15
16
#[cfg(feature = "local-offset")]
17
use crate::OffsetDateTime;
18
#[cfg(any(feature = "formatting", feature = "parsing"))]
19
use crate::PrivateMethod;
20
use crate::error;
21
#[cfg(feature = "formatting")]
22
use crate::formatting::Formattable;
23
use crate::internal_macros::ensure_ranged;
24
use crate::num_fmt::{str_from_raw_parts, two_digits_zero_padded};
25
#[cfg(feature = "parsing")]
26
use crate::parsing::{Parsable, Parsed};
27
#[cfg(feature = "local-offset")]
28
use crate::sys::local_offset_at;
29
use crate::unit::*;
30
31
/// The type of the `hours` field of `UtcOffset`.
32
pub(crate) type Hours = ri8<-25, 25>;
33
/// The type of the `minutes` field of `UtcOffset`.
34
pub(crate) type Minutes =
35
    ri8<{ -(Minute::per_t::<i8>(Hour) - 1) }, { Minute::per_t::<i8>(Hour) - 1 }>;
36
/// The type of the `seconds` field of `UtcOffset`.
37
pub(crate) type Seconds =
38
    ri8<{ -(Second::per_t::<i8>(Minute) - 1) }, { Second::per_t::<i8>(Minute) - 1 }>;
39
/// The type capable of storing the range of whole seconds that a `UtcOffset` can encompass.
40
type WholeSeconds = ri32<
41
    {
42
        Hours::MIN.get() as i32 * Second::per_t::<i32>(Hour)
43
            + Minutes::MIN.get() as i32 * Second::per_t::<i32>(Minute)
44
            + Seconds::MIN.get() as i32
45
    },
46
    {
47
        Hours::MAX.get() as i32 * Second::per_t::<i32>(Hour)
48
            + Minutes::MAX.get() as i32 * Second::per_t::<i32>(Minute)
49
            + Seconds::MAX.get() as i32
50
    },
51
>;
52
53
/// An offset from UTC.
54
///
55
/// This struct can store values up to ±25:59:59. If you need support outside this range, please
56
/// file an issue with your use case.
57
// All three components _must_ have the same sign.
58
#[derive(Clone, Copy, Eq)]
59
#[cfg_attr(not(docsrs), repr(C))]
60
pub struct UtcOffset {
61
    // The order of this struct's fields matter. Do not reorder them.
62
63
    // Little endian version
64
    #[cfg(target_endian = "little")]
65
    seconds: Seconds,
66
    #[cfg(target_endian = "little")]
67
    minutes: Minutes,
68
    #[cfg(target_endian = "little")]
69
    hours: Hours,
70
71
    // Big endian version
72
    #[cfg(target_endian = "big")]
73
    hours: Hours,
74
    #[cfg(target_endian = "big")]
75
    minutes: Minutes,
76
    #[cfg(target_endian = "big")]
77
    seconds: Seconds,
78
}
79
80
impl Hash for UtcOffset {
81
    #[inline]
82
0
    fn hash<H>(&self, state: &mut H)
83
0
    where
84
0
        H: Hasher,
85
    {
86
0
        state.write_u32(self.as_u32_for_equality());
87
0
    }
88
}
89
90
impl PartialEq for UtcOffset {
91
    #[inline]
92
0
    fn eq(&self, other: &Self) -> bool {
93
0
        self.as_u32_for_equality().eq(&other.as_u32_for_equality())
94
0
    }
95
}
96
97
impl PartialOrd for UtcOffset {
98
    #[inline]
99
0
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
100
0
        Some(self.cmp(other))
101
0
    }
102
}
103
104
impl Ord for UtcOffset {
105
    #[inline]
106
0
    fn cmp(&self, other: &Self) -> Ordering {
107
0
        self.as_i32_for_comparison()
108
0
            .cmp(&other.as_i32_for_comparison())
109
0
    }
110
}
111
112
impl UtcOffset {
113
    /// Provide a representation of the `UtcOffset` as a `i32`. This value can be used for equality,
114
    /// and hashing. This value is not suitable for ordering; use `as_i32_for_comparison` instead.
115
    #[inline]
116
10.4k
    pub(crate) const fn as_u32_for_equality(self) -> u32 {
117
        // Safety: Size and alignment are handled by the compiler. Both the source and destination
118
        // types are plain old data (POD) types.
119
        unsafe {
120
            if const { cfg!(target_endian = "little") } {
121
10.4k
                core::mem::transmute::<[i8; 4], u32>([
122
10.4k
                    self.seconds.get(),
123
10.4k
                    self.minutes.get(),
124
10.4k
                    self.hours.get(),
125
10.4k
                    0,
126
10.4k
                ])
127
            } else {
128
0
                core::mem::transmute::<[i8; 4], u32>([
129
0
                    self.hours.get(),
130
0
                    self.minutes.get(),
131
0
                    self.seconds.get(),
132
0
                    0,
133
0
                ])
134
            }
135
        }
136
10.4k
    }
Unexecuted instantiation: <time::utc_offset::UtcOffset>::as_u32_for_equality
<time::utc_offset::UtcOffset>::as_u32_for_equality
Line
Count
Source
116
10.4k
    pub(crate) const fn as_u32_for_equality(self) -> u32 {
117
        // Safety: Size and alignment are handled by the compiler. Both the source and destination
118
        // types are plain old data (POD) types.
119
        unsafe {
120
            if const { cfg!(target_endian = "little") } {
121
10.4k
                core::mem::transmute::<[i8; 4], u32>([
122
10.4k
                    self.seconds.get(),
123
10.4k
                    self.minutes.get(),
124
10.4k
                    self.hours.get(),
125
10.4k
                    0,
126
10.4k
                ])
127
            } else {
128
0
                core::mem::transmute::<[i8; 4], u32>([
129
0
                    self.hours.get(),
130
0
                    self.minutes.get(),
131
0
                    self.seconds.get(),
132
0
                    0,
133
0
                ])
134
            }
135
        }
136
10.4k
    }
137
138
    /// Provide a representation of the `UtcOffset` as a `i32`. This value can be used for ordering.
139
    /// While it is suitable for equality, `as_u32_for_equality` is preferred for performance
140
    /// reasons.
141
    #[inline]
142
3.11k
    const fn as_i32_for_comparison(self) -> i32 {
143
3.11k
        (self.hours.get() as i32) << 16
144
3.11k
            | (self.minutes.get() as i32) << 8
145
3.11k
            | (self.seconds.get() as i32)
146
3.11k
    }
<time::utc_offset::UtcOffset>::as_i32_for_comparison
Line
Count
Source
142
3.11k
    const fn as_i32_for_comparison(self) -> i32 {
143
3.11k
        (self.hours.get() as i32) << 16
144
3.11k
            | (self.minutes.get() as i32) << 8
145
3.11k
            | (self.seconds.get() as i32)
146
3.11k
    }
Unexecuted instantiation: <time::utc_offset::UtcOffset>::as_i32_for_comparison
147
148
    /// A `UtcOffset` that is UTC.
149
    ///
150
    /// ```rust
151
    /// # use time::UtcOffset;
152
    /// # use time_macros::offset;
153
    /// assert_eq!(UtcOffset::UTC, offset!(UTC));
154
    /// ```
155
    pub const UTC: Self = Self::from_whole_seconds_ranged(WholeSeconds::new_static::<0>());
156
157
    /// Create a `UtcOffset` representing an offset of the hours, minutes, and seconds provided, the
158
    /// validity of which must be guaranteed by the caller. All three parameters must have the same
159
    /// sign.
160
    ///
161
    /// # Safety
162
    ///
163
    /// - Hours must be in the range `-25..=25`.
164
    /// - Minutes must be in the range `-59..=59`.
165
    /// - Seconds must be in the range `-59..=59`.
166
    ///
167
    /// While the signs of the parameters are required to match to avoid bugs, this is not a safety
168
    /// invariant.
169
    #[doc(hidden)]
170
    #[inline]
171
    #[track_caller]
172
0
    pub const unsafe fn __from_hms_unchecked(hours: i8, minutes: i8, seconds: i8) -> Self {
173
        // Safety: The caller must uphold the safety invariants.
174
        unsafe {
175
0
            Self::from_hms_ranged_unchecked(
176
0
                Hours::new_unchecked(hours),
177
0
                Minutes::new_unchecked(minutes),
178
0
                Seconds::new_unchecked(seconds),
179
            )
180
        }
181
0
    }
182
183
    /// Create a `UtcOffset` representing an offset by the number of hours, minutes, and seconds
184
    /// provided.
185
    ///
186
    /// The sign of all three components should match. If they do not, all smaller components will
187
    /// have their signs flipped.
188
    ///
189
    /// ```rust
190
    /// # use time::UtcOffset;
191
    /// assert_eq!(UtcOffset::from_hms(1, 2, 3)?.as_hms(), (1, 2, 3));
192
    /// assert_eq!(UtcOffset::from_hms(1, -2, -3)?.as_hms(), (1, 2, 3));
193
    /// # Ok::<_, time::Error>(())
194
    /// ```
195
    #[inline]
196
7.71k
    pub const fn from_hms(
197
7.71k
        hours: i8,
198
7.71k
        minutes: i8,
199
7.71k
        seconds: i8,
200
7.71k
    ) -> Result<Self, error::ComponentRange> {
201
7.70k
        Ok(Self::from_hms_ranged(
202
7.71k
            ensure_ranged!(Hours: hours("offset hour")),
203
7.71k
            ensure_ranged!(Minutes: minutes("offset minute")),
204
7.70k
            ensure_ranged!(Seconds: seconds("offset second")),
205
        ))
206
7.71k
    }
207
208
    /// Create a `UtcOffset` representing an offset of the hours, minutes, and seconds provided. All
209
    /// three parameters must have the same sign.
210
    ///
211
    /// While the signs of the parameters are required to match, this is not a safety invariant.
212
    #[inline]
213
    #[track_caller]
214
0
    pub(crate) const fn from_hms_ranged_unchecked(
215
0
        hours: Hours,
216
0
        minutes: Minutes,
217
0
        seconds: Seconds,
218
0
    ) -> Self {
219
0
        if hours.get() < 0 {
220
0
            debug_assert!(minutes.get() <= 0);
221
0
            debug_assert!(seconds.get() <= 0);
222
0
        } else if hours.get() > 0 {
223
0
            debug_assert!(minutes.get() >= 0);
224
0
            debug_assert!(seconds.get() >= 0);
225
0
        }
226
0
        if minutes.get() < 0 {
227
0
            debug_assert!(seconds.get() <= 0);
228
0
        } else if minutes.get() > 0 {
229
0
            debug_assert!(seconds.get() >= 0);
230
0
        }
231
232
0
        Self {
233
0
            hours,
234
0
            minutes,
235
0
            seconds,
236
0
        }
237
0
    }
238
239
    /// Create a `UtcOffset` representing an offset by the number of hours, minutes, and seconds
240
    /// provided.
241
    ///
242
    /// The sign of all three components should match. If they do not, all smaller components will
243
    /// have their signs flipped.
244
    #[inline]
245
7.70k
    pub(crate) const fn from_hms_ranged(
246
7.70k
        hours: Hours,
247
7.70k
        mut minutes: Minutes,
248
7.70k
        mut seconds: Seconds,
249
7.70k
    ) -> Self {
250
7.70k
        if (hours.get() > 0 && minutes.get() < 0) || (hours.get() < 0 && minutes.get() > 0) {
251
0
            minutes = minutes.neg();
252
7.70k
        }
253
7.70k
        if (hours.get() > 0 && seconds.get() < 0)
254
7.70k
            || (hours.get() < 0 && seconds.get() > 0)
255
7.70k
            || (minutes.get() > 0 && seconds.get() < 0)
256
7.70k
            || (minutes.get() < 0 && seconds.get() > 0)
257
0
        {
258
0
            seconds = seconds.neg();
259
7.70k
        }
260
261
7.70k
        Self {
262
7.70k
            hours,
263
7.70k
            minutes,
264
7.70k
            seconds,
265
7.70k
        }
266
7.70k
    }
267
268
    /// Create a `UtcOffset` representing an offset by the number of seconds provided.
269
    ///
270
    /// ```rust
271
    /// # use time::UtcOffset;
272
    /// assert_eq!(UtcOffset::from_whole_seconds(3_723)?.as_hms(), (1, 2, 3));
273
    /// # Ok::<_, time::Error>(())
274
    /// ```
275
    #[inline]
276
0
    pub const fn from_whole_seconds(seconds: i32) -> Result<Self, error::ComponentRange> {
277
0
        Ok(Self::from_whole_seconds_ranged(
278
0
            ensure_ranged!(WholeSeconds: seconds),
279
        ))
280
0
    }
281
282
    /// Create a `UtcOffset` representing an offset by the number of seconds provided.
283
    // ignore because the function is crate-private
284
    /// ```rust,ignore
285
    /// # use time::UtcOffset;
286
    /// # use deranged::RangedI32;
287
    /// assert_eq!(
288
    ///     UtcOffset::from_whole_seconds_ranged(RangedI32::new_static::<3_723>()).as_hms(),
289
    ///     (1, 2, 3)
290
    /// );
291
    /// # Ok::<_, time::Error>(())
292
    /// ```
293
    #[inline]
294
0
    pub(crate) const fn from_whole_seconds_ranged(seconds: WholeSeconds) -> Self {
295
        // Safety: The type of `seconds` guarantees that all values are in range.
296
        unsafe {
297
0
            Self::__from_hms_unchecked(
298
0
                (seconds.get() / Second::per_t::<i32>(Hour)) as i8,
299
0
                ((seconds.get() % Second::per_t::<i32>(Hour)) / Minute::per_t::<i32>(Hour)) as i8,
300
0
                (seconds.get() % Second::per_t::<i32>(Minute)) as i8,
301
            )
302
        }
303
0
    }
304
305
    /// Obtain the UTC offset as its hours, minutes, and seconds. The sign of all three components
306
    /// will always match. A positive value indicates an offset to the east; a negative to the west.
307
    ///
308
    /// ```rust
309
    /// # use time_macros::offset;
310
    /// assert_eq!(offset!(+1:02:03).as_hms(), (1, 2, 3));
311
    /// assert_eq!(offset!(-1:02:03).as_hms(), (-1, -2, -3));
312
    /// ```
313
    #[inline]
314
0
    pub const fn as_hms(self) -> (i8, i8, i8) {
315
0
        (self.hours.get(), self.minutes.get(), self.seconds.get())
316
0
    }
317
318
    /// Obtain the UTC offset as its hours, minutes, and seconds. The sign of all three components
319
    /// will always match. A positive value indicates an offset to the east; a negative to the west.
320
    #[inline]
321
    #[cfg(any(feature = "formatting", feature = "quickcheck"))]
322
0
    pub(crate) const fn as_hms_ranged(self) -> (Hours, Minutes, Seconds) {
323
0
        (self.hours, self.minutes, self.seconds)
324
0
    }
Unexecuted instantiation: <time::utc_offset::UtcOffset>::as_hms_ranged
Unexecuted instantiation: <time::utc_offset::UtcOffset>::as_hms_ranged
325
326
    /// Obtain the number of whole hours the offset is from UTC. A positive value indicates an
327
    /// offset to the east; a negative to the west.
328
    ///
329
    /// ```rust
330
    /// # use time_macros::offset;
331
    /// assert_eq!(offset!(+1:02:03).whole_hours(), 1);
332
    /// assert_eq!(offset!(-1:02:03).whole_hours(), -1);
333
    /// ```
334
    #[inline]
335
4.24k
    pub const fn whole_hours(self) -> i8 {
336
4.24k
        self.hours.get()
337
4.24k
    }
338
339
    /// Obtain the number of whole minutes the offset is from UTC. A positive value indicates an
340
    /// offset to the east; a negative to the west.
341
    ///
342
    /// ```rust
343
    /// # use time_macros::offset;
344
    /// assert_eq!(offset!(+1:02:03).whole_minutes(), 62);
345
    /// assert_eq!(offset!(-1:02:03).whole_minutes(), -62);
346
    /// ```
347
    #[inline]
348
0
    pub const fn whole_minutes(self) -> i16 {
349
0
        self.hours.get() as i16 * Minute::per_t::<i16>(Hour) + self.minutes.get() as i16
350
0
    }
351
352
    /// Obtain the number of minutes past the hour the offset is from UTC. A positive value
353
    /// indicates an offset to the east; a negative to the west.
354
    ///
355
    /// ```rust
356
    /// # use time_macros::offset;
357
    /// assert_eq!(offset!(+1:02:03).minutes_past_hour(), 2);
358
    /// assert_eq!(offset!(-1:02:03).minutes_past_hour(), -2);
359
    /// ```
360
    #[inline]
361
4.24k
    pub const fn minutes_past_hour(self) -> i8 {
362
4.24k
        self.minutes.get()
363
4.24k
    }
364
365
    /// Obtain the number of whole seconds the offset is from UTC. A positive value indicates an
366
    /// offset to the east; a negative to the west.
367
    ///
368
    /// ```rust
369
    /// # use time_macros::offset;
370
    /// assert_eq!(offset!(+1:02:03).whole_seconds(), 3723);
371
    /// assert_eq!(offset!(-1:02:03).whole_seconds(), -3723);
372
    /// ```
373
    // This may be useful for anyone manually implementing arithmetic, as it
374
    // would let them construct a `SignedDuration` directly.
375
    #[inline]
376
10.3k
    pub const fn whole_seconds(self) -> i32 {
377
10.3k
        self.hours.get() as i32 * Second::per_t::<i32>(Hour)
378
10.3k
            + self.minutes.get() as i32 * Second::per_t::<i32>(Minute)
379
10.3k
            + self.seconds.get() as i32
380
10.3k
    }
<time::utc_offset::UtcOffset>::whole_seconds
Line
Count
Source
376
10.3k
    pub const fn whole_seconds(self) -> i32 {
377
10.3k
        self.hours.get() as i32 * Second::per_t::<i32>(Hour)
378
10.3k
            + self.minutes.get() as i32 * Second::per_t::<i32>(Minute)
379
10.3k
            + self.seconds.get() as i32
380
10.3k
    }
Unexecuted instantiation: <time::utc_offset::UtcOffset>::whole_seconds
381
382
    /// Obtain the number of seconds past the minute the offset is from UTC. A positive value
383
    /// indicates an offset to the east; a negative to the west.
384
    ///
385
    /// ```rust
386
    /// # use time_macros::offset;
387
    /// assert_eq!(offset!(+1:02:03).seconds_past_minute(), 3);
388
    /// assert_eq!(offset!(-1:02:03).seconds_past_minute(), -3);
389
    /// ```
390
    #[inline]
391
4.24k
    pub const fn seconds_past_minute(self) -> i8 {
392
4.24k
        self.seconds.get()
393
4.24k
    }
394
395
    /// Check if the offset is exactly UTC.
396
    ///
397
    ///
398
    /// ```rust
399
    /// # use time_macros::offset;
400
    /// assert!(!offset!(+1:02:03).is_utc());
401
    /// assert!(!offset!(-1:02:03).is_utc());
402
    /// assert!(offset!(UTC).is_utc());
403
    /// ```
404
    #[inline]
405
5.24k
    pub const fn is_utc(self) -> bool {
406
5.24k
        self.as_u32_for_equality() == Self::UTC.as_u32_for_equality()
407
5.24k
    }
Unexecuted instantiation: <time::utc_offset::UtcOffset>::is_utc
<time::utc_offset::UtcOffset>::is_utc
Line
Count
Source
405
5.24k
    pub const fn is_utc(self) -> bool {
406
5.24k
        self.as_u32_for_equality() == Self::UTC.as_u32_for_equality()
407
5.24k
    }
408
409
    /// Check if the offset is positive, or east of UTC.
410
    ///
411
    /// ```rust
412
    /// # use time_macros::offset;
413
    /// assert!(offset!(+1:02:03).is_positive());
414
    /// assert!(!offset!(-1:02:03).is_positive());
415
    /// assert!(!offset!(UTC).is_positive());
416
    /// ```
417
    #[inline]
418
0
    pub const fn is_positive(self) -> bool {
419
0
        self.as_i32_for_comparison() > Self::UTC.as_i32_for_comparison()
420
0
    }
421
422
    /// Check if the offset is negative, or west of UTC.
423
    ///
424
    /// ```rust
425
    /// # use time_macros::offset;
426
    /// assert!(!offset!(+1:02:03).is_negative());
427
    /// assert!(offset!(-1:02:03).is_negative());
428
    /// assert!(!offset!(UTC).is_negative());
429
    /// ```
430
    #[inline]
431
1.55k
    pub const fn is_negative(self) -> bool {
432
1.55k
        self.as_i32_for_comparison() < Self::UTC.as_i32_for_comparison()
433
1.55k
    }
<time::utc_offset::UtcOffset>::is_negative
Line
Count
Source
431
1.55k
    pub const fn is_negative(self) -> bool {
432
1.55k
        self.as_i32_for_comparison() < Self::UTC.as_i32_for_comparison()
433
1.55k
    }
Unexecuted instantiation: <time::utc_offset::UtcOffset>::is_negative
434
435
    /// Attempt to obtain the system's UTC offset at a known moment in time. If the offset cannot be
436
    /// determined, an error is returned.
437
    ///
438
    /// ```rust
439
    /// # use time::{UtcOffset, OffsetDateTime};
440
    /// let local_offset = UtcOffset::local_offset_at(OffsetDateTime::UNIX_EPOCH);
441
    /// # if false {
442
    /// assert!(local_offset.is_ok());
443
    /// # }
444
    /// ```
445
    #[cfg(feature = "local-offset")]
446
    #[inline]
447
    pub fn local_offset_at(datetime: OffsetDateTime) -> Result<Self, error::IndeterminateOffset> {
448
        local_offset_at(datetime).ok_or(error::IndeterminateOffset)
449
    }
450
451
    /// Attempt to obtain the system's current UTC offset. If the offset cannot be determined, an
452
    /// error is returned.
453
    ///
454
    /// ```rust
455
    /// # use time::UtcOffset;
456
    /// let local_offset = UtcOffset::current_local_offset();
457
    /// # if false {
458
    /// assert!(local_offset.is_ok());
459
    /// # }
460
    /// ```
461
    #[cfg(feature = "local-offset")]
462
    #[inline]
463
    pub fn current_local_offset() -> Result<Self, error::IndeterminateOffset> {
464
        let now = OffsetDateTime::now_utc();
465
        local_offset_at(now).ok_or(error::IndeterminateOffset)
466
    }
467
}
468
469
#[cfg(feature = "formatting")]
470
impl UtcOffset {
471
    /// Format the `UtcOffset` using the provided [format description](crate::format_description).
472
    #[inline]
473
0
    pub fn format_into(
474
0
        self,
475
0
        output: &mut (impl io::Write + ?Sized),
476
0
        format: &(impl Formattable + ?Sized),
477
0
    ) -> Result<usize, error::Format> {
478
0
        format.format_into(output, &self, &mut Default::default(), PrivateMethod)
479
0
    }
480
481
    /// Format the `UtcOffset` using the provided [format description](crate::format_description).
482
    ///
483
    /// ```rust
484
    /// # use time::format_description;
485
    /// # use time_macros::offset;
486
    /// let format =
487
    ///     format_description::parse_borrowed::<3>("[offset_hour sign:mandatory]:[offset_minute]")?;
488
    /// assert_eq!(offset!(+1).format(&format)?, "+01:00");
489
    /// # Ok::<_, time::Error>(())
490
    /// ```
491
    #[inline]
492
0
    pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result<String, error::Format> {
493
0
        format.format(&self, &mut Default::default(), PrivateMethod)
494
0
    }
495
}
496
497
#[cfg(feature = "parsing")]
498
impl UtcOffset {
499
    /// Parse a `UtcOffset` from the input using the provided [format
500
    /// description](crate::format_description).
501
    ///
502
    /// ```rust
503
    /// # use time::UtcOffset;
504
    /// # use time_macros::{offset, format_description};
505
    /// let format = format_description!("[offset_hour]:[offset_minute]");
506
    /// assert_eq!(UtcOffset::parse("-03:42", &format)?, offset!(-3:42));
507
    /// # Ok::<_, time::Error>(())
508
    /// ```
509
    #[inline]
510
0
    pub fn parse(
511
0
        input: &str,
512
0
        description: &(impl Parsable + ?Sized),
513
0
    ) -> Result<Self, error::Parse> {
514
0
        description.parse_offset(input.as_bytes(), None, PrivateMethod)
515
0
    }
516
517
    /// Parse a `UtcOffset` from the input using the provided [format
518
    /// description](crate::format_description) and default values.
519
    ///
520
    /// ```rust
521
    /// # use time::UtcOffset;
522
    /// # use time::parsing::Parsed;
523
    /// # use time_macros::{offset, format_description};
524
    /// let format = format_description!("[offset_hour sign:mandatory]");
525
    /// let defaults = Parsed::new()
526
    ///     .with_offset_minute_signed(30)
527
    ///     .expect("30 is a valid offset minute");
528
    /// assert_eq!(
529
    ///     UtcOffset::parse_with_defaults(b"+05", &format, defaults)?,
530
    ///     offset!(+5:30)
531
    /// );
532
    /// # Ok::<_, time::Error>(())
533
    /// ```
534
    #[inline]
535
0
    pub fn parse_with_defaults(
536
0
        input: &[u8],
537
0
        description: &(impl Parsable + ?Sized),
538
0
        defaults: Parsed,
539
0
    ) -> Result<Self, error::Parse> {
540
0
        description.parse_offset(input, Some(defaults), PrivateMethod)
541
0
    }
542
}
543
544
// This no longer needs special handling, as the format is fixed and doesn't require anything
545
// advanced. Trait impls can't be deprecated and the info is still useful for other types
546
// implementing `SmartDisplay`, so leave it as-is for now.
547
impl SmartDisplay for UtcOffset {
548
    type Metadata = ();
549
550
    #[inline]
551
0
    fn metadata(&self, _: FormatterOptions) -> Metadata<'_, Self> {
552
0
        Metadata::new(9, self, ())
553
0
    }
554
555
    #[inline]
556
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
557
0
        fmt::Display::fmt(self, f)
558
0
    }
559
}
560
561
impl UtcOffset {
562
    /// The maximum number of bytes that the `fmt_into_buffer` method will write, which is also used
563
    /// for the `Display` implementation.
564
    pub(crate) const DISPLAY_BUFFER_SIZE: usize = 9;
565
566
    /// Format the `UtcOffset` into the provided buffer, returning the number of bytes written.
567
    #[inline]
568
1.55k
    pub(crate) const fn fmt_into_buffer(
569
1.55k
        self,
570
1.55k
        buf: &mut [MaybeUninit<u8>; Self::DISPLAY_BUFFER_SIZE],
571
1.55k
    ) -> usize {
572
1.55k
        let hours = self.hours.get().unsigned_abs();
573
1.55k
        let minutes = self.minutes.get().unsigned_abs();
574
1.55k
        let seconds = self.seconds.get().unsigned_abs();
575
576
1.55k
        let sign = if self.is_negative() { b'-' } else { b'+' };
577
1.55k
        buf[0] = MaybeUninit::new(sign);
578
1.55k
        buf[3] = MaybeUninit::new(b':');
579
1.55k
        buf[6] = MaybeUninit::new(b':');
580
581
        // Safety: `hours`, `minutes` and `seconds` are all less than 100. Both the source and
582
        // destination are valid for two bytes, aligned, and do not overlap.
583
1.55k
        unsafe {
584
1.55k
            two_digits_zero_padded(ru8::new_unchecked(hours))
585
1.55k
                .as_ptr()
586
1.55k
                .copy_to_nonoverlapping(buf.as_mut_ptr().add(1).cast(), 2);
587
1.55k
            two_digits_zero_padded(ru8::new_unchecked(minutes))
588
1.55k
                .as_ptr()
589
1.55k
                .copy_to_nonoverlapping(buf.as_mut_ptr().add(4).cast(), 2);
590
1.55k
            two_digits_zero_padded(ru8::new_unchecked(seconds))
591
1.55k
                .as_ptr()
592
1.55k
                .copy_to_nonoverlapping(buf.as_mut_ptr().add(7).cast(), 2);
593
1.55k
        }
594
595
        // The number of bytes written does not vary; it is always 9.
596
1.55k
        9
597
1.55k
    }
<time::utc_offset::UtcOffset>::fmt_into_buffer
Line
Count
Source
568
1.55k
    pub(crate) const fn fmt_into_buffer(
569
1.55k
        self,
570
1.55k
        buf: &mut [MaybeUninit<u8>; Self::DISPLAY_BUFFER_SIZE],
571
1.55k
    ) -> usize {
572
1.55k
        let hours = self.hours.get().unsigned_abs();
573
1.55k
        let minutes = self.minutes.get().unsigned_abs();
574
1.55k
        let seconds = self.seconds.get().unsigned_abs();
575
576
1.55k
        let sign = if self.is_negative() { b'-' } else { b'+' };
577
1.55k
        buf[0] = MaybeUninit::new(sign);
578
1.55k
        buf[3] = MaybeUninit::new(b':');
579
1.55k
        buf[6] = MaybeUninit::new(b':');
580
581
        // Safety: `hours`, `minutes` and `seconds` are all less than 100. Both the source and
582
        // destination are valid for two bytes, aligned, and do not overlap.
583
1.55k
        unsafe {
584
1.55k
            two_digits_zero_padded(ru8::new_unchecked(hours))
585
1.55k
                .as_ptr()
586
1.55k
                .copy_to_nonoverlapping(buf.as_mut_ptr().add(1).cast(), 2);
587
1.55k
            two_digits_zero_padded(ru8::new_unchecked(minutes))
588
1.55k
                .as_ptr()
589
1.55k
                .copy_to_nonoverlapping(buf.as_mut_ptr().add(4).cast(), 2);
590
1.55k
            two_digits_zero_padded(ru8::new_unchecked(seconds))
591
1.55k
                .as_ptr()
592
1.55k
                .copy_to_nonoverlapping(buf.as_mut_ptr().add(7).cast(), 2);
593
1.55k
        }
594
595
        // The number of bytes written does not vary; it is always 9.
596
1.55k
        9
597
1.55k
    }
Unexecuted instantiation: <time::utc_offset::UtcOffset>::fmt_into_buffer
598
}
599
600
impl fmt::Display for UtcOffset {
601
    #[inline]
602
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
603
0
        let mut buf = [MaybeUninit::uninit(); Self::DISPLAY_BUFFER_SIZE];
604
0
        let len = self.fmt_into_buffer(&mut buf);
605
        // Safety: All bytes up to `len` have been initialized with ASCII characters.
606
0
        let s = unsafe { str_from_raw_parts(buf.as_ptr().cast(), len) };
607
0
        f.pad(s)
608
0
    }
609
}
610
611
impl fmt::Debug for UtcOffset {
612
    #[inline]
613
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
614
0
        fmt::Display::fmt(self, f)
615
0
    }
616
}
617
618
impl Neg for UtcOffset {
619
    type Output = Self;
620
621
    #[inline]
622
0
    fn neg(self) -> Self::Output {
623
0
        Self::from_hms_ranged(self.hours.neg(), self.minutes.neg(), self.seconds.neg())
624
0
    }
625
}