#![allow(unknown_lints)]
#![allow(clippy::doc_markdown)]
#![cfg_attr(not(any(test, feature = "std")), no_std)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![allow(unused_attributes)]
#![warn(absolute_paths_not_starting_with_crate)]
#![warn(elided_lifetimes_in_paths)]
#![warn(explicit_outlives_requirements)]
#![warn(meta_variable_misuse)]
#![warn(missing_copy_implementations)]
#![warn(missing_debug_implementations)]
#![warn(missing_docs)]
#![warn(trivial_casts)]
#![warn(unreachable_pub)]
#![warn(unused_extern_crates)]
#![warn(unused_lifetimes)]
#![warn(unused_results)]
#[cfg(docsrs)]
pub mod changelog;
#[cfg(feature = "arbitrary")]
mod feat_arbitrary;
#[cfg(feature = "castaway")]
mod feat_castaway;
#[cfg(feature = "proptest")]
mod feat_proptest;
#[cfg(feature = "quickcheck")]
mod feat_quickcheck;
#[cfg(feature = "rkyv")]
mod feat_rkyv;
#[cfg(feature = "serde")]
mod feat_serde;
#[cfg_attr(
any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "linux",
target_os = "macos",
target_os = "openbsd",
target_os = "redox",
),
path = "impl_rustix.rs"
)]
#[cfg_attr(
any(
target_os = "android",
target_os = "darwin",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "haiku",
target_os = "illumos",
target_os = "netbsd",
target_os = "solaris",
),
path = "impl_libc.rs"
)]
#[cfg_attr(target_os = "wasi", path = "impl_wasi.rs")]
#[cfg_attr(target_os = "windows", path = "impl_winapi.rs")]
#[cfg_attr(
all(target_arch = "wasm32", not(target_os = "wasi")),
path = "impl_web.rs"
)]
mod platform;
#[cfg(test)]
mod test;
mod u30;
use core::convert::{TryFrom, TryInto};
use core::fmt;
use core::time::Duration;
#[cfg(feature = "std")]
use std::time::SystemTime;
use crate::platform::OsError;
use crate::u30::U30;
pub const IMPLEMENTED: bool = platform::IMPLEMENTED;
pub const INFALLIBLE: bool = platform::INFALLIBLE;
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UtcTime {
secs: i64,
nanos: U30,
}
impl UtcTime {
pub const EPOCH: UtcTime = UtcTime {
secs: 0,
nanos: U30::ZERO,
};
#[inline]
pub fn now() -> Result<Self> {
utcnow()
}
#[inline]
#[must_use]
#[const_fn::const_fn("1.56")]
pub unsafe fn new_unchecked(secs: i64, nanos: u32) -> Self {
let nanos = U30::new_unchecked(nanos);
Self { secs, nanos }
}
#[must_use]
#[const_fn::const_fn("1.56")]
pub fn new(secs: i64, nanos: u32) -> Option<Self> {
const NANOS_PER_SEC: u32 = 1_000_000_000;
if nanos < NANOS_PER_SEC {
return Some(unsafe { Self::new_unchecked(secs, nanos) });
}
let extra_seconds = nanos / NANOS_PER_SEC;
let nanos = nanos % NANOS_PER_SEC;
match secs.checked_add(extra_seconds as i64) {
Some(secs) => Some(unsafe { Self::new_unchecked(secs, nanos) }),
None => None,
}
}
#[must_use]
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub fn from_system_time(value: SystemTime) -> Option<Self> {
Self::from_duration(value.duration_since(SystemTime::UNIX_EPOCH).ok()?)
}
#[must_use]
#[allow(clippy::cast_possible_wrap)]
#[const_fn::const_fn("1.56")]
pub fn from_duration(value: Duration) -> Option<Self> {
const I64_MAX: u64 = i64::MAX as u64;
let secs = match value.as_secs() {
secs @ 0..=I64_MAX => secs as i64,
_ => return None,
};
let nanos = value.subsec_nanos();
Some(unsafe { Self::new_unchecked(secs, nanos) })
}
#[must_use]
#[inline]
pub const fn as_secs(self) -> i64 {
self.secs
}
#[must_use]
#[const_fn::const_fn("1.56")]
pub fn as_millis(self) -> i128 {
(self.secs as i128 * 1_000) + (self.nanos.get() as i128 / 1_000_000)
}
#[must_use]
#[const_fn::const_fn("1.56")]
pub fn as_micros(self) -> i128 {
(self.secs as i128 * 1_000_000) + (self.nanos.get() as i128 / 1_000)
}
#[must_use]
#[const_fn::const_fn("1.56")]
pub fn as_nanos(self) -> i128 {
(self.secs as i128 * 1_000_000_000) + (self.nanos.get() as i128)
}
#[must_use]
#[const_fn::const_fn("1.56")]
pub fn subsec_millis(self) -> u32 {
self.nanos.get() / 1_000_000
}
#[must_use]
#[const_fn::const_fn("1.56")]
pub fn subsec_micros(self) -> u32 {
self.nanos.get() / 1_000
}
#[must_use]
#[inline]
#[const_fn::const_fn("1.56")]
pub fn subsec_nanos(self) -> u32 {
self.nanos.get()
}
#[allow(clippy::cast_sign_loss)]
#[const_fn::const_fn("1.58")]
pub fn into_duration(self) -> Result<Duration, ConversionError> {
let secs = match self.secs {
secs @ 0..=i64::MAX => secs as u64,
_ => return Err(ConversionError),
};
Ok(Duration::new(secs, self.nanos.get()))
}
#[inline]
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub fn into_system_time(self) -> Result<SystemTime, ConversionError> {
SystemTime::UNIX_EPOCH
.checked_add(self.into_duration()?)
.ok_or(ConversionError)
}
}
impl fmt::Display for UtcTime {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}.{:09}", self.secs, self.nanos)
}
}
impl TryFrom<&str> for UtcTime {
type Error = ConversionError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
if matches!(value, "" | ".") || !value.is_ascii() {
return Err(ConversionError);
}
let (secs, nanos) = match value
.as_bytes()
.iter()
.enumerate()
.find(|(_, &c)| c == b'.')
{
Some((idx, _)) => unsafe {
(value.get_unchecked(..idx), value.get_unchecked(idx + 1..))
},
None => (value, ""),
};
let secs = match secs {
"" => 0,
secs => secs.parse().map_err(|_| ConversionError)?,
};
let nanos = match nanos {
"" => 0,
nanos => {
let (nanos, factor) = if nanos.len() <= 9 {
let factor = match nanos.len() {
8 => 10,
7 => 100,
6 => 1000,
5 => 10000,
4 => 100_000,
3 => 1_000_000,
2 => 10_000_000,
1 => 100_000_000,
_ => 1,
};
(nanos, factor)
} else {
let nanos = unsafe { nanos.get_unchecked(..9) };
let suffix = unsafe { nanos.get_unchecked(9..) };
if suffix.as_bytes().iter().any(|c| !c.is_ascii_digit()) {
return Err(ConversionError);
}
(nanos, 1)
};
nanos.parse::<u32>().map_err(|_| ConversionError)? * factor
},
};
Ok(unsafe { Self::new_unchecked(secs, nanos) })
}
}
impl core::str::FromStr for UtcTime {
type Err = ConversionError;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.try_into()
}
}
#[inline]
pub fn utcnow() -> Result<UtcTime> {
platform::utcnow()
}
impl TryFrom<UtcTime> for Duration {
type Error = ConversionError;
#[inline]
fn try_from(value: UtcTime) -> Result<Self, ConversionError> {
value.into_duration()
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl TryFrom<UtcTime> for SystemTime {
type Error = ConversionError;
#[inline]
fn try_from(value: UtcTime) -> Result<Self, ConversionError> {
value.into_system_time()
}
}
impl TryFrom<Duration> for UtcTime {
type Error = ConversionError;
#[inline]
fn try_from(value: Duration) -> Result<Self, ConversionError> {
Self::from_duration(value).ok_or(ConversionError)
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl TryFrom<SystemTime> for UtcTime {
type Error = ConversionError;
#[inline]
fn try_from(value: SystemTime) -> Result<Self, ConversionError> {
Self::from_system_time(value).ok_or(ConversionError)
}
}
pub type Result<T, E = Error> = core::result::Result<T, E>;
#[derive(Debug, Clone, Copy)]
pub struct Error(OsError);
impl fmt::Display for Error {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl From<OsError> for Error {
#[inline]
fn from(err: OsError) -> Self {
Self(err)
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl std::error::Error for Error {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ConversionError;
impl fmt::Display for ConversionError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("cannot convert a negative UtcTime")
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl std::error::Error for ConversionError {}
#[cfg(test)]
const _: () = {
trait NoSuchTrait {}
impl<T: ?Sized> NoSuchTrait for T {}
#[cfg(has_core__panic__RefUnwindSafe)]
use core::panic::RefUnwindSafe;
#[cfg(has_core__panic__UnwindSafe)]
use core::panic::UnwindSafe;
#[cfg(not(has_core__panic__RefUnwindSafe))]
use NoSuchTrait as RefUnwindSafe;
#[cfg(not(has_core__panic__UnwindSafe))]
use NoSuchTrait as UnwindSafe;
trait AutoTraits {
const AUTO_TRAITS: bool;
}
impl<T> AutoTraits for T
where
T: 'static + RefUnwindSafe + Send + Sync + Unpin + UnwindSafe,
{
const AUTO_TRAITS: bool = true;
}
const _: bool = ConversionError::AUTO_TRAITS;
const _: bool = Error::AUTO_TRAITS;
const _: bool = Option::<U30>::AUTO_TRAITS;
const _: bool = OsError::AUTO_TRAITS;
const _: bool = Result::<U30>::AUTO_TRAITS;
const _: bool = U30::AUTO_TRAITS;
const _: bool = UtcTime::AUTO_TRAITS;
};