1mod timezone;
25
26pub use self::timezone::*;
27
28use std::cmp::Ordering;
29use std::str::{from_utf8, FromStr};
30use std::time::{Duration, SystemTime};
31use ParseError;
32
33const MAX_SECONDS: u64 = 315569433600;
34const UNIX_EPOCH: Time = Time {
35 sec: 62167132800,
36 nano: 0,
37};
38
39const SECS_PER_MINUTE: u64 = 60;
40const SECS_PER_HOUR: u64 = 60 * SECS_PER_MINUTE;
41const SECS_PER_DAY: u64 = 24 * SECS_PER_HOUR;
42const DAYS_PER_400_YEARS: u32 = 365 * 400 + 97;
43const DAYS_PER_100_YEARS: u32 = 365 * 100 + 24;
44const DAYS_PER_4_YEARS: u32 = 365 * 4 + 1;
45const DAYS_BEFORE: [u32; 13] = [
46 0,
47 31,
48 31 + 28,
49 31 + 28 + 31,
50 31 + 28 + 31 + 30,
51 31 + 28 + 31 + 30 + 31,
52 31 + 28 + 31 + 30 + 31 + 30,
53 31 + 28 + 31 + 30 + 31 + 30 + 31,
54 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
55 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
56 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
57 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
58 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31,
59];
60
61const DATE_TIME_FORMAT_MIN_LENGTH: usize = 10; const DATE_TIME_FORMAT_WITH_TIME: usize = 19; const DATE_TIME_FORMAT_MAX_LENGTH: usize = 35; #[derive(Debug, Eq, PartialEq)]
67pub struct Time {
68 sec: u64,
69 nano: u32,
70}
71
72impl Time {
73 pub const UNIX_EPOCH: Time = UNIX_EPOCH;
75
76 pub fn from_timetuple(
78 year: u32,
79 month: u32,
80 day: u32,
81 hour: u32,
82 minute: u32,
83 second: u32,
84 nano: u32,
85 timezone: TimeZone,
86 ) -> Option<Time> {
87 if !in_range(year, 0, 10000)
88 || !in_range(month, 1, 12)
89 || !in_range(day, 1, 31)
90 || !in_range(hour, 0, 23)
91 || !in_range(minute, 0, 59)
92 || !in_range(second, 0, 59)
93 || !in_range(nano, 0, 1_000_000_000 - 1)
94 {
95 return None;
96 }
97
98 let is_leap = is_leap_year(year);
99
100 if !is_day_validate(is_leap, month, day) {
101 return None;
102 }
103
104 let mut d: u32 = 0;
105
106 let mut y = year;
107
108 let mut n: u32 = y / 400;
109 y -= 400 * n;
110 d += DAYS_PER_400_YEARS * n;
111
112 n = y / 100;
113 y -= n * 100;
114 d += DAYS_PER_100_YEARS * n;
115
116 n = y / 4;
117 y -= n * 4;
118 d += DAYS_PER_4_YEARS * n;
119
120 n = y;
121 d += 365 * n;
122
123 d += DAYS_BEFORE[(month - 1) as usize];
124 if year > 0 && is_leap && month <= 2 {
126 d -= 1;
127 }
128
129 d += day - 1;
130
131 let mut sec: u64 = d as u64 * SECS_PER_DAY
132 + hour as u64 * SECS_PER_HOUR
133 + minute as u64 * SECS_PER_MINUTE
134 + second as u64;
135
136 let offset = timezone.offset();
137 if offset >= 0 {
138 let minus = offset as u64;
139 if minus > sec {
140 return None;
141 }
142
143 sec -= minus;
144 } else {
145 sec += (-offset) as u64;
146 }
147
148 if sec >= MAX_SECONDS {
149 return None;
150 }
151
152 Some(Time {
153 sec: sec,
154 nano: nano,
155 })
156 }
157
158 pub fn to_system_time(&self) -> Option<SystemTime> {
160 if let Some(d) = self.since(&UNIX_EPOCH) {
161 return Some(SystemTime::UNIX_EPOCH + d);
162 }
163
164 None
165 }
166
167 pub fn since(&self, earlier: &Time) -> Option<Duration> {
169 if self < earlier {
170 return None;
171 }
172
173 let mut sec = self.sec - earlier.sec;
174 let mut nano = self.nano;
175 if nano < earlier.nano {
176 sec -= 1;
177 nano += 1_000_000_000;
178 }
179 nano -= earlier.nano;
180
181 Some(Duration::new(sec, nano))
182 }
183}
184
185fn is_leap_year(y: u32) -> bool {
186 return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);
187}
188
189fn in_range(n: u32, min: u32, max: u32) -> bool {
190 return min <= n && n <= max;
191}
192
193fn is_day_validate(is_leap: bool, m: u32, d: u32) -> bool {
194 match m {
195 2 if is_leap => d <= 29,
196 2 => d <= 28,
197 4 | 6 | 9 | 11 => d <= 30,
198 _ => d <= 31,
199 }
200}
201
202impl FromStr for Time {
203 type Err = ParseError;
204
205 fn from_str(s: &str) -> Result<Self, Self::Err> {
206 parse_rfc3339(s)
207 }
208}
209
210impl PartialOrd for Time {
211 fn partial_cmp(&self, other: &Time) -> Option<Ordering> {
212 Some(self.cmp(other))
213 }
214}
215
216impl Ord for Time {
217 fn cmp(&self, other: &Time) -> Ordering {
218 let ord = self.sec.cmp(&other.sec);
219 match ord {
220 Ordering::Equal => self.nano.cmp(&other.nano),
221 _ => ord,
222 }
223 }
224}
225
226pub fn parse_rfc3339(s: &str) -> Result<Time, ParseError> {
230 let bs = s.trim().as_bytes();
231 let size = bs.len();
232 if size == 0 {
233 return Err(ParseError::EmptyInput);
234 }
235
236 if size < DATE_TIME_FORMAT_MIN_LENGTH
237 || (size > DATE_TIME_FORMAT_MIN_LENGTH && size < DATE_TIME_FORMAT_WITH_TIME)
238 {
239 return Err(ParseError::TooShort);
240 }
241
242 if size > DATE_TIME_FORMAT_MAX_LENGTH {
243 return Err(ParseError::TooLong);
244 }
245
246 if !check_pattern(bs) {
247 return Err(ParseError::Malformed);
248 }
249
250 let year = read_u32(&bs[0..4])?;
251 let month = read_u32(&bs[5..7])?;
252 let day = read_u32(&bs[8..10])?;
253
254 let hour: u32;
255 let minute: u32;
256 let second: u32;
257 if size > DATE_TIME_FORMAT_MIN_LENGTH {
258 hour = read_u32(&bs[DATE_TIME_FORMAT_MIN_LENGTH + 1..DATE_TIME_FORMAT_MIN_LENGTH + 3])?;
259 minute = read_u32(&bs[DATE_TIME_FORMAT_MIN_LENGTH + 4..DATE_TIME_FORMAT_MIN_LENGTH + 6])?;
260 second = read_u32(&bs[DATE_TIME_FORMAT_MIN_LENGTH + 7..DATE_TIME_FORMAT_MIN_LENGTH + 9])?;
261 } else {
262 hour = 0;
263 minute = 0;
264 second = 0;
265 }
266
267 let nano: u32;
268 let tzstr: &str;
269 if size > DATE_TIME_FORMAT_WITH_TIME {
270 let tz_start: usize;
271 if bs[DATE_TIME_FORMAT_WITH_TIME] == b'.' {
272 let (v, read) = read_nano(&bs[DATE_TIME_FORMAT_WITH_TIME + 1..]);
273 if read == 0 {
274 return Err(ParseError::MissingValue);
275 }
276 nano = v;
277 tz_start = DATE_TIME_FORMAT_WITH_TIME + 1 + read;
278 } else {
279 nano = 0;
280 tz_start = DATE_TIME_FORMAT_WITH_TIME;
281 }
282
283 tzstr = from_utf8(&bs[tz_start..]).or(Err(ParseError::InvalidTimezone))?;
284 } else {
285 nano = 0;
286 tzstr = "";
287 }
288
289 let tz = tzstr.parse::<TimeZone>()?;
290
291 Time::from_timetuple(year, month, day, hour, minute, second, nano, tz)
292 .ok_or(ParseError::Overflow)
293}
294
295fn check_pattern(bs: &[u8]) -> bool {
296 if bs[4] != b'-' || bs[7] != b'-' {
297 return false;
298 }
299
300 if bs.len() > DATE_TIME_FORMAT_MIN_LENGTH {
301 if (bs[DATE_TIME_FORMAT_MIN_LENGTH] != b'T' && bs[DATE_TIME_FORMAT_MIN_LENGTH] != b' ')
302 || bs[DATE_TIME_FORMAT_MIN_LENGTH + 3] != b':'
303 || bs[DATE_TIME_FORMAT_MIN_LENGTH + 6] != b':'
304 {
305 return false;
306 }
307 }
308
309 if bs.len() > DATE_TIME_FORMAT_WITH_TIME {
310 if bs[DATE_TIME_FORMAT_WITH_TIME] != b'.'
311 && bs[DATE_TIME_FORMAT_WITH_TIME] != b'Z'
312 && bs[DATE_TIME_FORMAT_WITH_TIME] != b'+'
313 && bs[DATE_TIME_FORMAT_WITH_TIME] != b'-'
314 {
315 return false;
316 }
317 }
318
319 true
320}
321
322fn read_u32(bs: &[u8]) -> Result<u32, ParseError> {
323 let mut read: usize = 0;
324 let mut n: u32 = 0;
325
326 while read < bs.len() {
327 let c = bs[read];
328 if c < b'0' || c > b'9' {
329 return Err(ParseError::InvalidValue);
330 }
331
332 n = n * 10;
333 n += (c - b'0') as u32;
334
335 read += 1;
336 }
337
338 Ok(n)
339}
340
341fn read_nano(bs: &[u8]) -> (u32, usize) {
342 let mut read: usize = 0;
343 let mut n: u32 = 0;
344
345 while read < bs.len() && read <= 9 {
346 let c = bs[read];
347 if c < b'0' || c > b'9' {
348 break;
349 }
350
351 n = n * 10;
352 n += (c - b'0') as u32;
353
354 read += 1;
355 }
356
357 if read < 9 {
358 n = n * 10_u32.pow((9 - read) as u32);
359 }
360
361 (n, read)
362}
363
364#[cfg(test)]
365mod tests;