1#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
8pub struct AmigaDate {
9 pub days: i32,
11 pub mins: i32,
13 pub ticks: i32,
15}
16
17impl AmigaDate {
18 #[inline]
20 pub const fn new(days: i32, mins: i32, ticks: i32) -> Self {
21 Self { days, mins, ticks }
22 }
23
24 #[inline]
26 pub fn to_date_time(self) -> DateTime {
27 let (year, month, day) = days_to_date(self.days);
28 let hour = (self.mins / 60) as u8;
29 let minute = (self.mins % 60) as u8;
30 let second = (self.ticks / 50) as u8;
31
32 DateTime {
33 year,
34 month,
35 day,
36 hour,
37 minute,
38 second,
39 }
40 }
41
42 #[inline]
50 pub const fn to_unix_timestamp(self) -> i64 {
51 const SECONDS_PER_DAY: i64 = 86400;
52 const SECONDS_PER_MINUTE: i64 = 60;
53 const TICKS_PER_SECOND: i64 = 50;
54 const EPOCH_OFFSET: i64 = 2922 * SECONDS_PER_DAY;
57
58 (self.days as i64) * SECONDS_PER_DAY
59 + (self.mins as i64) * SECONDS_PER_MINUTE
60 + (self.ticks as i64) / TICKS_PER_SECOND
61 + EPOCH_OFFSET
62 }
63}
64
65#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
67pub struct DateTime {
68 pub year: u16,
70 pub month: u8,
72 pub day: u8,
74 pub hour: u8,
76 pub minute: u8,
78 pub second: u8,
80}
81
82fn days_to_date(mut days: i32) -> (u16, u8, u8) {
84 const DAYS_IN_MONTH: [i32; 12] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
85
86 let mut year = 1978u16;
87
88 loop {
90 let days_in_year = if is_leap_year(year) { 366 } else { 365 };
91 if days < days_in_year {
92 break;
93 }
94 days -= days_in_year;
95 year += 1;
96 }
97
98 let mut month = 1u8;
100 let leap = is_leap_year(year);
101 for (i, &days_in_month) in DAYS_IN_MONTH.iter().enumerate() {
102 let dim = if i == 1 && leap { 29 } else { days_in_month };
103 if days < dim {
104 break;
105 }
106 days -= dim;
107 month += 1;
108 }
109
110 (year, month, (days + 1) as u8)
111}
112
113#[inline]
115const fn is_leap_year(year: u16) -> bool {
116 if year.is_multiple_of(100) {
117 year.is_multiple_of(400)
118 } else {
119 year.is_multiple_of(4)
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126
127 #[test]
128 fn test_epoch() {
129 let date = AmigaDate::new(0, 0, 0);
130 let dt = date.to_date_time();
131 assert_eq!(dt.year, 1978);
132 assert_eq!(dt.month, 1);
133 assert_eq!(dt.day, 1);
134 assert_eq!(dt.hour, 0);
135 assert_eq!(dt.minute, 0);
136 assert_eq!(dt.second, 0);
137 }
138
139 #[test]
140 fn test_known_date() {
141 let date = AmigaDate::new(6988, 0, 0);
143 let dt = date.to_date_time();
144 assert_eq!(dt.year, 1997);
145 assert_eq!(dt.month, 2);
146 assert_eq!(dt.day, 18);
147 }
148
149 #[test]
150 fn test_time() {
151 let date = AmigaDate::new(0, 754, 150); let dt = date.to_date_time();
153 assert_eq!(dt.hour, 12);
154 assert_eq!(dt.minute, 34);
155 assert_eq!(dt.second, 3);
156 }
157
158 #[test]
159 fn test_leap_year() {
160 assert!(is_leap_year(2000));
161 assert!(!is_leap_year(1900));
162 assert!(is_leap_year(1984));
163 assert!(!is_leap_year(1983));
164 }
165}