datetime_string/common/
hms6_colon.rs

1//! Time string in `%H:%M:%S` (`hh:mm:ss`) format.
2//!
3//! This is also an RFC 3339 [`partial-time`] string without `secfrac` part.
4//!
5//! [`partial-time`]: https://tools.ietf.org/html/rfc3339#section-5.6
6
7use core::{
8    convert::TryFrom,
9    fmt,
10    ops::{self, Range},
11    str,
12};
13
14use crate::{parse::parse_digits2, str::write_digit2};
15
16#[cfg(feature = "alloc")]
17use alloc::{string::String, vec::Vec};
18
19use crate::error::{ComponentKind, Error, ErrorKind};
20
21/// Length of `hh:mm:ss` string.
22const HMS_LEN: usize = 8;
23/// Range of the hour in the string.
24const HOUR_RANGE: Range<usize> = 0..2;
25/// Range of the minute in the string.
26const MINUTE_RANGE: Range<usize> = 3..5;
27/// Range of the second in the string.
28const SECOND_RANGE: Range<usize> = 6..8;
29/// Maximum value of the hour.
30const HOUR_MAX: u8 = 23;
31/// Maximum value of the minute.
32const MINUTE_MAX: u8 = 59;
33/// Maximum value of the second.
34///
35/// Note that a leap second is always allowed.
36const SECOND_MAX: u8 = 60;
37
38/// Validates the given `%H:%M:%S` string.
39///
40/// In other words, this string can contain an RFC 3339 [`partial-time`] string without `secfrac` part.
41///
42/// This type allows leap seconds unconditionally, because leap seconds are
43/// irregular and cannot predict, and date and timezone is also necessary to
44/// check if a leap second really happened or will happen.
45/// It is user's responsibility to validate a leap second really happened or
46/// will happen, if the "second" component is 60.
47///
48/// [`partial-time`]: https://tools.ietf.org/html/rfc3339#section-5.6
49fn validate_bytes(s: &[u8]) -> Result<(), Error> {
50    let s: &[u8; HMS_LEN] = TryFrom::try_from(s).map_err(|_| {
51        if s.len() < HMS_LEN {
52            ErrorKind::TooShort
53        } else {
54            ErrorKind::TooLong
55        }
56    })?;
57
58    if (s[2] != b':') || (s[5] != b':') {
59        return Err(ErrorKind::InvalidSeparator.into());
60    }
61
62    let hour_s: [u8; 2] = [s[0], s[1]];
63    let minute_s: [u8; 2] = [s[3], s[4]];
64    let second_s: [u8; 2] = [s[6], s[7]];
65
66    if !hour_s.iter().all(u8::is_ascii_digit) {
67        return Err(ErrorKind::InvalidComponentType(ComponentKind::Hour).into());
68    }
69    if !minute_s.iter().all(u8::is_ascii_digit) {
70        return Err(ErrorKind::InvalidComponentType(ComponentKind::Minute).into());
71    }
72    if !second_s.iter().all(u8::is_ascii_digit) {
73        return Err(ErrorKind::InvalidComponentType(ComponentKind::Second).into());
74    }
75
76    let hour = parse_digits2(hour_s);
77    if hour > HOUR_MAX {
78        return Err(ErrorKind::ComponentOutOfRange(ComponentKind::Hour).into());
79    }
80    let minute = parse_digits2(minute_s);
81    if minute > MINUTE_MAX {
82        return Err(ErrorKind::ComponentOutOfRange(ComponentKind::Minute).into());
83    }
84    let second = parse_digits2(second_s);
85    // Leap second is always allowed for this type. See the documentation for the types.
86    if second > SECOND_MAX {
87        return Err(ErrorKind::ComponentOutOfRange(ComponentKind::Minute).into());
88    }
89
90    Ok(())
91}
92
93/// String slice for a time in `%H:%M:%S` (`hh:mm:ss`) format, such as `01:23:45`.
94///
95/// This is also an RFC 3339 [`partial-time`] string without `secfrac` part.
96///
97/// [`partial-time`]: https://tools.ietf.org/html/rfc3339#section-5.6
98#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
99#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
100#[repr(transparent)]
101// Note that `derive(Serialize)` cannot used here, because it encodes this as
102// `[u8]` rather than as a string.
103//
104// Comparisons implemented for the type are consistent (at least it is intended to be so).
105// See <https://github.com/rust-lang/rust-clippy/issues/2025>.
106// Note that `clippy::derive_ord_xor_partial_ord` would be introduced since Rust 1.47.0.
107#[allow(clippy::derive_hash_xor_eq)]
108#[allow(unknown_lints, clippy::derive_ord_xor_partial_ord)]
109pub struct Hms6ColonStr([u8]);
110
111impl Hms6ColonStr {
112    /// Creates a `&Hms6ColonStr` from the given byte slice.
113    ///
114    /// This performs assertion in debug build, but not in release build.
115    ///
116    /// # Safety
117    ///
118    /// `validate_bytes(s)` should return `Ok(())`.
119    #[inline]
120    #[must_use]
121    pub(crate) unsafe fn from_bytes_maybe_unchecked(v: &[u8]) -> &Self {
122        debug_assert_ok!(validate_bytes(v));
123        &*(v as *const [u8] as *const Self)
124    }
125
126    /// Creates a `&mut Hms6ColonStr` from the given mutable byte slice.
127    ///
128    /// This performs assertion in debug build, but not in release build.
129    ///
130    /// # Safety
131    ///
132    /// `validate_bytes(s)` should return `Ok(())`.
133    #[inline]
134    #[must_use]
135    pub(crate) unsafe fn from_bytes_maybe_unchecked_mut(s: &mut [u8]) -> &mut Self {
136        debug_assert_ok!(validate_bytes(s));
137        &mut *(s as *mut [u8] as *mut Self)
138    }
139
140    /// Creates a `&mut Hms6ColonStr` from the given mutable string slice.
141    ///
142    /// This performs assertion in debug build, but not in release build.
143    ///
144    /// # Safety
145    ///
146    /// `validate_bytes(s.as_bytes())` should return `Ok(())`.
147    #[inline]
148    #[must_use]
149    unsafe fn from_str_maybe_unchecked_mut(s: &mut str) -> &mut Self {
150        // This is safe because `Hms6ColonStr` ensures that the underlying bytes
151        // are ASCII string after modification.
152        Self::from_bytes_maybe_unchecked_mut(s.as_bytes_mut())
153    }
154
155    /// Creates a new `&Hms6ColonStr` from a string slice.
156    ///
157    /// # Examples
158    ///
159    /// ```
160    /// # use datetime_string::common::Hms6ColonStr;
161    /// let time = Hms6ColonStr::from_str("12:34:56")?;
162    /// assert_eq!(time.as_str(), "12:34:56");
163    ///
164    /// assert!(Hms6ColonStr::from_str("00:00:00").is_ok());
165    /// assert!(Hms6ColonStr::from_str("23:59:59").is_ok());
166    /// assert!(Hms6ColonStr::from_str("23:59:60").is_ok(), "Leap second is always allowed");
167    ///
168    /// assert!(Hms6ColonStr::from_str("24:00:00").is_err(), "Invalid hour");
169    /// assert!(Hms6ColonStr::from_str("00:60:00").is_err(), "Invalid minute");
170    /// assert!(Hms6ColonStr::from_str("00:00:61").is_err(), "Invalid second");
171    /// # Ok::<_, datetime_string::Error>(())
172    /// ```
173    #[inline]
174    // `FromStr` trait cannot be implemented for a slice.
175    #[allow(clippy::should_implement_trait)]
176    pub fn from_str(s: &str) -> Result<&Self, Error> {
177        TryFrom::try_from(s)
178    }
179
180    /// Creates a new `&mut Hms6ColonStr` from a mutable string slice.
181    ///
182    /// # Examples
183    ///
184    /// ```
185    /// # use datetime_string::common::Hms6ColonStr;
186    /// let mut buf = "12:34:56".to_owned();
187    /// let time = Hms6ColonStr::from_mut_str(&mut buf)?;
188    /// assert_eq!(time.as_str(), "12:34:56");
189    ///
190    /// time.set_hour(0)?;
191    /// assert_eq!(time.as_str(), "00:34:56");
192    ///
193    /// assert_eq!(buf, "00:34:56");
194    /// # Ok::<_, datetime_string::Error>(())
195    /// ```
196    #[inline]
197    pub fn from_mut_str(s: &mut str) -> Result<&mut Self, Error> {
198        TryFrom::try_from(s)
199    }
200
201    /// Creates a new `&Hms6ColonStr` from a byte slice.
202    ///
203    /// # Examples
204    ///
205    /// ```
206    /// # use datetime_string::common::Hms6ColonStr;
207    /// let time = Hms6ColonStr::from_bytes(b"12:34:56")?;
208    /// assert_eq!(time.as_str(), "12:34:56");
209    ///
210    /// assert!(Hms6ColonStr::from_bytes(b"00:00:00").is_ok());
211    /// assert!(Hms6ColonStr::from_bytes(b"23:59:59").is_ok());
212    /// assert!(Hms6ColonStr::from_bytes(b"23:59:60").is_ok(), "Leap second is always allowed");
213    ///
214    /// assert!(Hms6ColonStr::from_bytes(b"24:00:00").is_err(), "Invalid hour");
215    /// assert!(Hms6ColonStr::from_bytes(b"00:60:00").is_err(), "Invalid minute");
216    /// assert!(Hms6ColonStr::from_bytes(b"00:00:61").is_err(), "Invalid second");
217    /// # Ok::<_, datetime_string::Error>(())
218    /// ```
219    #[inline]
220    pub fn from_bytes(s: &[u8]) -> Result<&Self, Error> {
221        TryFrom::try_from(s)
222    }
223
224    /// Creates a new `&mut Hms6ColonStr` from a mutable byte slice.
225    ///
226    /// # Examples
227    ///
228    /// ```
229    /// # use datetime_string::common::Hms6ColonStr;
230    /// let mut buf: [u8; 8] = *b"12:34:56";
231    /// let time = Hms6ColonStr::from_bytes_mut(&mut buf[..])?;
232    /// assert_eq!(time.as_str(), "12:34:56");
233    ///
234    /// time.set_hour(0)?;
235    /// assert_eq!(time.as_str(), "00:34:56");
236    ///
237    /// assert_eq!(&buf[..], b"00:34:56");
238    /// # Ok::<_, datetime_string::Error>(())
239    /// ```
240    #[inline]
241    pub fn from_bytes_mut(s: &mut [u8]) -> Result<&mut Self, Error> {
242        TryFrom::try_from(s)
243    }
244
245    /// Assigns the given value.
246    ///
247    /// # Examples
248    ///
249    /// ```
250    /// # use datetime_string::common::Hms6ColonStr;
251    /// let mut buf: [u8; 8] = *b"12:34:56";
252    /// let time = Hms6ColonStr::from_bytes_mut(&mut buf[..])?;
253    /// assert_eq!(time.as_str(), "12:34:56");
254    ///
255    /// let newtime = Hms6ColonStr::from_str("01:01:01")?;
256    ///
257    /// time.assign(newtime);
258    /// assert_eq!(time.as_str(), "01:01:01");
259    /// assert_eq!(buf, *b"01:01:01");
260    /// # Ok::<_, datetime_string::Error>(())
261    /// ```
262    #[inline]
263    pub fn assign(&mut self, v: &Self) {
264        debug_assert_eq!(self.0.len(), v.0.len());
265        self.0.copy_from_slice(&v.0);
266    }
267
268    /// Returns a string slice.
269    ///
270    /// # Examples
271    ///
272    /// ```
273    /// # use datetime_string::common::Hms6ColonStr;
274    /// let time = Hms6ColonStr::from_str("12:34:56")?;
275    ///
276    /// assert_eq!(time.as_str(), "12:34:56");
277    /// # Ok::<_, datetime_string::Error>(())
278    /// ```
279    #[inline]
280    #[must_use]
281    pub fn as_str(&self) -> &str {
282        unsafe {
283            // This is safe because the `Hms6ColonStr` ensures that the
284            // underlying bytes are ASCII string.
285            debug_assert_safe_version_ok!(str::from_utf8(&self.0));
286            str::from_utf8_unchecked(&self.0)
287        }
288    }
289
290    /// Returns a byte slice.
291    ///
292    /// If you want to use indexed access, prefer [`as_bytes_fixed_len`].
293    ///
294    /// # Examples
295    ///
296    /// ```
297    /// # use datetime_string::common::Hms6ColonStr;
298    /// let time = Hms6ColonStr::from_bytes(b"12:34:56")?;
299    ///
300    /// assert_eq!(time.as_bytes(), b"12:34:56");
301    /// # Ok::<_, datetime_string::Error>(())
302    /// ```
303    ///
304    /// [`as_bytes_fixed_len`]: #method.as_bytes_fixed_len
305    #[inline]
306    #[must_use]
307    pub fn as_bytes(&self) -> &[u8] {
308        &self.0
309    }
310
311    /// Returns a fixed length byte slice.
312    ///
313    /// # Examples
314    ///
315    /// ```
316    /// # use datetime_string::common::Hms6ColonStr;
317    /// let time = Hms6ColonStr::from_str("12:34:56")?;
318    ///
319    /// let fixed_len: &[u8; 8] = time.as_bytes_fixed_len();
320    /// assert_eq!(fixed_len, b"12:34:56");
321    /// # Ok::<_, datetime_string::Error>(())
322    /// ```
323    #[inline]
324    #[must_use]
325    pub fn as_bytes_fixed_len(&self) -> &[u8; 8] {
326        debug_assert_eq!(self.len(), HMS_LEN, "Hms6ColonStr must always be 8 bytes");
327
328        debug_assert_safe_version_ok!(<&[u8; 8]>::try_from(&self.0[..HMS_LEN]));
329        let ptr = self.0.as_ptr() as *const [u8; HMS_LEN];
330        // This must be always safe because the length is already checked.
331        unsafe { &*ptr }
332    }
333
334    /// Returns the hour as a string slice.
335    ///
336    /// # Examples
337    ///
338    /// ```
339    /// # use datetime_string::common::Hms6ColonStr;
340    /// let time = Hms6ColonStr::from_str("12:34:56")?;
341    ///
342    /// assert_eq!(time.hour_str(), "12");
343    /// # Ok::<_, datetime_string::Error>(())
344    /// ```
345    #[inline]
346    #[must_use]
347    pub fn hour_str(&self) -> &str {
348        unsafe {
349            // This is safe because the string is ASCII string and `HOUR_RANGE`
350            // is always inside the string.
351            debug_assert_safe_version_ok!(str::from_utf8(&self.0[HOUR_RANGE]));
352            str::from_utf8_unchecked(self.0.get_unchecked(HOUR_RANGE))
353        }
354    }
355
356    /// Returns the hour as a fixed length byte slice.
357    ///
358    /// # Examples
359    ///
360    /// ```
361    /// # use datetime_string::common::Hms6ColonStr;
362    /// let time = Hms6ColonStr::from_str("12:34:56")?;
363    ///
364    /// let hour_fixed_len: &[u8; 2] = time.hour_bytes_fixed_len();
365    /// assert_eq!(hour_fixed_len, b"12");
366    /// # Ok::<_, datetime_string::Error>(())
367    /// ```
368    #[inline]
369    #[must_use]
370    pub fn hour_bytes_fixed_len(&self) -> &[u8; 2] {
371        unsafe {
372            // This is safe because `HOUR_RANGE` fits inside the string.
373            debug_assert_safe_version_ok!(<&[u8; 2]>::try_from(&self.0[HOUR_RANGE]));
374            let ptr = self.0.as_ptr().add(HOUR_RANGE.start) as *const [u8; 2];
375            &*ptr
376        }
377    }
378
379    /// Returns the hour as a fixed length mutable byte slice.
380    ///
381    /// # Safety
382    ///
383    /// The returned slice should have only ASCII digits.
384    /// If non-ASCII digits are stored, it may lead to undefined behavior.
385    #[inline]
386    #[must_use]
387    unsafe fn hour_bytes_mut_fixed_len(&mut self) -> &mut [u8; 2] {
388        // This is safe because `HOUR_RANGE` fits inside the string.
389        debug_assert_safe_version_ok!(<&mut [u8; 2]>::try_from(&mut self.0[HOUR_RANGE]));
390        let ptr = self.0.as_mut_ptr().add(HOUR_RANGE.start) as *mut [u8; 2];
391        &mut *ptr
392    }
393
394    /// Returns the hour as an integer.
395    ///
396    /// # Examples
397    ///
398    /// ```
399    /// # use datetime_string::common::Hms6ColonStr;
400    /// let time = Hms6ColonStr::from_str("12:34:56")?;
401    ///
402    /// assert_eq!(time.hour(), 12);
403    /// # Ok::<_, datetime_string::Error>(())
404    /// ```
405    #[inline]
406    #[must_use]
407    pub fn hour(&self) -> u8 {
408        parse_digits2(*self.hour_bytes_fixed_len())
409    }
410
411    /// Returns the minute as a string slice.
412    ///
413    /// # Examples
414    ///
415    /// ```
416    /// # use datetime_string::common::Hms6ColonStr;
417    /// let time = Hms6ColonStr::from_str("12:34:56")?;
418    ///
419    /// assert_eq!(time.minute_str(), "34");
420    /// # Ok::<_, datetime_string::Error>(())
421    /// ```
422    #[inline]
423    #[must_use]
424    pub fn minute_str(&self) -> &str {
425        unsafe {
426            // This is safe because the string is ASCII string and `MINUTE_RANGE`
427            // is always inside the string.
428            debug_assert_safe_version_ok!(str::from_utf8(&self.0[MINUTE_RANGE]));
429            str::from_utf8_unchecked(self.0.get_unchecked(MINUTE_RANGE))
430        }
431    }
432
433    /// Returns the minute as a fixed length byte slice.
434    ///
435    /// # Examples
436    ///
437    /// ```
438    /// # use datetime_string::common::Hms6ColonStr;
439    /// let time = Hms6ColonStr::from_str("12:34:56")?;
440    ///
441    /// let minute_fixed_len: &[u8; 2] = time.minute_bytes_fixed_len();
442    /// assert_eq!(minute_fixed_len, b"34");
443    /// # Ok::<_, datetime_string::Error>(())
444    /// ```
445    #[inline]
446    #[must_use]
447    pub fn minute_bytes_fixed_len(&self) -> &[u8; 2] {
448        unsafe {
449            // This is safe because `MINUTE_RANGE` fits inside the string.
450            debug_assert_safe_version_ok!(<&[u8; 2]>::try_from(&self.0[MINUTE_RANGE]));
451            let ptr = self.0.as_ptr().add(MINUTE_RANGE.start) as *const [u8; 2];
452            &*ptr
453        }
454    }
455
456    /// Returns the minute as a fixed length mutable byte slice.
457    ///
458    /// # Safety
459    ///
460    /// The returned slice should have only ASCII digits.
461    /// If non-ASCII digits are stored, it may lead to undefined behavior.
462    #[inline]
463    #[must_use]
464    unsafe fn minute_bytes_mut_fixed_len(&mut self) -> &mut [u8; 2] {
465        // This is safe because `MINUTE_RANGE` fits inside the string.
466        debug_assert_safe_version_ok!(<&mut [u8; 2]>::try_from(&mut self.0[MINUTE_RANGE]));
467        let ptr = self.0.as_mut_ptr().add(MINUTE_RANGE.start) as *mut [u8; 2];
468        &mut *ptr
469    }
470
471    /// Returns the minute as an integer.
472    ///
473    /// # Examples
474    ///
475    /// ```
476    /// # use datetime_string::common::Hms6ColonStr;
477    /// let time = Hms6ColonStr::from_str("12:34:56")?;
478    ///
479    /// assert_eq!(time.minute(), 34);
480    /// # Ok::<_, datetime_string::Error>(())
481    /// ```
482    #[inline]
483    #[must_use]
484    pub fn minute(&self) -> u8 {
485        parse_digits2(*self.minute_bytes_fixed_len())
486    }
487
488    /// Returns the second as a string slice.
489    ///
490    /// # Examples
491    ///
492    /// ```
493    /// # use datetime_string::common::Hms6ColonStr;
494    /// let time = Hms6ColonStr::from_str("12:34:56")?;
495    ///
496    /// assert_eq!(time.second_str(), "56");
497    /// # Ok::<_, datetime_string::Error>(())
498    /// ```
499    #[inline]
500    #[must_use]
501    pub fn second_str(&self) -> &str {
502        unsafe {
503            // This is safe because the string is ASCII string and `SECOND_RANGE`
504            // is always inside the string.
505            debug_assert_safe_version_ok!(str::from_utf8(&self.0[SECOND_RANGE]));
506            str::from_utf8_unchecked(self.0.get_unchecked(SECOND_RANGE))
507        }
508    }
509
510    /// Returns the second as a fixed length byte slice.
511    ///
512    /// # Examples
513    ///
514    /// ```
515    /// # use datetime_string::common::Hms6ColonStr;
516    /// let time = Hms6ColonStr::from_str("12:34:56")?;
517    ///
518    /// let second_fixed_len: &[u8; 2] = time.second_bytes_fixed_len();
519    /// assert_eq!(second_fixed_len, b"56");
520    /// # Ok::<_, datetime_string::Error>(())
521    /// ```
522    #[inline]
523    #[must_use]
524    pub fn second_bytes_fixed_len(&self) -> &[u8; 2] {
525        unsafe {
526            // This is safe because `SECOND_RANGE` fits inside the string.
527            debug_assert_safe_version_ok!(<&[u8; 2]>::try_from(&self.0[SECOND_RANGE]));
528            let ptr = self.0.as_ptr().add(SECOND_RANGE.start) as *const [u8; 2];
529            &*ptr
530        }
531    }
532
533    /// Returns the second as a fixed length mutable byte slice.
534    ///
535    /// # Safety
536    ///
537    /// The returned slice should have only ASCII digits.
538    /// If non-ASCII digits are stored, it may lead to undefined behavior.
539    #[inline]
540    #[must_use]
541    unsafe fn second_bytes_mut_fixed_len(&mut self) -> &mut [u8; 2] {
542        // This is safe because `SECOND_RANGE` fits inside the string.
543        debug_assert_safe_version_ok!(<&mut [u8; 2]>::try_from(&mut self.0[SECOND_RANGE]));
544        let ptr = self.0.as_mut_ptr().add(SECOND_RANGE.start) as *mut [u8; 2];
545        &mut *ptr
546    }
547
548    /// Returns the second as an integer.
549    ///
550    /// # Examples
551    ///
552    /// ```
553    /// # use datetime_string::common::Hms6ColonStr;
554    /// let time = Hms6ColonStr::from_str("12:34:56")?;
555    ///
556    /// assert_eq!(time.second(), 56);
557    /// # Ok::<_, datetime_string::Error>(())
558    /// ```
559    #[inline]
560    #[must_use]
561    pub fn second(&self) -> u8 {
562        parse_digits2(*self.second_bytes_fixed_len())
563    }
564
565    /// Sets the given hour value to the string.
566    ///
567    /// # Failures
568    ///
569    /// * Fails if the time after modification is invalid.
570    ///
571    /// # Examples
572    ///
573    /// ```
574    /// # use datetime_string::common::Hms6ColonStr;
575    /// let mut buf: [u8; 8] = *b"12:34:56";
576    /// let time = Hms6ColonStr::from_bytes_mut(&mut buf[..])?;
577    /// assert_eq!(time.as_str(), "12:34:56");
578    ///
579    /// time.set_hour(0)?;
580    /// assert_eq!(time.as_str(), "00:34:56");
581    ///
582    /// assert!(time.set_hour(24).is_err(), "24:34:56 is invalid");
583    /// # Ok::<_, datetime_string::Error>(())
584    /// ```
585    pub fn set_hour(&mut self, hour: u8) -> Result<(), Error> {
586        if hour > HOUR_MAX {
587            return Err(ErrorKind::ComponentOutOfRange(ComponentKind::Hour).into());
588        }
589        unsafe {
590            // This is safe because `write_digit2()` fills the slice with ASCII digits.
591            write_digit2(self.hour_bytes_mut_fixed_len(), hour);
592        }
593        debug_assert_ok!(validate_bytes(&self.0));
594
595        Ok(())
596    }
597
598    /// Sets the given minute value to the string.
599    ///
600    /// # Failures
601    ///
602    /// * Fails if the time after modification is invalid.
603    ///
604    /// # Examples
605    ///
606    /// ```
607    /// # use datetime_string::common::Hms6ColonStr;
608    /// let mut buf: [u8; 8] = *b"12:34:56";
609    /// let time = Hms6ColonStr::from_bytes_mut(&mut buf[..])?;
610    /// assert_eq!(time.as_str(), "12:34:56");
611    ///
612    /// time.set_minute(0)?;
613    /// assert_eq!(time.as_str(), "12:00:56");
614    ///
615    /// assert!(time.set_minute(60).is_err(), "24:60:56 is invalid");
616    /// # Ok::<_, datetime_string::Error>(())
617    /// ```
618    pub fn set_minute(&mut self, minute: u8) -> Result<(), Error> {
619        if minute > MINUTE_MAX {
620            return Err(ErrorKind::ComponentOutOfRange(ComponentKind::Minute).into());
621        }
622        unsafe {
623            // This is safe because `write_digit2()` fills the slice with ASCII digits.
624            write_digit2(self.minute_bytes_mut_fixed_len(), minute);
625        }
626        debug_assert_ok!(validate_bytes(&self.0));
627
628        Ok(())
629    }
630
631    /// Sets the given second value to the string.
632    ///
633    /// # Failures
634    ///
635    /// * Fails if the time after modification is invalid.
636    ///
637    /// # Examples
638    ///
639    /// ```
640    /// # use datetime_string::common::Hms6ColonStr;
641    /// let mut buf: [u8; 8] = *b"12:34:56";
642    /// let time = Hms6ColonStr::from_bytes_mut(&mut buf[..])?;
643    /// assert_eq!(time.as_str(), "12:34:56");
644    ///
645    /// time.set_second(0)?;
646    /// assert_eq!(time.as_str(), "12:34:00");
647    ///
648    /// assert!(time.set_second(61).is_err(), "24:34:61 is invalid");
649    /// # Ok::<_, datetime_string::Error>(())
650    /// ```
651    pub fn set_second(&mut self, second: u8) -> Result<(), Error> {
652        if second > SECOND_MAX {
653            return Err(ErrorKind::ComponentOutOfRange(ComponentKind::Second).into());
654        }
655        unsafe {
656            // This is safe because `write_digit2()` fills the slice with ASCII digits.
657            write_digit2(self.second_bytes_mut_fixed_len(), second);
658        }
659        debug_assert_ok!(validate_bytes(&self.0));
660
661        Ok(())
662    }
663
664    /// Sets the given hour and minute values to the string.
665    ///
666    /// # Failures
667    ///
668    /// * Fails if the time after modification is invalid.
669    ///
670    /// # Examples
671    ///
672    /// ```
673    /// # use datetime_string::common::Hms6ColonStr;
674    /// let mut buf: [u8; 8] = *b"12:34:56";
675    /// let time = Hms6ColonStr::from_bytes_mut(&mut buf[..])?;
676    /// assert_eq!(time.as_str(), "12:34:56");
677    ///
678    /// time.set_hour_minute(21, 10)?;
679    /// assert_eq!(time.as_str(), "21:10:56");
680    ///
681    /// assert!(time.set_hour_minute(23, 60).is_err(), "23:60:56 is invalid");
682    /// # Ok::<_, datetime_string::Error>(())
683    /// ```
684    pub fn set_hour_minute(&mut self, hour: u8, minute: u8) -> Result<(), Error> {
685        if hour > HOUR_MAX {
686            return Err(ErrorKind::ComponentOutOfRange(ComponentKind::Hour).into());
687        }
688        if minute > MINUTE_MAX {
689            return Err(ErrorKind::ComponentOutOfRange(ComponentKind::Minute).into());
690        }
691        unsafe {
692            // These are safe because `write_digit2()` fills the slice with ASCII digits.
693            write_digit2(self.hour_bytes_mut_fixed_len(), hour);
694            write_digit2(self.minute_bytes_mut_fixed_len(), minute);
695        }
696        debug_assert_ok!(validate_bytes(&self.0));
697
698        Ok(())
699    }
700
701    /// Sets the given minute and second values to the string.
702    ///
703    /// # Failures
704    ///
705    /// * Fails if the time after modification is invalid.
706    ///
707    /// # Examples
708    ///
709    /// ```
710    /// # use datetime_string::common::Hms6ColonStr;
711    /// let mut buf: [u8; 8] = *b"12:34:56";
712    /// let time = Hms6ColonStr::from_bytes_mut(&mut buf[..])?;
713    /// assert_eq!(time.as_str(), "12:34:56");
714    ///
715    /// time.set_minute_second(54, 32)?;
716    /// assert_eq!(time.as_str(), "12:54:32");
717    ///
718    /// assert!(time.set_minute_second(60, 59).is_err(), "12:60:59 is invalid");
719    /// # Ok::<_, datetime_string::Error>(())
720    /// ```
721    pub fn set_minute_second(&mut self, minute: u8, second: u8) -> Result<(), Error> {
722        if minute > MINUTE_MAX {
723            return Err(ErrorKind::ComponentOutOfRange(ComponentKind::Minute).into());
724        }
725        if second > SECOND_MAX {
726            return Err(ErrorKind::ComponentOutOfRange(ComponentKind::Second).into());
727        }
728        unsafe {
729            // These are safe because `write_digit2()` fills the slice with ASCII digits.
730            write_digit2(self.minute_bytes_mut_fixed_len(), minute);
731            write_digit2(self.second_bytes_mut_fixed_len(), second);
732        }
733        debug_assert_ok!(validate_bytes(&self.0));
734
735        Ok(())
736    }
737
738    /// Sets the given hour, minute, and second values to the string.
739    ///
740    /// # Failures
741    ///
742    /// * Fails if the time after modification is invalid.
743    ///
744    /// # Examples
745    ///
746    /// ```
747    /// # use datetime_string::common::Hms6ColonStr;
748    /// let mut buf: [u8; 8] = *b"12:34:56";
749    /// let time = Hms6ColonStr::from_bytes_mut(&mut buf[..])?;
750    /// assert_eq!(time.as_str(), "12:34:56");
751    ///
752    /// time.set_time(23, 12, 1)?;
753    /// assert_eq!(time.as_str(), "23:12:01");
754    ///
755    /// assert!(time.set_time(24, 0, 0).is_err(), "24:00:00 is invalid");
756    /// # Ok::<_, datetime_string::Error>(())
757    /// ```
758    pub fn set_time(&mut self, hour: u8, minute: u8, second: u8) -> Result<(), Error> {
759        if hour > HOUR_MAX {
760            return Err(ErrorKind::ComponentOutOfRange(ComponentKind::Hour).into());
761        }
762        if minute > MINUTE_MAX {
763            return Err(ErrorKind::ComponentOutOfRange(ComponentKind::Minute).into());
764        }
765        if second > SECOND_MAX {
766            return Err(ErrorKind::ComponentOutOfRange(ComponentKind::Second).into());
767        }
768        unsafe {
769            // These are safe because `write_digit2()` fills the slice with ASCII digits.
770            write_digit2(self.hour_bytes_mut_fixed_len(), hour);
771            write_digit2(self.minute_bytes_mut_fixed_len(), minute);
772            write_digit2(self.second_bytes_mut_fixed_len(), second);
773        }
774        debug_assert_ok!(validate_bytes(&self.0));
775
776        Ok(())
777    }
778
779    /// Retruns the seconds from the start of the day.
780    ///
781    /// # Example
782    ///
783    /// ```
784    /// # use datetime_string::common::Hms6ColonStr;
785    /// let time = Hms6ColonStr::from_str("12:34:56")?;
786    /// assert_eq!(time.to_seconds(), 12 * 60 * 60 + 34 * 60 + 56);
787    ///
788    /// let zero = Hms6ColonStr::from_str("00:00:00")?;
789    /// assert_eq!(zero.to_seconds(), 0);
790    ///
791    /// let last = Hms6ColonStr::from_str("23:59:59")?;
792    /// assert_eq!(last.to_seconds(), 24 * 60 * 60 - 1);
793    ///
794    /// let last_leap = Hms6ColonStr::from_str("23:59:60")?;
795    /// assert_eq!(last_leap.to_seconds(), 24 * 60 * 60);
796    /// # Ok::<_, datetime_string::Error>(())
797    /// ```
798    #[inline]
799    pub fn to_seconds(&self) -> u32 {
800        u32::from(self.hour()) * 3600 + u32::from(self.minute()) * 60 + u32::from(self.second())
801    }
802
803    /// Returns `true` if the time points to the leap second.
804    ///
805    /// # Example
806    ///
807    /// ```
808    /// # use datetime_string::common::Hms6ColonStr;
809    /// let normal = Hms6ColonStr::from_str("12:34:59")?;
810    /// assert!(!normal.is_leap_second());
811    ///
812    /// let leap = Hms6ColonStr::from_str("12:34:60")?;
813    /// assert!(leap.is_leap_second());
814    /// # Ok::<_, datetime_string::Error>(())
815    /// ```
816    #[inline]
817    #[must_use]
818    pub fn is_leap_second(&self) -> bool {
819        self.second_bytes_fixed_len() == b"60"
820    }
821}
822
823#[cfg(feature = "alloc")]
824#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
825impl alloc::borrow::ToOwned for Hms6ColonStr {
826    type Owned = Hms6ColonString;
827
828    #[inline]
829    fn to_owned(&self) -> Self::Owned {
830        self.into()
831    }
832}
833
834impl AsRef<[u8]> for Hms6ColonStr {
835    #[inline]
836    fn as_ref(&self) -> &[u8] {
837        self.as_bytes()
838    }
839}
840
841impl AsRef<str> for Hms6ColonStr {
842    #[inline]
843    fn as_ref(&self) -> &str {
844        self.as_str()
845    }
846}
847
848impl AsRef<Hms6ColonStr> for Hms6ColonStr {
849    #[inline]
850    fn as_ref(&self) -> &Hms6ColonStr {
851        self
852    }
853}
854
855impl AsMut<Hms6ColonStr> for Hms6ColonStr {
856    #[inline]
857    fn as_mut(&mut self) -> &mut Hms6ColonStr {
858        self
859    }
860}
861
862impl<'a> From<&'a Hms6ColonStr> for &'a str {
863    #[inline]
864    fn from(v: &'a Hms6ColonStr) -> Self {
865        v.as_str()
866    }
867}
868
869#[cfg(feature = "chrono04")]
870#[cfg_attr(docsrs, doc(cfg(feature = "chrono04")))]
871impl From<&Hms6ColonStr> for chrono04::NaiveTime {
872    fn from(v: &Hms6ColonStr) -> Self {
873        let hour = u32::from(v.hour());
874        let minute = u32::from(v.minute());
875        let second = u32::from(v.second());
876
877        // Note that `chrono04::NaiveTime::from_hms()` does not allow leap second.
878        // See <https://docs.rs/chrono/0.4.19/chrono/naive/struct.NaiveTime.html#representing-leap-seconds>.
879        if second == 60 {
880            Self::from_hms_milli(hour, minute, 59, 1000)
881        } else {
882            Self::from_hms(hour, minute, second)
883        }
884    }
885}
886
887#[cfg(feature = "time03")]
888#[cfg_attr(docsrs, doc(cfg(feature = "time03")))]
889impl From<&Hms6ColonStr> for time03::Time {
890    fn from(v: &Hms6ColonStr) -> Self {
891        let hour = v.hour();
892        let minute = v.minute();
893        // Note that `time` v0.3 does not allow leap seconds.
894        let second = v.second().min(59);
895
896        Self::from_hms(hour, minute, second)
897            .expect("[validity] the time must be valid and leap second is already handled")
898    }
899}
900
901impl<'a> TryFrom<&'a [u8]> for &'a Hms6ColonStr {
902    type Error = Error;
903
904    #[inline]
905    fn try_from(v: &'a [u8]) -> Result<Self, Self::Error> {
906        validate_bytes(v)?;
907        Ok(unsafe {
908            // This is safe because the value is successfully validated.
909            Hms6ColonStr::from_bytes_maybe_unchecked(v)
910        })
911    }
912}
913
914impl<'a> TryFrom<&'a mut [u8]> for &'a mut Hms6ColonStr {
915    type Error = Error;
916
917    #[inline]
918    fn try_from(v: &'a mut [u8]) -> Result<Self, Self::Error> {
919        validate_bytes(v)?;
920        Ok(unsafe {
921            // This is safe because the value is successfully validated.
922            Hms6ColonStr::from_bytes_maybe_unchecked_mut(v)
923        })
924    }
925}
926
927impl<'a> TryFrom<&'a str> for &'a Hms6ColonStr {
928    type Error = Error;
929
930    #[inline]
931    fn try_from(v: &'a str) -> Result<Self, Self::Error> {
932        TryFrom::try_from(v.as_bytes())
933    }
934}
935
936impl<'a> TryFrom<&'a mut str> for &'a mut Hms6ColonStr {
937    type Error = Error;
938
939    #[inline]
940    fn try_from(v: &'a mut str) -> Result<Self, Self::Error> {
941        validate_bytes(v.as_bytes())?;
942        Ok(unsafe {
943            // This is safe because the value is successfully validated, and
944            // `Hms6ColonStr` ensures the value after modification is an ASCII string.
945            Hms6ColonStr::from_str_maybe_unchecked_mut(v)
946        })
947    }
948}
949
950impl fmt::Display for Hms6ColonStr {
951    #[inline]
952    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
953        self.as_str().fmt(f)
954    }
955}
956
957impl ops::Deref for Hms6ColonStr {
958    type Target = str;
959
960    #[inline]
961    fn deref(&self) -> &Self::Target {
962        self.as_str()
963    }
964}
965
966impl_cmp_symmetric!(str, Hms6ColonStr, &Hms6ColonStr);
967impl_cmp_symmetric!([u8], Hms6ColonStr, [u8]);
968impl_cmp_symmetric!([u8], Hms6ColonStr, &[u8]);
969impl_cmp_symmetric!([u8], &Hms6ColonStr, [u8]);
970impl_cmp_symmetric!(str, Hms6ColonStr, str);
971impl_cmp_symmetric!(str, Hms6ColonStr, &str);
972impl_cmp_symmetric!(str, &Hms6ColonStr, str);
973
974#[cfg(feature = "serde")]
975#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
976impl serde::Serialize for Hms6ColonStr {
977    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
978    where
979        S: serde::Serializer,
980    {
981        serializer.serialize_str(self.as_str())
982    }
983}
984
985/// Owned string for a time in `%H:%M:%S` (`hh:mm:ss`) format, such as `01:23:45`.
986///
987/// This is also an RFC 3339 [`partial-time`] string without `secfrac` part.
988///
989/// This is a fixed length string, and implements [`Copy`] trait.
990///
991/// To create a value of this type, use [`str::parse`] method or
992/// [`std::convert::TryFrom`] trait, or convert from `&Hms6ColonStr`.
993///
994/// # Examples
995///
996/// ```
997/// # use datetime_string::common::Hms6ColonString;
998/// use datetime_string::common::Hms6ColonStr;
999/// use std::convert::TryFrom;
1000///
1001/// let try_from = Hms6ColonString::try_from("12:34:56")?;
1002///
1003/// let parse = "12:34:56".parse::<Hms6ColonString>()?;
1004/// let parse2: Hms6ColonString = "12:34:56".parse()?;
1005///
1006/// let to_owned = Hms6ColonStr::from_str("12:34:56")?.to_owned();
1007/// let into: Hms6ColonString = Hms6ColonStr::from_str("12:34:56")?.into();
1008/// # Ok::<_, datetime_string::Error>(())
1009/// ```
1010///
1011/// [`partial-time`]: https://tools.ietf.org/html/rfc3339#section-5.6
1012#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1013#[repr(transparent)]
1014// Note that `derive(Serialize)` cannot used here, because it encodes this as
1015// `[u8; 8]` rather than as a string.
1016//
1017// Comparisons implemented for the type are consistent (at least it is intended to be so).
1018// See <https://github.com/rust-lang/rust-clippy/issues/2025>.
1019// Note that `clippy::derive_ord_xor_partial_ord` would be introduced since Rust 1.47.0.
1020#[allow(clippy::derive_hash_xor_eq)]
1021#[allow(unknown_lints, clippy::derive_ord_xor_partial_ord)]
1022pub struct Hms6ColonString([u8; HMS_LEN]);
1023
1024impl Hms6ColonString {
1025    /// Creates a `Hms6ColonString` from the given bytes.
1026    ///
1027    /// # Safety
1028    ///
1029    /// `validate_bytes(&s)` should return `Ok(())`.
1030    #[inline]
1031    #[must_use]
1032    unsafe fn new_maybe_unchecked(s: [u8; 8]) -> Self {
1033        debug_assert_ok!(validate_bytes(&s));
1034        Self(s)
1035    }
1036
1037    /// Returns `00:00:00`.
1038    #[inline]
1039    #[must_use]
1040    fn zero() -> Self {
1041        unsafe {
1042            // This is safe because `00:00:00` is valid.
1043            debug_assert_safe_version_ok!(Self::try_from(*b"00:00:00"));
1044            Self::new_maybe_unchecked(*b"00:00:00")
1045        }
1046    }
1047
1048    /// Creates a new `Hms6ColonString` from the given time.
1049    ///
1050    /// # Examples
1051    ///
1052    /// ```
1053    /// # use datetime_string::common::Hms6ColonString;
1054    /// let time = Hms6ColonString::from_hms(12, 34, 56)?;
1055    /// assert_eq!(time.as_str(), "12:34:56");
1056    ///
1057    /// assert!(Hms6ColonString::from_hms(0, 0, 61).is_err(), "00:00:61 is invaild time");
1058    /// # Ok::<_, datetime_string::Error>(())
1059    /// ```
1060    pub fn from_hms(hour: u8, minute: u8, second: u8) -> Result<Self, Error> {
1061        let mut v = Self::zero();
1062        v.set_time(hour, minute, second)?;
1063        Ok(v)
1064    }
1065
1066    /// Returns a `&Hms6ColonStr` for the string.
1067    ///
1068    /// # Examples
1069    ///
1070    /// ```
1071    /// # use datetime_string::common::Hms6ColonString;
1072    /// use datetime_string::common::Hms6ColonStr;
1073    /// let time = "12:34:56".parse::<Hms6ColonString>()?;
1074    ///
1075    /// // Usually you don't need to call `as_deref()` explicitly, because
1076    /// // `Deref<Target = Hms6ColonStr>` trait is implemented.
1077    /// let _: &Hms6ColonStr = time.as_deref();
1078    /// # Ok::<_, datetime_string::Error>(())
1079    /// ```
1080    #[inline]
1081    #[must_use]
1082    pub fn as_deref(&self) -> &Hms6ColonStr {
1083        unsafe {
1084            // This is safe because the string is already validated.
1085            debug_assert_safe_version_ok!(Hms6ColonStr::from_bytes(&self.0));
1086            Hms6ColonStr::from_bytes_maybe_unchecked(&self.0)
1087        }
1088    }
1089
1090    /// Returns a `&mut Hms6ColonStr` for the string.
1091    ///
1092    /// # Examples
1093    ///
1094    /// ```
1095    /// # use datetime_string::common::Hms6ColonString;
1096    /// use datetime_string::common::Hms6ColonStr;
1097    /// let mut time = "12:34:56".parse::<Hms6ColonString>()?;
1098    ///
1099    /// // Usually you don't need to call `as_deref_mut()` explicitly, because
1100    /// // `DerefMut` trait is implemented.
1101    /// let _: &mut Hms6ColonStr = time.as_deref_mut();
1102    /// # Ok::<_, datetime_string::Error>(())
1103    /// ```
1104    #[inline]
1105    #[must_use]
1106    pub fn as_deref_mut(&mut self) -> &mut Hms6ColonStr {
1107        unsafe {
1108            // This is safe because the string is already validated.
1109            debug_assert_ok!(Hms6ColonStr::from_bytes(&self.0));
1110            Hms6ColonStr::from_bytes_maybe_unchecked_mut(&mut self.0)
1111        }
1112    }
1113}
1114
1115impl core::borrow::Borrow<Hms6ColonStr> for Hms6ColonString {
1116    #[inline]
1117    fn borrow(&self) -> &Hms6ColonStr {
1118        self.as_deref()
1119    }
1120}
1121
1122impl core::borrow::BorrowMut<Hms6ColonStr> for Hms6ColonString {
1123    #[inline]
1124    fn borrow_mut(&mut self) -> &mut Hms6ColonStr {
1125        self.as_deref_mut()
1126    }
1127}
1128
1129impl AsRef<[u8]> for Hms6ColonString {
1130    #[inline]
1131    fn as_ref(&self) -> &[u8] {
1132        self.as_bytes()
1133    }
1134}
1135
1136impl AsRef<str> for Hms6ColonString {
1137    #[inline]
1138    fn as_ref(&self) -> &str {
1139        self.as_str()
1140    }
1141}
1142
1143impl AsRef<Hms6ColonStr> for Hms6ColonString {
1144    #[inline]
1145    fn as_ref(&self) -> &Hms6ColonStr {
1146        self
1147    }
1148}
1149
1150impl AsMut<Hms6ColonStr> for Hms6ColonString {
1151    #[inline]
1152    fn as_mut(&mut self) -> &mut Hms6ColonStr {
1153        self
1154    }
1155}
1156
1157#[cfg(feature = "alloc")]
1158#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
1159impl From<Hms6ColonString> for Vec<u8> {
1160    #[inline]
1161    fn from(v: Hms6ColonString) -> Vec<u8> {
1162        (*v.as_bytes_fixed_len()).into()
1163    }
1164}
1165
1166#[cfg(feature = "alloc")]
1167#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
1168impl From<Hms6ColonString> for String {
1169    #[inline]
1170    fn from(v: Hms6ColonString) -> String {
1171        let vec: Vec<u8> = (*v.as_bytes_fixed_len()).into();
1172        unsafe {
1173            // This is safe because a valid `hh:mm:ss` string is also an ASCII string.
1174            String::from_utf8_unchecked(vec)
1175        }
1176    }
1177}
1178
1179impl From<&Hms6ColonStr> for Hms6ColonString {
1180    fn from(v: &Hms6ColonStr) -> Self {
1181        unsafe {
1182            // This is safe because the value is already validated.
1183            Self::new_maybe_unchecked(*v.as_bytes_fixed_len())
1184        }
1185    }
1186}
1187
1188impl TryFrom<&[u8]> for Hms6ColonString {
1189    type Error = Error;
1190
1191    #[inline]
1192    fn try_from(v: &[u8]) -> Result<Self, Self::Error> {
1193        Hms6ColonStr::from_bytes(v).map(Into::into)
1194    }
1195}
1196
1197impl TryFrom<&str> for Hms6ColonString {
1198    type Error = Error;
1199
1200    #[inline]
1201    fn try_from(v: &str) -> Result<Self, Self::Error> {
1202        Hms6ColonStr::from_str(v).map(Into::into)
1203    }
1204}
1205
1206impl TryFrom<[u8; 8]> for Hms6ColonString {
1207    type Error = Error;
1208
1209    #[inline]
1210    fn try_from(v: [u8; 8]) -> Result<Self, Self::Error> {
1211        validate_bytes(&v)?;
1212        Ok(unsafe {
1213            // This is safe because the value is successfully validated.
1214            Self::new_maybe_unchecked(v)
1215        })
1216    }
1217}
1218
1219#[cfg(feature = "chrono04")]
1220#[cfg_attr(docsrs, doc(cfg(feature = "chrono04")))]
1221impl From<&chrono04::NaiveTime> for Hms6ColonString {
1222    fn from(v: &chrono04::NaiveTime) -> Self {
1223        use chrono04::Timelike;
1224
1225        Self::from_hms(v.hour() as u8, v.minute() as u8, v.second() as u8)
1226            .expect("`chrono04::NaiveTime` must always have a valid time")
1227    }
1228}
1229
1230impl fmt::Display for Hms6ColonString {
1231    #[inline]
1232    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1233        self.as_deref().fmt(f)
1234    }
1235}
1236
1237impl ops::Deref for Hms6ColonString {
1238    type Target = Hms6ColonStr;
1239
1240    #[inline]
1241    fn deref(&self) -> &Self::Target {
1242        self.as_deref()
1243    }
1244}
1245
1246impl ops::DerefMut for Hms6ColonString {
1247    #[inline]
1248    fn deref_mut(&mut self) -> &mut Self::Target {
1249        self.as_deref_mut()
1250    }
1251}
1252
1253impl str::FromStr for Hms6ColonString {
1254    type Err = Error;
1255
1256    #[inline]
1257    fn from_str(s: &str) -> Result<Self, Self::Err> {
1258        Self::try_from(s)
1259    }
1260}
1261
1262impl_cmp_symmetric!(Hms6ColonStr, Hms6ColonString, &Hms6ColonString);
1263impl_cmp_symmetric!(Hms6ColonStr, Hms6ColonString, Hms6ColonStr);
1264impl_cmp_symmetric!(Hms6ColonStr, Hms6ColonString, &Hms6ColonStr);
1265impl_cmp_symmetric!(str, Hms6ColonString, str);
1266impl_cmp_symmetric!(str, Hms6ColonString, &str);
1267impl_cmp_symmetric!(str, &Hms6ColonString, str);
1268impl_cmp_symmetric!([u8], Hms6ColonString, [u8]);
1269impl_cmp_symmetric!([u8], Hms6ColonString, &[u8]);
1270impl_cmp_symmetric!([u8], &Hms6ColonString, [u8]);
1271
1272#[cfg(feature = "serde")]
1273#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
1274impl serde::Serialize for Hms6ColonString {
1275    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1276    where
1277        S: serde::Serializer,
1278    {
1279        serializer.serialize_str(self.as_str())
1280    }
1281}
1282
1283/// Items for serde support.
1284#[cfg(feature = "serde")]
1285mod serde_ {
1286    use super::*;
1287
1288    use serde::de::{Deserialize, Deserializer, Visitor};
1289
1290    /// Visitor for `&Hms6ColonStr`.
1291    struct StrVisitor;
1292
1293    impl<'de> Visitor<'de> for StrVisitor {
1294        type Value = &'de Hms6ColonStr;
1295
1296        #[inline]
1297        fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1298            f.write_str("hh:mm:ss time string")
1299        }
1300
1301        #[inline]
1302        fn visit_borrowed_bytes<E>(self, v: &'de [u8]) -> Result<Self::Value, E>
1303        where
1304            E: serde::de::Error,
1305        {
1306            Self::Value::try_from(v).map_err(E::custom)
1307        }
1308
1309        #[inline]
1310        fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
1311        where
1312            E: serde::de::Error,
1313        {
1314            Self::Value::try_from(v).map_err(E::custom)
1315        }
1316    }
1317
1318    impl<'de> Deserialize<'de> for &'de Hms6ColonStr {
1319        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1320        where
1321            D: Deserializer<'de>,
1322        {
1323            deserializer.deserialize_any(StrVisitor)
1324        }
1325    }
1326
1327    /// Visitor for `Hms6ColonString`.
1328    struct StringVisitor;
1329
1330    impl<'de> Visitor<'de> for StringVisitor {
1331        type Value = Hms6ColonString;
1332
1333        #[inline]
1334        fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1335            f.write_str("hh:mm:ss time string")
1336        }
1337
1338        #[inline]
1339        fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
1340        where
1341            E: serde::de::Error,
1342        {
1343            Self::Value::try_from(v).map_err(E::custom)
1344        }
1345
1346        #[inline]
1347        fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
1348        where
1349            E: serde::de::Error,
1350        {
1351            Self::Value::try_from(v).map_err(E::custom)
1352        }
1353    }
1354
1355    impl<'de> Deserialize<'de> for Hms6ColonString {
1356        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1357        where
1358            D: Deserializer<'de>,
1359        {
1360            deserializer.deserialize_any(StringVisitor)
1361        }
1362    }
1363}
1364
1365#[cfg(test)]
1366mod tests {
1367    #[cfg(feature = "serde")]
1368    use super::*;
1369
1370    use super::validate_bytes as s_validate;
1371
1372    #[cfg(feature = "serde")]
1373    use serde_test::{assert_de_tokens, assert_tokens, Token};
1374
1375    #[test]
1376    fn validate_bytes() {
1377        assert!(s_validate(b"00:00:00").is_ok());
1378        assert!(s_validate(b"00:00:30").is_ok());
1379        assert!(s_validate(b"00:00:59").is_ok());
1380        assert!(s_validate(b"00:00:60").is_ok());
1381        assert!(s_validate(b"00:30:00").is_ok());
1382        assert!(s_validate(b"00:59:00").is_ok());
1383        assert!(s_validate(b"12:00:00").is_ok());
1384        assert!(s_validate(b"23:00:00").is_ok());
1385
1386        assert!(s_validate(b"00:00:61").is_err());
1387        assert!(s_validate(b"00:00:99").is_err());
1388        assert!(s_validate(b"00:60:00").is_err());
1389        assert!(s_validate(b"00:99:00").is_err());
1390        assert!(s_validate(b"24:00:00").is_err());
1391        assert!(s_validate(b"99:00:00").is_err());
1392
1393        assert!(s_validate(b"+0:00:00").is_err());
1394        assert!(s_validate(b"-0:00:00").is_err());
1395        assert!(s_validate(b"00:+0:00").is_err());
1396        assert!(s_validate(b"00:-0:00").is_err());
1397        assert!(s_validate(b"00:00:+0").is_err());
1398        assert!(s_validate(b"00:00:-0").is_err());
1399    }
1400
1401    #[cfg(feature = "serde")]
1402    #[test]
1403    fn ser_de_str() {
1404        let raw: &'static str = "12:34:56";
1405        assert_tokens(
1406            &Hms6ColonStr::from_str(raw).unwrap(),
1407            &[Token::BorrowedStr(raw)],
1408        );
1409    }
1410
1411    #[cfg(feature = "serde")]
1412    #[test]
1413    fn ser_de_string() {
1414        let raw: &'static str = "12:34:56";
1415        assert_tokens(&Hms6ColonString::try_from(raw).unwrap(), &[Token::Str(raw)]);
1416    }
1417
1418    #[cfg(feature = "serde")]
1419    #[test]
1420    fn de_bytes_slice() {
1421        let raw: &'static [u8; 8] = b"12:34:56";
1422        assert_de_tokens(
1423            &Hms6ColonStr::from_bytes(raw).unwrap(),
1424            &[Token::BorrowedBytes(raw)],
1425        );
1426    }
1427
1428    #[cfg(feature = "serde")]
1429    #[test]
1430    fn de_bytes() {
1431        let raw: &'static [u8; 8] = b"12:34:56";
1432        assert_de_tokens(
1433            &Hms6ColonString::try_from(&raw[..]).unwrap(),
1434            &[Token::Bytes(raw)],
1435        );
1436    }
1437}