Coverage Report

Created: 2026-01-16 07:00

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