1use alloc::{fmt, format, string::String};
2use serde::{Deserialize, Serialize};
3
4const fn is_leap_year(year: u16) -> bool {
6 (year.is_multiple_of(4) && !year.is_multiple_of(100)) || year.is_multiple_of(400)
7}
8
9const DAYS_IN_MONTH: [[u16; 12]; 2] = [
11 [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], ];
14
15#[derive(Debug, PartialEq, Ord, PartialOrd, Eq, Clone, Copy, Default, Serialize, Deserialize)]
37pub struct Date {
38 pub year: u16,
40 pub month: u8,
42 pub day: u8,
44 pub hour: u8,
46 pub minute: u8,
48 pub second: u8,
50}
51impl Date {
52 pub fn new(year: u16, month: u8, day: u8) -> Date {
54 Date { year, month, day, hour: 0, minute: 0, second: 0 }
55 }
56
57 pub fn new_full(year: u16, month: u8, day: u8, hour: u8, minute: u8, second: u8) -> Date {
59 Date { year, month, day, hour, minute, second }
60 }
61
62 pub fn from_time(time: i64) -> Date {
64 let mut date = Date::default();
65 date.set_time(time);
66 date
67 }
68
69 pub fn set_time(&mut self, time: i64) {
71 let mut days = time / 86_400_000;
73 let mut ms_remaining = time % 86_400_000;
74 if ms_remaining < 0 {
75 ms_remaining += 86_400_000;
76 days -= 1;
77 }
78
79 let mut year = 1970;
81 loop {
82 let year_days = if is_leap_year(year) { 366 } else { 365 };
83 if days < year_days {
84 break;
85 }
86 days -= year_days;
87 year += 1;
88 }
89
90 let leap = is_leap_year(year) as usize;
92 let mut month = 0;
93 while days >= DAYS_IN_MONTH[leap][month] as i64 {
94 days -= DAYS_IN_MONTH[leap][month] as i64;
95 month += 1;
96 }
97
98 self.year = year;
99 self.month = (month + 1) as u8;
100 self.day = (days + 1) as u8;
101
102 self.hour = (ms_remaining / 3_600_000) as u8;
104 self.minute = ((ms_remaining % 3_600_000) / 60_000) as u8;
105 self.second = ((ms_remaining % 60_000) / 1_000) as u8;
106 }
107
108 pub fn get_time(&self) -> i64 {
110 let mut days = 0;
111
112 for y in 1970..self.year {
114 days += if is_leap_year(y) { 366 } else { 365 };
115 }
116
117 let leap = is_leap_year(self.year) as usize;
119 for m in 0..(self.month as usize - 1) {
120 days += DAYS_IN_MONTH[leap][m % 12] as i64;
121 }
122
123 days += self.day as i64 - 1;
125
126 days * 86_400_000
128 + (self.hour as i64 * 3_600_000)
129 + (self.minute as i64 * 60_000)
130 + (self.second as i64 * 1_000)
131 }
132
133 pub fn to_iso_string(&self) -> String {
135 format!(
136 "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}.000Z",
137 self.year, self.month, self.day, self.hour, self.minute, self.second
138 )
139 }
140}
141impl From<&str> for Date {
142 fn from(s: &str) -> Date {
143 if let Ok(ms) = s.parse::<i64>() {
145 return Date::from_time(ms);
146 }
147
148 let mut date = Date::default();
151
152 let year = s[0..4].parse().unwrap();
153 let month = s[5..7].parse().unwrap();
154 let day = s[8..10].parse().unwrap();
155
156 let mut hour = 0;
157 let mut minute = 0;
158 let mut second = 0;
159
160 if s.len() >= 19 {
161 hour = s[11..13].parse().unwrap();
162 minute = s[14..16].parse().unwrap();
163 second = s[17..19].parse().unwrap();
164 }
165
166 date.year = year;
167 date.month = month;
168 date.day = day;
169 date.hour = hour;
170 date.minute = minute;
171 date.second = second;
172
173 date
174 }
175}
176impl fmt::Display for Date {
177 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178 write!(f, "{:04}{:02}{:02}", self.year, self.month + 1, self.day)
179 }
180}