1use crate::error::ParseError;
7use serde::{de, ser};
8use std::fmt;
9use std::str::FromStr;
10
11#[derive(Clone, Copy, Debug, PartialEq)]
23pub struct DateTime {
24 date: Date,
25 time: Time,
26 time_offset: TimeOffset,
27}
28
29#[derive(Clone, Copy, Debug, PartialEq)]
41pub struct Date {
42 year: u16,
43 month: u8,
44 day: u8,
45}
46
47#[derive(Clone, Copy, Debug, PartialEq)]
60pub struct Time {
61 hour: u8,
62 minute: u8,
63 second: u8,
64 nanosecond: u32,
65}
66
67#[derive(Clone, Copy, Debug, PartialEq)]
77pub struct TimeOffset(_TimeOffset);
78
79#[derive(Clone, Copy, Debug, PartialEq)]
81enum _TimeOffset {
82 Z,
83 Positive(u8, u8),
84 Negative(u8, u8),
85}
86
87fn days_in_month(year: u16, month: u8) -> Option<u8> {
89 match month {
90 4 | 6 | 9 | 11 => Some(30),
92 1 | 3 | 5 | 7 | 8 | 10 | 12 => Some(31),
94 2 => Some(if is_leap_year(year) { 29 } else { 28 }),
96 _ => None,
98 }
99}
100
101fn is_leap_year(year: u16) -> bool {
103 year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
104}
105
106fn digit(b: u8) -> Option<u8> {
108 if b >= b'0' && b <= b'9' {
109 Some(b - b'0')
110 } else {
111 None
112 }
113}
114
115fn parse_4_digits(ascii: &[u8]) -> Option<u16> {
117 if ascii.len() == 4 {
118 if let (Some(b0), Some(b1), Some(b2), Some(b3)) = (
119 digit(ascii[0]),
120 digit(ascii[1]),
121 digit(ascii[2]),
122 digit(ascii[3]),
123 ) {
124 return Some(
125 u16::from(b0) * 1000
126 + u16::from(b1) * 100
127 + u16::from(b2) * 10
128 + u16::from(b3),
129 );
130 }
131 }
132 None
133}
134
135fn parse_year(year: &[u8]) -> Option<u16> {
137 match parse_4_digits(year) {
138 Some(year) => Some(year),
139 _ => None,
140 }
141}
142
143fn parse_2_digits(ascii: &[u8]) -> Option<u8> {
145 if ascii.len() == 2 {
146 if let (Some(b0), Some(b1)) = (digit(ascii[0]), digit(ascii[1])) {
147 return Some(b0 * 10 + b1);
148 }
149 }
150 None
151}
152
153fn parse_month(month: &[u8]) -> Option<u8> {
155 match parse_2_digits(month) {
156 Some(month) if month >= 1 && month <= 12 => Some(month),
157 _ => None,
158 }
159}
160
161fn parse_day(day: &[u8]) -> Option<u8> {
163 match parse_2_digits(day) {
164 Some(day) if day >= 1 && day <= 31 => Some(day),
165 _ => None,
166 }
167}
168
169fn parse_hour(hour: &[u8]) -> Option<u8> {
171 match parse_2_digits(hour) {
172 Some(hour) if hour < 24 => Some(hour),
173 _ => None,
174 }
175}
176
177fn parse_minute(minute: &[u8]) -> Option<u8> {
179 match parse_2_digits(minute) {
180 Some(minute) if minute < 60 => Some(minute),
181 _ => None,
182 }
183}
184
185fn parse_second(second: &[u8], leap_sec: bool) -> Option<u8> {
187 let max_seconds = if leap_sec { 60 } else { 59 };
188 match parse_2_digits(second) {
189 Some(second) if second <= max_seconds => Some(second),
190 _ => None,
191 }
192}
193
194fn parse_nanosecond(nano: &[u8]) -> Option<u32> {
196 if nano.len() == 0 {
197 Some(0)
198 } else if nano.len() >= 2 && nano[0] == b'.' {
199 let mut ns = 0;
200 for (i, b) in nano[1..].iter().enumerate() {
201 match digit(*b) {
202 Some(b) => {
203 if i < 9 {
204 ns += u32::from(b) * 10_u32.pow(8 - i as u32);
205 }
206 }
207 None => return None,
208 }
209 }
210 Some(ns)
211 } else {
212 None
213 }
214}
215
216impl fmt::Display for DateTime {
217 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
218 self.date.fmt(f)?;
219 write!(f, "T")?;
220 self.time.fmt(f)?;
221 self.time_offset.fmt(f)
222 }
223}
224
225impl FromStr for DateTime {
226 type Err = ParseError;
227
228 fn from_str(datetime: &str) -> Result<Self, Self::Err> {
229 DateTime::new(datetime.as_bytes())
230 }
231}
232
233impl ser::Serialize for DateTime {
234 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
235 where
236 S: ser::Serializer,
237 {
238 serializer.serialize_str(&self.to_string())
239 }
240}
241
242impl<'de> de::Deserialize<'de> for DateTime {
243 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
244 where
245 D: de::Deserializer<'de>,
246 {
247 struct DateTimeVisitor;
248
249 impl<'de> de::Visitor<'de> for DateTimeVisitor {
250 type Value = DateTime;
251
252 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
253 write!(formatter, "DateTime")
254 }
255
256 fn visit_str<E: de::Error>(
257 self,
258 s: &str,
259 ) -> Result<Self::Value, E> {
260 match s.parse() {
261 Ok(datetime) => Ok(datetime),
262 Err(_) => Err(de::Error::invalid_value(
263 de::Unexpected::Str(&s),
264 &self,
265 )),
266 }
267 }
268 }
269 deserializer.deserialize_str(DateTimeVisitor)
270 }
271}
272
273impl DateTime {
274 fn new(bytes: &[u8]) -> Result<DateTime, ParseError> {
276 let len = bytes.len();
279 if len >= 20 {
280 let offset = TimeOffset::rindex(bytes);
281 if offset >= 11 && bytes[10] == b'T' {
282 let date = Date::new(&bytes[..10])?;
283 let time = Time::new(&bytes[11..offset], false)?;
284 let time_offset = TimeOffset::new(&bytes[offset..])?;
285 return Ok(DateTime {
286 date,
287 time,
288 time_offset,
289 });
290 }
291 }
292 Err(ParseError::ExpectedDateTime)
293 }
294 pub fn date(&self) -> Date {
296 self.date
297 }
298 pub fn time(&self) -> Time {
300 self.time
301 }
302 pub fn time_offset(&self) -> TimeOffset {
304 self.time_offset
305 }
306}
307
308impl fmt::Display for Date {
309 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
310 write!(f, "{:04}-{:02}-{:02}", self.year, self.month, self.day)
311 }
312}
313
314impl FromStr for Date {
315 type Err = ParseError;
316
317 fn from_str(date: &str) -> Result<Self, Self::Err> {
318 Date::new(date.as_bytes())
319 }
320}
321
322impl ser::Serialize for Date {
323 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
324 where
325 S: ser::Serializer,
326 {
327 serializer.serialize_str(&self.to_string())
328 }
329}
330
331impl<'de> de::Deserialize<'de> for Date {
332 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
333 where
334 D: de::Deserializer<'de>,
335 {
336 struct DateVisitor;
337
338 impl<'de> de::Visitor<'de> for DateVisitor {
339 type Value = Date;
340
341 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
342 write!(formatter, "Date")
343 }
344
345 fn visit_str<E: de::Error>(
346 self,
347 s: &str,
348 ) -> Result<Self::Value, E> {
349 match s.parse() {
350 Ok(date) => Ok(date),
351 Err(_) => Err(de::Error::invalid_value(
352 de::Unexpected::Str(&s),
353 &self,
354 )),
355 }
356 }
357 }
358 deserializer.deserialize_str(DateVisitor)
359 }
360}
361
362impl Date {
363 fn new(bytes: &[u8]) -> Result<Self, ParseError> {
365 if bytes.len() == 10 && bytes[4] == b'-' && bytes[7] == b'-' {
366 if let Some(year) = parse_year(&bytes[..4]) {
367 if let Some(month) = parse_month(&bytes[5..7]) {
368 if let Some(mdays) = days_in_month(year, month) {
369 if let Some(day) = parse_day(&bytes[8..]) {
370 if day <= mdays {
371 return Ok(Date { year, month, day });
372 }
373 }
374 }
375 }
376 }
377 }
378 Err(ParseError::ExpectedDate)
379 }
380 pub fn year(&self) -> u16 {
382 self.year
383 }
384 pub fn month(&self) -> u8 {
386 self.month
387 }
388 pub fn day(&self) -> u8 {
390 self.day
391 }
392}
393
394impl fmt::Display for Time {
395 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
396 write!(f, "{:02}:{:02}:{:02}", self.hour, self.minute, self.second)?;
397 if self.nanosecond > 0 {
398 let ns = format!("{:09}", self.nanosecond);
399 write!(f, ".{}", ns.trim_end_matches('0'))
400 } else {
401 Ok(())
402 }
403 }
404}
405
406impl FromStr for Time {
407 type Err = ParseError;
408
409 fn from_str(time: &str) -> Result<Self, Self::Err> {
410 Time::new(time.as_bytes(), false)
411 }
412}
413
414impl ser::Serialize for Time {
415 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
416 where
417 S: ser::Serializer,
418 {
419 serializer.serialize_str(&self.to_string())
420 }
421}
422
423impl<'de> de::Deserialize<'de> for Time {
424 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
425 where
426 D: de::Deserializer<'de>,
427 {
428 struct TimeVisitor;
429
430 impl<'de> de::Visitor<'de> for TimeVisitor {
431 type Value = Time;
432
433 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
434 write!(formatter, "Time")
435 }
436
437 fn visit_str<E: de::Error>(
438 self,
439 s: &str,
440 ) -> Result<Self::Value, E> {
441 match s.parse::<Time>() {
442 Ok(time) => Ok(time),
443 Err(_) => Err(de::Error::invalid_value(
444 de::Unexpected::Str(&s),
445 &self,
446 )),
447 }
448 }
449 }
450 deserializer.deserialize_str(TimeVisitor)
451 }
452}
453
454impl Time {
455 fn new(bytes: &[u8], leap_sec: bool) -> Result<Self, ParseError> {
457 if bytes.len() >= 8 && bytes[2] == b':' && bytes[5] == b':' {
458 if let Some(hour) = parse_hour(&bytes[..2]) {
459 if let Some(minute) = parse_minute(&bytes[3..5]) {
460 if let Some(second) = parse_second(&bytes[6..8], leap_sec) {
461 if let Some(nanosecond) = parse_nanosecond(&bytes[8..])
462 {
463 return Ok(Time {
464 hour,
465 minute,
466 second,
467 nanosecond,
468 });
469 }
470 }
471 }
472 }
473 }
474 Err(ParseError::ExpectedTime)
475 }
476 pub fn hour(&self) -> u8 {
478 self.hour
479 }
480 pub fn minute(&self) -> u8 {
482 self.minute
483 }
484 pub fn second(&self) -> u8 {
486 self.second
487 }
488 pub fn nanosecond(&self) -> u32 {
490 self.nanosecond
491 }
492}
493
494impl fmt::Display for TimeOffset {
495 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
496 match self.0 {
497 _TimeOffset::Z => write!(f, "Z"),
498 _TimeOffset::Positive(h, m) => write!(f, "+{:02}:{:02}", h, m),
499 _TimeOffset::Negative(h, m) => write!(f, "-{:02}:{:02}", h, m),
500 }
501 }
502}
503
504impl FromStr for TimeOffset {
505 type Err = ParseError;
506
507 fn from_str(offset: &str) -> Result<Self, Self::Err> {
508 TimeOffset::new(offset.as_bytes())
509 }
510}
511
512impl TimeOffset {
513 fn new(bytes: &[u8]) -> Result<Self, ParseError> {
515 if bytes.len() == 1 && bytes[0] == b'Z' {
516 return Ok(TimeOffset(_TimeOffset::Z));
517 } else if bytes.len() == 6
518 && bytes[3] == b':'
519 && (bytes[0] == b'+' || bytes[0] == b'-')
520 {
521 if let Some(h) = parse_hour(&bytes[1..3]) {
522 if let Some(m) = parse_minute(&bytes[4..6]) {
523 if bytes[0] == b'+' {
524 return Ok(TimeOffset(_TimeOffset::Positive(h, m)));
525 } else {
526 return Ok(TimeOffset(_TimeOffset::Negative(h, m)));
527 }
528 }
529 }
530 }
531 Err(ParseError::ExpectedTimeOffset)
532 }
533 fn rindex(bytes: &[u8]) -> usize {
535 const MAX: usize = std::usize::MAX;
536 let len = bytes.len();
537 match len {
538 1..=MAX if bytes[len - 1] == b'Z' => len - 1,
539 6..=MAX => len - 6,
540 _ => 0,
541 }
542 }
543 pub fn seconds(&self) -> i32 {
545 match self.0 {
546 _TimeOffset::Z => 0,
547 _TimeOffset::Positive(h, m) => hour_minute_to_seconds(h, m),
548 _TimeOffset::Negative(h, m) => -hour_minute_to_seconds(h, m),
549 }
550 }
551}
552
553fn hour_minute_to_seconds(hour: u8, minute: u8) -> i32 {
555 3600 * i32::from(hour) + 60 * i32::from(minute)
556}
557
558#[cfg(test)]
559mod test {
560 use super::*;
561
562 #[test]
563 fn date_time_ok() -> Result<(), Box<ParseError>> {
564 assert_eq!(
565 2011,
566 "2011-01-01T12:30:15Z".parse::<DateTime>()?.date().year()
567 );
568 assert_eq!(
569 4,
570 "2002-04-02T04:57:19.001+00:00"
571 .parse::<DateTime>()?
572 .date()
573 .month()
574 );
575 assert_eq!(
576 15,
577 "1975-03-15T19:23:00+07:00"
578 .parse::<DateTime>()?
579 .date()
580 .day()
581 );
582 assert_eq!(
583 22,
584 "2009-10-03T22:03:19-05:00"
585 .parse::<DateTime>()?
586 .time()
587 .hour()
588 );
589 assert_eq!(
590 59,
591 "2025-09-29T14:59:13.392853953+10:45"
592 .parse::<DateTime>()?
593 .time()
594 .minute()
595 );
596 assert_eq!(
597 48,
598 "2015-05-27T18:31:48.123-06:00"
599 .parse::<DateTime>()?
600 .time()
601 .second()
602 );
603 assert_eq!(
604 987_654_321,
605 "2003-08-22T01:55:11.987654321+02:30"
606 .parse::<DateTime>()?
607 .time()
608 .nanosecond()
609 );
610 assert_eq!(
611 0,
612 "2007-01-11T05:45:12+04:00"
613 .parse::<DateTime>()?
614 .time()
615 .nanosecond()
616 );
617 assert_eq!(
618 -21600,
619 "2012-06-21T19:03:00.0-06:00"
620 .parse::<DateTime>()?
621 .time_offset()
622 .seconds()
623 );
624 Ok(())
625 }
626
627 #[test]
628 fn date_time_err() -> Result<(), Box<ParseError>> {
629 assert!("".parse::<DateTime>().is_err());
630 assert!("0000".parse::<DateTime>().is_err());
631 assert!("0000-00-00T00:00:00Z".parse::<DateTime>().is_err());
632 assert!("2000-01-01t00:00:00Z".parse::<DateTime>().is_err());
633 assert!("2000-01-01TT00:00:00Z".parse::<DateTime>().is_err());
634 assert!("2000-01-01 00:00:00Z".parse::<DateTime>().is_err());
635 assert!("2000-01-01T00:00:00 Z".parse::<DateTime>().is_err());
636 assert!("2000-01-01T00:00:00=00:00".parse::<DateTime>().is_err());
637 assert!("2000-01-01T00:00:00.00 +00:00".parse::<DateTime>().is_err());
638 assert!("2000-01-01T00:00:00.00.-00:00".parse::<DateTime>().is_err());
639 Ok(())
640 }
641
642 #[test]
643 fn date_ok() -> Result<(), Box<ParseError>> {
644 assert_eq!(2011, "2011-01-01".parse::<Date>()?.year());
645 assert_eq!(2050, "2050-04-30".parse::<Date>()?.year());
646 assert_eq!(1, "1999-01-31".parse::<Date>()?.month());
647 assert_eq!(12, "2004-12-01".parse::<Date>()?.month());
648 assert_eq!(1, "1950-09-01".parse::<Date>()?.day());
649 assert_eq!(31, "2019-07-31".parse::<Date>()?.day());
650 assert_eq!(29, "2400-02-29".parse::<Date>()?.day());
651 assert_eq!(29, "2004-02-29".parse::<Date>()?.day());
652 assert_eq!(29, "2000-02-29".parse::<Date>()?.day());
653 Ok(())
654 }
655
656 #[test]
657 fn date_err() -> Result<(), Box<ParseError>> {
658 assert!("".parse::<Date>().is_err());
659 assert!("0000".parse::<Date>().is_err());
660 assert!("0000-00".parse::<Date>().is_err());
661 assert!("0000-00-".parse::<Date>().is_err());
662 assert!("0000-00-0".parse::<Date>().is_err());
663 assert!("0000-00-00".parse::<Date>().is_err());
664 assert!("1999-00-01".parse::<Date>().is_err());
665 assert!("2010-01-32".parse::<Date>().is_err());
666 assert!("2011-04-31".parse::<Date>().is_err());
667 assert!("2015-13-01".parse::<Date>().is_err());
668 assert!("2018-01-00".parse::<Date>().is_err());
669 assert!("1900-02-29".parse::<Date>().is_err());
670 assert!("2019:07-31".parse::<Date>().is_err());
671 Ok(())
672 }
673
674 #[test]
675 fn time_ok() -> Result<(), Box<ParseError>> {
676 assert_eq!(0, "00:00:00".parse::<Time>()?.hour());
677 assert_eq!(23, "23:00:00".parse::<Time>()?.hour());
678 assert_eq!(0, "12:00:34".parse::<Time>()?.minute());
679 assert_eq!(45, "12:45:34".parse::<Time>()?.minute());
680 assert_eq!(0, "12:34:00".parse::<Time>()?.second());
681 assert_eq!(15, "12:45:15".parse::<Time>()?.second());
682 Ok(())
683 }
684
685 #[test]
686 fn time_err() -> Result<(), Box<ParseError>> {
687 assert!("".parse::<Time>().is_err());
688 assert!("00".parse::<Time>().is_err());
689 assert!("00:00".parse::<Time>().is_err());
690 assert!("00:00:".parse::<Time>().is_err());
691 assert!("00:00:0".parse::<Time>().is_err());
692 assert!("00;00:00".parse::<Time>().is_err());
693 assert!("00:00:00:0".parse::<Time>().is_err());
694 assert!("24:00:00".parse::<Time>().is_err());
695 assert!("00:60:00".parse::<Time>().is_err());
696 assert!("00:00:60".parse::<Time>().is_err());
697 Ok(())
698 }
699
700 #[test]
701 fn offset_ok() -> Result<(), Box<ParseError>> {
702 assert_eq!(0, "Z".parse::<TimeOffset>()?.seconds());
703 assert_eq!(0, "-00:00".parse::<TimeOffset>()?.seconds());
704 assert_eq!(3600, "+01:00".parse::<TimeOffset>()?.seconds());
705 assert_eq!(-18000, "-05:00".parse::<TimeOffset>()?.seconds());
706 assert_eq!(-1800, "-00:30".parse::<TimeOffset>()?.seconds());
707 assert_eq!(38700, "+10:45".parse::<TimeOffset>()?.seconds());
708 assert_eq!(86340, "+23:59".parse::<TimeOffset>()?.seconds());
709 Ok(())
710 }
711
712 #[test]
713 fn offset_err() -> Result<(), Box<ParseError>> {
714 assert!("".parse::<TimeOffset>().is_err());
715 assert!("00:00".parse::<TimeOffset>().is_err());
716 assert!("0000".parse::<TimeOffset>().is_err());
717 assert!("_00;00".parse::<TimeOffset>().is_err());
718 assert!(" 00:00".parse::<TimeOffset>().is_err());
719 assert!("+0A:00".parse::<TimeOffset>().is_err());
720 assert!("+00:60".parse::<TimeOffset>().is_err());
721 assert!("+24:00".parse::<TimeOffset>().is_err());
722 Ok(())
723 }
724}