//! Internal Helper types
use crate ::{
formats::{Flexible, Format, Strict, Strictness},
prelude::*,
};
#[ derive(Copy, Clone, PartialEq, Eq)]
#[ cfg_attr(test, derive(Debug))]
pub (crate ) enum Sign {
Positive,
Negative,
}
impl Sign {
#[ allow(dead_code)]
pub (crate ) fn is_positive(&self ) -> bool {
*self == Sign::Positive
}
#[ allow(dead_code)]
pub (crate ) fn is_negative(&self ) -> bool {
*self == Sign::Negative
}
pub (crate ) fn apply<T>(&self , value: T) -> T
where
T: core::ops::Neg<Output = T>,
{
match *self {
Sign::Positive => value,
Sign::Negative => value.neg(),
}
}
}
#[ derive(Copy, Clone)]
pub (crate ) struct DurationSigned {
pub (crate ) sign: Sign,
pub (crate ) duration: Duration,
}
impl DurationSigned {
pub (crate ) fn new(sign: Sign, secs: u64, nanosecs: u32) -> Self {
Self {
sign,
duration: Duration::new(secs, nanosecs),
}
}
#[ cfg(any(feature = "chrono_0_4" , feature = "time_0_3" ))]
pub (crate ) fn with_duration(sign: Sign, duration: Duration) -> Self {
Self { sign, duration }
}
#[ cfg(feature = "std" )]
pub (crate ) fn to_system_time<'de, D>(self) -> Result<SystemTime, D::Error>
where
D: Deserializer<'de>,
{
match self .sign {
Sign::Positive => SystemTime::UNIX_EPOCH.checked_add(self .duration),
Sign::Negative => SystemTime::UNIX_EPOCH.checked_sub(self .duration),
}
.ok_or_else(|| DeError::custom("timestamp is outside the range for std::time::SystemTime" ))
}
#[ cfg(feature = "std" )]
pub (crate ) fn to_std_duration<'de, D>(self) -> Result<Duration, D::Error>
where
D: Deserializer<'de>,
{
match self .sign {
Sign::Positive => Ok(self .duration),
Sign::Negative => Err(DeError::custom("std::time::Duration cannot be negative" )),
}
}
}
impl From<&Duration> for DurationSigned {
fn from(&duration: &Duration) -> Self {
Self {
sign: Sign::Positive,
duration,
}
}
}
#[ cfg(feature = "std" )]
impl From<&SystemTime> for DurationSigned {
fn from(time: &SystemTime) -> Self {
match time.duration_since(SystemTime::UNIX_EPOCH) {
Ok(dur) => DurationSigned {
sign: Sign::Positive,
duration: dur,
},
Err(err) => DurationSigned {
sign: Sign::Negative,
duration: err.duration(),
},
}
}
}
impl core::ops::Mul<u32> for DurationSigned {
type Output = DurationSigned;
fn mul(mut self , rhs: u32) -> Self ::Output {
self .duration *= rhs;
self
}
}
impl core::ops::Div<u32> for DurationSigned {
type Output = DurationSigned;
fn div(mut self , rhs: u32) -> Self ::Output {
self .duration /= rhs;
self
}
}
impl <STRICTNESS> SerializeAs<DurationSigned> for DurationSeconds<u64, STRICTNESS>
where
STRICTNESS: Strictness,
{
fn serialize_as<S>(source: &DurationSigned, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if source.sign.is_negative() {
return Err(SerError::custom(
"cannot serialize a negative Duration as u64" ,
));
}
let mut secs = source.duration.as_secs();
// Properly round the value
if source.duration.subsec_millis() >= 500 {
if source.sign.is_positive() {
secs += 1 ;
} else {
secs -= 1 ;
}
}
secs.serialize(serializer)
}
}
impl <STRICTNESS> SerializeAs<DurationSigned> for DurationSeconds<i64, STRICTNESS>
where
STRICTNESS: Strictness,
{
fn serialize_as<S>(source: &DurationSigned, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut secs = source.sign.apply(source.duration.as_secs() as i64);
// Properly round the value
if source.duration.subsec_millis() >= 500 {
if source.sign.is_positive() {
secs += 1 ;
} else {
secs -= 1 ;
}
}
secs.serialize(serializer)
}
}
impl <STRICTNESS> SerializeAs<DurationSigned> for DurationSeconds<f64, STRICTNESS>
where
STRICTNESS: Strictness,
{
fn serialize_as<S>(source: &DurationSigned, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut secs = source.sign.apply(source.duration.as_secs() as f64);
// Properly round the value
if source.duration.subsec_millis() >= 500 {
if source.sign.is_positive() {
secs += 1 .;
} else {
secs -= 1 .;
}
}
secs.serialize(serializer)
}
}
#[ cfg(feature = "alloc" )]
impl <STRICTNESS> SerializeAs<DurationSigned> for DurationSeconds<String, STRICTNESS>
where
STRICTNESS: Strictness,
{
fn serialize_as<S>(source: &DurationSigned, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut secs = source.sign.apply(source.duration.as_secs() as i64);
// Properly round the value
if source.duration.subsec_millis() >= 500 {
if source.sign.is_positive() {
secs += 1 ;
} else {
secs -= 1 ;
}
}
secs.to_string().serialize(serializer)
}
}
impl <STRICTNESS> SerializeAs<DurationSigned> for DurationSecondsWithFrac<f64, STRICTNESS>
where
STRICTNESS: Strictness,
{
fn serialize_as<S>(source: &DurationSigned, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
source
.sign
.apply(utils::duration_as_secs_f64(&source.duration))
.serialize(serializer)
}
}
#[ cfg(feature = "alloc" )]
impl <STRICTNESS> SerializeAs<DurationSigned> for DurationSecondsWithFrac<String, STRICTNESS>
where
STRICTNESS: Strictness,
{
fn serialize_as<S>(source: &DurationSigned, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
source
.sign
.apply(utils::duration_as_secs_f64(&source.duration))
.to_string()
.serialize(serializer)
}
}
macro_rules! duration_impls {
($($inner:ident { $($factor:literal => $outer:ident,)+ })+) => {
$($(
impl <FORMAT, STRICTNESS> SerializeAs<DurationSigned> for $outer<FORMAT, STRICTNESS>
where
FORMAT: Format,
STRICTNESS: Strictness,
$inner<FORMAT, STRICTNESS>: SerializeAs<DurationSigned>
{
fn serialize_as<S>(source: &DurationSigned, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
$inner::<FORMAT, STRICTNESS>::serialize_as(&(*source * $factor), serializer)
}
}
impl <'de, FORMAT, STRICTNESS> DeserializeAs<' de, DurationSigned> for $outer<FORMAT, STRICTNESS>
where
FORMAT: Format,
STRICTNESS: Strictness,
$inner<FORMAT, STRICTNESS>: DeserializeAs<'de, DurationSigned>,
{
fn deserialize_as<D>(deserializer: D) -> Result<DurationSigned, D::Error>
where
D: Deserializer<'de>,
{
let dur = $inner::<FORMAT, STRICTNESS>::deserialize_as(deserializer)?;
Ok(dur / $factor)
}
}
)+)+ };
}
duration_impls!(
DurationSeconds {
1000 u32 => DurationMilliSeconds,
1 _000 _000 u32 => DurationMicroSeconds,
1 _000 _000 _000 u32 => DurationNanoSeconds,
}
DurationSecondsWithFrac {
1000 u32 => DurationMilliSecondsWithFrac,
1 _000 _000 u32 => DurationMicroSecondsWithFrac,
1 _000 _000 _000 u32 => DurationNanoSecondsWithFrac,
}
);
struct DurationVisitorFlexible;
impl <'de> Visitor<' de> for DurationVisitorFlexible {
type Value = DurationSigned;
fn expecting(&self , formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("an integer, a float, or a string containing a number" )
}
fn visit_i64<E>(self , value: i64) -> Result<Self ::Value, E>
where
E: DeError,
{
if value >= 0 {
Ok(DurationSigned::new(Sign::Positive, value as u64, 0 ))
} else {
Ok(DurationSigned::new(Sign::Negative, (-value) as u64, 0 ))
}
}
fn visit_u64<E>(self , secs: u64) -> Result<Self ::Value, E>
where
E: DeError,
{
Ok(DurationSigned::new(Sign::Positive, secs, 0 ))
}
fn visit_f64<E>(self , secs: f64) -> Result<Self ::Value, E>
where
E: DeError,
{
utils::duration_signed_from_secs_f64(secs).map_err(DeError::custom)
}
fn visit_str<E>(self , value: &str) -> Result<Self ::Value, E>
where
E: DeError,
{
match parse_float_into_time_parts(value) {
Ok((sign, seconds, subseconds)) => Ok(DurationSigned::new(sign, seconds, subseconds)),
Err(ParseFloatError::InvalidValue) => {
Err(DeError::invalid_value(Unexpected::Str(value), &self ))
}
Err(ParseFloatError::Custom(msg)) => Err(DeError::custom(msg)),
}
}
}
impl <'de> DeserializeAs<' de, DurationSigned> for DurationSeconds<u64, Strict> {
fn deserialize_as<D>(deserializer: D) -> Result<DurationSigned, D::Error>
where
D: Deserializer<'de>,
{
u64::deserialize(deserializer).map(|secs: u64| DurationSigned::new(Sign::Positive, secs, 0 ))
}
}
impl <'de> DeserializeAs<' de, DurationSigned> for DurationSeconds<i64, Strict> {
fn deserialize_as<D>(deserializer: D) -> Result<DurationSigned, D::Error>
where
D: Deserializer<'de>,
{
i64::deserialize(deserializer).map(|mut secs: i64| {
let mut sign = Sign::Positive;
if secs.is_negative() {
secs = -secs;
sign = Sign::Negative;
}
DurationSigned::new(sign, secs as u64, 0 )
})
}
}
// round() only works on std
#[ cfg(feature = "std" )]
impl <'de> DeserializeAs<' de, DurationSigned> for DurationSeconds<f64, Strict> {
fn deserialize_as<D>(deserializer: D) -> Result<DurationSigned, D::Error>
where
D: Deserializer<'de>,
{
let val = f64::deserialize(deserializer)?.round();
utils::duration_signed_from_secs_f64(val).map_err(DeError::custom)
}
}
#[ cfg(feature = "alloc" )]
impl <'de> DeserializeAs<' de, DurationSigned> for DurationSeconds<String, Strict> {
fn deserialize_as<D>(deserializer: D) -> Result<DurationSigned, D::Error>
where
D: Deserializer<'de>,
{
struct DurationDeserializationVisitor;
impl <'de> Visitor<' de> for DurationDeserializationVisitor {
type Value = DurationSigned;
fn expecting(&self , formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "a string containing a number" )
}
fn visit_str<E>(self , value: &str) -> Result<Self ::Value, E>
where
E: DeError,
{
let mut secs: i64 = value.parse().map_err(DeError::custom)?;
let mut sign = Sign::Positive;
if secs.is_negative() {
secs = -secs;
sign = Sign::Negative;
}
Ok(DurationSigned::new(sign, secs as u64, 0 ))
}
}
deserializer.deserialize_str(DurationDeserializationVisitor)
}
}
impl <'de, FORMAT> DeserializeAs<' de, DurationSigned> for DurationSeconds<FORMAT, Flexible>
where
FORMAT: Format,
{
fn deserialize_as<D>(deserializer: D) -> Result<DurationSigned, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_any(DurationVisitorFlexible)
}
}
impl <'de> DeserializeAs<' de, DurationSigned> for DurationSecondsWithFrac<f64, Strict> {
fn deserialize_as<D>(deserializer: D) -> Result<DurationSigned, D::Error>
where
D: Deserializer<'de>,
{
let val = f64::deserialize(deserializer)?;
utils::duration_signed_from_secs_f64(val).map_err(DeError::custom)
}
}
#[ cfg(feature = "alloc" )]
impl <'de> DeserializeAs<' de, DurationSigned> for DurationSecondsWithFrac<String, Strict> {
fn deserialize_as<D>(deserializer: D) -> Result<DurationSigned, D::Error>
where
D: Deserializer<'de>,
{
let value = String::deserialize(deserializer)?;
match parse_float_into_time_parts(&value) {
Ok((sign, seconds, subseconds)) => Ok(DurationSigned {
sign,
duration: Duration::new(seconds, subseconds),
}),
Err(ParseFloatError::InvalidValue) => Err(DeError::invalid_value(
Unexpected::Str(&value),
&"a string containing an integer or float" ,
)),
Err(ParseFloatError::Custom(msg)) => Err(DeError::custom(msg)),
}
}
}
impl <'de, FORMAT> DeserializeAs<' de, DurationSigned> for DurationSecondsWithFrac<FORMAT, Flexible>
where
FORMAT: Format,
{
fn deserialize_as<D>(deserializer: D) -> Result<DurationSigned, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_any(DurationVisitorFlexible)
}
}
#[ cfg_attr(test, derive(Debug, PartialEq))]
pub (crate ) enum ParseFloatError {
InvalidValue,
#[ cfg(not(feature = "alloc" ))]
Custom(&'static str),
#[ cfg(feature = "alloc" )]
Custom(String),
}
fn parse_float_into_time_parts(mut value: &str) -> Result<(Sign, u64, u32), ParseFloatError> {
let sign = match value.chars().next() {
// Advance by the size of the parsed char
Some('+' ) => {
value = &value[1 ..];
Sign::Positive
}
Some('-' ) => {
value = &value[1 ..];
Sign::Negative
}
_ => Sign::Positive,
};
let partslen = value.split('.' ).count();
let mut parts = value.split('.' );
match partslen {
1 => {
let seconds = parts.next().expect("Float contains exactly one part" );
if let Ok(seconds) = seconds.parse() {
Ok((sign, seconds, 0 ))
} else {
Err(ParseFloatError::InvalidValue)
}
}
2 => {
let seconds = parts.next().expect("Float contains exactly one part" );
if let Ok(seconds) = seconds.parse() {
let subseconds = parts.next().expect("Float contains exactly one part" );
let subseclen = subseconds.chars().count() as u32;
if subseclen > 9 {
#[ cfg(feature = "alloc" )]
return Err(ParseFloatError::Custom(alloc::format!(
"Duration and Timestamps with no more than 9 digits precision, but '{value}' has more"
)));
#[ cfg(not(feature = "alloc" ))]
return Err(ParseFloatError::Custom(
"Duration and Timestamps with no more than 9 digits precision" ,
));
}
if let Ok(mut subseconds) = subseconds.parse() {
// convert subseconds to nanoseconds (10^-9), require 9 places for nanoseconds
subseconds *= 10 u32.pow(9 - subseclen);
Ok((sign, seconds, subseconds))
} else {
Err(ParseFloatError::InvalidValue)
}
} else {
Err(ParseFloatError::InvalidValue)
}
}
_ => Err(ParseFloatError::InvalidValue),
}
}
#[ test]
fn test_parse_float_into_time_parts() {
// Test normal behavior
assert_eq!(
Ok((Sign::Positive, 123 , 456 _000 _000 )),
parse_float_into_time_parts("+123.456" )
);
assert_eq!(
Ok((Sign::Negative, 123 , 987 _000 )),
parse_float_into_time_parts("-123.000987" )
);
assert_eq!(
Ok((Sign::Positive, 18446744073709551615 , 123 _456 _789 )),
parse_float_into_time_parts("18446744073709551615.123456789" )
);
// Test behavior around 0
assert_eq!(
Ok((Sign::Positive, 0 , 456 _000 _000 )),
parse_float_into_time_parts("+0.456" )
);
assert_eq!(
Ok((Sign::Negative, 0 , 987 _000 )),
parse_float_into_time_parts("-0.000987" )
);
assert_eq!(
Ok((Sign::Positive, 0 , 123 _456 _789 )),
parse_float_into_time_parts("0.123456789" )
);
}
Messung V0.5 in Prozent C=89 H=99 G=94
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-06-23)
¤
*© Formatika GbR, Deutschland