1use crate::decimal::Sign;
2use crate::ion_data::{IonDataHash, IonDataOrd, IonEq};
3use crate::result::{IonError, IonFailure, IonResult};
4use crate::types::{CountDecimalDigits, Decimal};
5use chrono::{
6 DateTime, Datelike, FixedOffset, LocalResult, NaiveDate, NaiveDateTime, TimeZone, Timelike,
7};
8use std::cmp::Ordering;
9use std::fmt::{Debug, Display, Formatter};
10use std::hash::{Hash, Hasher};
11use std::marker::PhantomData;
12
13#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Default, Hash)]
15pub enum TimestampPrecision {
16 #[default]
18 Year,
19 Month,
21 Day,
23 HourAndMinute,
25 Second,
27}
28
29#[derive(Debug, Clone, PartialEq, Eq)]
39pub enum Mantissa {
40 Digits(u32),
45 Arbitrary(Decimal),
50}
51
52impl Mantissa {
53 fn decimals_equal(d1: &Decimal, d2: &Decimal) -> bool {
54 (d1.is_empty() && d2.is_empty())
56 || d1.eq(d2)
58 || (d1.coefficient().is_zero() && d2.coefficient().is_zero() && d1.exponent == d2.exponent)
60 }
61
62 fn decimals_compare(d1: &Decimal, d2: &Decimal) -> Ordering {
63 if d1.is_empty() && d2.is_empty() {
65 Ordering::Equal
66 } else if d1.coefficient().is_zero() && d2.coefficient().is_zero() {
67 d1.exponent.cmp(&d2.exponent)
69 } else {
70 d1.cmp(d2)
72 }
73 }
74}
75
76trait EmptyMantissa {
77 fn is_empty(&self) -> bool;
81}
82
83impl EmptyMantissa for Decimal {
84 fn is_empty(&self) -> bool {
85 self.coefficient().is_zero() && self.exponent == 0
86 }
87}
88
89impl EmptyMantissa for Mantissa {
90 fn is_empty(&self) -> bool {
91 match self {
92 Mantissa::Digits(0) => true,
94 Mantissa::Arbitrary(d) => d.is_empty(),
96 _ => false,
97 }
98 }
99}
100
101fn first_n_digits_of(num_digits: u32, value: u32) -> u32 {
104 let total_digits = value.count_decimal_digits();
105 if total_digits <= num_digits {
106 return value;
107 }
108 value / 10u32.pow(total_digits - num_digits)
110}
111
112fn offset_east(seconds_east: i32) -> FixedOffset {
115 FixedOffset::east_opt(seconds_east)
116 .expect("seconds_east was outside the supported range")
118}
119
120fn datetime_at_offset(utc_datetime: &NaiveDateTime, seconds_east: i32) -> DateTime<FixedOffset> {
123 offset_east(seconds_east).from_utc_datetime(utc_datetime)
124}
125
126#[derive(Debug, Clone)]
130pub struct Timestamp {
131 pub(crate) date_time: NaiveDateTime,
132 pub(crate) offset: Option<FixedOffset>,
133 pub(crate) precision: TimestampPrecision,
134 pub(crate) fractional_seconds: Option<Mantissa>,
135}
136
137impl Timestamp {
138 #[cfg(feature = "experimental-chrono")]
142 pub fn from_datetime<D>(datetime: D, precision: TimestampPrecision) -> Timestamp
143 where
144 D: Datelike + Timelike + Into<Timestamp>,
145 {
146 let mut timestamp: Timestamp = datetime.into();
147 if precision < TimestampPrecision::Second {
148 timestamp.fractional_seconds = None;
149 }
150 timestamp.precision = precision;
151 timestamp
152 }
153
154 pub(crate) fn from_naive_datetime(date_time: NaiveDateTime) -> Self {
155 Timestamp {
156 date_time,
157 offset: None,
158 precision: TimestampPrecision::Second,
159 fractional_seconds: Some(Mantissa::Digits(9)),
160 }
161 }
162
163 pub(crate) fn from_fixed_offset_datetime(
164 fixed_offset_date_time: DateTime<FixedOffset>,
165 ) -> Self {
166 let date_time = fixed_offset_date_time.naive_utc();
167 let offset = Some(*fixed_offset_date_time.offset());
168 Timestamp {
169 date_time,
170 offset,
171 precision: TimestampPrecision::Second,
172 fractional_seconds: Some(Mantissa::Digits(9)),
173 }
174 }
175
176 pub(crate) fn try_to_naive_datetime(&self) -> IonResult<NaiveDateTime> {
177 if self.offset.is_some() {
178 return IonResult::illegal_operation(
179 "cannot convert a Timestamp with a known offset into a NaiveDateTime",
180 );
181 }
182 Ok(downconvert_to_naive_datetime_with_nanoseconds(self))
183 }
184
185 pub(crate) fn try_to_datetime_fixed_offset(&self) -> IonResult<DateTime<FixedOffset>> {
186 if self.offset.is_none() {
187 return IonResult::illegal_operation(
188 "cannot convert a Timestamp with an unknown offset into a DateTime<FixedOffset>",
189 );
190 }
191 let date_time = downconvert_to_naive_datetime_with_nanoseconds(self);
192 Ok(self.offset.unwrap().from_utc_datetime(&date_time))
193 }
194
195 pub fn fractional_seconds_scale(&self) -> Option<i64> {
200 use Mantissa::*;
202 match self.fractional_seconds.as_ref() {
203 Some(Digits(number_of_digits)) => Some(*number_of_digits as i64),
207 Some(Arbitrary(decimal)) => Some(decimal.scale()),
209 None => None,
211 }
212 }
213
214 pub(crate) fn fractional_seconds_as_decimal(&self) -> Option<Decimal> {
220 use Mantissa::*;
222 match self.fractional_seconds.as_ref() {
223 Some(Digits(number_of_digits)) => {
226 const MAX_NANOSECOND_DIGITS: u32 = 9; let nanoseconds = self.date_time.nanosecond();
228 let leading_zeros = MAX_NANOSECOND_DIGITS - nanoseconds.count_decimal_digits();
229 let coefficient = if leading_zeros >= *number_of_digits {
230 0
231 } else {
232 first_n_digits_of(*number_of_digits - leading_zeros, nanoseconds)
233 };
234 let exponent = -i64::from(*number_of_digits);
235 Some(Decimal::new(coefficient, exponent))
236 }
237 Some(Arbitrary(decimal)) => Some(decimal.clone()),
239 None => None,
241 }
242 }
243
244 fn fractional_seconds_as_nanoseconds(&self) -> Option<u32> {
251 use Mantissa::*;
254 match self.fractional_seconds.as_ref() {
255 Some(Digits(_number_of_digits)) => Some(self.date_time.nanosecond()),
259 Some(Arbitrary(decimal)) => {
262 let nano_exp = decimal.exponent + 9;
264 let mag = decimal.coefficient().magnitude();
265 let nanos: u128 = if nano_exp >= 0 {
266 let factor = 10u128.saturating_pow(nano_exp as u32);
267 mag.as_u128().unwrap_or(0).saturating_mul(factor)
268 } else {
269 let divisor = 10u128.saturating_pow(nano_exp.unsigned_abs() as u32);
270 let divided = mag.data / divisor;
271 u128::try_from(divided).unwrap_or(0)
273 };
274 Some(nanos as u32)
278 }
279 None => None,
281 }
282 }
283
284 fn fractional_seconds_compare(&self, other: &Timestamp) -> Ordering {
287 use Mantissa::*;
288 match (
289 self.fractional_seconds.as_ref(),
290 other.fractional_seconds.as_ref(),
291 ) {
292 (None, None) => Ordering::Equal,
293 (Some(_m), None) => {
294 let d1 = self.fractional_seconds_as_decimal().unwrap();
295 let d2 = Decimal::new(0u64, 0);
296 d1.cmp(&d2)
297 }
298 (None, Some(_m)) => {
299 let d1 = Decimal::new(0u64, 0);
300 let d2 = other.fractional_seconds_as_decimal().unwrap();
301 d1.cmp(&d2)
302 }
303 (Some(Digits(_d1)), Some(Digits(_d2))) => {
304 let d1 = self.date_time.nanosecond();
305 let d2 = other.date_time.nanosecond();
306 d1.cmp(&d2)
307 }
308 (Some(Arbitrary(d1)), Some(Arbitrary(d2))) => Mantissa::decimals_compare(d1, d2),
309 (Some(Digits(_d1)), Some(Arbitrary(d2))) => {
310 let d1 = &self.fractional_seconds_as_decimal().unwrap();
311 Mantissa::decimals_compare(d1, d2)
312 }
313 (Some(Arbitrary(d1)), Some(Digits(_d2))) => {
314 let d2 = &other.fractional_seconds_as_decimal().unwrap();
315 Mantissa::decimals_compare(d1, d2)
316 }
317 }
318 }
319
320 fn fractional_seconds_equal(&self, other: &Timestamp) -> bool {
323 use Mantissa::*;
324
325 let m1 = match &self.fractional_seconds {
328 None => &Mantissa::Digits(0),
329 Some(m) => m,
330 };
331
332 let m2 = match &other.fractional_seconds {
333 None => &Mantissa::Digits(0),
334 Some(m) => m,
335 };
336
337 match (m1, m2) {
339 (Digits(d1), Digits(d2)) => {
340 if d1 != d2 {
341 return false;
343 }
344 let d1 = first_n_digits_of(*d1, self.date_time.nanosecond());
345 let d2 = first_n_digits_of(*d2, other.date_time.nanosecond());
346 d1 == d2
347 }
348 (Arbitrary(d1), Arbitrary(d2)) => Mantissa::decimals_equal(d1, d2),
349 (Digits(_d1), Arbitrary(d2)) => {
350 let d1 = match self.fractional_seconds_as_decimal() {
351 Some(decimal_value) => decimal_value,
352 None => Decimal::new(0, 0),
353 };
354 Mantissa::decimals_equal(&d1, d2)
355 }
356 (Arbitrary(d1), Digits(_d2)) => {
357 let d2 = match other.fractional_seconds_as_decimal() {
358 Some(decimal_value) => decimal_value,
359 None => Decimal::new(0, 0),
360 };
361 Mantissa::decimals_equal(d1, &d2)
362 }
363 }
364 }
365
366 fn format_fractional_seconds<W: std::fmt::Write>(&self, output: &mut W) -> IonResult<()> {
368 if self.fractional_seconds.is_none() {
369 return Ok(());
371 }
372 let mantissa = self.fractional_seconds.as_ref().unwrap();
373 if mantissa.is_empty() {
374 return Ok(());
376 }
377 match mantissa {
378 Mantissa::Digits(num_digits) => {
379 let scaled = self.date_time.nanosecond() / 10u32.pow(9 - *num_digits);
384 let actual_num_digits = scaled.count_decimal_digits();
389 let num_leading_zeros = *num_digits - actual_num_digits;
390 write!(output, ".")?;
391 for _ in 0..num_leading_zeros {
392 write!(output, "0")?;
393 }
394 write!(output, "{scaled}")?;
395 Ok(())
396 }
397 Mantissa::Arbitrary(decimal) => {
398 let exponent = decimal.exponent;
399 let coefficient = &decimal.coefficient();
400 if exponent >= 0 {
401 return IonResult::encoding_error(
405 "found fractional seconds decimal that was >= 1.",
406 );
407 }
408
409 let num_digits = decimal.coefficient().number_of_decimal_digits();
410 let abs_exponent = decimal.exponent.unsigned_abs();
411 let num_leading_zeros = abs_exponent - num_digits as u64;
414 write!(output, ".")?;
415 for _ in 0..num_leading_zeros {
416 write!(output, "0")?;
417 }
418 if coefficient.is_negative_zero() {
419 write!(output, "0")?;
420 } else if coefficient.sign() == Sign::Negative {
421 return IonResult::encoding_error(
422 "fractional seconds cannot have a negative coefficient (other than -0)",
423 );
424 } else {
425 write!(output, "{}", decimal.coefficient())?;
426 }
427 Ok(())
428 }
429 }
430 }
431
432 pub(crate) fn format<W: std::fmt::Write>(&self, output: &mut W) -> IonResult<()> {
433 let (offset_minutes, datetime) = if let Some(minutes) = self.offset {
434 let datetime: DateTime<FixedOffset> = self.try_to_datetime_fixed_offset()?;
436 (Some(minutes.local_minus_utc() / 60), datetime)
438 } else {
439 let datetime: NaiveDateTime = self.try_to_naive_datetime()?;
443 let datetime: DateTime<FixedOffset> = datetime_at_offset(&datetime, 0);
444 (None, datetime)
445 };
446
447 write!(output, "{:0>4}", datetime.year())?;
448 if self.precision == TimestampPrecision::Year {
450 write!(output, "T")?;
451 return Ok(());
452 }
453
454 write!(output, "-{:0>2}", datetime.month())?;
455 if self.precision == TimestampPrecision::Month {
457 write!(output, "T")?;
458 return Ok(());
459 }
460
461 write!(output, "-{:0>2}", datetime.day())?;
462 if self.precision == TimestampPrecision::Day {
464 write!(output, "T")?;
465 return Ok(());
466 }
467
468 write!(
469 output,
470 "T{:0>2}:{:0>2}",
471 datetime.hour(),
473 datetime.minute()
474 )?;
475 if self.precision == TimestampPrecision::HourAndMinute {
476 self.format_offset(offset_minutes, output)?;
477 return Ok(());
478 }
479
480 write!(output, ":{:0>2}", datetime.second())?;
481 self.format_fractional_seconds(output)?;
483 self.format_offset(offset_minutes, output)?;
484 Ok(())
485 }
486
487 fn format_offset<W: std::fmt::Write>(
488 &self,
489 offset_minutes: Option<i32>,
490 output: &mut W,
491 ) -> IonResult<()> {
492 let (sign, hours, minutes) = match offset_minutes {
493 None => ("-", 0, 0),
494 Some(offset_minutes) => {
495 const MINUTES_PER_HOUR: i32 = 60;
496 let sign = if offset_minutes >= 0 { "+" } else { "-" };
498 let offset_minutes = offset_minutes.abs();
499 let hours = offset_minutes / MINUTES_PER_HOUR;
500 let minutes = offset_minutes % MINUTES_PER_HOUR;
501
502 (sign, hours, minutes)
503 }
504 };
505 write!(output, "{sign}{hours:0>2}:{minutes:0>2}")?;
506 Ok(())
507 }
508
509 pub fn with_year(year: u32) -> TimestampBuilder<HasYear> {
511 TimestampBuilder::with_year(year)
512 }
513
514 pub fn with_ymd(year: u32, month: u32, day: u32) -> TimestampBuilder<HasDay> {
517 TimestampBuilder::with_year(year)
518 .with_month(month)
519 .with_day(day)
520 }
521
522 pub fn offset(&self) -> Option<i32> {
525 self.offset.map(|offset| offset.local_minus_utc() / 60)
526 }
527
528 pub fn precision(&self) -> TimestampPrecision {
530 self.precision
531 }
532
533 pub fn year(&self) -> u32 {
535 if let Some(offset) = self.offset {
537 let local_date_time =
540 DateTime::<FixedOffset>::from_naive_utc_and_offset(self.date_time, offset);
541 return local_date_time.year() as u32;
542 }
543 self.date_time.year() as u32
544 }
545
546 pub fn month(&self) -> u32 {
550 if let Some(offset) = self.offset {
552 let local_date_time =
555 DateTime::<FixedOffset>::from_naive_utc_and_offset(self.date_time, offset);
556 return local_date_time.month();
557 }
558 self.date_time.month()
559 }
560
561 pub fn day(&self) -> u32 {
565 if let Some(offset) = self.offset {
567 let local_date_time =
570 DateTime::<FixedOffset>::from_naive_utc_and_offset(self.date_time, offset);
571 return local_date_time.day();
572 }
573 self.date_time.day()
574 }
575
576 pub fn hour(&self) -> u32 {
579 if let Some(offset) = self.offset {
581 let local_date_time =
584 DateTime::<FixedOffset>::from_naive_utc_and_offset(self.date_time, offset);
585 return local_date_time.hour();
586 }
587 self.date_time.hour()
588 }
589
590 pub fn minute(&self) -> u32 {
593 if let Some(offset) = self.offset {
595 let local_date_time =
598 DateTime::<FixedOffset>::from_naive_utc_and_offset(self.date_time, offset);
599 return local_date_time.minute();
600 }
601 self.date_time.minute()
602 }
603
604 pub fn second(&self) -> u32 {
607 self.date_time.second()
608 }
609
610 pub fn to_utc(&self) -> Timestamp {
612 Self::from_naive_datetime(self.date_time)
613 }
614
615 pub fn nanoseconds(&self) -> u32 {
621 self.fractional_seconds_as_nanoseconds().unwrap_or_default()
622 }
623
624 pub fn microseconds(&self) -> u32 {
630 self.fractional_seconds_as_nanoseconds()
631 .map(|s| s / 1_000)
632 .unwrap_or_default()
633 }
634
635 pub fn milliseconds(&self) -> u32 {
641 self.fractional_seconds_as_nanoseconds()
642 .map(|s| s / 1_000_000)
643 .unwrap_or_default()
644 }
645}
646
647impl Display for Timestamp {
649 fn fmt(&self, output: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
650 self.format(output).map_err(|_| std::fmt::Error)?;
651 Ok(())
652 }
653}
654
655impl PartialOrd for Timestamp {
656 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
657 Some(self.cmp(other))
658 }
659}
660
661impl Ord for Timestamp {
662 fn cmp(&self, other: &Self) -> Ordering {
663 let self_datetime = self.date_time.with_nanosecond(0).unwrap();
664 let other_datetime = other.date_time.with_nanosecond(0).unwrap();
665
666 let self_datetime = self
667 .offset
668 .map(|offset| offset.from_utc_datetime(&self_datetime))
669 .unwrap_or_else(|| datetime_at_offset(&self_datetime, 0));
670 let other_datetime = other
671 .offset
672 .map(|offset| offset.from_utc_datetime(&other_datetime))
673 .unwrap_or_else(|| datetime_at_offset(&other_datetime, 0));
674
675 let date_time_comparison = self_datetime.cmp(&other_datetime);
676
677 match date_time_comparison {
678 Ordering::Equal => self.fractional_seconds_compare(other),
681 _ => date_time_comparison,
684 }
685 }
686}
687
688impl PartialEq for Timestamp {
695 fn eq(&self, other: &Self) -> bool {
696 if !self.fractional_seconds_equal(other) {
701 return false;
702 }
703
704 let self_datetime = self.date_time.with_nanosecond(0).unwrap();
713 let other_datetime = other.date_time.with_nanosecond(0).unwrap();
714
715 let self_datetime = self
717 .offset
718 .map(|offset| offset.from_utc_datetime(&self_datetime))
719 .unwrap_or_else(|| datetime_at_offset(&self_datetime, 0));
720 let other_datetime = other
721 .offset
722 .map(|offset| offset.from_utc_datetime(&other_datetime))
723 .unwrap_or_else(|| datetime_at_offset(&other_datetime, 0));
724
725 self_datetime == other_datetime
727 }
728}
729
730impl Eq for Timestamp {}
731
732impl IonEq for Timestamp {
733 fn ion_eq(&self, other: &Self) -> bool {
734 if self.precision != other.precision {
735 return false;
736 }
737 if self.offset != other.offset {
739 return false;
740 }
741 let self_dt = self.date_time;
742 let other_dt = other.date_time;
743 if self_dt.year() != other_dt.year() {
744 return false;
745 }
746 if self.precision >= TimestampPrecision::Month && self_dt.month() != other_dt.month() {
747 return false;
748 }
749 if self.precision >= TimestampPrecision::Day && self_dt.day() != other_dt.day() {
750 return false;
751 }
752 if self.precision >= TimestampPrecision::HourAndMinute
753 && (self_dt.hour() != other_dt.hour() || self_dt.minute() != other_dt.minute())
754 {
755 return false;
756 }
757 if self.precision <= TimestampPrecision::HourAndMinute {
758 return true;
759 }
760
761 if self_dt.second() != other_dt.second() || !self.fractional_seconds_equal(other) {
762 return false;
763 }
764
765 true
766 }
767}
768
769impl IonDataOrd for Timestamp {
770 fn ion_cmp(&self, other: &Self) -> Ordering {
771 let ord = self.cmp(other);
773 if ord != Ordering::Equal {
774 return ord;
775 };
776
777 let ord = self.precision.cmp(&other.precision);
779 if ord != Ordering::Equal {
780 return ord;
781 };
782 match [
783 self.fractional_seconds_scale(),
784 other.fractional_seconds_scale(),
785 ] {
786 [None, Some(b)] if b > 0 => return Ordering::Less,
787 [Some(a), None] if a > 0 => return Ordering::Greater,
788 [Some(a), Some(b)] => {
789 let ord = a.cmp(&b);
790 if ord != Ordering::Equal {
791 return ord;
792 }
793 }
794 _ => {}
795 }
796
797 match [self.offset, other.offset] {
799 [None, Some(_)] => Ordering::Less,
800 [None, None] => Ordering::Equal,
801 [Some(_), None] => Ordering::Greater,
802 [Some(o1), Some(o2)] => o1.local_minus_utc().cmp(&o2.local_minus_utc()),
803 }
804 }
805}
806
807impl IonDataHash for Timestamp {
808 fn ion_data_hash<H: Hasher>(&self, state: &mut H) {
809 self.precision.hash(state);
810 let self_dt = self.date_time;
811 self_dt.year().hash(state);
812 if self.precision >= TimestampPrecision::Month {
813 self_dt.month().hash(state)
814 }
815 if self.precision >= TimestampPrecision::Day {
816 self_dt.day().hash(state)
817 }
818 if self.precision >= TimestampPrecision::HourAndMinute {
819 self_dt.hour().hash(state);
820 self_dt.minute().hash(state);
821 }
822 if self.precision == TimestampPrecision::Second {
823 self_dt.second().hash(state);
824
825 let fractional_seconds_scale = self.fractional_seconds_scale();
826 match fractional_seconds_scale {
827 None | Some(0) => {}
828 Some(1..=9) => {
829 fractional_seconds_scale.unwrap().hash(state);
830 self.fractional_seconds_as_nanoseconds()
831 .unwrap()
832 .hash(state);
833 }
834 Some(_) => {
835 self.fractional_seconds_as_decimal()
836 .unwrap()
837 .ion_data_hash(state);
838 }
839 }
840 }
841 self.offset.hash(state);
842 }
843}
844
845#[derive(Debug, Clone)]
851pub struct TimestampBuilder<T> {
852 _state: PhantomData<T>,
853 fields_are_utc: bool,
854 precision: TimestampPrecision,
855 offset: Option<i32>,
856 year: u32,
858 month: u32,
859 day: u32,
860 hour: u32,
861 minute: u32,
862 second: u32,
863 fractional_seconds: Option<Mantissa>,
864 nanoseconds: Option<u32>,
865}
866
867impl<T> TimestampBuilder<T> {
868 fn change_state<U>(self) -> TimestampBuilder<U> {
869 TimestampBuilder {
872 _state: PhantomData,
873 fields_are_utc: self.fields_are_utc,
874 precision: self.precision,
875 offset: self.offset,
876 year: self.year,
877 month: self.month,
878 day: self.day,
879 hour: self.hour,
880 minute: self.minute,
881 second: self.second,
882 fractional_seconds: self.fractional_seconds,
883 nanoseconds: self.nanoseconds,
884 }
885 }
886
887 fn configure_datetime<D>(&mut self, mut datetime: D) -> IonResult<D>
891 where
892 D: Datelike + Timelike + Debug,
893 {
894 if self.year == 0 || self.year > 9999 {
895 return IonResult::illegal_operation(format!(
896 "Timestamp year '{}' out of range (1-9999)",
897 self.year
898 ));
899 }
900 datetime = datetime.with_year(self.year as i32).ok_or_else(|| {
901 IonError::illegal_operation(format!("specified year ('{}') is invalid", self.year))
902 })?;
903 if self.precision == TimestampPrecision::Year {
904 return Ok(datetime);
905 }
906
907 let month = self.month;
909 datetime = datetime.with_month(month).ok_or_else(|| {
910 IonError::illegal_operation(format!("specified month ('{month}') is invalid"))
911 })?;
912 if self.precision == TimestampPrecision::Month {
913 return Ok(datetime);
914 }
915
916 let day = self.day;
918 datetime = datetime.with_day(day).ok_or_else(|| {
919 IonError::illegal_operation(format!("specified day ('{day}') is invalid"))
920 })?;
921 if self.precision == TimestampPrecision::Day {
922 return Ok(datetime);
923 }
924
925 let hour = self.hour;
927 datetime = datetime.with_hour(hour).ok_or_else(|| {
928 IonError::illegal_operation(format!("specified hour ('{hour}') is invalid"))
929 })?;
930 let minute = self.minute;
931 datetime = datetime.with_minute(minute).ok_or_else(|| {
932 IonError::illegal_operation(format!("specified minute ('{minute}') is invalid"))
933 })?;
934 if self.precision == TimestampPrecision::HourAndMinute {
935 return Ok(datetime);
936 }
937
938 let second = self.second;
940 datetime = datetime.with_second(second).ok_or_else(|| {
941 IonError::illegal_operation(format!("provided second ('{second}') is invalid."))
942 })?;
943
944 datetime = datetime
950 .with_nanosecond(self.nanoseconds.unwrap_or(0))
951 .ok_or_else(|| {
952 IonError::illegal_operation(format!("provided nanosecond ('{second}') is invalid"))
953 })?;
954
955 Ok(datetime)
956 }
957
958 fn apply_offset(
962 offset_minutes: i32,
963 fields_are_utc: bool,
964 datetime: NaiveDateTime,
965 ) -> IonResult<DateTime<FixedOffset>> {
966 const SECONDS_PER_MINUTE: i32 = 60;
968 let offset_seconds = offset_minutes * SECONDS_PER_MINUTE;
969 let offset = FixedOffset::east_opt(offset_seconds).ok_or_else(|| {
970 IonError::illegal_operation(format!(
971 "specified offset ({offset_minutes} minutes) is invalid"
972 ))
973 })?;
974
975 if fields_are_utc {
978 return Ok(offset.from_utc_datetime(&datetime));
979 }
980
981 match offset.from_local_datetime(&datetime) {
984 LocalResult::None => {
985 IonResult::illegal_operation(
986 format!(
987 "specified offset/datetime pair is invalid (offset={offset_minutes}, datetime={datetime})"
988 )
989 )
990 },
991 LocalResult::Single(datetime) => Ok(datetime),
992 LocalResult::Ambiguous(_min, _max) => {
993 IonResult::illegal_operation(
994 format!(
995 "specified offset/datetime pair produces an ambiguous timestamp (offset={offset_minutes}, datetime={datetime})"
996 )
997 )
998 }
999 }
1000 }
1001
1002 pub fn build(mut self) -> IonResult<Timestamp> {
1007 let mut datetime: NaiveDateTime = NaiveDate::from_ymd_opt(0, 1, 1)
1009 .unwrap()
1010 .and_hms_nano_opt(0, 0, 0, 0)
1011 .unwrap();
1012 datetime = self.configure_datetime(datetime)?;
1014 let mut timestamp: Timestamp = if let Some(offset_minutes) = self.offset {
1016 let datetime_with_offset: DateTime<FixedOffset> =
1018 Self::apply_offset(offset_minutes, self.fields_are_utc, datetime)?;
1019 Timestamp::from_fixed_offset_datetime(datetime_with_offset)
1021 } else {
1022 Timestamp::from_naive_datetime(datetime)
1025 };
1026 if self.precision < TimestampPrecision::Second {
1027 timestamp.fractional_seconds = None;
1028 }
1029 timestamp.precision = self.precision;
1030
1031 if self.precision == TimestampPrecision::Second {
1033 timestamp.fractional_seconds = self.fractional_seconds;
1034 if let Some(Mantissa::Arbitrary(ref decimal)) = ×tamp.fractional_seconds {
1035 if decimal.is_less_than_zero() {
1036 return IonResult::illegal_operation(
1037 "cannot create a timestamp with negative fractional seconds",
1038 );
1039 }
1040 if decimal.is_greater_than_or_equal_to_one() {
1041 return IonResult::illegal_operation(
1042 "cannot create a timestamp with a fractional seconds >= 1.0",
1043 );
1044 }
1045 if decimal.is_zero() && decimal.exponent >= 0 {
1046 timestamp.fractional_seconds = None;
1047 }
1048 }
1049 }
1050 Ok(timestamp)
1051 }
1052
1053 pub(crate) fn build_utc_fields_at_offset(
1056 mut self,
1057 offset_minutes: i32,
1058 ) -> IonResult<Timestamp> {
1059 self.fields_are_utc = true;
1060 self.offset = Some(offset_minutes);
1061 self.build()
1062 }
1063}
1064
1065#[derive(Debug, Clone)]
1068pub struct HasYear;
1069impl TimestampBuilder<HasYear> {
1070 pub fn with_year(year: u32) -> Self {
1071 TimestampBuilder {
1072 _state: Default::default(),
1073 fields_are_utc: false,
1074 precision: TimestampPrecision::Year,
1075 offset: None,
1076 year,
1077 month: 1,
1078 day: 1,
1079 hour: 0,
1080 minute: 0,
1081 second: 0,
1082 fractional_seconds: None,
1083 nanoseconds: None,
1084 }
1085 }
1086
1087 pub fn with_ymd(year: u32, month: u32, day: u32) -> TimestampBuilder<HasDay> {
1088 Self::with_year(year).with_month(month).with_day(day)
1089 }
1090
1091 pub fn with_month0(self, month: u32) -> TimestampBuilder<HasMonth> {
1097 self.with_month(month + 1)
1098 }
1099
1100 pub fn with_month(mut self, month: u32) -> TimestampBuilder<HasMonth> {
1102 self.precision = TimestampPrecision::Month;
1103 self.month = month;
1104 self.change_state()
1105 }
1106}
1107
1108#[derive(Debug, Clone)]
1109pub struct HasMonth;
1110impl TimestampBuilder<HasMonth> {
1111 pub fn with_day0(self, day: u32) -> TimestampBuilder<HasDay> {
1117 self.with_day(day + 1)
1118 }
1119
1120 pub fn with_day(mut self, day: u32) -> TimestampBuilder<HasDay> {
1122 self.precision = TimestampPrecision::Day;
1123 self.day = day;
1124 self.change_state()
1125 }
1126}
1127
1128#[derive(Debug, Clone)]
1129pub struct HasDay;
1130impl TimestampBuilder<HasDay> {
1131 pub fn with_hms(self, hour: u32, minute: u32, second: u32) -> TimestampBuilder<HasSeconds> {
1132 self.with_hour(hour).with_minute(minute).with_second(second)
1133 }
1134
1135 pub fn with_hour_and_minute(mut self, hour: u32, minute: u32) -> TimestampBuilder<HasMinute> {
1136 self.precision = TimestampPrecision::HourAndMinute;
1137 self.hour = hour;
1138 self.minute = minute;
1139 self.change_state()
1140 }
1141
1142 pub fn with_hour(mut self, hour: u32) -> TimestampBuilder<HasHour> {
1143 self.precision = TimestampPrecision::HourAndMinute;
1144 self.hour = hour;
1145 self.change_state()
1146 }
1147}
1148
1149macro_rules! with_offset {
1150 () => {
1151 pub fn with_offset(mut self, offset_minutes: i32) -> TimestampBuilder<HasOffset> {
1157 self.offset = Some(offset_minutes);
1158 self.change_state()
1159 }
1160 };
1161}
1162
1163#[derive(Debug, Clone)]
1164pub struct HasHour;
1165impl TimestampBuilder<HasHour> {
1166 pub fn with_minute(mut self, minute: u32) -> TimestampBuilder<HasMinute> {
1167 self.precision = TimestampPrecision::HourAndMinute;
1168 self.minute = minute;
1169 self.change_state()
1170 }
1171
1172 with_offset!();
1173}
1174
1175#[derive(Debug, Clone)]
1176pub struct HasMinute;
1177impl TimestampBuilder<HasMinute> {
1178 pub fn with_second(mut self, second: u32) -> TimestampBuilder<HasSeconds> {
1179 self.precision = TimestampPrecision::Second;
1180 self.second = second;
1181 self.change_state()
1182 }
1183
1184 with_offset!();
1185}
1186
1187#[derive(Debug, Clone)]
1188pub struct HasSeconds;
1189impl TimestampBuilder<HasSeconds> {
1190 pub fn with_nanoseconds(mut self, nanosecond: u32) -> TimestampBuilder<HasFractionalSeconds> {
1194 self.fractional_seconds = Some(Mantissa::Digits(9));
1195 self.nanoseconds = Some(nanosecond);
1196 self.change_state()
1197 }
1198
1199 pub fn with_microseconds(mut self, microsecond: u32) -> TimestampBuilder<HasFractionalSeconds> {
1200 self.fractional_seconds = Some(Mantissa::Digits(6));
1201 self.nanoseconds = Some(microsecond * 1000);
1202 self.change_state()
1203 }
1204
1205 pub fn with_milliseconds(mut self, millisecond: u32) -> TimestampBuilder<HasFractionalSeconds> {
1206 self.fractional_seconds = Some(Mantissa::Digits(3));
1207 self.nanoseconds = Some(millisecond * 1_000_000);
1208 self.change_state()
1209 }
1210
1211 pub fn with_nanoseconds_and_precision(
1212 mut self,
1213 nanoseconds: u32,
1214 precision_digits: u32,
1215 ) -> TimestampBuilder<HasFractionalSeconds> {
1216 self.fractional_seconds = Some(Mantissa::Digits(precision_digits));
1217 self.nanoseconds = Some(nanoseconds);
1218 self.change_state()
1219 }
1220
1221 pub fn with_fractional_seconds(
1222 mut self,
1223 fractional_seconds: Decimal,
1224 ) -> TimestampBuilder<HasFractionalSeconds> {
1225 self.fractional_seconds = Some(Mantissa::Arbitrary(fractional_seconds));
1226 self.nanoseconds = None;
1227 self.change_state()
1228 }
1229
1230 with_offset!();
1231}
1232
1233#[derive(Debug, Clone)]
1234pub struct HasFractionalSeconds;
1235impl TimestampBuilder<HasFractionalSeconds> {
1236 with_offset!();
1237}
1238
1239#[derive(Debug, Clone)]
1240pub struct HasOffset;
1241fn downconvert_to_naive_datetime_with_nanoseconds(timestamp: &Timestamp) -> NaiveDateTime {
1244 if timestamp.precision == TimestampPrecision::Second {
1245 let nanoseconds = timestamp.fractional_seconds_as_nanoseconds().unwrap_or(0);
1250 timestamp.date_time.with_nanosecond(nanoseconds).unwrap()
1253 } else {
1254 timestamp.date_time
1256 }
1257}
1258
1259#[cfg(feature = "experimental-chrono")]
1260impl TryInto<NaiveDateTime> for Timestamp {
1261 type Error = IonError;
1262
1263 fn try_into(self) -> Result<NaiveDateTime, Self::Error> {
1264 self.try_to_naive_datetime()
1265 }
1266}
1267
1268#[cfg(feature = "experimental-chrono")]
1269impl TryInto<DateTime<FixedOffset>> for Timestamp {
1270 type Error = IonError;
1271
1272 fn try_into(self) -> Result<DateTime<FixedOffset>, Self::Error> {
1273 self.try_to_datetime_fixed_offset()
1274 }
1275}
1276
1277#[cfg(feature = "experimental-chrono")]
1278impl From<NaiveDateTime> for Timestamp {
1279 fn from(date_time: NaiveDateTime) -> Self {
1280 Self::from_naive_datetime(date_time)
1281 }
1282}
1283
1284#[cfg(feature = "experimental-chrono")]
1285impl From<DateTime<FixedOffset>> for Timestamp {
1286 fn from(fixed_offset_date_time: DateTime<FixedOffset>) -> Self {
1287 Self::from_fixed_offset_datetime(fixed_offset_date_time)
1288 }
1289}
1290
1291#[cfg(test)]
1292mod timestamp_tests {
1293 use super::*;
1294 use crate::ion_data::IonEq;
1295 use crate::result::IonResult;
1296 use crate::types::Mantissa;
1297 use crate::{Decimal, Int, Timestamp, TimestampPrecision};
1298 use chrono::NaiveDateTime;
1299 use rstest::*;
1300 use std::cmp::Ordering;
1301 use std::io::Write;
1302 use std::ops::Mul;
1303 use std::str::FromStr;
1304
1305 #[test]
1306 fn test_timestamps_with_same_ymd_hms_millis_at_known_offset_are_equal() -> IonResult<()> {
1307 let builder = TimestampBuilder::with_ymd(2021, 2, 5)
1308 .with_hms(16, 43, 51)
1309 .with_milliseconds(192);
1310 let timestamp1 = builder.clone().with_offset(5 * 60).build()?;
1311 let timestamp2 = builder.with_offset(5 * 60).build()?;
1312 assert_eq!(timestamp1, timestamp2);
1313 assert!(timestamp1.ion_eq(×tamp2));
1314 Ok(())
1315 }
1316
1317 #[test]
1318 fn test_timestamps_with_same_ymd_hms_millis_at_known_offset_are_equal_ordering() -> IonResult<()>
1319 {
1320 let builder = TimestampBuilder::with_ymd(2021, 2, 5)
1321 .with_hms(16, 43, 51)
1322 .with_milliseconds(192);
1323 let timestamp1 = builder.clone().with_offset(5 * 60).build()?;
1324 let timestamp2 = builder.with_offset(5 * 60).build()?;
1325 assert!(timestamp1 == timestamp2);
1326 Ok(())
1327 }
1328
1329 #[test]
1330 fn test_timestamps_with_same_ymd_hms_millis_at_unknown_offset_are_equal() -> IonResult<()> {
1331 let builder = TimestampBuilder::with_ymd(2021, 2, 5)
1332 .with_hms(16, 43, 51)
1333 .with_milliseconds(192);
1334 let timestamp1 = builder.clone().build()?;
1335 let timestamp2 = builder.build()?;
1336 assert_eq!(timestamp1, timestamp2);
1337 assert!(timestamp1.ion_eq(×tamp2));
1338 Ok(())
1339 }
1340
1341 #[test]
1342 fn test_timestamps_with_same_ymd_hms_at_known_offset_are_equal() -> IonResult<()> {
1343 let builder = TimestampBuilder::with_ymd(2021, 2, 5).with_hms(16, 43, 51);
1344 let timestamp1 = builder.clone().with_offset(5 * 60).build()?;
1345 let timestamp2 = builder.with_offset(5 * 60).build()?;
1346 assert_eq!(timestamp1, timestamp2);
1347 assert!(timestamp1.ion_eq(×tamp2));
1348 Ok(())
1349 }
1350
1351 #[test]
1352 fn test_timestamps_from_utc_and_local_hm_fields_at_same_offset_are_equal() -> IonResult<()> {
1353 let builder1 = TimestampBuilder::with_ymd(2021, 2, 5).with_hour_and_minute(11, 43);
1355 let timestamp1 = builder1.with_offset(-5 * 60).build()?;
1356 let builder2 = TimestampBuilder::with_ymd(2021, 2, 5).with_hour_and_minute(16, 43);
1358 let timestamp2 = builder2.build_utc_fields_at_offset(-5 * 60)?;
1359 assert_eq!(timestamp1, timestamp2);
1360 assert!(timestamp1.ion_eq(×tamp2));
1361 Ok(())
1362 }
1363
1364 #[test]
1365 fn test_timestamps_from_utc_and_local_hms_fields_at_same_offset_are_equal() -> IonResult<()> {
1366 let builder1 = TimestampBuilder::with_ymd(2021, 2, 5).with_hms(11, 43, 51);
1368 let timestamp1 = builder1.with_offset(-5 * 60).build()?;
1369 let builder2 = TimestampBuilder::with_ymd(2021, 2, 5).with_hms(16, 43, 51);
1371 let timestamp2 = builder2.build_utc_fields_at_offset(-5 * 60)?;
1372 assert_eq!(timestamp1, timestamp2);
1373 assert!(timestamp1.ion_eq(×tamp2));
1374 Ok(())
1375 }
1376
1377 #[test]
1378 fn test_timestamps_with_same_ymd_hms_at_unknown_offset_are_equal() -> IonResult<()> {
1379 let builder = TimestampBuilder::with_ymd(2021, 2, 5).with_hms(16, 43, 51);
1380 let timestamp1 = builder.clone().build()?;
1381 let timestamp2 = builder.build()?;
1382 assert_eq!(timestamp1, timestamp2);
1383 assert!(timestamp1.ion_eq(×tamp2));
1384 Ok(())
1385 }
1386
1387 #[test]
1388 fn test_timestamps_with_same_ymd_hm_at_known_offset_are_equal() -> IonResult<()> {
1389 let builder = TimestampBuilder::with_ymd(2021, 2, 5).with_hour_and_minute(16, 43);
1390 let timestamp1 = builder.clone().with_offset(5 * 60).build()?;
1391 let timestamp2 = builder.with_offset(5 * 60).build()?;
1392 assert_eq!(timestamp1, timestamp2);
1393 assert!(timestamp1.ion_eq(×tamp2));
1394 Ok(())
1395 }
1396
1397 #[test]
1398 fn test_timestamps_with_same_ymd_hm_at_unknown_offset_are_equal() -> IonResult<()> {
1399 let builder = TimestampBuilder::with_ymd(2021, 2, 5).with_hour_and_minute(16, 43);
1400 let timestamp1 = builder.clone().build()?;
1401 let timestamp2 = builder.build()?;
1402 assert_eq!(timestamp1, timestamp2);
1403 assert!(timestamp1.ion_eq(×tamp2));
1404 Ok(())
1405 }
1406
1407 #[test]
1408 fn test_timestamps_with_same_ymd_at_unknown_offset_are_equal() -> IonResult<()> {
1409 let builder = TimestampBuilder::with_ymd(2021, 2, 5);
1410 let timestamp1 = builder.clone().build()?;
1411 let timestamp2 = builder.build()?;
1412 assert_eq!(timestamp1, timestamp2);
1413 assert!(timestamp1.ion_eq(×tamp2));
1414 Ok(())
1415 }
1416
1417 #[test]
1418 fn test_timestamps_with_same_ym_at_unknown_offset_are_equal() -> IonResult<()> {
1419 let builder = TimestampBuilder::with_year(2021).with_month(2);
1420 let timestamp1 = builder.clone().build()?;
1421 let timestamp2 = builder.build()?;
1422 assert_eq!(timestamp1, timestamp2);
1423 assert!(timestamp1.ion_eq(×tamp2));
1424 Ok(())
1425 }
1426
1427 #[test]
1428 fn test_timestamps_with_same_year_at_unknown_offset_are_equal() -> IonResult<()> {
1429 let builder = TimestampBuilder::with_year(2021);
1430 let timestamp1 = builder.clone().build()?;
1431 let timestamp2 = builder.build()?;
1432 assert_eq!(timestamp1, timestamp2);
1433 assert!(timestamp1.ion_eq(×tamp2));
1434 Ok(())
1435 }
1436
1437 #[test]
1438 fn test_timestamps_at_different_offsets_are_not_equal() -> IonResult<()> {
1439 let builder = TimestampBuilder::with_ymd(2021, 2, 5)
1440 .with_hms(16, 43, 51)
1441 .with_milliseconds(192);
1442 let timestamp1 = builder.clone().with_offset(5 * 60).build()?;
1443 let timestamp2 = builder.with_offset(4 * 60).build()?;
1444 assert_ne!(timestamp1, timestamp2);
1445 assert!(!timestamp1.ion_eq(×tamp2));
1446 Ok(())
1447 }
1448
1449 #[test]
1450 fn test_timestamps_with_known_and_unknown_offsets_are_not_equal() -> IonResult<()> {
1451 let builder = TimestampBuilder::with_ymd(2021, 2, 5)
1452 .with_hms(16, 43, 51)
1453 .with_milliseconds(192);
1454 let timestamp1 = builder.clone().with_offset(5 * 60).build()?;
1455 let timestamp2 = builder.build()?;
1456 assert_ne!(timestamp1, timestamp2);
1457 assert!(!timestamp1.ion_eq(×tamp2));
1458 Ok(())
1459 }
1460
1461 #[test]
1462 fn test_timestamps_with_different_precisions_are_not_equal() -> IonResult<()> {
1463 let builder = TimestampBuilder::with_ymd(2021, 2, 5).with_hms(16, 43, 51);
1464 let timestamp1 = builder.clone().with_offset(5 * 60).build()?;
1465 let timestamp2 = builder.with_milliseconds(192).with_offset(5 * 60).build()?;
1466 assert_ne!(timestamp1, timestamp2);
1467 assert!(!timestamp1.ion_eq(×tamp2));
1468 Ok(())
1469 }
1470
1471 #[test]
1472 fn test_timestamps_with_different_fractional_second_precision_are_not_equal() -> IonResult<()> {
1473 let builder = TimestampBuilder::with_ymd(2021, 2, 5).with_hms(16, 43, 51);
1474 let timestamp1 = builder
1475 .clone()
1476 .with_milliseconds(192)
1477 .with_offset(5 * 60)
1478 .build()?;
1479 let timestamp2 = builder
1481 .with_microseconds(193 * 1_000)
1482 .with_offset(5 * 60)
1483 .build()?;
1484 assert_ne!(timestamp1, timestamp2);
1485 assert!(!timestamp1.ion_eq(×tamp2));
1486 Ok(())
1487 }
1488
1489 #[test]
1490 fn test_timestamps_with_different_fractional_seconds_are_not_equal() -> IonResult<()> {
1491 let builder = TimestampBuilder::with_ymd(2021, 2, 5).with_hms(16, 43, 51);
1492 let timestamp1 = builder
1493 .clone()
1494 .with_milliseconds(192)
1495 .with_offset(5 * 60)
1496 .build()?;
1497 let timestamp2 = builder.with_milliseconds(193).with_offset(5 * 60).build()?;
1498 assert_ne!(timestamp1, timestamp2);
1499 assert!(!timestamp1.ion_eq(×tamp2));
1500 Ok(())
1501 }
1502
1503 #[test]
1504 fn test_timestamps_with_different_seconds_are_not_equal() -> IonResult<()> {
1505 let builder = TimestampBuilder::with_ymd(2021, 2, 5).with_hour_and_minute(16, 43);
1506 let timestamp1 = builder
1507 .clone()
1508 .with_second(12)
1509 .with_offset(5 * 60)
1510 .build()?;
1511 let timestamp2 = builder.with_second(13).with_offset(5 * 60).build()?;
1512 assert_ne!(timestamp1, timestamp2);
1513 assert!(!timestamp1.ion_eq(×tamp2));
1514 Ok(())
1515 }
1516
1517 #[test]
1518 fn test_timestamps_with_different_minutes_are_not_equal() -> IonResult<()> {
1519 let builder = TimestampBuilder::with_ymd(2021, 2, 5);
1520 let timestamp1 = builder
1521 .clone()
1522 .with_hour_and_minute(16, 42)
1523 .with_offset(5 * 60)
1524 .build()?;
1525 let timestamp2 = builder
1526 .with_hour_and_minute(16, 43)
1527 .with_offset(5 * 60)
1528 .build()?;
1529 assert_ne!(timestamp1, timestamp2);
1530 assert!(!timestamp1.ion_eq(×tamp2));
1531 Ok(())
1532 }
1533
1534 #[test]
1535 fn test_timestamps_with_different_hours_are_not_equal() -> IonResult<()> {
1536 let builder = TimestampBuilder::with_ymd(2021, 2, 5);
1537 let timestamp1 = builder
1538 .clone()
1539 .with_hour_and_minute(16, 42)
1540 .with_offset(5 * 60)
1541 .build()?;
1542 let timestamp2 = builder
1543 .with_hour_and_minute(17, 42)
1544 .with_offset(5 * 60)
1545 .build()?;
1546 assert_ne!(timestamp1, timestamp2);
1547 assert!(!timestamp1.ion_eq(×tamp2));
1548 Ok(())
1549 }
1550
1551 #[test]
1552 fn test_timestamps_with_different_days_are_not_equal() -> IonResult<()> {
1553 let builder = TimestampBuilder::with_year(2021).with_month(2);
1554 let timestamp1 = builder.clone().with_day(5).build()?;
1555 let timestamp2 = builder.with_day(6).build()?;
1556 assert_ne!(timestamp1, timestamp2);
1557 assert!(!timestamp1.ion_eq(×tamp2));
1558 Ok(())
1559 }
1560
1561 #[test]
1562 fn test_timestamps_with_different_months_are_not_equal() -> IonResult<()> {
1563 let builder = TimestampBuilder::with_year(2021);
1564 let timestamp1 = builder.clone().with_month(2).build()?;
1565 let timestamp2 = builder.with_month(3).build()?;
1566 assert_ne!(timestamp1, timestamp2);
1567 assert!(!timestamp1.ion_eq(×tamp2));
1568 Ok(())
1569 }
1570
1571 #[test]
1572 fn test_timestamps_with_different_years_are_not_equal() -> IonResult<()> {
1573 let timestamp1 = TimestampBuilder::with_year(2021).build()?;
1574 let timestamp2 = TimestampBuilder::with_year(2022).build()?;
1575 assert_ne!(timestamp1, timestamp2);
1576 assert!(!timestamp1.ion_eq(×tamp2));
1577 Ok(())
1578 }
1579
1580 #[cfg(feature = "experimental-chrono")]
1581 #[test]
1582 fn test_timestamp_try_into_naive_datetime() -> IonResult<()> {
1583 let timestamp = TimestampBuilder::with_ymd(2021, 4, 6)
1584 .with_hms(10, 15, 0)
1585 .build()?;
1586 let naive_datetime: NaiveDateTime = timestamp.try_into()?;
1587 let expected = NaiveDate::from_ymd_opt(2021, 4, 6)
1588 .unwrap()
1589 .and_hms_opt(10, 15, 0)
1590 .unwrap();
1591 assert_eq!(expected, naive_datetime);
1592 Ok(())
1593 }
1594
1595 #[cfg(feature = "experimental-chrono")]
1596 #[test]
1597 fn test_timestamp_try_into_naive_datetime_fractional_seconds() -> IonResult<()> {
1598 let timestamp = TimestampBuilder::with_ymd(2021, 4, 6)
1599 .with_hms(10, 15, 0)
1600 .with_milliseconds(449)
1601 .build()?;
1602 let datetime: NaiveDateTime = timestamp.try_into()?;
1603 let naive_datetime = NaiveDate::from_ymd_opt(2021, 4, 6)
1604 .unwrap()
1605 .and_hms_opt(10, 15, 0)
1606 .unwrap()
1607 .with_nanosecond(449000000)
1608 .unwrap();
1609 assert_eq!(datetime, naive_datetime);
1610 Ok(())
1611 }
1612
1613 #[cfg(feature = "experimental-chrono")]
1614 #[test]
1615 fn test_timestamp_try_into_naive_datetime_error() -> IonResult<()> {
1616 let timestamp = TimestampBuilder::with_ymd(2021, 1, 1)
1617 .with_hms(0, 0, 0)
1618 .with_offset(0)
1619 .build()?;
1620 let result: IonResult<NaiveDateTime> = timestamp.try_into();
1622 assert!(result.is_err());
1623 Ok(())
1624 }
1625
1626 #[cfg(feature = "experimental-chrono")]
1627 #[test]
1628 fn test_timestamp_try_into_fixed_offset_datetime() -> IonResult<()> {
1629 let timestamp = TimestampBuilder::with_ymd(2021, 4, 6)
1630 .with_hms(10, 15, 0)
1631 .with_offset(-5 * 60)
1632 .build()?;
1633 let datetime: DateTime<FixedOffset> = timestamp.try_into()?;
1635 let expected_offset = offset_east(-5 * 60 * 60);
1637 let naive_datetime = NaiveDate::from_ymd_opt(2021, 4, 6)
1638 .unwrap()
1639 .and_hms_opt(10, 15, 0)
1640 .unwrap();
1641 let expected_datetime = expected_offset
1642 .from_local_datetime(&naive_datetime)
1643 .unwrap();
1644 assert_eq!(datetime, expected_datetime);
1645 Ok(())
1646 }
1647
1648 #[cfg(feature = "experimental-chrono")]
1649 #[test]
1650 fn test_timestamp_try_into_fixed_offset_datetime_fractional_seconds() -> IonResult<()> {
1651 let timestamp = TimestampBuilder::with_ymd(2021, 4, 6)
1652 .with_hms(10, 15, 0)
1653 .with_milliseconds(449)
1654 .with_offset(-5 * 60)
1655 .build()?;
1656 let datetime: DateTime<FixedOffset> = timestamp.try_into()?;
1658 let expected_offset = offset_east(-5 * 60 * 60);
1660 let naive_datetime = NaiveDate::from_ymd_opt(2021, 4, 6)
1661 .unwrap()
1662 .and_hms_opt(10, 15, 0)
1663 .unwrap()
1664 .with_nanosecond(449000000)
1665 .unwrap();
1666 let expected_datetime = expected_offset
1667 .from_local_datetime(&naive_datetime)
1668 .unwrap();
1669 assert_eq!(datetime, expected_datetime);
1670 Ok(())
1671 }
1672
1673 #[cfg(feature = "experimental-chrono")]
1674 #[test]
1675 fn test_timestamp_try_into_datetime_fixedoffset_error() -> IonResult<()> {
1676 let timestamp = TimestampBuilder::with_ymd(2021, 1, 1)
1677 .with_hms(0, 0, 0)
1678 .build()?;
1679 let result: IonResult<DateTime<FixedOffset>> = timestamp.try_into();
1681 assert!(result.is_err());
1682 Ok(())
1683 }
1684
1685 #[test]
1686 fn test_timestamp_builder() {
1687 let timestamp1 = TimestampBuilder::with_year(2021)
1690 .with_month(2)
1691 .with_day(5)
1692 .with_hour(17)
1693 .with_minute(39)
1694 .with_second(51)
1695 .with_milliseconds(194)
1696 .with_offset(-4 * 60)
1697 .build()
1698 .unwrap_or_else(|e| panic!("Couldn't build timestamp: {e:?}"));
1699
1700 let timestamp2 = TimestampBuilder::with_ymd(2021, 2, 5)
1701 .with_hms(17, 39, 51)
1702 .with_milliseconds(194)
1703 .with_offset(-4 * 60)
1704 .build()
1705 .unwrap_or_else(|e| panic!("Couldn't build timestamp: {e:?}"));
1706
1707 assert_eq!(timestamp1.precision, TimestampPrecision::Second);
1708 assert_eq!(timestamp1.fractional_seconds, Some(Mantissa::Digits(3)));
1709 assert_eq!(timestamp1, timestamp2);
1710
1711 assert!(timestamp1.ion_eq(×tamp2));
1712 }
1713
1714 #[test]
1715 fn test_timestamp_builder_without_minutes() {
1716 let timestamp1 = TimestampBuilder::with_year(2021)
1718 .with_month(2)
1719 .with_day(5)
1720 .with_hour(17)
1721 .with_offset(60)
1722 .build()
1723 .unwrap_or_else(|e| panic!("Couldn't build timestamp: {e:?}"));
1724
1725 let timestamp2 = TimestampBuilder::with_ymd(2021, 2, 5)
1726 .with_hour_and_minute(17, 0)
1727 .with_offset(60)
1728 .build()
1729 .unwrap_or_else(|e| panic!("Couldn't build timestamp: {e:?}"));
1730
1731 assert_eq!(timestamp1.precision, TimestampPrecision::HourAndMinute);
1732 assert_eq!(timestamp1, timestamp2)
1733 }
1734
1735 #[test]
1736 fn test_timestamp_fixed_offset() -> IonResult<()> {
1737 let timestamp = TimestampBuilder::with_ymd(2021, 4, 6)
1738 .with_hms(10, 15, 0)
1739 .with_milliseconds(449)
1740 .with_offset(-5 * 60)
1741 .build()?;
1742 let expected_offset = -5 * 60;
1745
1746 assert_eq!(timestamp.offset().unwrap(), expected_offset);
1747 Ok(())
1748 }
1749
1750 #[test]
1751 fn test_timestamp_precision() -> IonResult<()> {
1752 let timestamp = Timestamp::with_year(2021).with_month(2).build()?;
1753 assert_eq!(timestamp.precision(), TimestampPrecision::Month);
1754 Ok(())
1755 }
1756
1757 #[test]
1758 fn test_timestamp_year() -> IonResult<()> {
1759 let timestamp_1 = TimestampBuilder::with_year(2021).with_month(2).build()?;
1760 assert_eq!(timestamp_1.year(), 2021);
1761
1762 let timestamp_2 = TimestampBuilder::with_ymd(2021, 12, 31)
1763 .with_hms(10, 15, 30)
1764 .with_offset(-11 * 60)
1765 .build()?;
1766
1767 assert_eq!(timestamp_2.year(), 2021);
1768
1769 let timestamp_3 = TimestampBuilder::with_ymd(2021, 12, 31)
1770 .with_hms(15, 15, 30)
1771 .with_offset(10 * 60)
1772 .build()?;
1773
1774 assert_eq!(timestamp_3.year(), 2021);
1775
1776 Ok(())
1777 }
1778
1779 #[test]
1780 fn test_timestamp_month() -> IonResult<()> {
1781 let timestamp_1 = TimestampBuilder::with_year(2021).with_month(2).build()?;
1782 assert_eq!(timestamp_1.month(), 2);
1783
1784 let timestamp_2 = TimestampBuilder::with_ymd(2021, 1, 31)
1785 .with_hms(10, 15, 30)
1786 .with_offset(-11 * 60)
1787 .build()?;
1788
1789 assert_eq!(timestamp_2.month(), 1);
1790
1791 let timestamp_3 = TimestampBuilder::with_ymd(2021, 1, 31)
1792 .with_hms(15, 15, 30)
1793 .with_offset(10 * 60)
1794 .build()?;
1795
1796 assert_eq!(timestamp_3.month(), 1);
1797
1798 Ok(())
1799 }
1800
1801 #[test]
1802 fn test_timestamp_day() -> IonResult<()> {
1803 let timestamp_1 = TimestampBuilder::with_year(2021).with_month(2).build()?;
1804 assert_eq!(timestamp_1.day(), 1);
1805
1806 let timestamp_2 = TimestampBuilder::with_year(2021)
1807 .with_month(2)
1808 .with_day(4)
1809 .build()?;
1810
1811 assert_eq!(timestamp_2.day(), 4);
1812
1813 let timestamp_3 = TimestampBuilder::with_ymd(2021, 4, 6)
1814 .with_hms(10, 15, 30)
1815 .with_offset(-11 * 60)
1816 .build()?;
1817
1818 assert_eq!(timestamp_3.day(), 6);
1819
1820 let timestamp_4 = TimestampBuilder::with_ymd(2021, 4, 6)
1821 .with_hms(15, 15, 30)
1822 .with_offset(10 * 60)
1823 .build()?;
1824
1825 assert_eq!(timestamp_4.day(), 6);
1826
1827 Ok(())
1828 }
1829
1830 #[rstest]
1831 #[case(TimestampBuilder::with_ymd(2021, 4, 6).with_hms(10, 15, 30).with_offset(-90).build(), 10)]
1832 #[case(TimestampBuilder::with_ymd(2021, 4, 6).with_hms(10, 15, 30).with_offset(-5 * 60).build(), 10)]
1833 #[case(TimestampBuilder::with_ymd(2021, 4, 6).with_hms(10, 15, 30).with_offset(5 * 60).build(), 10)]
1834 #[case(TimestampBuilder::with_ymd(2021, 4, 6).with_hms(10, 15, 30).with_offset(15).build(), 10)]
1835 #[case(TimestampBuilder::with_ymd(2021, 4, 6).with_hms(10, 15, 30).with_offset(30).build(), 10)]
1836 #[case(TimestampBuilder::with_ymd(2021, 4, 6).with_hms(10, 15, 30).with_offset(0).build(), 10)]
1837 #[case(TimestampBuilder::with_ymd(2021, 4, 6).with_hms(0, 15, 30).with_offset(5 * 60).build(), 0)]
1838 #[case(TimestampBuilder::with_ymd(2021, 4, 6).with_hms(23, 15, 30).with_offset(-5 * 60).build(), 23)]
1839 #[case(TimestampBuilder::with_ymd(2021, 4, 6).with_hms(0, 15, 30).with_offset(23 * 60).build(), 0)]
1840 #[case(TimestampBuilder::with_ymd(2021, 4, 6).with_hms(10, 15, 30).with_offset(-11 * 60).build(), 10)]
1841 #[case(TimestampBuilder::with_ymd(2021, 4, 6).with_hms(15, 15, 30).with_offset(10 * 60).build(), 15)]
1842 fn test_timestamp_hour(
1843 #[case] timestamp: IonResult<Timestamp>,
1844 #[case] expected_hours: u32,
1845 ) -> IonResult<()> {
1846 assert_eq!(timestamp?.hour(), expected_hours);
1847 Ok(())
1848 }
1849
1850 #[rstest]
1851 #[case(TimestampBuilder::with_ymd(2021, 4, 6).with_hms(10, 15, 30).with_offset(-90).build(), 15)]
1852 #[case(TimestampBuilder::with_ymd(2021, 4, 6).with_hms(10, 15, 30).with_offset(-5 * 60).build(), 15)]
1853 #[case(TimestampBuilder::with_ymd(2021, 4, 6).with_hms(10, 15, 30).with_offset(5 * 60).build(), 15)]
1854 #[case(TimestampBuilder::with_ymd(2021, 4, 6).with_hms(10, 15, 30).with_offset(0).build(), 15)]
1855 #[case(TimestampBuilder::with_ymd(2021, 4, 6).with_hms(10, 0, 30).with_offset(5 * 60).build(), 0)]
1856 #[case(TimestampBuilder::with_ymd(2021, 4, 6).with_hms(10, 59, 30).with_offset(5 * 60).build(), 59)]
1857 #[case(TimestampBuilder::with_ymd(2021, 4, 6).with_hms(10, 15, 30).with_offset(-11 * 60).build(), 15)]
1858 #[case(TimestampBuilder::with_ymd(2021, 4, 6).with_hms(15, 15, 30).with_offset(10 * 60).build(), 15)]
1859 fn test_timestamp_minute(
1860 #[case] timestamp: IonResult<Timestamp>,
1861 #[case] expected_minutes: u32,
1862 ) -> IonResult<()> {
1863 assert_eq!(timestamp?.minute(), expected_minutes);
1864 Ok(())
1865 }
1866
1867 #[test]
1868 fn test_timestamp_second() -> IonResult<()> {
1869 let timestamp = TimestampBuilder::with_ymd(2021, 4, 6)
1870 .with_hms(10, 15, 30)
1871 .with_offset(-5 * 60)
1872 .build()?;
1873 assert_eq!(timestamp.second(), 30);
1874 Ok(())
1875 }
1876
1877 #[test]
1878 fn test_timestamp_nanoseconds() -> IonResult<()> {
1879 let timestamp_1 = TimestampBuilder::with_ymd(2021, 4, 6)
1880 .with_hms(10, 15, 30)
1881 .with_nanoseconds(192)
1882 .with_offset(-5 * 60)
1883 .build()?;
1884 assert_eq!(timestamp_1.nanoseconds(), 192);
1885
1886 let timestamp_2 = TimestampBuilder::with_ymd(2021, 4, 6)
1887 .with_hms(10, 15, 30)
1888 .with_milliseconds(192)
1889 .with_offset(-5 * 60)
1890 .build()?;
1891 assert_eq!(timestamp_2.nanoseconds(), 192000000);
1892
1893 let timestamp_3 = TimestampBuilder::with_ymd(2021, 4, 6)
1894 .with_hms(10, 15, 30)
1895 .with_offset(-5 * 60)
1896 .build()?;
1897 assert_eq!(timestamp_3.nanoseconds(), 0);
1898
1899 let big_coefficient: Int = Int::from(i128::MAX).data.mul(4).into();
1901 let timestamp_4 = Timestamp::with_ymd(2023, 1, 1)
1902 .with_hour_and_minute(0, 0)
1903 .with_second(0)
1904 .with_fractional_seconds(Decimal::new(big_coefficient, -39))
1905 .build()?;
1906 assert_eq!(timestamp_4.nanoseconds(), 680564733);
1907
1908 let timestamp_5 = Timestamp::with_ymd(2023, 1, 1)
1910 .with_hour_and_minute(0, 0)
1911 .with_second(0)
1912 .with_fractional_seconds(Decimal::new(1i64, -50))
1913 .build()?;
1914 assert_eq!(timestamp_5.nanoseconds(), 0);
1915
1916 Ok(())
1917 }
1918
1919 #[test]
1920 fn test_timestamp_milliseconds() -> IonResult<()> {
1921 let timestamp_1 = TimestampBuilder::with_ymd(2021, 4, 6)
1922 .with_hms(10, 15, 30)
1923 .with_milliseconds(192)
1924 .with_offset(-5 * 60)
1925 .build()?;
1926 assert_eq!(timestamp_1.milliseconds(), 192);
1927
1928 let timestamp_2 = TimestampBuilder::with_ymd(2021, 4, 6)
1929 .with_hms(10, 15, 30)
1930 .with_offset(-5 * 60)
1931 .build()?;
1932 assert_eq!(timestamp_2.milliseconds(), 0);
1933 Ok(())
1934 }
1935
1936 #[test]
1937 fn test_timestamp_to_utc() -> IonResult<()> {
1938 let new_years_eve_nyc = TimestampBuilder::with_ymd(2022, 12, 31)
1939 .with_hms(23, 59, 00)
1940 .with_offset(-5 * 60)
1941 .build()?;
1942
1943 let london = new_years_eve_nyc.to_utc();
1944 assert_eq!(london.year(), 2023);
1945 assert_eq!(london.month(), 1);
1946 assert_eq!(london.day(), 1);
1947 assert_eq!(london.hour(), 4);
1948 assert_eq!(london.minute(), 59);
1949 assert_eq!(london.second(), 0);
1950 Ok(())
1951 }
1952
1953 #[test]
1954 fn test_timestamp_fractional_seconds_scale() -> IonResult<()> {
1955 let timestamp_with_micro_seconds = TimestampBuilder::with_ymd(2021, 4, 6)
1957 .with_hms(10, 15, 0)
1958 .with_fractional_seconds(Decimal::new(553u64, -6))
1959 .with_offset(-5 * 60)
1960 .build()?;
1961
1962 assert_eq!(
1963 timestamp_with_micro_seconds
1964 .fractional_seconds_scale()
1965 .unwrap(),
1966 6
1967 );
1968
1969 let timestamp_with_redundant_fractional_seconds = TimestampBuilder::with_ymd(2021, 4, 6)
1972 .with_hms(10, 15, 0)
1973 .with_fractional_seconds(Decimal::new(0, 6))
1974 .with_offset(-5 * 60)
1975 .build()?;
1976 assert_eq!(
1977 timestamp_with_redundant_fractional_seconds.precision,
1978 TimestampPrecision::Second
1979 );
1980 assert_eq!(
1981 timestamp_with_redundant_fractional_seconds.fractional_seconds_scale(),
1982 None
1983 );
1984
1985 let timestamp_with_milliseconds = TimestampBuilder::with_ymd(2021, 4, 6)
1987 .with_hms(10, 15, 0)
1988 .with_milliseconds(449)
1989 .with_offset(-5 * 60)
1990 .build()?;
1991
1992 assert_eq!(
1993 timestamp_with_milliseconds
1994 .fractional_seconds_scale()
1995 .unwrap(),
1996 3
1997 );
1998
1999 let timestamp_with_seconds = TimestampBuilder::with_ymd(2021, 4, 6)
2001 .with_hms(10, 15, 0)
2002 .with_offset(-5 * 60)
2003 .build()?;
2004
2005 assert_eq!(timestamp_with_seconds.fractional_seconds_scale(), None);
2007 Ok(())
2008 }
2009
2010 #[test]
2011 fn test_first_n_digits_of() {
2012 assert_eq!(0, super::first_n_digits_of(1, 0));
2013 assert_eq!(1, super::first_n_digits_of(1, 1));
2014 assert_eq!(2, super::first_n_digits_of(1, 2));
2015
2016 assert_eq!(0, super::first_n_digits_of(3, 0));
2017 assert_eq!(1, super::first_n_digits_of(3, 1));
2018 assert_eq!(2, super::first_n_digits_of(3, 2));
2019 assert_eq!(99, super::first_n_digits_of(9, 99));
2020 assert_eq!(999, super::first_n_digits_of(9, 999));
2021 assert_eq!(9999, super::first_n_digits_of(9, 9999));
2022
2023 assert_eq!(0, super::first_n_digits_of(0, 123456789));
2024 assert_eq!(1, super::first_n_digits_of(1, 123456789));
2025 assert_eq!(12, super::first_n_digits_of(2, 123456789));
2026 assert_eq!(123, super::first_n_digits_of(3, 123456789));
2027 assert_eq!(1234, super::first_n_digits_of(4, 123456789));
2028 assert_eq!(12345, super::first_n_digits_of(5, 123456789));
2029 assert_eq!(123456, super::first_n_digits_of(6, 123456789));
2030 assert_eq!(1234567, super::first_n_digits_of(7, 123456789));
2031 assert_eq!(12345678, super::first_n_digits_of(8, 123456789));
2032 assert_eq!(123456789, super::first_n_digits_of(9, 123456789));
2033 }
2034
2035 #[rstest]
2036 #[case::timestamp_with_same_year(TimestampBuilder::with_year(2020).build().unwrap(), TimestampBuilder::with_year(2020).build().unwrap(), Ordering::Equal)]
2037 #[case::timestamp_with_different_year(TimestampBuilder::with_year(2020).build().unwrap(), TimestampBuilder::with_year(2021).build().unwrap(), Ordering::Less)]
2038 #[case::timestamp_with_milliseconds(TimestampBuilder::with_ymd(2021, 4, 6).with_hms(10, 15, 0).with_milliseconds(449).with_offset(5 * 60).build().unwrap(), TimestampBuilder::with_ymd(2021, 4, 6).with_hms(10, 15, 0).with_milliseconds(449).with_offset(5 * 60).build().unwrap(), Ordering::Equal)]
2039 #[case::timestamp_with_milliseconds_nanoseconds(TimestampBuilder::with_ymd(2021, 4, 6).with_hms(10, 15, 0).with_milliseconds(449).with_offset(5 * 60).build().unwrap(), TimestampBuilder::with_ymd(2021, 4, 6).with_hms(10, 15, 0).with_nanoseconds(449000005).with_offset(5 * 60).build().unwrap(), Ordering::Less)]
2040 #[case::timestamp_with_fractional_seconds(TimestampBuilder::with_ymd(2021, 4, 6).with_hms(10, 15, 0).with_fractional_seconds(Decimal::new(449u64, -3)).with_offset(5 * 60).build().unwrap(), TimestampBuilder::with_ymd(2021, 4, 6).with_hms(10, 15, 0).with_nanoseconds(449000000).with_offset(5 * 60).build().unwrap(), Ordering::Equal)]
2041 #[case::timestamp_with_different_precision(TimestampBuilder::with_year(2020).with_month(3).build().unwrap(), TimestampBuilder::with_year(2020).build().unwrap(), Ordering::Greater)]
2042 #[case::timestamp_with_same_offset(TimestampBuilder::with_ymd(2021, 4, 6).with_hms(10, 15, 0).with_offset(-5 * 60).build().unwrap(), TimestampBuilder::with_ymd(2021, 4, 6).with_hms(10, 15, 0).with_offset(-5 * 60).build().unwrap(), Ordering::Equal)]
2043 #[case::timestamp_with_different_offset(TimestampBuilder::with_ymd(2021, 4, 6).with_hms(10, 15, 0).with_offset(5 * 60).build().unwrap(), TimestampBuilder::with_ymd(2021, 4, 6).with_hms(10, 15, 0).with_offset(-5 * 60).build().unwrap(), Ordering::Less)]
2044 #[case::timestamp_with_unknown_offset(TimestampBuilder::with_ymd(2021, 4, 6).with_hms(10, 15, 0).build().unwrap(), TimestampBuilder::with_ymd(2021, 4, 6).with_hms(10, 15, 0).with_offset(-5 * 60).build().unwrap(), Ordering::Less)]
2045 #[case::timestamp_with_unknown_offset(TimestampBuilder::with_ymd(2021, 4, 6).with_hms(10, 15, 0).with_nanoseconds(0).build().unwrap(), TimestampBuilder::with_ymd(2021, 4, 6).with_hms(10, 15, 0).build().unwrap(), Ordering::Equal)]
2046 #[case::timestamp_with_unknown_offset(TimestampBuilder::with_ymd(2021, 4, 6).with_hms(10, 15, 0).with_nanoseconds(449000005).build().unwrap(), TimestampBuilder::with_ymd(2021, 4, 6).with_hms(10, 15, 0).build().unwrap(), Ordering::Greater)]
2047 #[case::timestamp_with_second_precison_and_year_precision(TimestampBuilder::with_ymd(2001, 1, 1).build().unwrap(), TimestampBuilder::with_ymd(2001, 1, 1).with_hms(0, 0, 0).with_fractional_seconds(Decimal::new(00000000000000000000i128, -20)).build().unwrap(), Ordering::Equal)]
2048 fn timestamp_ordering_tests(
2049 #[case] this: Timestamp,
2050 #[case] other: Timestamp,
2051 #[case] expected: Ordering,
2052 ) {
2053 assert_eq!(this.cmp(&other), expected)
2054 }
2055
2056 #[test]
2057 fn ion_eq_fraction_seconds_mixed_mantissa() {
2058 let t1 = Timestamp {
2059 date_time: NaiveDateTime::from_str("1857-05-29T19:25:59.100").unwrap(),
2060 offset: Some(offset_east(60 * 60 * 23 + 60 * 59)),
2061 precision: TimestampPrecision::Second,
2062 fractional_seconds: Some(Mantissa::Digits(1)),
2063 };
2064 let t2 = Timestamp {
2065 date_time: NaiveDateTime::from_str("1857-05-29T19:25:59").unwrap(),
2066 offset: Some(offset_east(60 * 60 * 23 + 60 * 59)),
2067 precision: TimestampPrecision::Second,
2068 fractional_seconds: Some(Mantissa::Arbitrary(Decimal::new(1u64, -1))),
2069 };
2070 assert_eq!(t1, t2);
2071 assert!(t1.ion_eq(&t2));
2072 }
2073
2074 #[test]
2075 fn ion_eq_fraction_seconds_mixed_mantissa_2() {
2076 let t1 = Timestamp {
2077 date_time: NaiveDateTime::from_str("2001-08-01T18:18:49.006").unwrap(),
2078 offset: Some(offset_east(60 * 60 + 60)),
2079 precision: TimestampPrecision::Second,
2080 fractional_seconds: Some(Mantissa::Digits(5)),
2081 };
2082 let t2 = Timestamp {
2083 date_time: NaiveDateTime::from_str("2001-08-01T18:18:49").unwrap(),
2084 offset: Some(offset_east(60 * 60 + 60)),
2085 precision: TimestampPrecision::Second,
2086 fractional_seconds: Some(Mantissa::Arbitrary(Decimal::new(600u64, -5))),
2087 };
2088 assert_eq!(t1, t2);
2089 assert!(t1.ion_eq(&t2));
2090 }
2091
2092 #[rstest]
2093 #[case(TimestampBuilder::with_year(3030).build().unwrap(), "3030T")]
2094 #[case(TimestampBuilder::with_year(3030).with_month(11).build().unwrap(), "3030-11T")]
2095 #[case(TimestampBuilder::with_ymd(3030, 3, 31).build().unwrap(), "3030-03-31T")]
2096 #[case(TimestampBuilder::with_ymd(3030, 3, 31).with_hour_and_minute(17, 31).build().unwrap(), "3030-03-31T17:31-00:00")]
2097 #[case(TimestampBuilder::with_ymd(3030, 3, 31).with_hour_and_minute(17, 31).with_offset(-420).build().unwrap(), "3030-03-31T17:31-07:00")]
2098 #[case(TimestampBuilder::with_ymd(3030, 3, 31).with_hour_and_minute(17, 31).build_utc_fields_at_offset(-420).unwrap(), "3030-03-31T10:31-07:00")]
2099 #[case(TimestampBuilder::with_ymd(3030, 3, 31).with_hms(17, 31, 57).with_offset(0).build().unwrap(), "3030-03-31T17:31:57+00:00")]
2100 #[case(TimestampBuilder::with_ymd(3030, 3, 31).with_hms(17, 31, 57).with_milliseconds(27).with_offset(0).build().unwrap(), "3030-03-31T17:31:57.027+00:00")]
2101 #[case(TimestampBuilder::with_ymd(3030, 3, 31).with_hms(17, 31, 57).with_microseconds(27).with_offset(0).build().unwrap(), "3030-03-31T17:31:57.000027+00:00")]
2102 #[case(TimestampBuilder::with_ymd(3030, 3, 31).with_hms(17, 31, 57).with_nanoseconds(27).with_offset(0).build().unwrap(), "3030-03-31T17:31:57.000000027+00:00")]
2103 #[case(TimestampBuilder::with_ymd(3030, 3, 31).with_hms(17, 31, 57).with_fractional_seconds(Decimal::new(27, -12)).with_offset(0).build().unwrap(), "3030-03-31T17:31:57.000000000027+00:00")]
2104 fn test_display(#[case] ts: Timestamp, #[case] expect: String) {
2105 let mut buf = Vec::new();
2106 write!(&mut buf, "{ts}").unwrap();
2107 assert_eq!(expect, String::from_utf8(buf).unwrap());
2108 }
2109}