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