moxcms/
dat.rs

1/*
2 * // Copyright (c) Radzivon Bartoshyk 3/2025. All rights reserved.
3 * //
4 * // Redistribution and use in source and binary forms, with or without modification,
5 * // are permitted provided that the following conditions are met:
6 * //
7 * // 1.  Redistributions of source code must retain the above copyright notice, this
8 * // list of conditions and the following disclaimer.
9 * //
10 * // 2.  Redistributions in binary form must reproduce the above copyright notice,
11 * // this list of conditions and the following disclaimer in the documentation
12 * // and/or other materials provided with the distribution.
13 * //
14 * // 3.  Neither the name of the copyright holder nor the names of its
15 * // contributors may be used to endorse or promote products derived from
16 * // this software without specific prior written permission.
17 * //
18 * // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 * // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 * // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29use crate::CmsError;
30use crate::writer::write_u16_be;
31use std::time::{SystemTime, UNIX_EPOCH};
32
33#[repr(C)]
34#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Default)]
35pub struct ColorDateTime {
36    pub year: u16,
37    pub month: u16,
38    pub day_of_the_month: u16,
39    pub hours: u16,
40    pub minutes: u16,
41    pub seconds: u16,
42}
43
44fn is_leap(year: i32) -> bool {
45    (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)
46}
47
48fn days_in_month(year: i32, month: i32) -> i32 {
49    match month {
50        1 => 31,
51        2 => {
52            if is_leap(year) {
53                29
54            } else {
55                28
56            }
57        }
58        3 => 31,
59        4 => 30,
60        5 => 31,
61        6 => 30,
62        7 => 31,
63        8 => 31,
64        9 => 30,
65        10 => 31,
66        11 => 30,
67        12 => 31,
68        _ => unreachable!("Unknown month"),
69    }
70}
71
72impl ColorDateTime {
73    /// Parses slice for date time
74    pub fn new_from_slice(slice: &[u8]) -> Result<ColorDateTime, CmsError> {
75        if slice.len() != 12 {
76            return Err(CmsError::InvalidProfile);
77        }
78        let year = u16::from_be_bytes([slice[0], slice[1]]);
79        let month = u16::from_be_bytes([slice[2], slice[3]]);
80        let day_of_the_month = u16::from_be_bytes([slice[4], slice[5]]);
81        let hours = u16::from_be_bytes([slice[6], slice[7]]);
82        let minutes = u16::from_be_bytes([slice[8], slice[9]]);
83        let seconds = u16::from_be_bytes([slice[10], slice[11]]);
84        Ok(ColorDateTime {
85            year,
86            month,
87            day_of_the_month,
88            hours,
89            minutes,
90            seconds,
91        })
92    }
93
94    /// Creates a new `ColorDateTime` from the current system time (UTC)
95    pub fn now() -> Self {
96        let now = match SystemTime::now().duration_since(UNIX_EPOCH) {
97            Ok(v) => v,
98            Err(_) => return Self::default(),
99        };
100        let mut days = (now.as_secs() / 86_400) as i64;
101        let secs_of_day = (now.as_secs() % 86_400) as i64;
102
103        let mut year = 1970;
104        loop {
105            let year_days = if is_leap(year) { 366 } else { 365 };
106            if days >= year_days {
107                days -= year_days;
108                year += 1;
109            } else {
110                break;
111            }
112        }
113
114        let mut month = 1;
115        loop {
116            let mdays = days_in_month(year, month);
117            if days >= mdays as i64 {
118                days -= mdays as i64;
119                month += 1;
120            } else {
121                break;
122            }
123        }
124        let day = days + 1; // days from zero based to 1 base
125
126        let hour = secs_of_day / 3600;
127        let min = (secs_of_day % 3600) / 60;
128        let sec = secs_of_day % 60;
129        Self {
130            year: year as u16,
131            month: month as u16,
132            day_of_the_month: day as u16,
133            hours: hour as u16,
134            minutes: min as u16,
135            seconds: sec as u16,
136        }
137    }
138
139    #[inline]
140    pub(crate) fn encode(&self, into: &mut Vec<u8>) {
141        let year = self.year;
142        let month = self.month;
143        let day_of_the_month = self.day_of_the_month;
144        let hours = self.hours;
145        let minutes = self.minutes;
146        let seconds = self.seconds;
147        write_u16_be(into, year);
148        write_u16_be(into, month);
149        write_u16_be(into, day_of_the_month);
150        write_u16_be(into, hours);
151        write_u16_be(into, minutes);
152        write_u16_be(into, seconds);
153    }
154}