use crate::{AsciiStr, Dt, DtErr, GregorianTime, STRFTIME_SIZE, Scale, tzdb::offset_info_at_utc};
#[cfg(feature = "alloc")]
use crate::ATTOS_PER_SEC;
#[cfg(feature = "alloc")]
impl Dt {
#[inline]
pub fn to_str(&self, current: Scale, fmt: &str) -> Result<alloc::string::String, DtErr> {
self.to_str_with_offset(current, fmt, 0)
}
#[inline]
pub fn to_str_with_offset(
&self,
current: Scale,
fmt: &str,
secs: i32,
) -> Result<alloc::string::String, DtErr> {
let mut buf = [0u8; STRFTIME_SIZE];
let n = self.to_u8_with_offset(current, fmt, &mut buf, secs)?;
Ok(alloc::string::String::from_utf8_lossy(&buf[0..n]).into_owned())
}
#[inline]
pub fn to_str_with_tz(
&self,
current: Scale,
fmt: &str,
tz_name: &str,
) -> Result<alloc::string::String, DtErr> {
let mut buf = [0u8; STRFTIME_SIZE];
let n = self.to_u8_with_tz(current, fmt, &mut buf, tz_name)?;
Ok(alloc::string::String::from_utf8_lossy(&buf[0..n]).into_owned())
}
pub fn to_iso_duration(&self) -> alloc::string::String {
if self.is_zero() {
return alloc::string::String::from("PT0S");
}
let total = self.to_attos();
let negative = total < 0;
let mut attos = total.unsigned_abs();
let mut s = alloc::string::String::with_capacity(48);
if negative {
s.push('-');
}
s.push_str("PT");
const A_PER_S: u128 = ATTOS_PER_SEC as u128;
const A_PER_M: u128 = A_PER_S * 60;
const A_PER_H: u128 = A_PER_M * 60;
let hours = attos / A_PER_H;
attos %= A_PER_H;
let minutes = attos / A_PER_M;
attos %= A_PER_M;
let seconds = attos / A_PER_S;
let frac_attos = attos % A_PER_S;
if hours > 0 {
s.push_str(&alloc::format!("{}", hours));
s.push('H');
}
if minutes > 0 {
s.push_str(&alloc::format!("{}", minutes));
s.push('M');
}
if seconds > 0 || frac_attos > 0 {
s.push_str(&alloc::format!("{}", seconds));
if frac_attos != 0 {
let frac_str = alloc::format!("{frac_attos:018}");
let trimmed = frac_str.trim_end_matches('0');
s.push('.');
s.push_str(trimmed);
}
s.push('S');
}
s
}
}
impl Dt {
pub fn to_str_bin(&self, current: Scale, fmt: &str) -> Result<AsciiStr<STRFTIME_SIZE>, DtErr> {
let mut gt = self.to_gregorian_time(current);
gt.set_offset(Some(0)).set_tz_abbrev(None);
let mut buf = [0u8; STRFTIME_SIZE];
let mut pos = 0usize;
gt.format_to_buffer(fmt.as_bytes(), &mut buf, &mut pos)?;
Ok(AsciiStr::from_filled_buffer(buf))
}
pub fn to_str_bin_with_offset(
&self,
current: Scale,
fmt: &str,
secs: i32,
) -> Result<AsciiStr<STRFTIME_SIZE>, DtErr> {
let gt = self.gregorian_time_with_offset(current, secs);
let mut buf = [0u8; STRFTIME_SIZE];
let mut pos = 0usize;
gt.format_to_buffer(fmt.as_bytes(), &mut buf, &mut pos)?;
Ok(AsciiStr::from_filled_buffer(buf))
}
pub fn to_str_bin_with_tz(
&self,
current: Scale,
fmt: &str,
tz_name: &str,
) -> Result<AsciiStr<STRFTIME_SIZE>, DtErr> {
let gt = self.gregorian_time_with_tz(current, tz_name);
let mut buf = [0u8; STRFTIME_SIZE];
let mut pos = 0usize;
gt.format_to_buffer(fmt.as_bytes(), &mut buf, &mut pos)?;
Ok(AsciiStr::from_filled_buffer(buf))
}
#[inline]
pub const fn sec_as_hhmm(seconds: i32) -> (bool, u8, u8) {
let total = seconds.saturating_abs();
let hours = (total / 3600) as u8;
let minutes = ((total % 3600) / 60) as u8;
(seconds < 0, hours, minutes)
}
pub fn to_u8_with_offset(
&self,
current: Scale,
fmt: &str,
dest: &mut [u8],
secs: i32,
) -> Result<usize, DtErr> {
let gt = self.gregorian_time_with_offset(current, secs);
let mut internal_buf = [0u8; STRFTIME_SIZE];
let mut pos = 0usize;
gt.format_to_buffer(fmt.as_bytes(), &mut internal_buf, &mut pos)?;
let written = pos.min(dest.len());
if written > 0 {
dest[0..written].copy_from_slice(&internal_buf[0..written]);
}
Ok(written)
}
pub fn to_u8_with_tz(
&self,
current: Scale,
fmt: &str,
dest: &mut [u8],
tz_name: &str,
) -> Result<usize, DtErr> {
let gt = self.gregorian_time_with_tz(current, tz_name);
let mut internal_buf = [0u8; STRFTIME_SIZE];
let mut pos = 0usize;
gt.format_to_buffer(fmt.as_bytes(), &mut internal_buf, &mut pos)?;
let written = pos.min(dest.len());
if written > 0 {
dest[0..written].copy_from_slice(&internal_buf[0..written]);
}
Ok(written)
}
pub(crate) fn gregorian_time_with_offset(&self, current: Scale, secs: i32) -> GregorianTime {
let local_tp = if secs != 0 {
*self + Dt::new(secs as i64, 0)
} else {
*self
};
let mut gt = local_tp.to_gregorian_time(current);
gt.set_offset(Some(secs));
gt
}
pub(crate) fn gregorian_time_with_tz(&self, current: Scale, tz_name: &str) -> GregorianTime {
let utc_unix = self
.to(current, current.to_ut())
.to_diff_raw(Dt::UNIX_EPOCH);
let (offset_secs, abbrev) = match offset_info_at_utc(tz_name, utc_unix.sec) {
Some(info) => (info.offset, info.abbrev),
None => (0, "UTC"), };
let span = Dt::new(offset_secs as i64, 0);
let local_tp = *self + span;
let mut gt = local_tp.to_gregorian_time(current);
gt.set_offset(Some(offset_secs));
gt.set_tz(Some(tz_name));
gt.set_tz_abbrev(Some(abbrev));
gt
}
}