use crate::constants::*;
use crate::util::StrWriter;
use core::error::Error;
use core::fmt::{Display, Formatter, Write};
use core::num::ParseIntError;
use core::ops::*;
use core::time::Duration;
#[cfg(feature = "alloc")]
use alloc::{format, string::String};
#[cfg(feature = "std")]
use std::time::{SystemTime, SystemTimeError};
#[cfg_attr(not(feature = "std"), doc = "```rust,ignore")]
#[cfg_attr(feature = "std", doc = "```rust")]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct UTCTimestamp(Duration);
impl UTCTimestamp {
pub const ZERO: UTCTimestamp = UTCTimestamp(Duration::ZERO);
pub const MAX: UTCTimestamp = UTCTimestamp(Duration::MAX);
#[inline]
pub const fn from_day(day: UTCDay) -> Self {
let secs = day.0 * SECONDS_PER_DAY;
Self(Duration::from_secs(secs))
}
#[inline]
pub const fn from_day_and_tod(day: UTCDay, tod: UTCTimeOfDay) -> Self {
let secs = (day.0 * SECONDS_PER_DAY).saturating_add(tod.as_secs() as u64);
let subsec_ns = tod.as_subsec_ns();
Self(Duration::new(secs, subsec_ns))
}
#[cfg(feature = "std")]
pub fn try_from_system_time() -> Result<Self, SystemTimeError> {
let duration = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
Ok(UTCTimestamp(duration))
}
#[inline]
pub const fn from_duration(d: Duration) -> Self {
Self(d)
}
#[inline]
pub const fn as_duration(&self) -> Duration {
self.0
}
#[inline]
pub const fn to_duration(self) -> Duration {
self.0
}
#[inline]
pub const fn as_tod(&self) -> UTCTimeOfDay {
let ns = ((self.0.as_secs() % SECONDS_PER_DAY) * NANOS_PER_SECOND)
+ (self.0.subsec_nanos() as u64);
unsafe { UTCTimeOfDay::from_nanos_unchecked(ns) }
}
#[inline]
pub const fn as_day(&self) -> UTCDay {
UTCDay(self.0.as_secs() / SECONDS_PER_DAY)
}
#[inline]
pub const fn from_secs(secs: u64) -> Self {
UTCTimestamp(Duration::from_secs(secs))
}
#[inline]
pub const fn as_secs(&self) -> u64 {
self.0.as_secs()
}
#[inline]
pub const fn from_millis(millis: u64) -> Self {
UTCTimestamp(Duration::from_millis(millis))
}
#[inline]
pub const fn as_millis(&self) -> u128 {
self.0.as_millis()
}
#[inline]
pub const fn from_micros(micros: u64) -> Self {
UTCTimestamp(Duration::from_micros(micros))
}
#[inline]
pub const fn as_micros(&self) -> u128 {
self.0.as_micros()
}
#[inline]
pub const fn from_nanos(nanos: u64) -> Self {
UTCTimestamp(Duration::from_nanos(nanos))
}
#[inline]
pub const fn as_nanos(&self) -> u128 {
self.0.as_nanos()
}
#[inline]
pub const fn checked_add(self, rhs: UTCTimestamp) -> Option<UTCTimestamp> {
match self.0.checked_add(rhs.0) {
Some(duration) => Some(UTCTimestamp(duration)),
None => None,
}
}
#[inline]
pub const fn checked_add_duration(self, rhs: Duration) -> Option<UTCTimestamp> {
match self.0.checked_add(rhs) {
Some(duration) => Some(UTCTimestamp(duration)),
None => None,
}
}
#[inline]
pub const fn saturating_add(self, rhs: UTCTimestamp) -> UTCTimestamp {
match self.checked_add(rhs) {
Some(res) => res,
None => UTCTimestamp::MAX,
}
}
#[inline]
pub const fn saturating_add_duration(self, rhs: Duration) -> UTCTimestamp {
match self.checked_add_duration(rhs) {
Some(res) => res,
None => UTCTimestamp::MAX,
}
}
#[inline]
pub const fn saturating_add_nanos(self, rhs: u64) -> UTCTimestamp {
self.saturating_add(UTCTimestamp::from_nanos(rhs))
}
#[inline]
pub const fn saturating_add_micros(self, rhs: u64) -> UTCTimestamp {
self.saturating_add(UTCTimestamp::from_micros(rhs))
}
#[inline]
pub const fn saturating_add_millis(self, rhs: u64) -> UTCTimestamp {
self.saturating_add(UTCTimestamp::from_millis(rhs))
}
#[inline]
pub const fn saturating_add_secs(self, rhs: u64) -> UTCTimestamp {
self.saturating_add(UTCTimestamp::from_secs(rhs))
}
#[inline]
pub const fn checked_sub(self, rhs: UTCTimestamp) -> Option<UTCTimestamp> {
match self.0.checked_sub(rhs.0) {
Some(duration) => Some(UTCTimestamp(duration)),
None => None,
}
}
#[inline]
pub const fn checked_sub_duration(self, rhs: Duration) -> Option<UTCTimestamp> {
match self.0.checked_sub(rhs) {
Some(duration) => Some(UTCTimestamp(duration)),
None => None,
}
}
#[inline]
pub const fn saturating_sub(self, rhs: UTCTimestamp) -> UTCTimestamp {
match self.checked_sub(rhs) {
Some(res) => res,
None => UTCTimestamp::ZERO,
}
}
#[inline]
pub const fn saturating_sub_duration(self, rhs: Duration) -> UTCTimestamp {
match self.checked_sub_duration(rhs) {
Some(res) => res,
None => UTCTimestamp::ZERO,
}
}
#[inline]
pub const fn saturating_sub_nanos(self, rhs: u64) -> UTCTimestamp {
self.saturating_sub(UTCTimestamp::from_nanos(rhs))
}
#[inline]
pub const fn saturating_sub_micros(self, rhs: u64) -> UTCTimestamp {
self.saturating_sub(UTCTimestamp::from_micros(rhs))
}
#[inline]
pub const fn saturating_sub_millis(self, rhs: u64) -> UTCTimestamp {
self.saturating_sub(UTCTimestamp::from_millis(rhs))
}
#[inline]
pub const fn saturating_sub_secs(self, rhs: u64) -> UTCTimestamp {
self.saturating_sub(UTCTimestamp::from_secs(rhs))
}
#[inline]
pub const fn checked_mul(self, rhs: u32) -> Option<UTCTimestamp> {
match self.0.checked_mul(rhs) {
Some(duration) => Some(UTCTimestamp(duration)),
None => None,
}
}
#[inline]
pub const fn saturating_mul(self, rhs: u32) -> UTCTimestamp {
match self.checked_mul(rhs) {
Some(res) => res,
None => UTCTimestamp::MAX,
}
}
#[inline]
pub const fn checked_div(self, rhs: u32) -> Option<UTCTimestamp> {
match self.0.checked_div(rhs) {
Some(duration) => Some(UTCTimestamp(duration)),
None => None,
}
}
}
impl From<Duration> for UTCTimestamp {
fn from(value: Duration) -> Self {
Self(value)
}
}
impl From<UTCDay> for UTCTimestamp {
#[inline]
fn from(day: UTCDay) -> Self {
UTCTimestamp::from_day(day)
}
}
impl Add for UTCTimestamp {
type Output = UTCTimestamp;
fn add(self, rhs: Self) -> Self::Output {
self.checked_add(rhs)
.expect("overflow when adding timestamps")
}
}
impl Add<Duration> for UTCTimestamp {
type Output = UTCTimestamp;
fn add(self, rhs: Duration) -> Self::Output {
self.checked_add_duration(rhs)
.expect("overflow when adding timestamps")
}
}
impl AddAssign for UTCTimestamp {
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs
}
}
impl AddAssign<Duration> for UTCTimestamp {
fn add_assign(&mut self, rhs: Duration) {
*self = *self + rhs
}
}
impl Sub for UTCTimestamp {
type Output = UTCTimestamp;
fn sub(self, rhs: Self) -> Self::Output {
self.checked_sub(rhs)
.expect("overflow when subtracting timestamps")
}
}
impl Sub<Duration> for UTCTimestamp {
type Output = UTCTimestamp;
fn sub(self, rhs: Duration) -> Self::Output {
self.checked_sub_duration(rhs)
.expect("overflow when subtracting timestamps")
}
}
impl SubAssign for UTCTimestamp {
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
impl SubAssign<Duration> for UTCTimestamp {
fn sub_assign(&mut self, rhs: Duration) {
*self = *self - rhs;
}
}
impl Mul<u32> for UTCTimestamp {
type Output = UTCTimestamp;
fn mul(self, rhs: u32) -> Self::Output {
self.checked_mul(rhs)
.expect("overflow when multiplying timestamp by scalar")
}
}
impl Mul<UTCTimestamp> for u32 {
type Output = UTCTimestamp;
fn mul(self, rhs: UTCTimestamp) -> Self::Output {
rhs * self
}
}
impl MulAssign<u32> for UTCTimestamp {
fn mul_assign(&mut self, rhs: u32) {
*self = *self * rhs
}
}
impl Div<u32> for UTCTimestamp {
type Output = UTCTimestamp;
fn div(self, rhs: u32) -> Self::Output {
self.checked_div(rhs)
.expect("divide by zero error when dividing timestamp by scalar")
}
}
impl DivAssign<u32> for UTCTimestamp {
fn div_assign(&mut self, rhs: u32) {
*self = *self / rhs
}
}
#[cfg_attr(not(feature = "std"), doc = "```rust,ignore")]
#[cfg_attr(feature = "std", doc = "```rust")]
pub trait UTCTransformations
where
Self: Sized,
{
#[inline]
fn from_duration(duration: Duration) -> Self {
let timestamp = UTCTimestamp(duration);
Self::from_timestamp(timestamp)
}
#[inline]
fn as_duration(&self) -> Duration {
self.as_timestamp().as_duration()
}
#[inline]
fn from_secs(secs: u64) -> Self {
let timestamp = UTCTimestamp::from_secs(secs);
Self::from_timestamp(timestamp)
}
#[inline]
fn as_secs(&self) -> u64 {
self.as_timestamp().as_secs()
}
#[inline]
fn from_millis(millis: u64) -> Self {
let timestamp = UTCTimestamp::from_millis(millis);
Self::from_timestamp(timestamp)
}
#[inline]
fn as_millis(&self) -> u128 {
self.as_timestamp().as_millis()
}
#[inline]
fn from_micros(micros: u64) -> Self {
let timestamp = UTCTimestamp::from_micros(micros);
Self::from_timestamp(timestamp)
}
#[inline]
fn as_micros(&self) -> u128 {
self.as_timestamp().as_micros()
}
#[inline]
fn from_nanos(nanos: u64) -> Self {
let timestamp = UTCTimestamp::from_nanos(nanos);
Self::from_timestamp(timestamp)
}
#[inline]
fn as_nanos(&self) -> u128 {
self.as_timestamp().as_nanos()
}
#[cfg(feature = "std")]
fn try_from_system_time() -> Result<Self, SystemTimeError> {
let timestamp = UTCTimestamp::try_from_system_time()?;
Ok(Self::from_timestamp(timestamp))
}
fn from_timestamp(timestamp: UTCTimestamp) -> Self;
fn as_timestamp(&self) -> UTCTimestamp;
}
#[cfg_attr(not(feature = "std"), doc = "```rust,ignore")]
#[cfg_attr(feature = "std", doc = "```rust")]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct UTCDay(u64);
impl UTCDay {
pub const ZERO: Self = Self(0);
pub const MAX: Self = Self(213_503_982_334_601);
#[inline]
pub const unsafe fn from_u64_unchecked(u: u64) -> Self {
Self(u)
}
pub fn try_from_u64(u: u64) -> Result<Self, UTCDayErrOutOfRange> {
let day = unsafe { Self::from_u64_unchecked(u) };
if day > Self::MAX {
return Err(UTCDayErrOutOfRange(day.0));
}
Ok(day)
}
#[inline]
pub const fn as_u64(&self) -> u64 {
self.0
}
#[inline]
pub const fn to_u64(self) -> u64 {
self.0
}
pub fn as_weekday(&self) -> u8 {
((self.0 + 4) % 7) as u8
}
#[inline]
pub fn checked_add(self, rhs: UTCDay) -> Option<UTCDay> {
self.0
.checked_add(rhs.0)
.map(|u| UTCDay(u).min(UTCDay::MAX))
}
#[inline]
pub fn checked_add_u64(self, rhs: u64) -> Option<UTCDay> {
self.0.checked_add(rhs).map(|u| UTCDay(u).min(UTCDay::MAX))
}
#[inline]
pub fn saturating_add(self, rhs: UTCDay) -> UTCDay {
match self.checked_add(rhs) {
Some(res) => res,
None => UTCDay::MAX,
}
}
#[inline]
pub fn saturating_add_u64(self, rhs: u64) -> UTCDay {
match self.checked_add_u64(rhs) {
Some(res) => res,
None => UTCDay::MAX,
}
}
#[inline]
pub const fn checked_sub(self, rhs: UTCDay) -> Option<UTCDay> {
match self.0.checked_sub(rhs.0) {
Some(u) => Some(UTCDay(u)),
None => None,
}
}
#[inline]
pub const fn checked_sub_u64(self, rhs: u64) -> Option<UTCDay> {
match self.0.checked_sub(rhs) {
Some(u) => Some(UTCDay(u)),
None => None,
}
}
#[inline]
pub const fn saturating_sub(self, rhs: UTCDay) -> UTCDay {
match self.checked_sub(rhs) {
Some(res) => res,
None => UTCDay::ZERO,
}
}
#[inline]
pub const fn saturating_sub_u64(self, rhs: u64) -> UTCDay {
match self.checked_sub_u64(rhs) {
Some(res) => res,
None => UTCDay::ZERO,
}
}
#[inline]
pub fn checked_mul(self, rhs: u64) -> Option<UTCDay> {
self.0.checked_mul(rhs).map(|u| UTCDay(u).min(UTCDay::MAX))
}
#[inline]
pub fn saturating_mul(self, rhs: u64) -> UTCDay {
match self.checked_mul(rhs) {
Some(res) => res,
None => UTCDay::MAX,
}
}
#[inline]
pub const fn checked_div(self, rhs: u64) -> Option<UTCDay> {
match self.0.checked_div(rhs) {
Some(u) => Some(UTCDay(u)),
None => None,
}
}
}
#[derive(Debug, Clone)]
pub struct UTCDayErrOutOfRange(u64);
impl Display for UTCDayErrOutOfRange {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "UTC day ({}) exceeding maximum", self.0)
}
}
impl Error for UTCDayErrOutOfRange {}
impl UTCTransformations for UTCDay {
#[inline]
fn from_secs(secs: u64) -> Self {
Self(secs / SECONDS_PER_DAY)
}
#[inline]
fn as_secs(&self) -> u64 {
self.0 * SECONDS_PER_DAY
}
#[inline]
fn from_millis(millis: u64) -> Self {
Self(millis / MILLIS_PER_DAY)
}
#[inline]
fn as_millis(&self) -> u128 {
self.0 as u128 * MILLIS_PER_DAY as u128
}
#[inline]
fn from_micros(micros: u64) -> Self {
Self(micros / MICROS_PER_DAY)
}
#[inline]
fn as_micros(&self) -> u128 {
self.0 as u128 * MICROS_PER_DAY as u128
}
#[inline]
fn from_nanos(nanos: u64) -> Self {
Self(nanos / NANOS_PER_DAY)
}
#[inline]
fn as_nanos(&self) -> u128 {
self.0 as u128 * NANOS_PER_DAY as u128
}
#[inline]
fn from_timestamp(timestamp: UTCTimestamp) -> Self {
timestamp.as_day()
}
#[inline]
fn as_timestamp(&self) -> UTCTimestamp {
UTCTimestamp::from_day(*self)
}
}
impl Add for UTCDay {
type Output = UTCDay;
fn add(self, rhs: Self) -> Self::Output {
self.checked_add(rhs).expect("overflow when adding days")
}
}
impl Add<u64> for UTCDay {
type Output = UTCDay;
fn add(self, rhs: u64) -> Self::Output {
self.checked_add_u64(rhs)
.expect("overflow when adding days")
}
}
impl AddAssign for UTCDay {
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs
}
}
impl AddAssign<u64> for UTCDay {
fn add_assign(&mut self, rhs: u64) {
*self = *self + rhs
}
}
impl Sub for UTCDay {
type Output = UTCDay;
fn sub(self, rhs: Self) -> Self::Output {
self.checked_sub(rhs)
.expect("overflow when subtracting days")
}
}
impl Sub<u64> for UTCDay {
type Output = UTCDay;
fn sub(self, rhs: u64) -> Self::Output {
self.checked_sub_u64(rhs)
.expect("overflow when subtracting days")
}
}
impl SubAssign for UTCDay {
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
impl SubAssign<u64> for UTCDay {
fn sub_assign(&mut self, rhs: u64) {
*self = *self - rhs;
}
}
impl Mul<u64> for UTCDay {
type Output = UTCDay;
fn mul(self, rhs: u64) -> Self::Output {
self.checked_mul(rhs)
.expect("overflow when multiplying day by scalar")
}
}
impl Mul<UTCDay> for u64 {
type Output = UTCDay;
fn mul(self, rhs: UTCDay) -> Self::Output {
rhs * self
}
}
impl MulAssign<u64> for UTCDay {
fn mul_assign(&mut self, rhs: u64) {
*self = *self * rhs
}
}
impl Div<u64> for UTCDay {
type Output = UTCDay;
fn div(self, rhs: u64) -> Self::Output {
self.checked_div(rhs)
.expect("divide by zero error when dividing day by scalar")
}
}
impl DivAssign<u64> for UTCDay {
fn div_assign(&mut self, rhs: u64) {
*self = *self / rhs
}
}
impl TryFrom<u64> for UTCDay {
type Error = UTCDayErrOutOfRange;
fn try_from(value: u64) -> Result<Self, Self::Error> {
Self::try_from_u64(value)
}
}
impl From<Duration> for UTCDay {
#[inline]
fn from(duration: Duration) -> Self {
Self::from_duration(duration)
}
}
impl From<UTCTimestamp> for UTCDay {
#[inline]
fn from(timestamp: UTCTimestamp) -> Self {
Self::from_timestamp(timestamp)
}
}
#[cfg_attr(not(feature = "std"), doc = "```rust,ignore")]
#[cfg_attr(feature = "std", doc = "```rust")]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct UTCTimeOfDay(u64);
impl Display for UTCTimeOfDay {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
let (hrs, mins, secs) = self.as_hhmmss();
write!(
f,
"T{:02}:{:02}:{:02}.{:09}Z",
hrs,
mins,
secs,
self.as_subsec_ns()
)
}
}
impl UTCTimeOfDay {
pub const ZERO: Self = Self(0);
pub const MAX: Self = Self(NANOS_PER_DAY - 1);
pub const MIN_ISO_TOD_LEN: usize = 10;
pub const MAX_ISO_TOD_PRECISION: usize = 9;
#[inline]
pub const unsafe fn from_nanos_unchecked(nanos: u64) -> Self {
Self(nanos)
}
#[inline]
pub const unsafe fn from_micros_unchecked(micros: u64) -> Self {
Self(micros * NANOS_PER_MICRO)
}
#[inline]
pub const unsafe fn from_millis_unchecked(millis: u32) -> Self {
Self((millis as u64) * NANOS_PER_MILLI)
}
#[inline]
pub const unsafe fn from_secs_unchecked(secs: u32) -> Self {
Self((secs as u64) * NANOS_PER_SECOND)
}
const fn _ns_from_hhmmss(hrs: u8, mins: u8, secs: u8, subsec_ns: u32) -> u64 {
(subsec_ns as u64)
+ (hrs as u64) * NANOS_PER_HOUR
+ (mins as u64) * NANOS_PER_MINUTE
+ (secs as u64) * NANOS_PER_SECOND
}
#[inline]
pub const unsafe fn from_hhmmss_unchecked(hrs: u8, mins: u8, secs: u8, subsec_ns: u32) -> Self {
Self(Self::_ns_from_hhmmss(hrs, mins, secs, subsec_ns))
}
pub fn try_from_nanos(nanos: u64) -> Result<Self, UTCTimeOfDayError> {
let tod = unsafe { Self::from_nanos_unchecked(nanos) };
if tod > Self::MAX {
return Err(UTCTimeOfDayError::ExcessNanos(nanos));
}
Ok(tod)
}
pub fn try_from_micros(micros: u64) -> Result<Self, UTCTimeOfDayError> {
let tod = unsafe { Self::from_micros_unchecked(micros) };
if tod > Self::MAX {
return Err(UTCTimeOfDayError::ExcessMicros(micros));
}
Ok(tod)
}
pub fn try_from_millis(millis: u32) -> Result<Self, UTCTimeOfDayError> {
let tod = unsafe { Self::from_millis_unchecked(millis) };
if tod > Self::MAX {
return Err(UTCTimeOfDayError::ExcessMillis(millis));
}
Ok(tod)
}
pub fn try_from_secs(secs: u32) -> Result<Self, UTCTimeOfDayError> {
let tod = unsafe { Self::from_secs_unchecked(secs) };
if tod > Self::MAX {
return Err(UTCTimeOfDayError::ExcessSeconds(secs));
}
Ok(tod)
}
pub fn try_from_hhmmss(
hrs: u8,
mins: u8,
secs: u8,
subsec_ns: u32,
) -> Result<Self, UTCTimeOfDayError> {
Self::try_from_nanos(Self::_ns_from_hhmmss(hrs, mins, secs, subsec_ns))
}
#[inline]
pub const fn to_nanos(self) -> u64 {
self.0
}
#[inline]
pub const fn as_nanos(&self) -> u64 {
self.0
}
#[inline]
pub const fn as_micros(&self) -> u64 {
self.0 / NANOS_PER_MICRO
}
#[inline]
pub const fn as_millis(&self) -> u32 {
(self.0 / NANOS_PER_MILLI) as u32
}
#[inline]
pub const fn as_secs(&self) -> u32 {
(self.0 / NANOS_PER_SECOND) as u32
}
pub const fn as_hhmmss(&self) -> (u8, u8, u8) {
let hrs = (self.0 / NANOS_PER_HOUR) as u8;
let mins = ((self.0 % NANOS_PER_HOUR) / NANOS_PER_MINUTE) as u8;
let secs = ((self.0 % NANOS_PER_MINUTE) / NANOS_PER_SECOND) as u8;
(hrs, mins, secs)
}
#[inline]
pub const fn as_subsec_ns(&self) -> u32 {
(self.0 % NANOS_PER_SECOND) as u32
}
pub const fn from_timestamp(timestamp: UTCTimestamp) -> Self {
timestamp.as_tod()
}
pub fn try_from_iso_tod(iso: &str) -> Result<Self, UTCTimeOfDayError> {
let len = iso.len();
if len < Self::MIN_ISO_TOD_LEN {
return Err(UTCTimeOfDayError::InsufficientStrLen(
len,
Self::MIN_ISO_TOD_LEN,
));
}
let (hour_str, rem) = iso[1..].split_at(2); let (minute_str, rem) = rem[1..].split_at(2); let (second_str, rem) = rem[1..].split_at(2); let hrs: u8 = hour_str.parse()?;
let mins: u8 = minute_str.parse()?;
let secs: u8 = second_str.parse()?;
let rem_len = rem.len();
let subsec_ns: u32 = if rem_len > 1 {
let subsec_str = &rem[1..(rem_len - 1)]; let precision: u32 = subsec_str.len() as u32;
if precision > Self::MAX_ISO_TOD_PRECISION as u32 {
return Err(UTCTimeOfDayError::ExcessPrecision(precision));
}
if precision == 0 {
0
} else {
let subsec: u32 = subsec_str.parse()?;
subsec * 10u32.pow(Self::MAX_ISO_TOD_PRECISION as u32 - precision)
}
} else {
0
};
Self::try_from_hhmmss(hrs, mins, secs, subsec_ns)
}
#[cfg(feature = "alloc")]
pub fn as_iso_tod(&self, precision: usize) -> String {
let len = Self::iso_tod_len(precision);
let mut s = format!("{self}");
s.truncate(len - 1);
s.push('Z');
s
}
#[inline]
pub(crate) fn _write_iso_tod_trunc(&self, w: &mut StrWriter) {
write!(w, "{self}").unwrap();
w.buf[w.written - 1] = b'Z';
}
pub fn write_iso_tod(
&self,
buf: &mut [u8],
precision: usize,
) -> Result<usize, UTCTimeOfDayError> {
let write_len = Self::iso_tod_len(precision);
if write_len > buf.len() {
return Err(UTCTimeOfDayError::InsufficientStrLen(buf.len(), write_len));
}
let mut writer = StrWriter::new(&mut buf[..write_len]);
self._write_iso_tod_trunc(&mut writer);
Ok(writer.written)
}
#[inline]
pub const fn iso_tod_len(precision: usize) -> usize {
if precision == 0 {
Self::MIN_ISO_TOD_LEN
} else if precision < Self::MAX_ISO_TOD_PRECISION {
Self::MIN_ISO_TOD_LEN + precision + 1
} else {
Self::MIN_ISO_TOD_LEN + Self::MAX_ISO_TOD_PRECISION + 1
}
}
}
#[derive(Debug, Clone)]
pub enum UTCTimeOfDayError {
ParseErr(ParseIntError),
ExcessPrecision(u32),
ExcessNanos(u64),
ExcessMicros(u64),
ExcessMillis(u32),
ExcessSeconds(u32),
InsufficientStrLen(usize, usize),
}
impl Display for UTCTimeOfDayError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
Self::ParseErr(e) => e.fmt(f),
Self::ExcessPrecision(p) => write!(f, "ISO precision ({p}) exceeds maximum of 9"),
Self::ExcessNanos(n) => write!(f, "nanoseconds ({n}) not within a day"),
Self::ExcessMicros(u) => write!(f, "microseconds ({u}) not within a day"),
Self::ExcessMillis(m) => write!(f, "milliseconds ({m}) not within a day"),
Self::ExcessSeconds(s) => write!(f, "seconds ({s}) not within a day"),
Self::InsufficientStrLen(l, m) => {
write!(f, "insufficient ISO time str len ({l}), {m} required")
}
}
}
}
impl Error for UTCTimeOfDayError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::ParseErr(e) => e.source(),
_ => None,
}
}
}
impl From<ParseIntError> for UTCTimeOfDayError {
fn from(value: ParseIntError) -> Self {
Self::ParseErr(value)
}
}