#![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 = "proptest")]
mod feat_proptest;
#[cfg(feature = "quickcheck")]
mod feat_quickcheck;
#[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 = "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;
mod u30;
use core::convert::TryFrom;
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]
#[const_fn::const_fn("1.56")]
unsafe fn create(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::create(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::create(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::create(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)
}
}
#[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)]
#[test]
fn test_if_can_call() {
let _ = utcnow().unwrap();
}
#[cfg(test)]
#[test]
fn test_layout() {
use core::mem;
assert_eq!(mem::align_of::<u32>(), mem::align_of::<U30>());
assert_eq!(mem::size_of::<u32>(), mem::size_of::<U30>());
assert_eq!(mem::size_of::<u32>(), mem::size_of::<Option<U30>>());
assert_eq!(mem::size_of::<UtcTime>(), mem::size_of::<Option<UtcTime>>());
assert_eq!(mem::size_of::<UtcTime>(), mem::size_of::<Result<UtcTime>>());
}