1use crate::model::{OutOfRangeError, ParseDurationError};
2use std::convert::{TryFrom, TryInto};
3use std::fmt::{self, Debug, Display};
4use std::str::FromStr;
5use std::time::{SystemTime, UNIX_EPOCH};
6
7#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
26#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))]
27pub struct Duration {
28 pub(crate) micros: i64,
29}
30
31#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
33#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))]
34pub struct LocalDatetime {
35 pub(crate) micros: i64,
36}
37
38#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
40#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))]
41pub struct LocalDate {
42 pub(crate) days: i32,
43}
44
45#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
51#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))]
52pub struct LocalTime {
53 pub(crate) micros: u64,
54}
55
56#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
58#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))]
59pub struct Datetime {
60 pub(crate) micros: i64,
61}
62
63#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
65#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))]
66pub struct RelativeDuration {
67 pub(crate) micros: i64,
68 pub(crate) days: i32,
69 pub(crate) months: i32,
70}
71
72#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
74#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))]
75pub struct DateDuration {
76 pub(crate) days: i32,
77 pub(crate) months: i32,
78}
79
80const SECS_PER_DAY: u64 = 86_400;
81const MICROS_PER_DAY: u64 = SECS_PER_DAY * 1_000_000;
82
83const DAYS_IN_400_YEARS: u32 = 400 * 365 + 97;
85
86const MIN_YEAR: i32 = 1;
87const MAX_YEAR: i32 = 9999;
88
89const BASE_YEAR: i32 = -4800;
91
92#[allow(dead_code)] const DAYS_IN_2000_YEARS: i32 = 5 * DAYS_IN_400_YEARS as i32;
94
95const DAY_TO_MONTH_365: [u32; 13] = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365];
96const DAY_TO_MONTH_366: [u32; 13] = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366];
97
98const MICROS_PER_MS: i64 = 1_000;
99const MICROS_PER_SECOND: i64 = MICROS_PER_MS * 1_000;
100const MICROS_PER_MINUTE: i64 = MICROS_PER_SECOND * 60;
101const MICROS_PER_HOUR: i64 = MICROS_PER_MINUTE * 60;
102
103impl Duration {
104 pub const MIN: Duration = Duration { micros: i64::MIN };
105 pub const MAX: Duration = Duration { micros: i64::MAX };
106
107 pub fn from_micros(micros: i64) -> Duration {
108 Duration { micros }
109 }
110
111 pub fn to_micros(self) -> i64 {
112 self.micros
113 }
114
115 pub fn is_positive(&self) -> bool {
118 self.micros.is_positive()
119 }
120 pub fn is_negative(&self) -> bool {
123 self.micros.is_negative()
124 }
125 pub fn abs_duration(&self) -> std::time::Duration {
129 if self.micros.is_negative() {
130 std::time::Duration::from_micros(u64::MAX - self.micros as u64 + 1)
131 } else {
132 std::time::Duration::from_micros(self.micros as u64)
133 }
134 }
135
136 fn try_from_pg_simple_format(input: &str) -> Result<Self, ParseDurationError> {
137 let mut split = input.trim_end().splitn(3, ':');
138 let mut value: i64 = 0;
139 let negative;
140 let mut pos: usize = 0;
141
142 {
143 let hour_str = split.next().filter(|s| !s.is_empty()).ok_or_else(|| {
144 ParseDurationError::new("EOF met, expecting `+`, `-` or int")
145 .not_final()
146 .pos(input.len())
147 })?;
148 pos += hour_str.len() - 1;
149 let hour_str = hour_str.trim_start();
150 let hour = hour_str
151 .strip_prefix('-')
152 .unwrap_or(hour_str)
153 .parse::<i32>()
154 .map_err(|e| ParseDurationError::from(e).not_final().pos(pos))?;
155 negative = hour_str.starts_with('-');
156 value += (hour.abs() as i64) * MICROS_PER_HOUR;
157 }
158
159 {
160 pos += 1;
161 let minute_str = split.next().ok_or_else(|| {
162 ParseDurationError::new("EOF met, expecting `:`")
163 .not_final()
164 .pos(pos)
165 })?;
166 if !minute_str.is_empty() {
167 pos += minute_str.len();
168 let minute = minute_str
169 .parse::<u8>()
170 .map_err(|e| ParseDurationError::from(e).pos(pos))
171 .and_then(|m| {
172 if m <= 59 {
173 Ok(m)
174 } else {
175 Err(ParseDurationError::new("minutes value out of range").pos(pos))
176 }
177 })?;
178 value += (minute as i64) * MICROS_PER_MINUTE;
179 }
180 }
181
182 if let Some(remaining) = split.last() {
183 pos += 1;
184 let mut sec_split = remaining.splitn(2, '.');
185
186 {
187 let second_str = sec_split.next().unwrap();
188 pos += second_str.len();
189 let second = second_str
190 .parse::<u8>()
191 .map_err(|e| ParseDurationError::from(e).pos(pos))
192 .and_then(|s| {
193 if s <= 59 {
194 Ok(s)
195 } else {
196 Err(ParseDurationError::new("seconds value out of range").pos(pos))
197 }
198 })?;
199 value += (second as i64) * MICROS_PER_SECOND;
200 }
201
202 if let Some(sub_sec_str) = sec_split.last() {
203 pos += 1;
204 for (i, c) in sub_sec_str.char_indices() {
205 let d = c
206 .to_digit(10)
207 .ok_or_else(|| ParseDurationError::new("not a digit").pos(pos + i + 1))?;
208 if i < 6 {
209 value += (d * 10_u32.pow((5 - i) as u32)) as i64;
210 } else {
211 if d >= 5 {
212 value += 1;
213 }
214 break;
215 }
216 }
217 }
218 }
219
220 if negative {
221 value = -value;
222 }
223 Ok(Self { micros: value })
224 }
225
226 fn try_from_iso_format(input: &str) -> Result<Self, ParseDurationError> {
227 if let Some(input) = input.strip_prefix("PT") {
228 let mut pos = 2;
229 let mut result = 0;
230 let mut parts = input.split_inclusive(|c: char| c.is_alphabetic());
231 let mut current = parts.next();
232
233 if let Some(part) = current {
234 if let Some(hour_str) = part.strip_suffix('H') {
235 let hour = hour_str
236 .parse::<i32>()
237 .map_err(|e| ParseDurationError::from(e).pos(pos))?;
238 result += (hour as i64) * MICROS_PER_HOUR;
239 pos += part.len();
240 current = parts.next();
241 }
242 }
243
244 if let Some(part) = current {
245 if let Some(minute_str) = part.strip_suffix('M') {
246 let minute = minute_str
247 .parse::<i32>()
248 .map_err(|e| ParseDurationError::from(e).pos(pos))?;
249 result += (minute as i64) * MICROS_PER_MINUTE;
250 pos += part.len();
251 current = parts.next();
252 }
253 }
254
255 if let Some(part) = current {
256 if let Some(second_str) = part.strip_suffix('S') {
257 let (second_str, subsec_str) = second_str
258 .split_once('.')
259 .map(|(sec, sub)| (sec, sub.get(..6).or(Some(sub))))
260 .unwrap_or_else(|| (second_str, None));
261
262 let second = second_str
263 .parse::<i32>()
264 .map_err(|e| ParseDurationError::from(e).pos(pos))?;
265 result += (second as i64) * MICROS_PER_SECOND;
266 pos += second_str.len() + 1;
267
268 if let Some(subsec_str) = subsec_str {
269 let subsec = subsec_str
270 .parse::<i32>()
271 .map_err(|e| ParseDurationError::from(e).pos(pos))?;
272 result += (subsec as i64)
273 * 10_i64.pow((6 - subsec_str.len()) as u32)
274 * if second < 0 { -1 } else { 1 };
275 pos += subsec_str.len()
276 }
277 current = parts.next();
278 }
279 }
280
281 if current.is_some() {
282 Err(ParseDurationError::new("expecting EOF").pos(pos))
283 } else {
284 Ok(Self { micros: result })
285 }
286 } else {
287 Err(ParseDurationError::new("not ISO format").not_final())
288 }
289 }
290
291 fn get_pg_format_value(
292 input: &str,
293 start: usize,
294 end: usize,
295 ) -> Result<i64, ParseDurationError> {
296 if let Some(val) = input.get(start..end) {
297 match val.parse::<i32>() {
298 Ok(v) => Ok(v as i64),
299 Err(e) => Err(ParseDurationError::from(e).pos(end.saturating_sub(1))),
300 }
301 } else {
302 Err(ParseDurationError::new("expecting value").pos(end))
303 }
304 }
305
306 fn try_from_pg_format(input: &str) -> Result<Self, ParseDurationError> {
307 enum Expect {
308 Numeric { begin: usize },
309 Alphabetic { begin: usize, numeric: i64 },
310 Whitespace { numeric: Option<i64> },
311 }
312 let mut seen = Vec::new();
313 let mut get_unit = |start: usize, end: usize, default: Option<&str>| {
314 input
315 .get(start..end)
316 .or(default)
317 .and_then(|u| match u.to_lowercase().as_str() {
318 "h" | "hr" | "hrs" | "hour" | "hours" => Some(MICROS_PER_HOUR),
319 "m" | "min" | "mins" | "minute" | "minutes" => Some(MICROS_PER_MINUTE),
320 "ms" | "millisecon" | "millisecons" | "millisecond" | "milliseconds" => {
321 Some(MICROS_PER_MS)
322 }
323 "us" | "microsecond" | "microseconds" => Some(1),
324 "s" | "sec" | "secs" | "second" | "seconds" => Some(MICROS_PER_SECOND),
325 _ => None,
326 })
327 .ok_or_else(|| ParseDurationError::new("unknown unit").pos(start))
328 .and_then(|u| {
329 if seen.contains(&u) {
330 Err(ParseDurationError::new("specified more than once").pos(start))
331 } else {
332 seen.push(u);
333 Ok(u)
334 }
335 })
336 };
337 let mut state = Expect::Whitespace { numeric: None };
338 let mut result = 0;
339 for (pos, c) in input.char_indices() {
340 let is_whitespace = c.is_whitespace();
341 let is_numeric = c.is_numeric() || c == '+' || c == '-';
342 let is_alphabetic = c.is_alphabetic();
343 if !(is_whitespace || is_numeric || is_alphabetic) {
344 return Err(ParseDurationError::new("unexpected character").pos(pos));
345 }
346 match state {
347 Expect::Numeric { begin } if !is_numeric => {
348 let numeric = Self::get_pg_format_value(input, begin, pos)?;
349 if is_alphabetic {
350 state = Expect::Alphabetic {
351 begin: pos,
352 numeric,
353 };
354 } else {
355 state = Expect::Whitespace {
356 numeric: Some(numeric),
357 };
358 }
359 }
360 Expect::Alphabetic { begin, numeric } if !is_alphabetic => {
361 result += numeric * get_unit(begin, pos, None)?;
362 if is_numeric {
363 state = Expect::Numeric { begin: pos };
364 } else {
365 state = Expect::Whitespace { numeric: None };
366 }
367 }
368 Expect::Whitespace { numeric: None } if !is_whitespace => {
369 if is_numeric {
370 state = Expect::Numeric { begin: pos };
371 } else {
372 return Err(
373 ParseDurationError::new("expecting whitespace or numeric").pos(pos)
374 );
375 }
376 }
377 Expect::Whitespace {
378 numeric: Some(numeric),
379 } if !is_whitespace => {
380 if is_alphabetic {
381 state = Expect::Alphabetic {
382 begin: pos,
383 numeric,
384 };
385 } else {
386 return Err(
387 ParseDurationError::new("expecting whitespace or alphabetic").pos(pos),
388 );
389 }
390 }
391 _ => {}
392 }
393 }
394 match state {
395 Expect::Numeric { begin } => {
396 result += Self::get_pg_format_value(input, begin, input.len())? * MICROS_PER_SECOND;
397 }
398 Expect::Alphabetic { begin, numeric } => {
399 result += numeric * get_unit(begin, input.len(), Some("s"))?;
400 }
401 Expect::Whitespace {
402 numeric: Some(numeric),
403 } => {
404 result += numeric * MICROS_PER_SECOND;
405 }
406 _ => {}
407 }
408 Ok(Self { micros: result })
409 }
410}
411
412impl FromStr for Duration {
413 type Err = ParseDurationError;
414
415 fn from_str(input: &str) -> Result<Self, Self::Err> {
416 if let Ok(seconds) = input.trim().parse::<i64>() {
417 seconds
418 .checked_mul(MICROS_PER_SECOND)
419 .map(Self::from_micros)
420 .ok_or_else(|| Self::Err::new("seconds value out of range").pos(input.len() - 1))
421 } else {
422 Self::try_from_pg_simple_format(input)
423 .or_else(|e| {
424 if e.is_final {
425 Err(e)
426 } else {
427 Self::try_from_iso_format(input)
428 }
429 })
430 .or_else(|e| {
431 if e.is_final {
432 Err(e)
433 } else {
434 Self::try_from_pg_format(input)
435 }
436 })
437 }
438 }
439}
440
441impl LocalDatetime {
442 pub const MIN: LocalDatetime = LocalDatetime {
444 micros: -63082281600000000,
445 };
446 pub const MAX: LocalDatetime = LocalDatetime {
448 micros: 252455615999999999,
449 };
450
451 pub(crate) fn from_postgres_micros(micros: i64) -> Result<LocalDatetime, OutOfRangeError> {
452 if !(Self::MIN.micros..=Self::MAX.micros).contains(µs) {
453 return Err(OutOfRangeError);
454 }
455 Ok(LocalDatetime { micros })
456 }
457
458 #[deprecated(
459 since = "0.5.0",
460 note = "use Datetime::try_from_unix_micros(v).into() instead"
461 )]
462 pub fn from_micros(micros: i64) -> LocalDatetime {
463 Self::from_postgres_micros(micros).unwrap_or_else(|_| {
464 panic!(
465 "LocalDatetime::from_micros({}) is outside the valid datetime range",
466 micros
467 )
468 })
469 }
470
471 #[deprecated(since = "0.5.0", note = "use .to_utc().to_unix_micros() instead")]
472 pub fn to_micros(self) -> i64 {
473 self.micros
474 }
475
476 pub fn new(date: LocalDate, time: LocalTime) -> LocalDatetime {
477 let micros = date.to_days() as i64 * MICROS_PER_DAY as i64 + time.to_micros() as i64;
478 LocalDatetime { micros }
479 }
480
481 pub fn date(self) -> LocalDate {
482 LocalDate::from_days(self.micros.wrapping_div_euclid(MICROS_PER_DAY as i64) as i32)
483 }
484
485 pub fn time(self) -> LocalTime {
486 LocalTime::from_micros(self.micros.wrapping_rem_euclid(MICROS_PER_DAY as i64) as u64)
487 }
488
489 pub fn to_utc(self) -> Datetime {
490 Datetime {
491 micros: self.micros,
492 }
493 }
494}
495
496impl From<Datetime> for LocalDatetime {
497 fn from(d: Datetime) -> LocalDatetime {
498 LocalDatetime { micros: d.micros }
499 }
500}
501
502impl Display for LocalDatetime {
503 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
504 write!(f, "{} {}", self.date(), self.time())
505 }
506}
507
508impl Debug for LocalDatetime {
509 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
510 write!(f, "{}T{}", self.date(), self.time())
511 }
512}
513
514impl LocalTime {
515 pub const MIN: LocalTime = LocalTime { micros: 0 };
516 pub const MIDNIGHT: LocalTime = LocalTime { micros: 0 };
517 pub const MAX: LocalTime = LocalTime {
518 micros: MICROS_PER_DAY - 1,
519 };
520
521 pub(crate) fn try_from_micros(micros: u64) -> Result<LocalTime, OutOfRangeError> {
522 if micros < MICROS_PER_DAY {
523 Ok(LocalTime { micros })
524 } else {
525 Err(OutOfRangeError)
526 }
527 }
528
529 pub fn from_micros(micros: u64) -> LocalTime {
530 Self::try_from_micros(micros).expect("LocalTime is out of range")
531 }
532
533 pub fn to_micros(self) -> u64 {
534 self.micros
535 }
536
537 fn to_hmsu(self) -> (u8, u8, u8, u32) {
538 let micros = self.micros;
539
540 let microsecond = (micros % 1_000_000) as u32;
541 let micros = micros / 1_000_000;
542
543 let second = (micros % 60) as u8;
544 let micros = micros / 60;
545
546 let minute = (micros % 60) as u8;
547 let micros = micros / 60;
548
549 let hour = (micros % 24) as u8;
550 let micros = micros / 24;
551 debug_assert_eq!(0, micros);
552
553 (hour, minute, second, microsecond)
554 }
555
556 #[cfg(test)] fn from_hmsu(hour: u8, minute: u8, second: u8, microsecond: u32) -> LocalTime {
558 assert!(microsecond < 1_000_000);
559 assert!(second < 60);
560 assert!(minute < 60);
561 assert!(hour < 24);
562
563 let micros = microsecond as u64
564 + 1_000_000 * (second as u64 + 60 * (minute as u64 + 60 * (hour as u64)));
565 LocalTime::from_micros(micros)
566 }
567}
568
569impl Display for LocalTime {
570 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
571 Debug::fmt(self, f)
572 }
573}
574
575impl Debug for LocalTime {
576 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
577 let (hour, minute, second, microsecond) = self.to_hmsu();
578 write!(f, "{hour:02}:{minute:02}:{second:02}")?;
579 if microsecond != 0 {
581 if microsecond % 1000 == 0 {
582 write!(f, ".{:03}", microsecond / 1000)?;
583 } else {
584 write!(f, ".{microsecond:06}")?;
585 }
586 };
587 Ok(())
588 }
589}
590
591impl LocalDate {
592 pub const MIN: LocalDate = LocalDate { days: -730119 }; pub const MAX: LocalDate = LocalDate { days: 2921939 }; pub const UNIX_EPOCH: LocalDate = LocalDate {
595 days: -(30 * 365 + 7),
596 }; fn try_from_days(days: i32) -> Result<LocalDate, OutOfRangeError> {
599 if !(Self::MIN.days..=Self::MAX.days).contains(&days) {
600 return Err(OutOfRangeError);
601 }
602 Ok(LocalDate { days })
603 }
604
605 pub fn from_days(days: i32) -> LocalDate {
606 Self::try_from_days(days).unwrap_or_else(|_| {
607 panic!(
608 "LocalDate::from_days({}) is outside the valid date range",
609 days
610 )
611 })
612 }
613
614 pub fn to_days(self) -> i32 {
615 self.days
616 }
617
618 pub fn from_ymd(year: i32, month: u8, day: u8) -> LocalDate {
619 Self::try_from_ymd(year, month, day)
620 .unwrap_or_else(|_| panic!("invalid date {:04}-{:02}-{:02}", year, month, day))
621 }
622
623 fn try_from_ymd(year: i32, month: u8, day: u8) -> Result<LocalDate, OutOfRangeError> {
624 if !(1..=31).contains(&day) {
625 return Err(OutOfRangeError);
626 }
627 if !(1..=12).contains(&month) {
628 return Err(OutOfRangeError);
629 }
630 if !(MIN_YEAR..=MAX_YEAR).contains(&year) {
631 return Err(OutOfRangeError);
632 }
633
634 let passed_years = (year - BASE_YEAR - 1) as u32;
635 let days_from_year =
636 365 * passed_years + passed_years / 4 - passed_years / 100 + passed_years / 400 + 366;
637
638 let is_leap_year = (year % 400 == 0) || (year % 4 == 0 && year % 100 != 0);
639 let day_to_month = if is_leap_year {
640 DAY_TO_MONTH_366
641 } else {
642 DAY_TO_MONTH_365
643 };
644
645 let day_in_year = (day - 1) as u32 + day_to_month[month as usize - 1];
646 if day_in_year >= day_to_month[month as usize] {
647 return Err(OutOfRangeError);
648 }
649
650 LocalDate::try_from_days(
651 (days_from_year + day_in_year) as i32
652 - DAYS_IN_400_YEARS as i32 * ((2000 - BASE_YEAR) / 400),
653 )
654 }
655
656 fn to_ymd(self) -> (i32, u8, u8) {
657 const DAYS_IN_100_YEARS: u32 = 100 * 365 + 24;
658 const DAYS_IN_4_YEARS: u32 = 4 * 365 + 1;
659 const DAYS_IN_1_YEAR: u32 = 365;
660 const DAY_TO_MONTH_MARCH: [u32; 12] =
661 [0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337];
662 const MARCH_1: u32 = 31 + 29;
663 const MARCH_1_MINUS_BASE_YEAR_TO_POSTGRES_EPOCH: u32 =
664 (2000 - BASE_YEAR) as u32 / 400 * DAYS_IN_400_YEARS - MARCH_1;
665
666 let days = (self.days as u32).wrapping_add(MARCH_1_MINUS_BASE_YEAR_TO_POSTGRES_EPOCH);
667
668 let years400 = days / DAYS_IN_400_YEARS;
669 let days = days % DAYS_IN_400_YEARS;
670
671 let mut years100 = days / DAYS_IN_100_YEARS;
672 if years100 == 4 {
673 years100 = 3
674 }; let days = days - DAYS_IN_100_YEARS * years100;
676
677 let years4 = days / DAYS_IN_4_YEARS;
678 let days = days % DAYS_IN_4_YEARS;
679
680 let mut years1 = days / DAYS_IN_1_YEAR;
681 if years1 == 4 {
682 years1 = 3
683 }; let days = days - DAYS_IN_1_YEAR * years1;
685
686 let years = years1 + years4 * 4 + years100 * 100 + years400 * 400;
687 let month_entry = DAY_TO_MONTH_MARCH
688 .iter()
689 .filter(|d| days >= **d)
690 .enumerate()
691 .last()
692 .unwrap();
693 let months = years * 12 + 2 + month_entry.0 as u32;
694 let year = (months / 12) as i32 + BASE_YEAR;
695 let month = (months % 12 + 1) as u8;
696 let day = (days - month_entry.1 + 1) as u8;
697
698 (year, month, day)
699 }
700}
701
702impl Display for LocalDate {
703 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
704 Debug::fmt(self, f)
705 }
706}
707
708impl Debug for LocalDate {
709 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
710 let (year, month, day) = self.to_ymd();
711 if year >= 10_000 {
712 write!(f, "+")?;
714 }
715 if year >= 0 {
716 write!(f, "{year:04}-{month:02}-{day:02}")
717 } else {
718 write!(f, "{year:05}-{month:02}-{day:02}")
720 }
721 }
722}
723
724impl Datetime {
725 pub const MIN: Datetime = Datetime {
727 micros: LocalDatetime::MIN.micros,
728 };
729 pub const MAX: Datetime = Datetime {
731 micros: LocalDatetime::MAX.micros,
732 };
733 pub const UNIX_EPOCH: Datetime = Datetime {
734 micros: LocalDate::UNIX_EPOCH.days as i64 * MICROS_PER_DAY as i64,
736 };
737
738 pub fn try_from_unix_micros(micros: i64) -> Result<Datetime, OutOfRangeError> {
740 Self::_from_micros(micros).ok_or(OutOfRangeError)
741 }
742
743 #[deprecated(since = "0.5.0", note = "use try_from_unix_micros instead")]
744 pub fn try_from_micros(micros: i64) -> Result<Datetime, OutOfRangeError> {
745 Self::from_postgres_micros(micros)
746 }
747
748 pub(crate) fn from_postgres_micros(micros: i64) -> Result<Datetime, OutOfRangeError> {
749 if !(Self::MIN.micros..=Self::MAX.micros).contains(µs) {
750 return Err(OutOfRangeError);
751 }
752 Ok(Datetime { micros })
753 }
754
755 fn _from_micros(micros: i64) -> Option<Datetime> {
756 let micros = micros.checked_add(Self::UNIX_EPOCH.micros)?;
757 if !(Self::MIN.micros..=Self::MAX.micros).contains(µs) {
758 return None;
759 }
760 Some(Datetime { micros })
761 }
762
763 #[deprecated(since = "0.5.0", note = "use from_unix_micros instead")]
764 pub fn from_micros(micros: i64) -> Datetime {
765 Self::from_postgres_micros(micros).unwrap_or_else(|_| {
766 panic!(
767 "Datetime::from_micros({}) is outside the valid datetime range",
768 micros
769 )
770 })
771 }
772
773 pub fn from_unix_micros(micros: i64) -> Datetime {
779 if let Some(result) = Self::_from_micros(micros) {
780 return result;
781 }
782 panic!(
783 "Datetime::from_micros({}) is outside the valid datetime range",
784 micros
785 );
786 }
787
788 #[deprecated(since = "0.5.0", note = "use to_unix_micros instead")]
789 pub fn to_micros(self) -> i64 {
790 self.micros
791 }
792
793 pub fn to_unix_micros(self) -> i64 {
795 self.micros - Datetime::UNIX_EPOCH.micros
797 }
798
799 fn postgres_epoch_unix() -> SystemTime {
800 use std::time::Duration;
801 UNIX_EPOCH + Duration::from_micros((-Datetime::UNIX_EPOCH.micros) as u64)
803 }
804}
805
806impl Display for Datetime {
807 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
808 write!(
809 f,
810 "{} UTC",
811 LocalDatetime {
812 micros: self.micros
813 }
814 )
815 }
816}
817
818impl Debug for Datetime {
819 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
820 write!(
821 f,
822 "{:?}Z",
823 LocalDatetime {
824 micros: self.micros
825 }
826 )
827 }
828}
829
830impl TryFrom<Datetime> for SystemTime {
831 type Error = OutOfRangeError;
832
833 fn try_from(value: Datetime) -> Result<Self, Self::Error> {
834 use std::time::Duration;
835
836 if value.micros > 0 {
837 Datetime::postgres_epoch_unix().checked_add(Duration::from_micros(value.micros as u64))
838 } else {
839 Datetime::postgres_epoch_unix()
840 .checked_sub(Duration::from_micros((-value.micros) as u64))
841 }
842 .ok_or(OutOfRangeError)
843 }
844}
845
846impl TryFrom<std::time::Duration> for Duration {
847 type Error = OutOfRangeError;
848
849 fn try_from(value: std::time::Duration) -> Result<Self, Self::Error> {
850 TryFrom::try_from(&value)
851 }
852}
853
854impl TryFrom<&std::time::Duration> for Duration {
855 type Error = OutOfRangeError;
856
857 fn try_from(value: &std::time::Duration) -> Result<Self, Self::Error> {
858 let secs = value.as_secs();
859 let subsec_nanos = value.subsec_nanos();
860 let subsec_micros = nanos_to_micros(subsec_nanos.into());
861 let micros = i64::try_from(secs)
862 .ok()
863 .and_then(|x| x.checked_mul(1_000_000))
864 .and_then(|x| x.checked_add(subsec_micros))
865 .ok_or(OutOfRangeError)?;
866 Ok(Duration { micros })
867 }
868}
869
870impl TryFrom<&Duration> for std::time::Duration {
871 type Error = OutOfRangeError;
872
873 fn try_from(value: &Duration) -> Result<std::time::Duration, Self::Error> {
874 let micros = value.micros.try_into().map_err(|_| OutOfRangeError)?;
875 Ok(std::time::Duration::from_micros(micros))
876 }
877}
878impl TryFrom<Duration> for std::time::Duration {
879 type Error = OutOfRangeError;
880
881 fn try_from(value: Duration) -> Result<std::time::Duration, Self::Error> {
882 (&value).try_into()
883 }
884}
885
886impl TryFrom<SystemTime> for Datetime {
887 type Error = OutOfRangeError;
888
889 fn try_from(value: SystemTime) -> Result<Self, Self::Error> {
890 match value.duration_since(UNIX_EPOCH) {
891 Ok(duration) => {
892 let secs = duration.as_secs();
893 let subsec_nanos = duration.subsec_nanos();
894 let subsec_micros = nanos_to_micros(subsec_nanos.into());
895 let micros = i64::try_from(secs)
896 .ok()
897 .and_then(|x| x.checked_mul(1_000_000))
898 .and_then(|x| x.checked_add(subsec_micros))
899 .and_then(|x| x.checked_add(Datetime::UNIX_EPOCH.micros))
900 .ok_or(OutOfRangeError)?;
901 if micros > Datetime::MAX.micros {
902 return Err(OutOfRangeError);
903 }
904 Ok(Datetime { micros })
905 }
906 Err(e) => {
907 let mut secs = e.duration().as_secs();
908 let mut subsec_nanos = e.duration().subsec_nanos();
909 if subsec_nanos > 0 {
910 secs = secs.checked_add(1).ok_or(OutOfRangeError)?;
911 subsec_nanos = 1_000_000_000 - subsec_nanos;
912 }
913 let subsec_micros = nanos_to_micros(subsec_nanos.into());
914 let micros = i64::try_from(secs)
915 .ok()
916 .and_then(|x| x.checked_mul(1_000_000))
917 .and_then(|x| Datetime::UNIX_EPOCH.micros.checked_sub(x))
918 .and_then(|x| x.checked_add(subsec_micros))
919 .ok_or(OutOfRangeError)?;
920 if micros < Datetime::MIN.micros {
921 return Err(OutOfRangeError);
922 }
923 Ok(Datetime { micros })
924 }
925 }
926 }
927}
928
929impl std::ops::Add<&'_ std::time::Duration> for Datetime {
930 type Output = Datetime;
931 fn add(self, other: &std::time::Duration) -> Datetime {
932 let Ok(duration) = Duration::try_from(other) else {
933 debug_assert!(false, "duration is out of range");
934 return Datetime::MAX;
935 };
936 if let Some(micros) = self.micros.checked_add(duration.micros) {
937 Datetime { micros }
938 } else {
939 debug_assert!(false, "duration is out of range");
940 Datetime::MAX
941 }
942 }
943}
944
945impl std::ops::Add<std::time::Duration> for Datetime {
946 type Output = Datetime;
947 #[allow(clippy::op_ref)]
948 fn add(self, other: std::time::Duration) -> Datetime {
949 self + &other
950 }
951}
952
953impl Display for Duration {
954 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
955 let abs = if self.micros < 0 {
956 write!(f, "-")?;
957 -self.micros
958 } else {
959 self.micros
960 };
961 let (sec, micros) = (abs / 1_000_000, abs % 1_000_000);
962 if micros != 0 {
963 let mut fract = micros;
964 let mut zeros = 0;
965 while fract % 10 == 0 {
966 zeros += 1;
967 fract /= 10;
968 }
969 write!(
970 f,
971 "{hours}:{minutes:02}:{seconds:02}.{fract:0>fsize$}",
972 hours = sec / 3600,
973 minutes = sec / 60 % 60,
974 seconds = sec % 60,
975 fract = fract,
976 fsize = 6 - zeros,
977 )
978 } else {
979 write!(f, "{}:{:02}:{:02}", sec / 3600, sec / 60 % 60, sec % 60)
980 }
981 }
982}
983
984#[cfg(test)]
985mod test {
986 use super::*;
987
988 #[test]
989 fn micros_conv() {
990 let datetime = Datetime::from_unix_micros(1645681383000002);
991 assert_eq!(datetime.micros, 698996583000002);
992 assert_eq!(to_debug(datetime), "2022-02-24T05:43:03.000002Z");
993 }
994
995 #[test]
996 fn big_duration_abs() {
997 use super::Duration as Src;
998 use std::time::Duration as Trg;
999 assert_eq!(Src { micros: -1 }.abs_duration(), Trg::new(0, 1000));
1000 assert_eq!(Src { micros: -1000 }.abs_duration(), Trg::new(0, 1000000));
1001 assert_eq!(Src { micros: -1000000 }.abs_duration(), Trg::new(1, 0));
1002 assert_eq!(
1003 Src { micros: i64::MIN }.abs_duration(),
1004 Trg::new(9223372036854, 775808000)
1005 );
1006 }
1007
1008 #[test]
1009 fn local_date_from_ymd() {
1010 assert_eq!(0, LocalDate::from_ymd(2000, 1, 1).to_days());
1011 assert_eq!(-365, LocalDate::from_ymd(1999, 1, 1).to_days());
1012 assert_eq!(366, LocalDate::from_ymd(2001, 1, 1).to_days());
1013 assert_eq!(-730119, LocalDate::from_ymd(1, 1, 1).to_days());
1014 assert_eq!(2921575, LocalDate::from_ymd(9999, 1, 1).to_days());
1015
1016 assert_eq!(Err(OutOfRangeError), LocalDate::try_from_ymd(2001, 1, 32));
1017 assert_eq!(Err(OutOfRangeError), LocalDate::try_from_ymd(2001, 2, 29));
1018 }
1019
1020 #[test]
1021 fn local_date_from_ymd_leap_year() {
1022 let days_in_month_leap = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
1023 let mut total_days = 0;
1024 let start_of_year = 365 * 4 + 1;
1025 for month in 1..=12 {
1026 let start_of_month = LocalDate::from_ymd(2004, month as u8, 1).to_days();
1027 assert_eq!(total_days, start_of_month - start_of_year);
1028
1029 let days_in_current_month = days_in_month_leap[month - 1];
1030 total_days += days_in_current_month;
1031
1032 let end_of_month =
1033 LocalDate::from_ymd(2004, month as u8, days_in_current_month as u8).to_days();
1034 assert_eq!(total_days - 1, end_of_month - start_of_year);
1035 }
1036 assert_eq!(366, total_days);
1037 }
1038
1039 const DAYS_IN_MONTH_LEAP: [u8; 12] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
1040
1041 #[test]
1042 fn local_date_from_ymd_normal_year() {
1043 let mut total_days = 0;
1044 let start_of_year = 365 + 1;
1045 for month in 1..=12 {
1046 let start_of_month = LocalDate::from_ymd(2001, month as u8, 1).to_days();
1047 assert_eq!(total_days, start_of_month - start_of_year);
1048
1049 let days_in_current_month = DAYS_IN_MONTH_LEAP[month - 1];
1050 total_days += days_in_current_month as i32;
1051
1052 let end_of_month =
1053 LocalDate::from_ymd(2001, month as u8, days_in_current_month).to_days();
1054 assert_eq!(total_days - 1, end_of_month - start_of_year);
1055 }
1056 assert_eq!(365, total_days);
1057 }
1058
1059 pub const CHRONO_MAX_YEAR: i32 = 262_143;
1060
1061 fn extended_test_dates() -> impl Iterator<Item = (i32, u8, u8)> {
1062 const YEARS: [i32; 36] = [
1063 1,
1064 2,
1065 1000,
1066 1969,
1067 1970, 1971,
1069 1999,
1070 2000, 2001,
1072 2002,
1073 2003,
1074 2004,
1075 2008,
1076 2009,
1077 2010,
1078 2100,
1079 2200,
1080 2300,
1081 2400,
1082 9000,
1083 9999,
1084 10_000,
1085 10_001,
1086 11_000,
1087 20_000,
1088 100_000,
1089 200_000,
1090 CHRONO_MAX_YEAR - 1,
1091 CHRONO_MAX_YEAR,
1092 CHRONO_MAX_YEAR + 1,
1093 MAX_YEAR - 1000,
1094 MAX_YEAR - 31,
1095 MAX_YEAR - 30, MAX_YEAR - 29, MAX_YEAR - 1,
1098 MAX_YEAR,
1099 ];
1100
1101 const MONTHS: std::ops::RangeInclusive<u8> = 1u8..=12;
1102 const DAYS: [u8; 6] = [1u8, 13, 28, 29, 30, 31];
1103 let dates = MONTHS.flat_map(|month| DAYS.iter().map(move |day| (month, *day)));
1104
1105 YEARS
1106 .iter()
1107 .flat_map(move |year| dates.clone().map(move |date| (*year, date.0, date.1)))
1108 }
1109
1110 pub fn valid_test_dates() -> impl Iterator<Item = (i32, u8, u8)> {
1111 extended_test_dates().filter(|date| LocalDate::try_from_ymd(date.0, date.1, date.2).is_ok())
1112 }
1113
1114 pub fn test_times() -> impl Iterator<Item = u64> {
1115 const TIMES: [u64; 7] = [
1116 0,
1117 10,
1118 10_020,
1119 12345 * 1_000_000,
1120 12345 * 1_001_000,
1121 12345 * 1_001_001,
1122 MICROS_PER_DAY - 1,
1123 ];
1124 TIMES.iter().copied()
1125 }
1126
1127 #[test]
1128 fn check_test_dates() {
1129 assert!(valid_test_dates().count() > 1000);
1130 }
1131
1132 #[test]
1133 fn local_date_ymd_roundtrip() {
1134 for (year, month, day) in valid_test_dates() {
1135 let date = LocalDate::from_ymd(year, month, day);
1136 assert_eq!((year, month, day), date.to_ymd());
1137 }
1138 }
1139
1140 #[test]
1141 fn local_time_parts_roundtrip() {
1142 for time in test_times() {
1143 let expected_time = LocalTime::from_micros(time);
1144 let (hour, minute, second, microsecond) = expected_time.to_hmsu();
1145 let actual_time = LocalTime::from_hmsu(hour, minute, second, microsecond);
1146 assert_eq!(expected_time, actual_time);
1147 }
1148 }
1149
1150 #[test]
1151 fn format_local_date() {
1152 assert_eq!("2000-01-01", LocalDate::from_days(0).to_string());
1153 assert_eq!("0001-01-01", LocalDate::MIN.to_string());
1154 assert_eq!("9999-12-31", LocalDate::MAX.to_string());
1155 }
1156
1157 #[test]
1158 fn format_local_time() {
1159 assert_eq!("00:00:00", LocalTime::MIDNIGHT.to_string());
1160 assert_eq!("00:00:00.010", LocalTime::from_micros(10_000).to_string());
1161 assert_eq!(
1162 "00:00:00.010020",
1163 LocalTime::from_micros(10_020).to_string()
1164 );
1165 assert_eq!("23:59:59.999999", LocalTime::MAX.to_string());
1166 }
1167
1168 pub fn to_debug<T: Debug>(x: T) -> String {
1169 format!("{x:?}")
1170 }
1171
1172 #[test]
1173 #[allow(deprecated)]
1174 fn format_local_datetime() {
1175 assert_eq!(
1176 "2039-02-13 23:31:30.123456",
1177 LocalDatetime::from_micros(1_234_567_890_123_456).to_string()
1178 );
1179 assert_eq!(
1180 "2039-02-13T23:31:30.123456",
1181 to_debug(LocalDatetime::from_micros(1_234_567_890_123_456))
1182 );
1183
1184 assert_eq!("0001-01-01 00:00:00", LocalDatetime::MIN.to_string());
1185 assert_eq!("0001-01-01T00:00:00", to_debug(LocalDatetime::MIN));
1186
1187 assert_eq!("9999-12-31 23:59:59.999999", LocalDatetime::MAX.to_string());
1188 assert_eq!("9999-12-31T23:59:59.999999", to_debug(LocalDatetime::MAX));
1189 }
1190
1191 #[test]
1192 #[allow(deprecated)]
1193 fn format_datetime() {
1194 assert_eq!(
1195 "2039-02-13 23:31:30.123456 UTC",
1196 Datetime::from_micros(1_234_567_890_123_456).to_string()
1197 );
1198 assert_eq!(
1199 "2039-02-13T23:31:30.123456Z",
1200 to_debug(Datetime::from_micros(1_234_567_890_123_456))
1201 );
1202
1203 assert_eq!("0001-01-01 00:00:00 UTC", Datetime::MIN.to_string());
1204 assert_eq!("0001-01-01T00:00:00Z", to_debug(Datetime::MIN));
1205
1206 assert_eq!("9999-12-31 23:59:59.999999 UTC", Datetime::MAX.to_string());
1207 assert_eq!("9999-12-31T23:59:59.999999Z", to_debug(Datetime::MAX));
1208 }
1209
1210 #[test]
1211 fn format_duration() {
1212 fn dur_str(msec: i64) -> String {
1213 Duration::from_micros(msec).to_string()
1214 }
1215 assert_eq!(dur_str(1_000_000), "0:00:01");
1216 assert_eq!(dur_str(1), "0:00:00.000001");
1217 assert_eq!(dur_str(7_015_000), "0:00:07.015");
1218 assert_eq!(dur_str(10_000_000_015_000), "2777:46:40.015");
1219 assert_eq!(dur_str(12_345_678_000_000), "3429:21:18");
1220 }
1221
1222 #[test]
1223 fn parse_duration_str() {
1224 fn micros(input: &str) -> i64 {
1225 Duration::from_str(input).unwrap().micros
1226 }
1227 assert_eq!(micros(" 100 "), 100_000_000);
1228 assert_eq!(micros("123"), 123_000_000);
1229 assert_eq!(micros("-123"), -123_000_000);
1230 assert_eq!(micros(" 20 mins 1hr "), 4_800_000_000);
1231 assert_eq!(micros(" 20 mins -1hr "), -2_400_000_000);
1232 assert_eq!(micros(" 20us 1h 20 "), 3_620_000_020);
1233 assert_eq!(micros(" -20us 1h 20 "), 3_619_999_980);
1234 assert_eq!(micros(" -20US 1H 20 "), 3_619_999_980);
1235 assert_eq!(
1236 micros("1 hour 20 minutes 30 seconds 40 milliseconds 50 microseconds"),
1237 4_830_040_050
1238 );
1239 assert_eq!(
1240 micros("1 hour 20 minutes +30seconds 40 milliseconds -50microseconds"),
1241 4_830_039_950
1242 );
1243 assert_eq!(
1244 micros("1 houR 20 minutes 30SECOND 40 milliseconds 50 us"),
1245 4_830_040_050
1246 );
1247 assert_eq!(micros(" 20 us 1H 20 minutes "), 4_800_000_020);
1248 assert_eq!(micros("-1h"), -3_600_000_000);
1249 assert_eq!(micros("100h"), 360_000_000_000);
1250 let h12 = 12 * 3_600_000_000_i64;
1251 let m12 = 12 * 60_000_000_i64;
1252 assert_eq!(micros(" 12:12:12.2131 "), h12 + m12 + 12_213_100);
1253 assert_eq!(micros("-12:12:12.21313"), -(h12 + m12 + 12_213_130));
1254 assert_eq!(micros("-12:12:12.213134"), -(h12 + m12 + 12_213_134));
1255 assert_eq!(micros("-12:12:12.2131341"), -(h12 + m12 + 12_213_134));
1256 assert_eq!(micros("-12:12:12.2131341111111"), -(h12 + m12 + 12_213_134));
1257 assert_eq!(micros("-12:12:12.2131315111111"), -(h12 + m12 + 12_213_132));
1258 assert_eq!(micros("-12:12:12.2131316111111"), -(h12 + m12 + 12_213_132));
1259 assert_eq!(micros("-12:12:12.2131314511111"), -(h12 + m12 + 12_213_131));
1260 assert_eq!(micros("-0:12:12.2131"), -(m12 + 12_213_100));
1261 assert_eq!(micros("12:12"), h12 + m12);
1262 assert_eq!(micros("-12:12"), -(h12 + m12));
1263 assert_eq!(micros("-12:1:1"), -(h12 + 61_000_000));
1264 assert_eq!(micros("+12:1:1"), h12 + 61_000_000);
1265 assert_eq!(micros("-12:1:1.1234"), -(h12 + 61_123_400));
1266 assert_eq!(micros("1211:59:59.9999"), h12 * 100 + h12 - 100);
1267 assert_eq!(micros("-12:"), -h12);
1268 assert_eq!(micros("0"), 0);
1269 assert_eq!(micros("00:00:00"), 0);
1270 assert_eq!(micros("00:00:10.9"), 10_900_000);
1271 assert_eq!(micros("00:00:10.09"), 10_090_000);
1272 assert_eq!(micros("00:00:10.009"), 10_009_000);
1273 assert_eq!(micros("00:00:10.0009"), 10_000_900);
1274 assert_eq!(micros("00:00:00.5"), 500_000);
1275 assert_eq!(micros(" +00005"), 5_000_000);
1276 assert_eq!(micros(" -00005"), -5_000_000);
1277 assert_eq!(micros("PT"), 0);
1278 assert_eq!(micros("PT1H1M1S"), 3_661_000_000);
1279 assert_eq!(micros("PT1M1S"), 61_000_000);
1280 assert_eq!(micros("PT1S"), 1_000_000);
1281 assert_eq!(micros("PT1H1S"), 3_601_000_000);
1282 assert_eq!(micros("PT1H1M1.1S"), 3_661_100_000);
1283 assert_eq!(micros("PT1H1M1.01S"), 3_661_010_000);
1284 assert_eq!(micros("PT1H1M1.10S"), 3_661_100_000);
1285 assert_eq!(micros("PT1H1M1.1234567S"), 3_661_123_456);
1286 assert_eq!(micros("PT1H1M1.1234564S"), 3_661_123_456);
1287 assert_eq!(micros("PT-1H1M1.1S"), -3_538_900_000);
1288 assert_eq!(micros("PT+1H-1M1.1S"), 3_541_100_000);
1289 assert_eq!(micros("PT1H+1M-1.1S"), 3_658_900_000);
1290
1291 fn assert_error(input: &str, expected_pos: usize, pat: &str) {
1292 let ParseDurationError {
1293 pos,
1294 message,
1295 is_final: _,
1296 } = Duration::from_str(input).unwrap_err();
1297 assert_eq!(pos, expected_pos);
1298 assert!(
1299 message.contains(pat),
1300 "`{}` not found in `{}`",
1301 pat,
1302 message,
1303 );
1304 }
1305 assert_error("blah", 0, "numeric");
1306 assert_error("!", 0, "unexpected");
1307 assert_error("-", 0, "invalid digit");
1308 assert_error("+", 0, "invalid digit");
1309 assert_error(" 20 us 1H 20 30 minutes ", 14, "alphabetic");
1310 assert_error(" 12:12:121.2131 ", 11, "seconds");
1311 assert_error(" 12:60:21.2131 ", 7, "minutes");
1312 assert_error(" 20us 20 1h ", 12, "alphabetic");
1313 assert_error(" 20us $ 20 1h ", 7, "unexpected");
1314 assert_error(
1315 "1 houR 20 minutes 30SECOND 40 milliseconds 50 uss",
1316 47,
1317 "unit",
1318 );
1319 assert_error("PT1M1H", 4, "EOF");
1320 assert_error("PT1S1M", 4, "EOF");
1321 }
1322
1323 #[test]
1324 fn add_duration_rounding() {
1325 assert_eq!(
1327 Datetime::UNIX_EPOCH + std::time::Duration::new(17, 500),
1328 Datetime::UNIX_EPOCH + std::time::Duration::new(17, 0),
1329 );
1330 assert_eq!(
1332 Datetime::UNIX_EPOCH + std::time::Duration::new(12345, 1500),
1333 Datetime::UNIX_EPOCH + std::time::Duration::new(12345, 2000),
1334 );
1335 }
1336
1337 #[test]
1338 #[allow(deprecated)]
1339 fn to_and_from_unix_micros_roundtrip() {
1340 let zero_micros = 0;
1341 let datetime = Datetime::from_unix_micros(0);
1342 assert_eq!(zero_micros, datetime.to_unix_micros());
1344 assert_eq!(datetime.micros, datetime.to_micros());
1347 assert_eq!(datetime.micros, Datetime::UNIX_EPOCH.micros);
1348 assert_eq!(datetime.micros, -946684800000000);
1349 }
1350}
1351
1352impl RelativeDuration {
1353 pub fn try_from_years(years: i32) -> Result<RelativeDuration, OutOfRangeError> {
1354 Ok(RelativeDuration {
1355 months: years.checked_mul(12).ok_or(OutOfRangeError)?,
1356 days: 0,
1357 micros: 0,
1358 })
1359 }
1360 pub fn from_years(years: i32) -> RelativeDuration {
1361 RelativeDuration::try_from_years(years).unwrap()
1362 }
1363 pub fn try_from_months(months: i32) -> Result<RelativeDuration, OutOfRangeError> {
1364 Ok(RelativeDuration {
1365 months,
1366 days: 0,
1367 micros: 0,
1368 })
1369 }
1370 pub fn from_months(months: i32) -> RelativeDuration {
1371 RelativeDuration::try_from_months(months).unwrap()
1372 }
1373 pub fn try_from_days(days: i32) -> Result<RelativeDuration, OutOfRangeError> {
1374 Ok(RelativeDuration {
1375 months: 0,
1376 days,
1377 micros: 0,
1378 })
1379 }
1380 pub fn from_days(days: i32) -> RelativeDuration {
1381 RelativeDuration::try_from_days(days).unwrap()
1382 }
1383 pub fn try_from_hours(hours: i64) -> Result<RelativeDuration, OutOfRangeError> {
1384 Ok(RelativeDuration {
1385 months: 0,
1386 days: 0,
1387 micros: hours.checked_mul(3_600_000_000).ok_or(OutOfRangeError)?,
1388 })
1389 }
1390 pub fn from_hours(hours: i64) -> RelativeDuration {
1391 RelativeDuration::try_from_hours(hours).unwrap()
1392 }
1393 pub fn try_from_minutes(minutes: i64) -> Result<RelativeDuration, OutOfRangeError> {
1394 Ok(RelativeDuration {
1395 months: 0,
1396 days: 0,
1397 micros: minutes.checked_mul(60_000_000).ok_or(OutOfRangeError)?,
1398 })
1399 }
1400 pub fn from_minutes(minutes: i64) -> RelativeDuration {
1401 RelativeDuration::try_from_minutes(minutes).unwrap()
1402 }
1403 pub fn try_from_secs(secs: i64) -> Result<RelativeDuration, OutOfRangeError> {
1404 Ok(RelativeDuration {
1405 months: 0,
1406 days: 0,
1407 micros: secs.checked_mul(1_000_000).ok_or(OutOfRangeError)?,
1408 })
1409 }
1410 pub fn from_secs(secs: i64) -> RelativeDuration {
1411 RelativeDuration::try_from_secs(secs).unwrap()
1412 }
1413 pub fn try_from_millis(millis: i64) -> Result<RelativeDuration, OutOfRangeError> {
1414 Ok(RelativeDuration {
1415 months: 0,
1416 days: 0,
1417 micros: millis.checked_mul(1_000).ok_or(OutOfRangeError)?,
1418 })
1419 }
1420 pub fn from_millis(millis: i64) -> RelativeDuration {
1421 RelativeDuration::try_from_millis(millis).unwrap()
1422 }
1423 pub fn try_from_micros(micros: i64) -> Result<RelativeDuration, OutOfRangeError> {
1424 Ok(RelativeDuration {
1425 months: 0,
1426 days: 0,
1427 micros,
1428 })
1429 }
1430 pub fn from_micros(micros: i64) -> RelativeDuration {
1431 RelativeDuration::try_from_micros(micros).unwrap()
1432 }
1433 pub fn checked_add(self, other: Self) -> Option<Self> {
1434 Some(RelativeDuration {
1435 months: self.months.checked_add(other.months)?,
1436 days: self.days.checked_add(other.days)?,
1437 micros: self.micros.checked_add(other.micros)?,
1438 })
1439 }
1440 pub fn checked_sub(self, other: Self) -> Option<Self> {
1441 Some(RelativeDuration {
1442 months: self.months.checked_sub(other.months)?,
1443 days: self.days.checked_sub(other.days)?,
1444 micros: self.micros.checked_sub(other.micros)?,
1445 })
1446 }
1447}
1448
1449impl std::ops::Add for RelativeDuration {
1450 type Output = Self;
1451 fn add(self, other: Self) -> Self {
1452 RelativeDuration {
1453 months: self.months + other.months,
1454 days: self.days + other.days,
1455 micros: self.micros + other.micros,
1456 }
1457 }
1458}
1459
1460impl std::ops::Sub for RelativeDuration {
1461 type Output = Self;
1462 fn sub(self, other: Self) -> Self {
1463 RelativeDuration {
1464 months: self.months - other.months,
1465 days: self.days - other.days,
1466 micros: self.micros - other.micros,
1467 }
1468 }
1469}
1470
1471impl Display for RelativeDuration {
1472 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1473 if self.months == 0 && self.days == 0 && self.micros == 0 {
1474 return write!(f, "PT0S");
1475 }
1476 write!(f, "P")?;
1477 if self.months.abs() >= 12 {
1478 write!(f, "{}Y", self.months / 12)?;
1479 }
1480 if (self.months % 12).abs() > 0 {
1481 write!(f, "{}M", self.months % 12)?;
1482 }
1483 if self.days.abs() > 0 {
1484 write!(f, "{}D", self.days)?;
1485 }
1486 if self.micros.abs() > 0 {
1487 write!(f, "T")?;
1488 if self.micros.abs() >= 3_600_000_000 {
1489 write!(f, "{}H", self.micros / 3_600_000_000)?;
1490 }
1491 let minutes = self.micros % 3_600_000_000;
1492 if minutes.abs() >= 60_000_000 {
1493 write!(f, "{}M", minutes / 60_000_000)?;
1494 }
1495 let seconds = minutes % 60_000_000;
1496 if seconds.abs() >= 1_000_000 {
1497 write!(f, "{}", seconds / 1_000_000)?;
1498 }
1499 let micros = seconds % 1_000_000;
1500 if micros.abs() > 0 {
1501 let mut buf = [0u8; 6];
1502 let text = {
1503 use std::io::{Cursor, Write};
1504
1505 let mut cur = Cursor::new(&mut buf[..]);
1506 write!(cur, "{:06}", micros.abs()).unwrap();
1507 let mut len = buf.len();
1508 while buf[len - 1] == b'0' {
1509 len -= 1;
1510 }
1511 std::str::from_utf8(&buf[..len]).unwrap()
1512 };
1513 write!(f, ".{text}")?;
1514 }
1515 if seconds.abs() > 0 {
1516 write!(f, "S")?;
1517 }
1518 }
1519 Ok(())
1520 }
1521}
1522
1523#[test]
1524fn relative_duration_display() {
1525 let dur = RelativeDuration::from_years(2)
1526 + RelativeDuration::from_months(56)
1527 + RelativeDuration::from_days(-16)
1528 + RelativeDuration::from_hours(48)
1529 + RelativeDuration::from_minutes(245)
1530 + RelativeDuration::from_secs(7)
1531 + RelativeDuration::from_millis(600);
1532 assert_eq!(dur.to_string(), "P6Y8M-16DT52H5M7.6S");
1533
1534 let dur = RelativeDuration::from_years(2)
1535 + RelativeDuration::from_months(-56)
1536 + RelativeDuration::from_days(-16)
1537 + RelativeDuration::from_minutes(-245)
1538 + RelativeDuration::from_secs(7)
1539 + RelativeDuration::from_millis(600);
1540 assert_eq!(dur.to_string(), "P-2Y-8M-16DT-4H-4M-52.4S");
1541
1542 let dur = RelativeDuration::from_years(1);
1543 assert_eq!(dur.to_string(), "P1Y");
1544 let dur = RelativeDuration::from_months(1);
1545 assert_eq!(dur.to_string(), "P1M");
1546 let dur = RelativeDuration::from_hours(1);
1547 assert_eq!(dur.to_string(), "PT1H");
1548 let dur = RelativeDuration::from_minutes(1);
1549 assert_eq!(dur.to_string(), "PT1M");
1550 let dur = RelativeDuration::from_secs(1);
1551 assert_eq!(dur.to_string(), "PT1S");
1552}
1553
1554impl DateDuration {
1555 pub fn try_from_years(years: i32) -> Result<DateDuration, OutOfRangeError> {
1556 Ok(DateDuration {
1557 months: years.checked_mul(12).ok_or(OutOfRangeError)?,
1558 days: 0,
1559 })
1560 }
1561 pub fn from_years(years: i32) -> DateDuration {
1562 DateDuration::try_from_years(years).unwrap()
1563 }
1564 pub fn try_from_months(months: i32) -> Result<DateDuration, OutOfRangeError> {
1565 Ok(DateDuration { months, days: 0 })
1566 }
1567 pub fn from_months(months: i32) -> DateDuration {
1568 DateDuration::try_from_months(months).unwrap()
1569 }
1570 pub fn try_from_days(days: i32) -> Result<DateDuration, OutOfRangeError> {
1571 Ok(DateDuration { months: 0, days })
1572 }
1573 pub fn from_days(days: i32) -> DateDuration {
1574 DateDuration::try_from_days(days).unwrap()
1575 }
1576 pub fn checked_add(self, other: Self) -> Option<Self> {
1577 Some(DateDuration {
1578 months: self.months.checked_add(other.months)?,
1579 days: self.days.checked_add(other.days)?,
1580 })
1581 }
1582 pub fn checked_sub(self, other: Self) -> Option<Self> {
1583 Some(DateDuration {
1584 months: self.months.checked_sub(other.months)?,
1585 days: self.days.checked_sub(other.days)?,
1586 })
1587 }
1588}
1589
1590impl std::ops::Add for DateDuration {
1591 type Output = Self;
1592 fn add(self, other: Self) -> Self {
1593 DateDuration {
1594 months: self.months + other.months,
1595 days: self.days + other.days,
1596 }
1597 }
1598}
1599
1600impl std::ops::Sub for DateDuration {
1601 type Output = Self;
1602 fn sub(self, other: Self) -> Self {
1603 DateDuration {
1604 months: self.months - other.months,
1605 days: self.days - other.days,
1606 }
1607 }
1608}
1609
1610impl Display for DateDuration {
1611 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1612 if self.months == 0 && self.days == 0 {
1613 return write!(f, "PT0D"); }
1615 write!(f, "P")?;
1616 if self.months.abs() >= 12 {
1617 write!(f, "{}Y", self.months / 12)?;
1618 }
1619 if (self.months % 12).abs() > 0 {
1620 write!(f, "{}M", self.months % 12)?;
1621 }
1622 if self.days.abs() > 0 {
1623 write!(f, "{}D", self.days)?;
1624 }
1625 Ok(())
1626 }
1627}
1628
1629fn nanos_to_micros(nanos: i64) -> i64 {
1630 let mut micros = nanos / 1000;
1632 let remainder = nanos % 1000;
1633 if remainder == 500 && micros % 2 == 1 || remainder > 500 {
1634 micros += 1;
1635 }
1636 micros
1637}
1638
1639#[cfg(feature = "chrono")]
1640mod chrono_interop {
1641 use super::*;
1642 use chrono::naive::{NaiveDate, NaiveDateTime, NaiveTime};
1643 use chrono::DateTime;
1644
1645 type ChronoDatetime = chrono::DateTime<chrono::Utc>;
1646
1647 impl From<&LocalDatetime> for NaiveDateTime {
1648 fn from(value: &LocalDatetime) -> NaiveDateTime {
1649 let timestamp_seconds = value.micros.wrapping_div_euclid(1_000_000)
1650 - (Datetime::UNIX_EPOCH.micros / 1_000_000);
1651 let timestamp_nanos = (value.micros.wrapping_rem_euclid(1_000_000) * 1000) as u32;
1652 DateTime::from_timestamp(timestamp_seconds, timestamp_nanos)
1653 .expect("NaiveDateTime range is bigger than LocalDatetime")
1654 .naive_utc()
1655 }
1656 }
1657
1658 impl TryFrom<&NaiveDateTime> for LocalDatetime {
1659 type Error = OutOfRangeError;
1660 fn try_from(d: &NaiveDateTime) -> Result<LocalDatetime, Self::Error> {
1661 let secs = d.and_utc().timestamp();
1662 let subsec_nanos = d.and_utc().timestamp_subsec_nanos();
1663 let subsec_micros = nanos_to_micros(subsec_nanos.into());
1664 let micros = secs
1665 .checked_mul(1_000_000)
1666 .and_then(|x| x.checked_add(subsec_micros))
1667 .and_then(|x| x.checked_add(Datetime::UNIX_EPOCH.micros))
1668 .ok_or(OutOfRangeError)?;
1669 if !(LocalDatetime::MIN.micros..=LocalDatetime::MAX.micros).contains(µs) {
1670 return Err(OutOfRangeError);
1671 }
1672 Ok(LocalDatetime { micros })
1673 }
1674 }
1675
1676 impl From<&Datetime> for ChronoDatetime {
1677 fn from(value: &Datetime) -> ChronoDatetime {
1678 use chrono::TimeZone;
1679
1680 let pg_epoch = chrono::Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap();
1681 let duration = chrono::Duration::microseconds(value.micros);
1682 pg_epoch
1683 .checked_add_signed(duration)
1684 .expect("Gel datetime range is smaller than Chrono's")
1685 }
1686 }
1687
1688 impl From<Datetime> for ChronoDatetime {
1689 fn from(value: Datetime) -> ChronoDatetime {
1690 (&value).into()
1691 }
1692 }
1693
1694 impl TryFrom<&ChronoDatetime> for Datetime {
1695 type Error = OutOfRangeError;
1696
1697 fn try_from(value: &ChronoDatetime) -> Result<Datetime, Self::Error> {
1698 let min = ChronoDatetime::from(Datetime::MIN);
1699 let duration = value
1700 .signed_duration_since(min)
1701 .to_std()
1702 .map_err(|_| OutOfRangeError)?;
1703 let secs = duration.as_secs();
1704 let subsec_micros = nanos_to_micros(duration.subsec_nanos().into());
1705 let micros = i64::try_from(secs)
1706 .ok()
1707 .and_then(|x| x.checked_mul(1_000_000))
1708 .and_then(|x| x.checked_add(subsec_micros))
1709 .and_then(|x| x.checked_add(Datetime::MIN.micros))
1710 .ok_or(OutOfRangeError)?;
1711 if micros > Datetime::MAX.micros {
1712 return Err(OutOfRangeError);
1713 }
1714 Ok(Datetime { micros })
1715 }
1716 }
1717
1718 impl TryFrom<&NaiveDate> for LocalDate {
1719 type Error = OutOfRangeError;
1720 fn try_from(d: &NaiveDate) -> Result<LocalDate, Self::Error> {
1721 let days = chrono::Datelike::num_days_from_ce(d);
1722 Ok(LocalDate {
1723 days: days
1724 .checked_sub(DAYS_IN_2000_YEARS - 365)
1725 .ok_or(OutOfRangeError)?,
1726 })
1727 }
1728 }
1729
1730 impl From<&LocalDate> for NaiveDate {
1731 fn from(value: &LocalDate) -> NaiveDate {
1732 value
1733 .days
1734 .checked_add(DAYS_IN_2000_YEARS - 365)
1735 .and_then(NaiveDate::from_num_days_from_ce_opt)
1736 .expect("NaiveDate range is bigger than LocalDate")
1737 }
1738 }
1739
1740 impl From<&LocalTime> for NaiveTime {
1741 fn from(value: &LocalTime) -> NaiveTime {
1742 NaiveTime::from_num_seconds_from_midnight_opt(
1743 (value.micros / 1_000_000) as u32,
1744 ((value.micros % 1_000_000) * 1000) as u32,
1745 )
1746 .expect("localtime and native time have equal range")
1747 }
1748 }
1749
1750 impl From<&NaiveTime> for LocalTime {
1751 fn from(time: &NaiveTime) -> LocalTime {
1752 let sec = chrono::Timelike::num_seconds_from_midnight(time);
1753 let nanos = nanos_to_micros(chrono::Timelike::nanosecond(time) as i64) as u64;
1754 let mut micros = sec as u64 * 1_000_000 + nanos;
1755
1756 if micros >= 86_400_000_000 {
1757 micros -= 86_400_000_000;
1760 }
1761
1762 LocalTime { micros }
1763 }
1764 }
1765
1766 impl From<LocalDatetime> for NaiveDateTime {
1767 fn from(value: LocalDatetime) -> NaiveDateTime {
1768 (&value).into()
1769 }
1770 }
1771
1772 impl From<LocalDate> for NaiveDate {
1773 fn from(value: LocalDate) -> NaiveDate {
1774 (&value).into()
1775 }
1776 }
1777
1778 impl TryFrom<NaiveDate> for LocalDate {
1779 type Error = OutOfRangeError;
1780 fn try_from(d: NaiveDate) -> Result<LocalDate, Self::Error> {
1781 std::convert::TryFrom::try_from(&d)
1782 }
1783 }
1784
1785 impl From<LocalTime> for NaiveTime {
1786 fn from(value: LocalTime) -> NaiveTime {
1787 (&value).into()
1788 }
1789 }
1790
1791 impl TryFrom<NaiveDateTime> for LocalDatetime {
1792 type Error = OutOfRangeError;
1793 fn try_from(d: NaiveDateTime) -> Result<LocalDatetime, Self::Error> {
1794 std::convert::TryFrom::try_from(&d)
1795 }
1796 }
1797
1798 impl TryFrom<ChronoDatetime> for Datetime {
1799 type Error = OutOfRangeError;
1800 fn try_from(d: ChronoDatetime) -> Result<Datetime, Self::Error> {
1801 std::convert::TryFrom::try_from(&d)
1802 }
1803 }
1804
1805 impl From<NaiveTime> for LocalTime {
1806 fn from(time: NaiveTime) -> LocalTime {
1807 From::from(&time)
1808 }
1809 }
1810
1811 #[cfg(test)]
1812 mod test {
1813 use super::*;
1814 use crate::model::time::test::{test_times, to_debug, valid_test_dates, CHRONO_MAX_YEAR};
1815
1816 #[test]
1817 fn chrono_roundtrips() -> Result<(), Box<dyn std::error::Error>> {
1818 let naive = NaiveDateTime::from_str("2019-12-27T01:02:03.123456")?;
1819 assert_eq!(
1820 naive,
1821 Into::<NaiveDateTime>::into(LocalDatetime::try_from(naive)?)
1822 );
1823 let naive = NaiveDate::from_str("2019-12-27")?;
1824 assert_eq!(naive, Into::<NaiveDate>::into(LocalDate::try_from(naive)?));
1825 let naive = NaiveTime::from_str("01:02:03.123456")?;
1826 assert_eq!(naive, Into::<NaiveTime>::into(LocalTime::from(naive)));
1827 Ok(())
1828 }
1829
1830 fn check_display<E: Display, A: Display>(expected_value: E, actual_value: A) {
1831 let expected_display = expected_value.to_string();
1832 let actual_display = actual_value.to_string();
1833 assert_eq!(expected_display, actual_display);
1834 }
1835
1836 fn check_debug<E: Debug, A: Debug>(expected_value: E, actual_value: A) {
1837 let expected_debug = to_debug(expected_value);
1838 let actual_debug = to_debug(actual_value);
1839 assert_eq!(expected_debug, actual_debug);
1840 }
1841
1842 #[test]
1843 fn format_local_time() {
1844 for time in test_times() {
1845 let actual_value = LocalTime::from_micros(time);
1846 let expected_value = NaiveTime::from(actual_value);
1847
1848 check_display(expected_value, actual_value);
1849 check_debug(expected_value, actual_value);
1850 }
1851 }
1852
1853 #[test]
1854 fn format_local_date() {
1855 let dates = valid_test_dates().filter(|d| d.0 <= CHRONO_MAX_YEAR);
1856 for (y, m, d) in dates {
1857 let actual_value = LocalDate::from_ymd(y, m, d);
1858 let expected = NaiveDate::from_ymd_opt(y, m as u32, d as u32).unwrap();
1859
1860 check_display(expected, actual_value);
1861 check_debug(expected, actual_value);
1862 }
1863 }
1864
1865 #[test]
1866 fn format_local_datetime() {
1867 let dates = valid_test_dates().filter(|d| d.0 <= CHRONO_MAX_YEAR);
1868 for date in dates {
1869 for time in test_times() {
1870 let actual_date = LocalDate::from_ymd(date.0, date.1, date.2);
1871 let actual_time = LocalTime::from_micros(time);
1872 let actual_value = LocalDatetime::new(actual_date, actual_time);
1873 let expected_value = NaiveDateTime::from(actual_value);
1874
1875 check_display(expected_value, actual_value);
1876 check_debug(expected_value, actual_value);
1877 }
1878 }
1879 }
1880
1881 #[test]
1882 fn format_datetime() {
1883 let dates = valid_test_dates().filter(|d| d.0 <= CHRONO_MAX_YEAR);
1884 for date in dates {
1885 for time in test_times() {
1886 let actual_date = LocalDate::from_ymd(date.0, date.1, date.2);
1887 let actual_time = LocalTime::from_micros(time);
1888 let local_datetime = LocalDatetime::new(actual_date, actual_time);
1889 let actual_value = local_datetime.to_utc();
1890 let expected_value = ChronoDatetime::from(actual_value);
1891
1892 check_display(expected_value, actual_value);
1893 check_debug(expected_value, actual_value);
1894 }
1895 }
1896 }
1897
1898 #[test]
1899 fn date_duration() -> Result<(), Box<dyn std::error::Error>> {
1900 assert_eq!(DateDuration::from_years(1).to_string(), "P1Y");
1901 assert_eq!(DateDuration::from_months(1).to_string(), "P1M");
1902 assert_eq!(DateDuration::from_days(1).to_string(), "P1D");
1903 assert_eq!(DateDuration::from_months(10).to_string(), "P10M");
1904 assert_eq!(DateDuration::from_months(20).to_string(), "P1Y8M");
1905 assert_eq!(DateDuration::from_days(131).to_string(), "P131D");
1906 assert_eq!(
1907 (DateDuration::from_months(7) + DateDuration::from_days(131)).to_string(),
1908 "P7M131D"
1909 );
1910 Ok(())
1911 }
1912 }
1913}