speedate/date.rs
1use std::fmt;
2use std::str::FromStr;
3
4use crate::config::DateConfig;
5use crate::numbers::int_parse_bytes;
6use crate::util::timestamp_to_seconds_micros;
7use crate::{get_digit_unchecked, DateTime, ParseError};
8
9/// A Date
10///
11/// Allowed formats:
12/// * `YYYY-MM-DD`
13///
14/// Leap years are correct calculated according to the Gregorian calendar.
15/// Thus `2000-02-29` is a valid date, but `2001-02-29` is not.
16///
17/// # Comparison
18///
19/// `Date` supports equality (`==`) and inequality (`>`, `<`, `>=`, `<=`) comparisons.
20///
21/// ```
22/// use speedate::Date;
23///
24/// let d1 = Date::parse_str("2022-01-01").unwrap();
25/// let d2 = Date::parse_str("2022-01-02").unwrap();
26/// assert!(d2 > d1);
27/// ```
28#[derive(Debug, PartialEq, Eq, PartialOrd, Clone, Copy)]
29pub struct Date {
30 /// Year: four digits
31 pub year: u16,
32 /// Month: 1 to 12
33 pub month: u8,
34 /// Day: 1 to {28, 29, 30, 31} (based on month & year)
35 pub day: u8,
36}
37
38impl fmt::Display for Date {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 let mut buf: [u8; 10] = *b"0000-00-00";
41 crate::display_num_buf(4, 0, self.year as u32, &mut buf);
42 crate::display_num_buf(2, 5, self.month as u32, &mut buf);
43 crate::display_num_buf(2, 8, self.day as u32, &mut buf);
44 f.write_str(std::str::from_utf8(&buf[..]).unwrap())
45 }
46}
47
48impl FromStr for Date {
49 type Err = ParseError;
50
51 #[inline]
52 fn from_str(s: &str) -> Result<Self, Self::Err> {
53 // Delegate to parse_str, which is more permissive - users can call parse_str_rfc3339 directly instead if they
54 // want to be stricter
55 Self::parse_str(s)
56 }
57}
58
59// 2e10 if greater than this, the number is in ms, if less than or equal, it's in seconds
60// (in seconds this is 11th October 2603, in ms it's 20th August 1970)
61pub(crate) const MS_WATERSHED: i64 = 20_000_000_000;
62// 9999-12-31T23:59:59 as a unix timestamp, used as max allowed value below
63const UNIX_9999: i64 = 253_402_300_799;
64// 0000-01-01T00:00:00+00:00 as a unix timestamp, used as min allowed value below
65const UNIX_0000: i64 = -62_167_219_200;
66
67impl Date {
68 /// Parse a date from a string using RFC 3339 format
69 ///
70 /// # Arguments
71 ///
72 /// * `str` - The string to parse
73 ///
74 /// # Examples
75 ///
76 /// ```
77 /// use speedate::Date;
78 ///
79 /// let d = Date::parse_str_rfc3339("2020-01-01").unwrap();
80 /// assert_eq!(
81 /// d,
82 /// Date {
83 /// year: 2020,
84 /// month: 1,
85 /// day: 1
86 /// }
87 /// );
88 /// assert_eq!(d.to_string(), "2020-01-01");
89 /// ```
90 #[inline]
91 pub fn parse_str_rfc3339(str: &str) -> Result<Self, ParseError> {
92 Self::parse_bytes_rfc3339(str.as_bytes())
93 }
94
95 /// Parse a date from a string using RFC 3339 format, or a unix timestamp.
96 ///
97 /// In the input is purely numeric, then the number is interpreted as a unix timestamp,
98 /// using [`Date::from_timestamp`].
99 ///
100 /// # Arguments
101 ///
102 /// * `str` - The string to parse
103 ///
104 /// # Examples
105 ///
106 /// ```
107 /// use speedate::Date;
108 ///
109 /// let d = Date::parse_str("2020-01-01").unwrap();
110 /// assert_eq!(d.to_string(), "2020-01-01");
111 /// let d = Date::parse_str("1577836800").unwrap();
112 /// assert_eq!(d.to_string(), "2020-01-01");
113 /// ```
114 #[inline]
115 pub fn parse_str(str: &str) -> Result<Self, ParseError> {
116 Self::parse_bytes(str.as_bytes())
117 }
118
119 /// As with [`Date::parse_str`] but with a [`DateConfig`].
120 #[inline]
121 pub fn parse_str_with_config(str: &str, config: &DateConfig) -> Result<Self, ParseError> {
122 Self::parse_bytes_with_config(str.as_bytes(), config)
123 }
124
125 /// Parse a date from bytes using RFC 3339 format
126 ///
127 /// # Arguments
128 ///
129 /// * `bytes` - The bytes to parse
130 ///
131 /// # Examples
132 ///
133 /// ```
134 /// use speedate::Date;
135 ///
136 /// let d = Date::parse_bytes_rfc3339(b"2020-01-01").unwrap();
137 /// assert_eq!(
138 /// d,
139 /// Date {
140 /// year: 2020,
141 /// month: 1,
142 /// day: 1
143 /// }
144 /// );
145 /// assert_eq!(d.to_string(), "2020-01-01");
146 /// ```
147 #[inline]
148 pub fn parse_bytes_rfc3339(bytes: &[u8]) -> Result<Self, ParseError> {
149 let d = Self::parse_bytes_partial(bytes)?;
150
151 if bytes.len() > 10 {
152 return Err(ParseError::ExtraCharacters);
153 }
154
155 Ok(d)
156 }
157
158 /// Parse a date from bytes using RFC 3339 format, or a unix timestamp.
159 ///
160 /// In the input is purely numeric, then the number is interpreted as a unix timestamp,
161 /// using [`Date::from_timestamp`].
162 ///
163 /// # Arguments
164 ///
165 /// * `bytes` - The bytes to parse
166 ///
167 /// # Examples
168 ///
169 /// ```
170 /// use speedate::Date;
171 ///
172 /// let d = Date::parse_bytes(b"2020-01-01").unwrap();
173 /// assert_eq!(d.to_string(), "2020-01-01");
174 ///
175 /// let d = Date::parse_bytes(b"1577836800").unwrap();
176 /// assert_eq!(d.to_string(), "2020-01-01");
177 /// ```
178 #[inline]
179 pub fn parse_bytes(bytes: &[u8]) -> Result<Self, ParseError> {
180 Self::parse_bytes_with_config(bytes, &DateConfig::default())
181 }
182
183 /// Same as [`Date::parse_bytes`] but with a [`DateConfig`].
184 #[inline]
185 pub fn parse_bytes_with_config(bytes: &[u8], config: &DateConfig) -> Result<Self, ParseError> {
186 match Self::parse_bytes_rfc3339(bytes) {
187 Ok(d) => Ok(d),
188 Err(e) => match int_parse_bytes(bytes) {
189 Some(int) => Self::from_timestamp(int, true, config),
190 None => Err(e),
191 },
192 }
193 }
194
195 /// Create a date from a Unix Timestamp in seconds or milliseconds
196 ///
197 /// ("Unix Timestamp" means number of seconds or milliseconds since 1970-01-01)
198 ///
199 /// Input must be between `-62,167,219,200,000` (`0000-01-01`) and `253,402,300,799,000` (`9999-12-31`) inclusive.
200 ///
201 /// If the absolute value is > 2e10 (`20,000,000,000`) it is interpreted as being in milliseconds.
202 ///
203 /// That means:
204 /// * `20,000,000,000` is `2603-10-11`
205 /// * `20,000,000,001` is `1970-08-20`
206 /// * `-62,167,219,200,001` gives an error - `DateTooSmall` as it would be before 0000-01-01
207 /// * `-20,000,000,001` is `1969-05-14`
208 /// * `-20,000,000,000` is `1336-03-23`
209 ///
210 /// # Arguments
211 ///
212 /// * `timestamp` - timestamp in either seconds or milliseconds
213 /// * `require_exact` - if true, then the timestamp must be exactly at midnight, otherwise it will be rounded down
214 ///
215 /// # Examples
216 ///
217 /// ```
218 /// use speedate::{Date, DateConfig};
219 ///
220 /// let d = Date::from_timestamp(1_654_560_000, true, &DateConfig::default()).unwrap();
221 /// assert_eq!(d.to_string(), "2022-06-07");
222 /// ```
223 pub fn from_timestamp(timestamp: i64, require_exact: bool, config: &DateConfig) -> Result<Self, ParseError> {
224 let (seconds, microseconds) = timestamp_to_seconds_micros(timestamp, config.timestamp_unit)?;
225 let (d, remaining_seconds) = Self::from_timestamp_calc(seconds)?;
226 if require_exact && (remaining_seconds != 0 || microseconds != 0) {
227 return Err(ParseError::DateNotExact);
228 }
229 Ok(d)
230 }
231
232 /// Unix timestamp in seconds (number of seconds between self and 1970-01-01)
233 ///
234 /// # Example
235 ///
236 /// ```
237 /// use speedate::Date;
238 ///
239 /// let d = Date::parse_str("2022-06-07").unwrap();
240 /// assert_eq!(d.timestamp(), 1_654_560_000);
241 /// ```
242 pub fn timestamp(&self) -> i64 {
243 let days =
244 (self.year as i64) * 365 + (self.ordinal_day() - 1) as i64 + intervening_leap_years(self.year as i64);
245 days * 86400 + UNIX_0000
246 }
247
248 /// Unix timestamp in milliseconds (number of milliseconds between self and 1970-01-01)
249 ///
250 /// # Example
251 ///
252 /// ```
253 /// use speedate::Date;
254 ///
255 /// let d = Date::parse_str("2022-06-07").unwrap();
256 /// assert_eq!(d.timestamp_ms(), 1_654_560_000_000);
257 /// ```
258 pub fn timestamp_ms(&self) -> i64 {
259 self.timestamp() * 1000
260 }
261
262 /// Current date. Internally, this uses [DateTime::now].
263 ///
264 /// # Arguments
265 ///
266 /// * `tz_offset` - timezone offset in seconds, meaning as per [DateTime::now], must be less than `86_400`
267 ///
268 /// # Example
269 ///
270 /// ```
271 /// use speedate::Date;
272 ///
273 /// let d = Date::today(0).unwrap();
274 /// println!("The date today is: {}", d)
275 /// ```
276 pub fn today(tz_offset: i32) -> Result<Self, ParseError> {
277 Ok(DateTime::now(tz_offset)?.date)
278 }
279
280 /// Day of the year, starting from 1.
281 #[allow(clippy::bool_to_int_with_if)]
282 pub fn ordinal_day(&self) -> u16 {
283 let leap_extra = if is_leap_year(self.year) { 1 } else { 0 };
284 let day = self.day as u16;
285 match self.month {
286 1 => day,
287 2 => day + 31,
288 3 => day + 59 + leap_extra,
289 4 => day + 90 + leap_extra,
290 5 => day + 120 + leap_extra,
291 6 => day + 151 + leap_extra,
292 7 => day + 181 + leap_extra,
293 8 => day + 212 + leap_extra,
294 9 => day + 243 + leap_extra,
295 10 => day + 273 + leap_extra,
296 11 => day + 304 + leap_extra,
297 _ => day + 334 + leap_extra,
298 }
299 }
300
301 pub(crate) fn from_timestamp_calc(timestamp_second: i64) -> Result<(Self, u32), ParseError> {
302 if timestamp_second < UNIX_0000 {
303 return Err(ParseError::DateTooSmall);
304 }
305 if timestamp_second > UNIX_9999 {
306 return Err(ParseError::DateTooLarge);
307 }
308 let seconds_diff = timestamp_second - UNIX_0000;
309 let delta_days = seconds_diff / 86_400;
310 let delta_years = delta_days / 365;
311 let leap_years = intervening_leap_years(delta_years);
312
313 // year day is the day of the year, starting from 1
314 let mut ordinal_day: i16 = (delta_days % 365 - leap_years + 1) as i16;
315 let mut year: u16 = delta_years as u16;
316 let mut leap_year: bool = is_leap_year(year);
317 while ordinal_day < 1 {
318 year -= 1;
319 leap_year = is_leap_year(year);
320 ordinal_day += if leap_year { 366 } else { 365 };
321 }
322 let (month, day) = match leap_year {
323 true => leap_year_month_day(ordinal_day),
324 false => common_year_month_day(ordinal_day),
325 };
326 Ok((Self { year, month, day }, (timestamp_second.rem_euclid(86_400)) as u32))
327 }
328
329 /// Parse a date from bytes, no check is performed for extract characters at the end of the string
330 pub(crate) fn parse_bytes_partial(bytes: &[u8]) -> Result<Self, ParseError> {
331 if bytes.len() < 10 {
332 return Err(ParseError::TooShort);
333 }
334 let year: u16;
335 let month: u8;
336 let day: u8;
337 unsafe {
338 let y1 = get_digit_unchecked!(bytes, 0, InvalidCharYear) as u16;
339 let y2 = get_digit_unchecked!(bytes, 1, InvalidCharYear) as u16;
340 let y3 = get_digit_unchecked!(bytes, 2, InvalidCharYear) as u16;
341 let y4 = get_digit_unchecked!(bytes, 3, InvalidCharYear) as u16;
342 year = y1 * 1000 + y2 * 100 + y3 * 10 + y4;
343
344 match bytes.get_unchecked(4) {
345 b'-' => (),
346 _ => return Err(ParseError::InvalidCharDateSep),
347 }
348
349 let m1 = get_digit_unchecked!(bytes, 5, InvalidCharMonth);
350 let m2 = get_digit_unchecked!(bytes, 6, InvalidCharMonth);
351 month = m1 * 10 + m2;
352
353 match bytes.get_unchecked(7) {
354 b'-' => (),
355 _ => return Err(ParseError::InvalidCharDateSep),
356 }
357
358 let d1 = get_digit_unchecked!(bytes, 8, InvalidCharDay);
359 let d2 = get_digit_unchecked!(bytes, 9, InvalidCharDay);
360 day = d1 * 10 + d2;
361 }
362
363 // calculate the maximum number of days in the month, accounting for leap years in the
364 // gregorian calendar
365 let max_days = match month {
366 1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
367 4 | 6 | 9 | 11 => 30,
368 2 => {
369 if year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) {
370 29
371 } else {
372 28
373 }
374 }
375 _ => return Err(ParseError::OutOfRangeMonth),
376 };
377
378 if day < 1 || day > max_days {
379 return Err(ParseError::OutOfRangeDay);
380 }
381
382 Ok(Self { year, month, day })
383 }
384}
385
386fn is_leap_year(year: u16) -> bool {
387 if year % 100 == 0 {
388 year % 400 == 0
389 } else {
390 year % 4 == 0
391 }
392}
393
394/// internal function to calculate the number of leap years since 0000, `delta_years` is the number of
395/// years since 0000
396fn intervening_leap_years(delta_years: i64) -> i64 {
397 if delta_years == 0 {
398 0
399 } else {
400 (delta_years - 1) / 4 - (delta_years - 1) / 100 + (delta_years - 1) / 400 + 1
401 }
402}
403
404fn leap_year_month_day(day: i16) -> (u8, u8) {
405 match day {
406 1..=31 => (1, day as u8),
407 32..=60 => (2, day as u8 - 31),
408 61..=91 => (3, day as u8 - 60),
409 92..=121 => (4, day as u8 - 91),
410 122..=152 => (5, day as u8 - 121),
411 153..=182 => (6, day as u8 - 152),
412 183..=213 => (7, day as u8 - 182),
413 214..=244 => (8, day as u8 - 213),
414 245..=274 => (9, (day - 244) as u8),
415 275..=305 => (10, (day - 274) as u8),
416 306..=335 => (11, (day - 305) as u8),
417 _ => (12, (day - 335) as u8),
418 }
419}
420
421fn common_year_month_day(day: i16) -> (u8, u8) {
422 match day {
423 1..=31 => (1, day as u8),
424 32..=59 => (2, day as u8 - 31),
425 60..=90 => (3, day as u8 - 59),
426 91..=120 => (4, day as u8 - 90),
427 121..=151 => (5, day as u8 - 120),
428 152..=181 => (6, day as u8 - 151),
429 182..=212 => (7, day as u8 - 181),
430 213..=243 => (8, day as u8 - 212),
431 244..=273 => (9, (day - 243) as u8),
432 274..=304 => (10, (day - 273) as u8),
433 305..=334 => (11, (day - 304) as u8),
434 _ => (12, (day - 334) as u8),
435 }
436}