1use std::time::SystemTime;
8
9pub static DOW: &[&str] = &["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
10pub static MONTH: &[&str] = &[
11 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
12];
13
14const DOW_BYTES: [[u8; 3]; 7] = [
15 *b"Sun", *b"Mon", *b"Tue", *b"Wed", *b"Thu", *b"Fri", *b"Sat",
16];
17const MONTH_BYTES: [[u8; 3]; 12] = [
18 *b"Jan", *b"Feb", *b"Mar", *b"Apr", *b"May", *b"Jun", *b"Jul", *b"Aug", *b"Sep", *b"Oct",
19 *b"Nov", *b"Dec",
20];
21
22const RFC822_LEN: usize = 40;
23const SECONDS_PER_DAY: i64 = 86400;
24
25use super::Header;
26use crate::writer::Writer;
27
28#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
30pub struct Date {
31 pub date: i64,
32}
33
34#[inline(always)]
35fn push<const N: usize>(buffer: &mut [u8; RFC822_LEN], pos: &mut usize, bytes: [u8; N]) {
36 if let Some(slot) = buffer
37 .get_mut(*pos..)
38 .and_then(|tail| tail.first_chunk_mut::<N>())
39 {
40 *slot = bytes;
41 *pos += N;
42 }
43}
44
45#[inline(always)]
46const fn two_digits(value: u64) -> [u8; 2] {
47 [b'0' + (value / 10) as u8, b'0' + (value % 10) as u8]
48}
49
50#[inline(never)]
51fn push_wide_year(buffer: &mut [u8; RFC822_LEN], pos: &mut usize, year: i64) {
52 let mut digits = [0u8; 20];
53 let mut value = year.unsigned_abs();
54 let mut start = digits.len();
55
56 loop {
57 start -= 1;
58 if let Some(slot) = digits.get_mut(start) {
59 *slot = b'0' + (value % 10) as u8;
60 }
61 value /= 10;
62 if value == 0 || start == 0 {
63 break;
64 }
65 }
66
67 let mut width = digits.len() - start;
68 if year < 0 {
69 push(buffer, pos, *b"-");
70 width += 1;
71 }
72 while width < 4 {
73 push(buffer, pos, *b"0");
74 width += 1;
75 }
76 for &digit in digits.get(start..).unwrap_or_default() {
77 push(buffer, pos, [digit]);
78 }
79}
80
81impl Date {
82 pub fn new(date: i64) -> Self {
84 Self { date }
85 }
86
87 pub fn now() -> Self {
89 Self {
90 date: SystemTime::now()
91 .duration_since(SystemTime::UNIX_EPOCH)
92 .map(|d| d.as_secs())
93 .unwrap_or(0) as i64,
94 }
95 }
96
97 fn format_rfc822(&self, buffer: &mut [u8; RFC822_LEN]) -> usize {
98 let days = self.date.div_euclid(SECONDS_PER_DAY);
99 let seconds = self.date.rem_euclid(SECONDS_PER_DAY) as u64;
100
101 let z = days + 719468;
102 let era = z.div_euclid(146097);
103 let doe = (z - era * 146097) as u64;
104 let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
105 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
106 let mp = (5 * doy + 2) / 153;
107 let day = doy - (153 * mp + 2) / 5 + 1;
108 let month = if mp < 10 { mp + 3 } else { mp - 9 };
109 let year = yoe as i64 + era * 400 + i64::from(month <= 2);
110
111 let dow = DOW_BYTES[(days + 4).rem_euclid(7) as usize];
112 let mon = MONTH_BYTES
113 .get(month as usize - 1)
114 .copied()
115 .unwrap_or(*b" ");
116
117 let mut pos = 0;
118 push(buffer, &mut pos, [dow[0], dow[1], dow[2], b',', b' ']);
119 if day >= 10 {
120 push(buffer, &mut pos, two_digits(day));
121 } else {
122 push(buffer, &mut pos, [b'0' + day as u8]);
123 }
124 push(buffer, &mut pos, [b' ', mon[0], mon[1], mon[2], b' ']);
125
126 if (0..10000).contains(&year) {
127 let year = year as u64;
128 push(
129 buffer,
130 &mut pos,
131 [
132 b'0' + (year / 1000) as u8,
133 b'0' + (year / 100 % 10) as u8,
134 b'0' + (year / 10 % 10) as u8,
135 b'0' + (year % 10) as u8,
136 ],
137 );
138 } else {
139 push_wide_year(buffer, &mut pos, year);
140 }
141
142 let [hour, minute, second] =
143 [seconds / 3600, seconds / 60 % 60, seconds % 60].map(two_digits);
144 push(
145 buffer,
146 &mut pos,
147 [
148 b' ', hour[0], hour[1], b':', minute[0], minute[1], b':', second[0], second[1],
149 b' ',
150 ],
151 );
152 push(buffer, &mut pos, *b"+0000");
153
154 pos
155 }
156
157 pub fn to_rfc822(&self) -> String {
159 let mut buffer = [0u8; RFC822_LEN];
160 let len = self.format_rfc822(&mut buffer);
161 String::from_utf8(buffer.get(..len).unwrap_or_default().to_vec()).unwrap_or_default()
162 }
163
164 pub fn write_rfc822(&self, output: &mut impl Writer) {
166 let mut buffer = [0u8; RFC822_LEN];
167 let len = self.format_rfc822(&mut buffer);
168 output.write(buffer.get(..len).unwrap_or_default());
169 }
170}
171
172impl From<i64> for Date {
173 fn from(datetime: i64) -> Self {
174 Date::new(datetime)
175 }
176}
177
178impl From<u64> for Date {
179 fn from(datetime: u64) -> Self {
180 Date::new(datetime as i64)
181 }
182}
183
184impl Header for Date {
185 fn write_header(&self, output: &mut impl Writer, _column: usize) {
186 self.write_rfc822(output);
187 output.write(b"\r\n");
188 }
189}