Skip to main content

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;
31#[cfg(not(target_arch = "wasm32"))]
32use std::time::{SystemTime, UNIX_EPOCH};
33
34#[repr(C)]
35#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq)]
36pub struct ColorDateTime {
37    pub year: u16,
38    pub month: u16,
39    pub day_of_the_month: u16,
40    pub hours: u16,
41    pub minutes: u16,
42    pub seconds: u16,
43}
44
45fn is_leap(year: i32) -> bool {
46    (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)
47}
48
49fn days_in_month(year: i32, month: i32) -> i32 {
50    match month {
51        1 => 31,
52        2 => {
53            if is_leap(year) {
54                29
55            } else {
56                28
57            }
58        }
59        3 => 31,
60        4 => 30,
61        5 => 31,
62        6 => 30,
63        7 => 31,
64        8 => 31,
65        9 => 30,
66        10 => 31,
67        11 => 30,
68        12 => 31,
69        _ => unreachable!("Unknown month"),
70    }
71}
72
73impl Default for ColorDateTime {
74    fn default() -> Self {
75        Self::now()
76    }
77}
78
79impl ColorDateTime {
80    /// Parses slice for date time
81    pub fn new_from_slice(slice: &[u8]) -> Result<ColorDateTime, CmsError> {
82        if slice.len() != 12 {
83            return Err(CmsError::InvalidProfile);
84        }
85        let year = u16::from_be_bytes([slice[0], slice[1]]);
86        let month = u16::from_be_bytes([slice[2], slice[3]]);
87        let day_of_the_month = u16::from_be_bytes([slice[4], slice[5]]);
88        let hours = u16::from_be_bytes([slice[6], slice[7]]);
89        let minutes = u16::from_be_bytes([slice[8], slice[9]]);
90        let seconds = u16::from_be_bytes([slice[10], slice[11]]);
91        Ok(ColorDateTime {
92            year,
93            month,
94            day_of_the_month,
95            hours,
96            minutes,
97            seconds,
98        })
99    }
100
101    /// Creates a new `ColorDateTime` from the current system time (UTC)
102    pub fn now() -> Self {
103        #[cfg(not(target_arch = "wasm32"))]
104        let now = match SystemTime::now().duration_since(UNIX_EPOCH) {
105            Ok(v) => v,
106            Err(_) => return Self::default(),
107        };
108        #[cfg(target_arch = "wasm32")]
109        use std::time::Duration;
110        #[cfg(target_arch = "wasm32")]
111        let now = Duration::new(365 * 60 * 60 * 24 * 30, 0);
112        let mut days = (now.as_secs() / 86_400) as i64;
113        let secs_of_day = (now.as_secs() % 86_400) as i64;
114
115        let mut year = 1970;
116        loop {
117            let year_days = if is_leap(year) { 366 } else { 365 };
118            if days >= year_days {
119                days -= year_days;
120                year += 1;
121            } else {
122                break;
123            }
124        }
125
126        let mut month = 1;
127        loop {
128            let mdays = days_in_month(year, month);
129            if days >= mdays as i64 {
130                days -= mdays as i64;
131                month += 1;
132            } else {
133                break;
134            }
135        }
136        let day = days + 1; // days from zero based to 1 base
137
138        let hour = secs_of_day / 3600;
139        let min = (secs_of_day % 3600) / 60;
140        let sec = secs_of_day % 60;
141        Self {
142            year: year as u16,
143            month: month as u16,
144            day_of_the_month: day as u16,
145            hours: hour as u16,
146            minutes: min as u16,
147            seconds: sec as u16,
148        }
149    }
150
151    #[inline]
152    pub(crate) fn encode(&self, into: &mut Vec<u8>) {
153        let year = self.year;
154        let month = self.month;
155        let day_of_the_month = self.day_of_the_month;
156        let hours = self.hours;
157        let minutes = self.minutes;
158        let seconds = self.seconds;
159        write_u16_be(into, year);
160        write_u16_be(into, month);
161        write_u16_be(into, day_of_the_month);
162        write_u16_be(into, hours);
163        write_u16_be(into, minutes);
164        write_u16_be(into, seconds);
165    }
166}