Coverage Report

Created: 2025-12-28 06:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/asn1-rs-0.7.0/src/datetime.rs
Line
Count
Source
1
use crate::{Result, Tag};
2
use alloc::format;
3
#[cfg(not(feature = "std"))]
4
use alloc::string::ToString;
5
use core::fmt;
6
#[cfg(feature = "datetime")]
7
use time::OffsetDateTime;
8
9
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
10
pub enum ASN1TimeZone {
11
    /// No timezone provided
12
    Undefined,
13
    /// Coordinated universal time
14
    Z,
15
    /// Local zone, with offset to coordinated universal time
16
    ///
17
    /// `(offset_hour, offset_minute)`
18
    Offset(i8, i8),
19
}
20
21
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
22
pub struct ASN1DateTime {
23
    pub year: u32,
24
    pub month: u8,
25
    pub day: u8,
26
    pub hour: u8,
27
    pub minute: u8,
28
    pub second: u8,
29
    pub millisecond: Option<u16>,
30
    pub tz: ASN1TimeZone,
31
}
32
33
impl ASN1DateTime {
34
    #[allow(clippy::too_many_arguments)]
35
0
    pub const fn new(
36
0
        year: u32,
37
0
        month: u8,
38
0
        day: u8,
39
0
        hour: u8,
40
0
        minute: u8,
41
0
        second: u8,
42
0
        millisecond: Option<u16>,
43
0
        tz: ASN1TimeZone,
44
0
    ) -> Self {
45
0
        ASN1DateTime {
46
0
            year,
47
0
            month,
48
0
            day,
49
0
            hour,
50
0
            minute,
51
0
            second,
52
0
            millisecond,
53
0
            tz,
54
0
        }
55
0
    }
56
57
    #[cfg(feature = "datetime")]
58
0
    fn to_time_datetime(
59
0
        &self,
60
0
    ) -> core::result::Result<OffsetDateTime, time::error::ComponentRange> {
61
        use std::convert::TryFrom;
62
        use time::{Date, Month, PrimitiveDateTime, Time, UtcOffset};
63
64
0
        let month = Month::try_from(self.month)?;
65
0
        let date = Date::from_calendar_date(self.year as i32, month, self.day)?;
66
0
        let time = Time::from_hms_milli(
67
0
            self.hour,
68
0
            self.minute,
69
0
            self.second,
70
0
            self.millisecond.unwrap_or(0),
71
0
        )?;
72
0
        let primitive_date = PrimitiveDateTime::new(date, time);
73
0
        let offset = match self.tz {
74
0
            ASN1TimeZone::Offset(h, m) => UtcOffset::from_hms(h, m, 0)?,
75
0
            ASN1TimeZone::Undefined | ASN1TimeZone::Z => UtcOffset::UTC,
76
        };
77
0
        Ok(primitive_date.assume_offset(offset))
78
0
    }
79
80
    #[cfg(feature = "datetime")]
81
0
    pub fn to_datetime(&self) -> Result<OffsetDateTime> {
82
        use crate::Error;
83
84
0
        self.to_time_datetime().map_err(|_| Error::InvalidDateTime)
85
0
    }
86
}
87
88
impl fmt::Display for ASN1DateTime {
89
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90
0
        let fractional = match self.millisecond {
91
0
            None => "".to_string(),
92
0
            Some(v) => format!(".{}", v),
93
        };
94
0
        write!(
95
0
            f,
96
0
            "{:04}{:02}{:02}{:02}{:02}{:02}{}Z",
97
            self.year, self.month, self.day, self.hour, self.minute, self.second, fractional,
98
        )
99
0
    }
100
}
101
102
/// Decode 2-digit decimal value
103
0
pub(crate) fn decode_decimal(tag: Tag, hi: u8, lo: u8) -> Result<u8> {
104
0
    if hi.is_ascii_digit() && lo.is_ascii_digit() {
105
0
        Ok((hi - b'0') * 10 + (lo - b'0'))
106
    } else {
107
0
        Err(tag.invalid_value("expected digit"))
108
    }
109
0
}