use alloc::string::{String, ToString};
pub use apr_sys::{apr_interval_time_t, apr_time_t};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Time(apr_time_t);
impl Time {
pub fn now() -> Self {
let time = unsafe { apr_sys::apr_time_now() };
Self(time)
}
pub fn from_micros(micros: apr_time_t) -> Self {
Self(micros)
}
pub fn as_micros(&self) -> apr_time_t {
self.0
}
pub fn ctime(&self) -> String {
let mut buf: [u8; apr_sys::APR_CTIME_LEN as usize] = [0; apr_sys::APR_CTIME_LEN as usize];
unsafe {
apr_sys::apr_ctime(buf.as_mut_ptr() as *mut core::ffi::c_char, self.0);
}
String::from_utf8_lossy(&buf[..])
.trim_end_matches('\0')
.to_string()
}
pub fn rfc822_date(&self) -> String {
let mut buf: [u8; apr_sys::APR_RFC822_DATE_LEN as usize] =
[0; apr_sys::APR_RFC822_DATE_LEN as usize];
unsafe {
apr_sys::apr_rfc822_date(buf.as_mut_ptr() as *mut core::ffi::c_char, self.0);
}
String::from_utf8_lossy(&buf[..])
.trim_end_matches('\0')
.to_string()
}
}
#[cfg(feature = "std")]
pub fn to_apr_time(system_time: std::time::SystemTime) -> apr_time_t {
system_time
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_micros() as apr_time_t
}
#[cfg(feature = "std")]
pub fn to_system_time(apr_time: apr_time_t) -> std::time::SystemTime {
std::time::UNIX_EPOCH + std::time::Duration::from_micros(apr_time as u64)
}
#[cfg(feature = "std")]
impl From<std::time::SystemTime> for Time {
fn from(system_time: std::time::SystemTime) -> Self {
Self(to_apr_time(system_time))
}
}
#[cfg(feature = "std")]
impl From<Time> for std::time::SystemTime {
fn from(time: Time) -> Self {
to_system_time(time.0)
}
}
impl From<Time> for apr_time_t {
fn from(time: Time) -> Self {
time.0
}
}
impl From<apr_time_t> for Time {
fn from(time: apr_time_t) -> Self {
Self(time)
}
}
impl Default for Time {
fn default() -> Self {
Self::now()
}
}
impl core::fmt::Display for Time {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.rfc822_date())
}
}
impl AsRef<apr_time_t> for Time {
fn as_ref(&self) -> &apr_time_t {
&self.0
}
}
#[cfg(feature = "std")]
impl std::ops::Add<std::time::Duration> for Time {
type Output = Time;
fn add(self, duration: std::time::Duration) -> Self::Output {
Time(self.0 + duration.as_micros() as apr_time_t)
}
}
#[cfg(feature = "std")]
impl std::ops::Sub<std::time::Duration> for Time {
type Output = Time;
fn sub(self, duration: std::time::Duration) -> Self::Output {
Time(self.0.saturating_sub(duration.as_micros() as apr_time_t))
}
}
#[cfg(feature = "std")]
impl std::ops::Sub<Time> for Time {
type Output = std::time::Duration;
fn sub(self, other: Time) -> Self::Output {
let micros_diff = self.0.saturating_sub(other.0) as u64;
std::time::Duration::from_micros(micros_diff)
}
}
type Interval = apr_interval_time_t;
pub fn sleep(interval: Interval) {
unsafe {
apr_sys::apr_sleep(interval);
}
}
pub trait IntoTime {
fn as_apr_time(&self) -> Time;
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::format;
#[test]
fn test_time_now() {
Time::now();
}
#[test]
fn test_ctime() {
let t = Time::from(784111777000000);
assert_eq!(t.ctime(), "Sun Nov 06 08:49:37 1994");
}
#[test]
fn test_rfc822_date() {
let t = Time::from(784111777000000);
assert_eq!(t.rfc822_date(), "Sun, 06 Nov 1994 08:49:37 GMT");
}
#[test]
#[cfg(feature = "std")]
fn test_system_time_conversion() {
use std::time::{Duration, SystemTime};
let system_time = SystemTime::UNIX_EPOCH + Duration::from_secs(1234567890);
let apr_time = Time::from(system_time);
let converted_back: SystemTime = apr_time.into();
let diff = converted_back
.duration_since(system_time)
.unwrap_or_else(|_| system_time.duration_since(converted_back).unwrap());
assert!(diff < Duration::from_millis(1));
}
#[test]
#[cfg(feature = "std")]
fn test_utility_functions() {
use std::time::{Duration, SystemTime};
let system_time = SystemTime::UNIX_EPOCH + Duration::from_secs(1000);
let apr_time = to_apr_time(system_time);
let converted_back = to_system_time(apr_time);
let diff = converted_back
.duration_since(system_time)
.unwrap_or_else(|_| system_time.duration_since(converted_back).unwrap());
assert!(diff < Duration::from_millis(1));
}
#[test]
#[cfg(feature = "std")]
fn test_time_traits() {
let time1 = Time::now();
let display_str = format!("{}", time1);
assert!(display_str.contains("GMT"));
let duration = std::time::Duration::from_secs(60);
let time_plus = time1 + duration;
let time_minus = time_plus - duration;
let diff = if time1.as_micros() > time_minus.as_micros() {
time1 - time_minus
} else {
time_minus - time1
};
assert!(diff < std::time::Duration::from_secs(1));
let time_ref: &apr_time_t = time1.as_ref();
assert_eq!(*time_ref, time1.as_micros());
}
}