1use std::io::Read;
4
5#[cfg(feature = "use-serde")]
7use serde::{Deserialize, Serialize};
8use time::{format_description::well_known::Iso8601, OffsetDateTime, PrimitiveDateTime, UtcOffset};
9
10use crate::errors::GpxResult;
11use crate::parser::{string, Context};
12
13#[derive(Debug, Clone, Copy, Eq, Ord, PartialOrd, PartialEq, Hash)]
14#[cfg_attr(feature = "use-serde", derive(Serialize, Deserialize))]
15pub struct Time(OffsetDateTime);
16
17impl Time {
18 pub fn format(&self) -> GpxResult<String> {
20 self.0.format(&Iso8601::DEFAULT).map_err(From::from)
21 }
22}
23
24impl From<OffsetDateTime> for Time {
25 fn from(t: OffsetDateTime) -> Self {
26 Time(t)
27 }
28}
29
30impl From<Time> for OffsetDateTime {
31 fn from(t: Time) -> Self {
32 t.0
33 }
34}
35
36pub fn consume<R: Read>(context: &mut Context<R>) -> GpxResult<Time> {
38 let time_str = string::consume(context, "time", false)?;
39
40 let time = OffsetDateTime::parse(&time_str, &Iso8601::PARSING).or_else(|_| {
42 PrimitiveDateTime::parse(&time_str, &Iso8601::PARSING).map(PrimitiveDateTime::assume_utc)
44 })?;
45
46 Ok(time.to_offset(UtcOffset::UTC).into())
47}
48
49#[cfg(test)]
50mod tests {
51 use crate::GpxVersion;
52
53 use super::consume;
54
55 #[test]
56 fn consume_time() {
57 let result = consume!("<time>1996-12-19T16:39:57-08:00</time>", GpxVersion::Gpx11);
58 assert!(result.is_ok());
59
60 let result = consume!("<time>2001-10-26T21:32:52</time>", GpxVersion::Gpx11);
62 assert!(result.is_ok());
63
64 let result = consume!("<time>2001-10-26T21:32:52+02:00</time>", GpxVersion::Gpx11);
65 assert!(result.is_ok());
66
67 let result = consume!("<time>2001-10-26T19:32:52Z</time>", GpxVersion::Gpx11);
68 assert!(result.is_ok());
69
70 let result = consume!("<time>2001-10-26T19:32:52+00:00</time>", GpxVersion::Gpx11);
71 assert!(result.is_ok());
72
73 let result = consume!("<time>2001-10-26T21:32:52.12679</time>", GpxVersion::Gpx11);
74 assert!(result.is_ok());
75
76 let result = consume!("<time>2001-10-26T21:32</time>", GpxVersion::Gpx11);
77 assert!(result.is_ok());
78
79 let result = consume!("<time>2001-10-26</time>", GpxVersion::Gpx11);
81 assert!(result.is_err());
82
83 let result = consume!("<time>2001-10-26T25:32:52+02:00</time>", GpxVersion::Gpx11);
84 assert!(result.is_err());
85
86 let result = consume!("<time>01-10-26T21:32</time>", GpxVersion::Gpx11);
87 assert!(result.is_err());
88
89 let result = consume!("<time>-2001-10-26T21:32:52</time>", GpxVersion::Gpx11);
92 assert!(result.is_err());
93
94 let result = consume!("<time>2021-10-10T09:55:20.952</time>", GpxVersion::Gpx11);
96 assert!(result.is_ok());
97 }
98}