1use core::time::Duration;
2
3use crate::{
4 civil::{Date, DateTime, ISOWeekDate, Time},
5 error::{fmt::temporal::Error as E, Error},
6 fmt::{
7 buffer::{ArrayBuffer, BorrowedBuffer, BorrowedWriter},
8 temporal::{Pieces, PiecesOffset, TimeZoneAnnotationKind},
9 Write,
10 },
11 span::{Span, UnitSet},
12 tz::{Offset, TimeZone},
13 SignedDuration, Timestamp, Unit, Zoned,
14};
15
16const REASONABLE_ZONED_LEN: usize = 72;
51
52const REASONABLE_PIECES_LEN: usize = 73;
65
66const MAX_TIMESTAMP_ZULU_LEN: usize = 33;
71
72const MAX_TIMESTAMP_OFFSET_LEN: usize = 38;
77
78const MAX_DATETIME_LEN: usize = 32;
82
83const MAX_DATE_LEN: usize = 13;
87
88const MAX_TIME_LEN: usize = 18;
92
93const MAX_OFFSET_LEN: usize = 9;
97
98const MAX_ISO_WEEK_DATE_LEN: usize = 13;
102
103const MAX_SPAN_LEN: usize = 78;
111
112const MAX_DURATION_LEN: usize = 35;
118
119#[derive(Clone, Debug)]
120pub(super) struct DateTimePrinter {
121 lowercase: bool,
122 separator: u8,
123 precision: Option<u8>,
124}
125
126impl DateTimePrinter {
127 pub(super) const fn new() -> DateTimePrinter {
128 DateTimePrinter { lowercase: false, separator: b'T', precision: None }
129 }
130
131 pub(super) const fn lowercase(self, yes: bool) -> DateTimePrinter {
132 DateTimePrinter { lowercase: yes, ..self }
133 }
134
135 pub(super) const fn separator(self, ascii_char: u8) -> DateTimePrinter {
136 assert!(ascii_char.is_ascii(), "RFC3339 separator must be ASCII");
137 DateTimePrinter { separator: ascii_char, ..self }
138 }
139
140 pub(super) const fn precision(
141 self,
142 precision: Option<u8>,
143 ) -> DateTimePrinter {
144 DateTimePrinter { precision, ..self }
145 }
146
147 pub(super) fn print_zoned(
148 &self,
149 zdt: &Zoned,
150 wtr: &mut dyn Write,
151 ) -> Result<(), Error> {
152 const BASE: usize = 19 + 6 + 2;
171
172 let mut runtime_allocation = BASE
179 + zdt.time_zone().iana_name().map(|name| name.len()).unwrap_or(11);
180 if zdt.year() < 0 {
183 runtime_allocation += 3;
184 }
185 if zdt.subsec_nanosecond() != 0 || self.precision.is_some() {
189 runtime_allocation += 10;
190 }
191
192 if runtime_allocation <= REASONABLE_ZONED_LEN {
197 return BorrowedBuffer::with_writer::<REASONABLE_ZONED_LEN>(
198 wtr,
199 runtime_allocation,
200 |bbuf| Ok(self.print_zoned_buf(zdt, bbuf)),
201 );
202 }
203
204 let mut buf = ArrayBuffer::<REASONABLE_ZONED_LEN>::default();
207 let mut bbuf = buf.as_borrowed();
208 let mut wtr = BorrowedWriter::new(&mut bbuf, wtr);
209 self.print_zoned_wtr(zdt, &mut wtr)?;
210 wtr.finish()
211 }
212
213 fn print_zoned_buf(&self, zdt: &Zoned, bbuf: &mut BorrowedBuffer<'_>) {
214 self.print_datetime_buf(&zdt.datetime(), bbuf);
215 let tz = zdt.time_zone();
216 if tz.is_unknown() {
217 bbuf.write_str("Z[Etc/Unknown]");
218 } else {
219 self.print_offset_rounded_buf(&zdt.offset(), bbuf);
220 self.print_time_zone_annotation_buf(&tz, &zdt.offset(), bbuf);
221 }
222 }
223
224 fn print_zoned_wtr(
225 &self,
226 zdt: &Zoned,
227 wtr: &mut BorrowedWriter<'_, '_, '_>,
228 ) -> Result<(), Error> {
229 self.print_datetime_wtr(&zdt.datetime(), wtr)?;
230 let tz = zdt.time_zone();
231 if tz.is_unknown() {
232 wtr.write_str("Z[Etc/Unknown]")?;
233 } else {
234 self.print_offset_rounded_wtr(&zdt.offset(), wtr)?;
235 self.print_time_zone_annotation_wtr(&tz, &zdt.offset(), wtr)?;
236 }
237 Ok(())
238 }
239
240 pub(super) fn print_timestamp(
241 &self,
242 timestamp: &Timestamp,
243 wtr: &mut dyn Write,
244 ) -> Result<(), Error> {
245 let mut runtime_allocation = MAX_TIMESTAMP_ZULU_LEN;
246 if timestamp.subsec_nanosecond() == 0 && self.precision.is_none() {
248 runtime_allocation -= 10;
249 }
250 BorrowedBuffer::with_writer::<MAX_TIMESTAMP_ZULU_LEN>(
251 wtr,
252 runtime_allocation,
253 |bbuf| Ok(self.print_timestamp_buf(timestamp, bbuf)),
254 )
255 }
256
257 fn print_timestamp_buf(
258 &self,
259 timestamp: &Timestamp,
260 bbuf: &mut BorrowedBuffer<'_>,
261 ) {
262 let dt = Offset::UTC.to_datetime(*timestamp);
263 self.print_datetime_buf(&dt, bbuf);
264 self.print_zulu_buf(bbuf);
265 }
266
267 pub(super) fn print_timestamp_with_offset(
268 &self,
269 timestamp: &Timestamp,
270 offset: Offset,
271 wtr: &mut dyn Write,
272 ) -> Result<(), Error> {
273 let mut runtime_allocation = MAX_TIMESTAMP_OFFSET_LEN;
274 if timestamp.subsec_nanosecond() == 0 && self.precision.is_none() {
276 runtime_allocation -= 10;
277 }
278 BorrowedBuffer::with_writer::<MAX_TIMESTAMP_OFFSET_LEN>(
279 wtr,
280 runtime_allocation,
281 |bbuf| {
282 Ok(self
283 .print_timestamp_with_offset_buf(timestamp, offset, bbuf))
284 },
285 )
286 }
287
288 fn print_timestamp_with_offset_buf(
289 &self,
290 timestamp: &Timestamp,
291 offset: Offset,
292 bbuf: &mut BorrowedBuffer<'_>,
293 ) {
294 let dt = offset.to_datetime(*timestamp);
295 self.print_datetime_buf(&dt, bbuf);
296 self.print_offset_rounded_buf(&offset, bbuf);
297 }
298
299 pub(super) fn print_datetime(
300 &self,
301 dt: &DateTime,
302 wtr: &mut dyn Write,
303 ) -> Result<(), Error> {
304 let mut runtime_allocation = MAX_DATETIME_LEN;
305 if dt.subsec_nanosecond() == 0 && self.precision.is_none() {
307 runtime_allocation -= 10;
308 }
309 BorrowedBuffer::with_writer::<MAX_DATETIME_LEN>(
310 wtr,
311 runtime_allocation,
312 |bbuf| Ok(self.print_datetime_buf(dt, bbuf)),
313 )
314 }
315
316 fn print_datetime_buf(
317 &self,
318 dt: &DateTime,
319 bbuf: &mut BorrowedBuffer<'_>,
320 ) {
321 self.print_date_buf(&dt.date(), bbuf);
322 bbuf.write_ascii_char(if self.lowercase {
323 self.separator.to_ascii_lowercase()
324 } else {
325 self.separator
326 });
327 self.print_time_buf(&dt.time(), bbuf);
328 }
329
330 fn print_datetime_wtr(
331 &self,
332 dt: &DateTime,
333 wtr: &mut BorrowedWriter<'_, '_, '_>,
334 ) -> Result<(), Error> {
335 self.print_date_wtr(&dt.date(), wtr)?;
336 wtr.write_ascii_char(if self.lowercase {
337 self.separator.to_ascii_lowercase()
338 } else {
339 self.separator
340 })?;
341 self.print_time_wtr(&dt.time(), wtr)?;
342 Ok(())
343 }
344
345 pub(super) fn print_date(
346 &self,
347 date: &Date,
348 wtr: &mut dyn Write,
349 ) -> Result<(), Error> {
350 BorrowedBuffer::with_writer::<MAX_DATE_LEN>(
351 wtr,
352 MAX_DATE_LEN,
353 |bbuf| Ok(self.print_date_buf(date, bbuf)),
354 )
355 }
356
357 fn print_date_buf(&self, date: &Date, bbuf: &mut BorrowedBuffer<'_>) {
358 let year = date.year();
359 if year < 0 {
360 bbuf.write_str("-00");
361 }
362 bbuf.write_int_pad4(year.unsigned_abs());
363 bbuf.write_ascii_char(b'-');
364 bbuf.write_int_pad2(date.month().unsigned_abs());
365 bbuf.write_ascii_char(b'-');
366 bbuf.write_int_pad2(date.day().unsigned_abs());
367 }
368
369 fn print_date_wtr(
370 &self,
371 date: &Date,
372 wtr: &mut BorrowedWriter<'_, '_, '_>,
373 ) -> Result<(), Error> {
374 let year = date.year();
375 if year < 0 {
376 wtr.write_str("-00")?;
377 }
378 wtr.write_int_pad4(year.unsigned_abs())?;
379 wtr.write_ascii_char(b'-')?;
380 wtr.write_int_pad2(date.month().unsigned_abs())?;
381 wtr.write_ascii_char(b'-')?;
382 wtr.write_int_pad2(date.day().unsigned_abs())?;
383 Ok(())
384 }
385
386 pub(super) fn print_time(
387 &self,
388 time: &Time,
389 wtr: &mut dyn Write,
390 ) -> Result<(), Error> {
391 let mut runtime_allocation = MAX_TIME_LEN;
392 if time.subsec_nanosecond() == 0 && self.precision.is_none() {
394 runtime_allocation -= 10;
395 }
396 BorrowedBuffer::with_writer::<MAX_TIME_LEN>(
397 wtr,
398 runtime_allocation,
399 |bbuf| Ok(self.print_time_buf(time, bbuf)),
400 )
401 }
402
403 fn print_time_buf(&self, time: &Time, bbuf: &mut BorrowedBuffer<'_>) {
404 bbuf.write_int_pad2(time.hour().unsigned_abs());
405 bbuf.write_ascii_char(b':');
406 bbuf.write_int_pad2(time.minute().unsigned_abs());
407 bbuf.write_ascii_char(b':');
408 bbuf.write_int_pad2(time.second().unsigned_abs());
409 let fractional_nanosecond = time.subsec_nanosecond();
410 if self.precision.map_or(fractional_nanosecond != 0, |p| p > 0) {
411 bbuf.write_ascii_char(b'.');
412 bbuf.write_fraction(
413 self.precision,
414 fractional_nanosecond.unsigned_abs(),
415 );
416 }
417 }
418
419 fn print_time_wtr(
420 &self,
421 time: &Time,
422 wtr: &mut BorrowedWriter<'_, '_, '_>,
423 ) -> Result<(), Error> {
424 wtr.write_int_pad2(time.hour().unsigned_abs())?;
425 wtr.write_ascii_char(b':')?;
426 wtr.write_int_pad2(time.minute().unsigned_abs())?;
427 wtr.write_ascii_char(b':')?;
428 wtr.write_int_pad2(time.second().unsigned_abs())?;
429 let fractional_nanosecond = time.subsec_nanosecond();
430 if self.precision.map_or(fractional_nanosecond != 0, |p| p > 0) {
431 wtr.write_ascii_char(b'.')?;
432 wtr.write_fraction(
433 self.precision,
434 fractional_nanosecond.unsigned_abs(),
435 )?;
436 }
437 Ok(())
438 }
439
440 pub(super) fn print_time_zone<W: Write>(
441 &self,
442 tz: &TimeZone,
443 mut wtr: W,
444 ) -> Result<(), Error> {
445 self.print_time_zone_wtr(tz, &mut wtr)
455 }
456
457 fn print_time_zone_wtr(
458 &self,
459 tz: &TimeZone,
460 wtr: &mut dyn Write,
461 ) -> Result<(), Error> {
462 if let Some(iana_name) = tz.iana_name() {
463 return wtr.write_str(iana_name);
464 }
465 if tz.is_unknown() {
466 return wtr.write_str("Etc/Unknown");
467 }
468 if let Ok(offset) = tz.to_fixed_offset() {
469 let mut buf = ArrayBuffer::<MAX_OFFSET_LEN>::default();
472 let mut bbuf = buf.as_borrowed();
473 self.print_offset_full_precision(&offset, &mut bbuf);
474 return wtr.write_str(bbuf.filled());
475 }
476 if let Some(posix_tz) = tz.posix_tz() {
477 use core::fmt::Write as _;
478
479 return write!(crate::fmt::StdFmtWrite(wtr), "{posix_tz}")
485 .map_err(|_| {
486 Error::from(crate::error::fmt::Error::StdFmtWriteAdapter)
487 });
488 }
489 Err(Error::from(E::PrintTimeZoneFailure))
499 }
500
501 pub(super) fn print_pieces<W: Write>(
502 &self,
503 pieces: &Pieces,
504 mut wtr: W,
505 ) -> Result<(), Error> {
506 let mut buf = ArrayBuffer::<REASONABLE_PIECES_LEN>::default();
513 let mut bbuf = buf.as_borrowed();
514 let mut wtr = BorrowedWriter::new(&mut bbuf, &mut wtr);
515 self.print_pieces_wtr(pieces, &mut wtr)?;
516 wtr.finish()
517 }
518
519 fn print_pieces_wtr(
520 &self,
521 pieces: &Pieces,
522 wtr: &mut BorrowedWriter<'_, '_, '_>,
523 ) -> Result<(), Error> {
524 if let Some(time) = pieces.time() {
525 let dt = DateTime::from_parts(pieces.date(), time);
526 self.print_datetime_wtr(&dt, wtr)?;
527 if let Some(poffset) = pieces.offset() {
528 self.print_pieces_offset(&poffset, wtr)?;
529 }
530 } else if let Some(poffset) = pieces.offset() {
531 let dt = DateTime::from_parts(pieces.date(), Time::midnight());
535 self.print_datetime_wtr(&dt, wtr)?;
536 self.print_pieces_offset(&poffset, wtr)?;
537 } else {
538 self.print_date_wtr(&pieces.date(), wtr)?;
542 }
543 if let Some(ann) = pieces.time_zone_annotation() {
548 wtr.write_ascii_char(b'[')?;
549 if ann.is_critical() {
550 wtr.write_ascii_char(b'!')?;
551 }
552 match *ann.kind() {
553 TimeZoneAnnotationKind::Named(ref name) => {
554 wtr.write_str(name.as_str())?;
555 }
556 TimeZoneAnnotationKind::Offset(offset) => {
557 self.print_offset_rounded_wtr(&offset, wtr)?;
558 }
559 }
560 wtr.write_ascii_char(b']')?;
561 }
562 Ok(())
563 }
564
565 pub(super) fn print_iso_week_date(
566 &self,
567 iso_week_date: &ISOWeekDate,
568 wtr: &mut dyn Write,
569 ) -> Result<(), Error> {
570 BorrowedBuffer::with_writer::<MAX_ISO_WEEK_DATE_LEN>(
571 wtr,
572 MAX_ISO_WEEK_DATE_LEN,
573 |bbuf| Ok(self.print_iso_week_date_buf(iso_week_date, bbuf)),
574 )
575 }
576
577 fn print_iso_week_date_buf(
578 &self,
579 iso_week_date: &ISOWeekDate,
580 bbuf: &mut BorrowedBuffer<'_>,
581 ) {
582 let year = iso_week_date.year();
583 if year < 0 {
584 bbuf.write_str("-00");
585 }
586 bbuf.write_int_pad4(year.unsigned_abs());
587 bbuf.write_ascii_char(b'-');
588 bbuf.write_ascii_char(if self.lowercase { b'w' } else { b'W' });
589 bbuf.write_int_pad2(iso_week_date.week().unsigned_abs());
590 bbuf.write_ascii_char(b'-');
591 bbuf.write_int1(
592 iso_week_date.weekday().to_monday_one_offset().unsigned_abs(),
593 );
594 }
595
596 fn print_pieces_offset(
597 &self,
598 poffset: &PiecesOffset,
599 wtr: &mut BorrowedWriter<'_, '_, '_>,
600 ) -> Result<(), Error> {
601 match *poffset {
602 PiecesOffset::Zulu => self.print_zulu_wtr(wtr),
603 PiecesOffset::Numeric(ref noffset) => {
604 if noffset.offset().is_zero() && noffset.is_negative() {
605 wtr.write_str("-00:00")
606 } else {
607 self.print_offset_rounded_wtr(&noffset.offset(), wtr)
608 }
609 }
610 }
611 }
612
613 fn print_offset_rounded_buf(
618 &self,
619 offset: &Offset,
620 bbuf: &mut BorrowedBuffer<'_>,
621 ) {
622 bbuf.write_ascii_char(if offset.is_negative() { b'-' } else { b'+' });
623 let (offset_hours, offset_minutes) = offset.round_to_nearest_minute();
624 bbuf.write_int_pad2(offset_hours);
625 bbuf.write_ascii_char(b':');
626 bbuf.write_int_pad2(offset_minutes);
627 }
628
629 fn print_offset_rounded_wtr(
634 &self,
635 offset: &Offset,
636 wtr: &mut BorrowedWriter<'_, '_, '_>,
637 ) -> Result<(), Error> {
638 wtr.write_ascii_char(if offset.is_negative() { b'-' } else { b'+' })?;
639 let (offset_hours, offset_minutes) = offset.round_to_nearest_minute();
640 wtr.write_int_pad2(offset_hours)?;
641 wtr.write_ascii_char(b':')?;
642 wtr.write_int_pad2(offset_minutes)?;
643 Ok(())
644 }
645
646 fn print_offset_full_precision(
652 &self,
653 offset: &Offset,
654 bbuf: &mut BorrowedBuffer<'_>,
655 ) {
656 bbuf.write_ascii_char(if offset.is_negative() { b'-' } else { b'+' });
657 let hours = offset.part_hours().unsigned_abs();
658 let minutes = offset.part_minutes().unsigned_abs();
659 let seconds = offset.part_seconds().unsigned_abs();
660 bbuf.write_int_pad2(hours);
661 bbuf.write_ascii_char(b':');
662 bbuf.write_int_pad2(minutes);
663 if seconds > 0 {
664 bbuf.write_ascii_char(b':');
665 bbuf.write_int_pad2(seconds);
666 }
667 }
668
669 fn print_zulu_buf(&self, bbuf: &mut BorrowedBuffer<'_>) {
674 bbuf.write_ascii_char(if self.lowercase { b'z' } else { b'Z' });
675 }
676
677 fn print_zulu_wtr(
682 &self,
683 wtr: &mut BorrowedWriter<'_, '_, '_>,
684 ) -> Result<(), Error> {
685 wtr.write_ascii_char(if self.lowercase { b'z' } else { b'Z' })
686 }
687
688 fn print_time_zone_annotation_buf(
696 &self,
697 time_zone: &TimeZone,
698 offset: &Offset,
699 bbuf: &mut BorrowedBuffer<'_>,
700 ) {
701 bbuf.write_ascii_char(b'[');
702 if let Some(iana_name) = time_zone.iana_name() {
703 bbuf.write_str(iana_name);
704 } else {
705 self.print_offset_rounded_buf(offset, bbuf);
706 }
707 bbuf.write_ascii_char(b']');
708 }
709
710 fn print_time_zone_annotation_wtr(
718 &self,
719 time_zone: &TimeZone,
720 offset: &Offset,
721 wtr: &mut BorrowedWriter<'_, '_, '_>,
722 ) -> Result<(), Error> {
723 wtr.write_ascii_char(b'[')?;
724 if let Some(iana_name) = time_zone.iana_name() {
725 wtr.write_str(iana_name)?;
726 } else {
727 self.print_offset_rounded_wtr(offset, wtr)?;
728 }
729 wtr.write_ascii_char(b']')?;
730 Ok(())
731 }
732}
733
734impl Default for DateTimePrinter {
735 fn default() -> DateTimePrinter {
736 DateTimePrinter::new()
737 }
738}
739
740#[derive(Debug)]
744pub(super) struct SpanPrinter {
745 designators: &'static Designators,
747}
748
749impl SpanPrinter {
750 pub(super) const fn new() -> SpanPrinter {
752 SpanPrinter { designators: DESIGNATORS_UPPERCASE }
753 }
754
755 pub(super) const fn lowercase(self, yes: bool) -> SpanPrinter {
759 SpanPrinter {
760 designators: if yes {
761 DESIGNATORS_LOWERCASE
762 } else {
763 DESIGNATORS_UPPERCASE
764 },
765 }
766 }
767
768 pub(super) fn print_span<W: Write>(
772 &self,
773 span: &Span,
774 mut wtr: W,
775 ) -> Result<(), Error> {
776 let mut buf = ArrayBuffer::<MAX_SPAN_LEN>::default();
777 let mut bbuf = buf.as_borrowed();
778 self.print_span_impl(span, &mut bbuf);
779 wtr.write_str(bbuf.filled())
780 }
781
782 fn print_span_impl(&self, span: &Span, bbuf: &mut BorrowedBuffer<'_>) {
783 static SUBSECOND: UnitSet = UnitSet::from_slice(&[
784 Unit::Millisecond,
785 Unit::Microsecond,
786 Unit::Nanosecond,
787 ]);
788
789 if span.is_negative() {
790 bbuf.write_ascii_char(b'-');
791 }
792 bbuf.write_ascii_char(b'P');
793
794 let units = span.units();
795 if units.contains(Unit::Year) {
796 bbuf.write_int(span.get_years_unsigned());
797 bbuf.write_ascii_char(self.label(Unit::Year));
798 }
799 if units.contains(Unit::Month) {
800 bbuf.write_int(span.get_months_unsigned());
801 bbuf.write_ascii_char(self.label(Unit::Month));
802 }
803 if units.contains(Unit::Week) {
804 bbuf.write_int(span.get_weeks_unsigned());
805 bbuf.write_ascii_char(self.label(Unit::Week));
806 }
807 if units.contains(Unit::Day) {
808 bbuf.write_int(span.get_days_unsigned());
809 bbuf.write_ascii_char(self.label(Unit::Day));
810 }
811
812 if units.only_time().is_empty() {
813 if units.only_calendar().is_empty() {
814 bbuf.write_ascii_char(b'T');
815 bbuf.write_ascii_char(b'0');
816 bbuf.write_ascii_char(self.label(Unit::Second));
817 }
818 return;
819 }
820
821 bbuf.write_ascii_char(b'T');
822
823 if units.contains(Unit::Hour) {
824 bbuf.write_int(span.get_hours_unsigned());
825 bbuf.write_ascii_char(self.label(Unit::Hour));
826 }
827 if units.contains(Unit::Minute) {
828 bbuf.write_int(span.get_minutes_unsigned());
829 bbuf.write_ascii_char(self.label(Unit::Minute));
830 }
831
832 let has_subsecond = !units.intersection(SUBSECOND).is_empty();
837 if units.contains(Unit::Second) && !has_subsecond {
838 bbuf.write_int(span.get_seconds_unsigned());
839 bbuf.write_ascii_char(self.label(Unit::Second));
840 } else if has_subsecond {
841 let (seconds, millis, micros, nanos) = (
847 Duration::from_secs(span.get_seconds_unsigned()),
848 Duration::from_millis(span.get_milliseconds_unsigned()),
849 Duration::from_micros(span.get_microseconds_unsigned()),
850 Duration::from_nanos(span.get_nanoseconds_unsigned()),
851 );
852 let total = seconds + millis + micros + nanos;
856 let (secs, subsecs) = (total.as_secs(), total.subsec_nanos());
857 bbuf.write_int(secs);
858 if subsecs != 0 {
859 bbuf.write_ascii_char(b'.');
860 bbuf.write_fraction(None, subsecs);
861 }
862 bbuf.write_ascii_char(self.label(Unit::Second));
863 }
864 }
865
866 pub(super) fn print_signed_duration<W: Write>(
870 &self,
871 dur: &SignedDuration,
872 mut wtr: W,
873 ) -> Result<(), Error> {
874 let mut buf = ArrayBuffer::<MAX_DURATION_LEN>::default();
875 let mut bbuf = buf.as_borrowed();
876 if dur.is_negative() {
877 bbuf.write_ascii_char(b'-');
878 }
879 self.print_unsigned_duration_impl(&dur.unsigned_abs(), &mut bbuf);
880 wtr.write_str(bbuf.filled())
881 }
882
883 pub(super) fn print_unsigned_duration<W: Write>(
887 &self,
888 dur: &Duration,
889 mut wtr: W,
890 ) -> Result<(), Error> {
891 let mut buf = ArrayBuffer::<MAX_DURATION_LEN>::default();
892 let mut bbuf = buf.as_borrowed();
893 self.print_unsigned_duration_impl(dur, &mut bbuf);
894 wtr.write_str(bbuf.filled())
895 }
896
897 fn print_unsigned_duration_impl(
898 &self,
899 dur: &Duration,
900 bbuf: &mut BorrowedBuffer<'_>,
901 ) {
902 bbuf.write_ascii_char(b'P');
903 bbuf.write_ascii_char(b'T');
904
905 let (mut secs, nanos) = (dur.as_secs(), dur.subsec_nanos());
906 let non_zero_greater_than_second = secs >= 60;
907 if non_zero_greater_than_second {
908 let hours = secs / (60 * 60);
909 secs %= 60 * 60;
910 let minutes = secs / 60;
911 secs = secs % 60;
912 if hours != 0 {
913 bbuf.write_int(hours);
914 bbuf.write_ascii_char(self.label(Unit::Hour));
915 }
916 if minutes != 0 {
917 bbuf.write_int(minutes);
918 bbuf.write_ascii_char(self.label(Unit::Minute));
919 }
920 }
921 if !non_zero_greater_than_second || secs != 0 || nanos != 0 {
922 bbuf.write_int(secs);
923 if nanos != 0 {
924 bbuf.write_ascii_char(b'.');
925 bbuf.write_fraction(None, nanos);
926 }
927 bbuf.write_ascii_char(self.label(Unit::Second));
928 }
929 }
930
931 fn label(&self, unit: Unit) -> u8 {
935 self.designators.designator(unit)
936 }
937}
938
939#[derive(Clone, Debug)]
940struct Designators {
941 map: [u8; 10],
943}
944
945const DESIGNATORS_UPPERCASE: &'static Designators = &Designators {
946 map: [0, 0, 0, b'S', b'M', b'H', b'D', b'W', b'M', b'Y'],
949};
950
951const DESIGNATORS_LOWERCASE: &'static Designators = &Designators {
952 map: [0, 0, 0, b's', b'm', b'h', b'd', b'w', b'm', b'y'],
955};
956
957impl Designators {
958 fn designator(&self, unit: Unit) -> u8 {
960 self.map[unit as usize]
961 }
962}
963
964#[cfg(feature = "alloc")]
965#[cfg(test)]
966mod tests {
967 use alloc::string::{String, ToString};
968
969 use crate::{
970 civil::{date, time, Weekday},
971 fmt::StdFmtWrite,
972 span::ToSpan,
973 util::b,
974 };
975
976 use super::*;
977
978 #[test]
979 fn print_zoned() {
980 if crate::tz::db().is_definitively_empty() {
981 return;
982 }
983
984 let p = || DateTimePrinter::new();
985 let via_io = |dtp: DateTimePrinter, zdt| {
986 let mut buf = String::new();
987 dtp.print_zoned(&zdt, &mut StdFmtWrite(&mut buf)).unwrap();
988 buf
989 };
990 let to_string = |dtp: DateTimePrinter, zdt| {
991 let mut buf = String::new();
992 dtp.print_zoned(&zdt, &mut buf).unwrap();
993 let got_via_io = via_io(dtp, zdt);
994 assert_eq!(
995 buf, got_via_io,
996 "expected writes to `&mut String` to match `&mut StdFmtWrite`"
997 );
998 buf
999 };
1000
1001 let dt = date(2024, 3, 10).at(5, 34, 45, 0);
1002 let zdt = dt.in_tz("America/New_York").unwrap();
1003 let got = to_string(p(), zdt);
1004 assert_eq!(got, "2024-03-10T05:34:45-04:00[America/New_York]");
1005
1006 let dt = date(2024, 3, 10).at(5, 34, 45, 0);
1007 let zdt = dt.in_tz("America/New_York").unwrap();
1008 let zdt = zdt.with_time_zone(TimeZone::UTC);
1009 let got = to_string(p(), zdt);
1010 assert_eq!(got, "2024-03-10T09:34:45+00:00[UTC]");
1011
1012 let dt = date(2024, 3, 10).at(5, 34, 45, 0);
1013 let zdt = dt.to_zoned(TimeZone::fixed(Offset::MIN)).unwrap();
1014 let got = to_string(p(), zdt);
1015 assert_eq!(got, "2024-03-10T05:34:45-25:59[-25:59]");
1016
1017 let dt = date(2024, 3, 10).at(5, 34, 45, 0);
1018 let zdt = dt.to_zoned(TimeZone::fixed(Offset::MAX)).unwrap();
1019 let got = to_string(p(), zdt);
1020 assert_eq!(got, "2024-03-10T05:34:45+25:59[+25:59]");
1021
1022 let dt = date(2024, 3, 10).at(5, 34, 45, 123_456_789);
1023 let zdt = dt.in_tz("America/New_York").unwrap();
1024 let got = to_string(p(), zdt);
1025 assert_eq!(
1026 got,
1027 "2024-03-10T05:34:45.123456789-04:00[America/New_York]"
1028 );
1029
1030 let dt = date(2024, 3, 10).at(5, 34, 45, 0);
1031 let zdt = dt.in_tz("America/New_York").unwrap();
1032 let got = to_string(p().precision(Some(9)), zdt);
1033 assert_eq!(
1034 got,
1035 "2024-03-10T05:34:45.000000000-04:00[America/New_York]"
1036 );
1037
1038 let dt = date(-9999, 3, 1).at(23, 59, 59, 999_999_999);
1039 let zdt = dt.in_tz("America/Argentina/ComodRivadavia").unwrap();
1040 let got = to_string(p().precision(Some(9)), zdt);
1041 assert_eq!(
1042 got,
1043 "-009999-03-01T23:59:59.999999999-04:23[America/Argentina/ComodRivadavia]",
1044 );
1045
1046 let tz = TimeZone::tzif(
1049 "Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz/Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz/Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz",
1050 crate::tz::testdata::TzifTestFile::get("America/New_York").data,
1051 ).unwrap();
1052 let dt = date(-9999, 3, 1).at(23, 59, 59, 999_999_999);
1053 let zdt = dt.to_zoned(tz).unwrap();
1054 let got = to_string(p().precision(Some(9)), zdt);
1055 assert_eq!(
1056 got,
1057 "-009999-03-01T23:59:59.999999999-04:56[Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz/Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz/Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz]",
1058 );
1059 }
1060
1061 #[test]
1062 fn print_timestamp_zulu() {
1063 let p = || DateTimePrinter::new();
1064 let via_io = |dtp: DateTimePrinter, ts| {
1065 let mut buf = String::new();
1066 dtp.print_timestamp(&ts, &mut StdFmtWrite(&mut buf)).unwrap();
1067 buf
1068 };
1069 let to_string = |dtp: DateTimePrinter, ts| {
1070 let mut buf = String::new();
1071 dtp.print_timestamp(&ts, &mut buf).unwrap();
1072 let got_via_io = via_io(dtp, ts);
1073 assert_eq!(
1074 buf, got_via_io,
1075 "expected writes to `&mut String` to match `&mut StdFmtWrite`"
1076 );
1077 buf
1078 };
1079
1080 let tz = TimeZone::fixed(-Offset::constant(4));
1081
1082 let dt = date(2024, 3, 10).at(5, 34, 45, 0);
1083 let zoned: Zoned = dt.to_zoned(tz.clone()).unwrap();
1084 let got = to_string(p(), zoned.timestamp());
1085 assert_eq!(got, "2024-03-10T09:34:45Z");
1086
1087 let dt = date(-2024, 3, 10).at(5, 34, 45, 0);
1088 let zoned: Zoned = dt.to_zoned(tz.clone()).unwrap();
1089 let got = to_string(p(), zoned.timestamp());
1090 assert_eq!(got, "-002024-03-10T09:34:45Z");
1091
1092 let dt = date(2024, 3, 10).at(5, 34, 45, 123_456_789);
1093 let zoned: Zoned = dt.to_zoned(tz.clone()).unwrap();
1094 let got = to_string(p(), zoned.timestamp());
1095 assert_eq!(got, "2024-03-10T09:34:45.123456789Z");
1096
1097 let dt = date(2024, 3, 10).at(5, 34, 45, 0);
1098 let zoned: Zoned = dt.to_zoned(tz.clone()).unwrap();
1099 let got = to_string(p().precision(Some(9)), zoned.timestamp());
1100 assert_eq!(got, "2024-03-10T09:34:45.000000000Z");
1101 }
1102
1103 #[test]
1104 fn print_timestamp_with_offset() {
1105 let p = || DateTimePrinter::new();
1106 let via_io = |dtp: DateTimePrinter, ts, offset| {
1107 let mut buf = String::new();
1108 dtp.print_timestamp_with_offset(
1109 &ts,
1110 offset,
1111 &mut StdFmtWrite(&mut buf),
1112 )
1113 .unwrap();
1114 buf
1115 };
1116 let to_string = |dtp: DateTimePrinter, ts, offset| {
1117 let mut buf = String::new();
1118 dtp.print_timestamp_with_offset(&ts, offset, &mut buf).unwrap();
1119 let got_via_io = via_io(dtp, ts, offset);
1120 assert_eq!(
1121 buf, got_via_io,
1122 "expected writes to `&mut String` to match `&mut StdFmtWrite`"
1123 );
1124 buf
1125 };
1126
1127 let tz = TimeZone::fixed(-Offset::constant(4));
1128
1129 let dt = date(2024, 3, 10).at(5, 34, 45, 0);
1130 let zoned: Zoned = dt.to_zoned(tz.clone()).unwrap();
1131 let got = to_string(p(), zoned.timestamp(), zoned.offset());
1132 assert_eq!(got, "2024-03-10T05:34:45-04:00");
1133
1134 let dt = date(-2024, 3, 10).at(5, 34, 45, 0);
1135 let zoned: Zoned = dt.to_zoned(tz.clone()).unwrap();
1136 let got = to_string(p(), zoned.timestamp(), zoned.offset());
1137 assert_eq!(got, "-002024-03-10T05:34:45-04:00");
1138
1139 let dt = date(2024, 3, 10).at(5, 34, 45, 123_456_789);
1140 let zoned: Zoned = dt.to_zoned(tz.clone()).unwrap();
1141 let got = to_string(p(), zoned.timestamp(), zoned.offset());
1142 assert_eq!(got, "2024-03-10T05:34:45.123456789-04:00");
1143
1144 let dt = date(2024, 3, 10).at(5, 34, 45, 0);
1145 let zoned: Zoned = dt.to_zoned(tz.clone()).unwrap();
1146 let got = to_string(
1147 p().precision(Some(9)),
1148 zoned.timestamp(),
1149 zoned.offset(),
1150 );
1151 assert_eq!(got, "2024-03-10T05:34:45.000000000-04:00");
1152
1153 let dt = DateTime::MIN;
1154 let zoned: Zoned = dt.to_zoned(TimeZone::fixed(Offset::MIN)).unwrap();
1155 let got = to_string(
1156 p().precision(Some(9)),
1157 zoned.timestamp(),
1158 zoned.offset(),
1159 );
1160 assert_eq!(got, "-009999-01-01T00:00:00.000000000-25:59");
1161 }
1162
1163 #[test]
1164 fn print_datetime() {
1165 let p = || DateTimePrinter::new();
1166 let via_io = |dtp: DateTimePrinter, dt| {
1167 let mut buf = String::new();
1168 dtp.print_datetime(&dt, &mut StdFmtWrite(&mut buf)).unwrap();
1169 buf
1170 };
1171 let to_string = |dtp: DateTimePrinter, dt| {
1172 let mut buf = String::new();
1173 dtp.print_datetime(&dt, &mut buf).unwrap();
1174 let got_via_io = via_io(dtp, dt);
1175 assert_eq!(
1176 buf, got_via_io,
1177 "expected writes to `&mut String` to match `&mut StdFmtWrite`"
1178 );
1179 buf
1180 };
1181
1182 let dt = date(2024, 3, 10).at(5, 34, 45, 0);
1183 let got = to_string(p(), dt);
1184 assert_eq!(got, "2024-03-10T05:34:45");
1185
1186 let dt = date(-2024, 3, 10).at(5, 34, 45, 0);
1187 let got = to_string(p(), dt);
1188 assert_eq!(got, "-002024-03-10T05:34:45");
1189
1190 let dt = date(2024, 3, 10).at(5, 34, 45, 123_456_789);
1191 let got = to_string(p(), dt);
1192 assert_eq!(got, "2024-03-10T05:34:45.123456789");
1193
1194 let dt = date(2024, 3, 10).at(5, 34, 45, 0);
1195 let got = to_string(p().precision(Some(9)), dt);
1196 assert_eq!(got, "2024-03-10T05:34:45.000000000");
1197
1198 let dt = DateTime::MIN;
1199 let got = to_string(p().precision(Some(9)), dt);
1200 assert_eq!(got, "-009999-01-01T00:00:00.000000000");
1201 }
1202
1203 #[test]
1204 fn print_date() {
1205 let p = || DateTimePrinter::new();
1206 let via_io = |dtp: DateTimePrinter, date| {
1207 let mut buf = String::new();
1208 dtp.print_date(&date, &mut StdFmtWrite(&mut buf)).unwrap();
1209 buf
1210 };
1211 let to_string = |dtp: DateTimePrinter, date| {
1212 let mut buf = String::new();
1213 dtp.print_date(&date, &mut buf).unwrap();
1214 let got_via_io = via_io(dtp, date);
1215 assert_eq!(
1216 buf, got_via_io,
1217 "expected writes to `&mut String` to match `&mut StdFmtWrite`"
1218 );
1219 buf
1220 };
1221
1222 let d = date(2024, 3, 10);
1223 let got = to_string(p(), d);
1224 assert_eq!(got, "2024-03-10");
1225
1226 let d = date(-2024, 3, 10);
1227 let got = to_string(p(), d);
1228 assert_eq!(got, "-002024-03-10");
1229
1230 let d = date(2024, 3, 10);
1231 let got = to_string(p(), d);
1232 assert_eq!(got, "2024-03-10");
1233
1234 let d = Date::MIN;
1235 let got = to_string(p().precision(Some(9)), d);
1236 assert_eq!(got, "-009999-01-01");
1237 }
1238
1239 #[test]
1240 fn print_time() {
1241 let p = || DateTimePrinter::new();
1242 let via_io = |dtp: DateTimePrinter, time| {
1243 let mut buf = String::new();
1244 dtp.print_time(&time, &mut StdFmtWrite(&mut buf)).unwrap();
1245 buf
1246 };
1247 let to_string = |dtp: DateTimePrinter, time| {
1248 let mut buf = String::new();
1249 dtp.print_time(&time, &mut buf).unwrap();
1250 let got_via_io = via_io(dtp, time);
1251 assert_eq!(
1252 buf, got_via_io,
1253 "expected writes to `&mut String` to match `&mut StdFmtWrite`"
1254 );
1255 buf
1256 };
1257
1258 let t = time(5, 34, 45, 0);
1259 let got = to_string(p(), t);
1260 assert_eq!(got, "05:34:45");
1261
1262 let t = time(5, 34, 45, 0);
1263 let got = to_string(p(), t);
1264 assert_eq!(got, "05:34:45");
1265
1266 let t = time(5, 34, 45, 123_456_789);
1267 let got = to_string(p(), t);
1268 assert_eq!(got, "05:34:45.123456789");
1269
1270 let t = time(5, 34, 45, 0);
1271 let got = to_string(p().precision(Some(9)), t);
1272 assert_eq!(got, "05:34:45.000000000");
1273
1274 let t = Time::MIN;
1275 let got = to_string(p().precision(Some(9)), t);
1276 assert_eq!(got, "00:00:00.000000000");
1277
1278 let t = Time::MAX;
1279 let got = to_string(p().precision(Some(9)), t);
1280 assert_eq!(got, "23:59:59.999999999");
1281 }
1282
1283 #[test]
1284 fn print_iso_week_date() {
1285 let p = || DateTimePrinter::new();
1286 let via_io = |dtp: DateTimePrinter, date| {
1287 let mut buf = String::new();
1288 dtp.print_iso_week_date(&date, &mut StdFmtWrite(&mut buf))
1289 .unwrap();
1290 buf
1291 };
1292 let to_string = |dtp: DateTimePrinter, date| {
1293 let mut buf = String::new();
1294 dtp.print_iso_week_date(&date, &mut buf).unwrap();
1295 let got_via_io = via_io(dtp, date);
1296 assert_eq!(
1297 buf, got_via_io,
1298 "expected writes to `&mut String` to match `&mut StdFmtWrite`"
1299 );
1300 buf
1301 };
1302
1303 let d = ISOWeekDate::new(2024, 52, Weekday::Monday).unwrap();
1304 let got = to_string(p(), d);
1305 assert_eq!(got, "2024-W52-1");
1306
1307 let d = ISOWeekDate::new(2004, 1, Weekday::Sunday).unwrap();
1308 let got = to_string(p(), d);
1309 assert_eq!(got, "2004-W01-7");
1310
1311 let d = ISOWeekDate::MIN;
1312 let got = to_string(p(), d);
1313 assert_eq!(got, "-009999-W01-1");
1314
1315 let d = ISOWeekDate::MAX;
1316 let got = to_string(p(), d);
1317 assert_eq!(got, "9999-W52-5");
1318 }
1319
1320 #[test]
1321 fn print_pieces() {
1322 let p = || DateTimePrinter::new();
1323 let via_io = |dtp: DateTimePrinter, pieces| {
1324 let mut buf = String::new();
1325 dtp.print_pieces(&pieces, &mut StdFmtWrite(&mut buf)).unwrap();
1326 buf
1327 };
1328 let to_string = |dtp: DateTimePrinter, pieces| {
1329 let mut buf = String::new();
1330 dtp.print_pieces(&pieces, &mut buf).unwrap();
1331 let got_via_io = via_io(dtp, pieces);
1332 assert_eq!(
1333 buf, got_via_io,
1334 "expected writes to `&mut String` to match `&mut StdFmtWrite`"
1335 );
1336 buf
1337 };
1338
1339 let pieces = Pieces::from(date(2024, 3, 10).at(5, 34, 45, 0))
1340 .with_offset(Offset::constant(-4))
1341 .with_time_zone_name("America/New_York");
1342 let got = to_string(p(), pieces);
1343 assert_eq!(got, "2024-03-10T05:34:45-04:00[America/New_York]");
1344
1345 let pieces = Pieces::from(date(2024, 3, 10).at(5, 34, 45, 0))
1346 .with_offset(Offset::UTC)
1347 .with_time_zone_name("UTC");
1348 let got = to_string(p(), pieces);
1349 assert_eq!(got, "2024-03-10T05:34:45+00:00[UTC]");
1350
1351 let pieces = Pieces::from(date(2024, 3, 10).at(5, 34, 45, 0))
1352 .with_offset(Offset::MIN)
1353 .with_time_zone_offset(Offset::MIN);
1354 let got = to_string(p(), pieces);
1355 assert_eq!(got, "2024-03-10T05:34:45-25:59[-25:59]");
1356
1357 let pieces = Pieces::from(date(2024, 3, 10).at(5, 34, 45, 0))
1358 .with_offset(Offset::MAX)
1359 .with_time_zone_offset(Offset::MAX);
1360 let got = to_string(p(), pieces);
1361 assert_eq!(got, "2024-03-10T05:34:45+25:59[+25:59]");
1362
1363 let pieces =
1364 Pieces::from(date(2024, 3, 10).at(5, 34, 45, 123_456_789))
1365 .with_offset(Offset::constant(-4))
1366 .with_time_zone_name("America/New_York");
1367 let got = to_string(p(), pieces);
1368 assert_eq!(
1369 got,
1370 "2024-03-10T05:34:45.123456789-04:00[America/New_York]"
1371 );
1372
1373 let pieces = Pieces::from(date(2024, 3, 10).at(5, 34, 45, 0))
1374 .with_offset(Offset::constant(-4))
1375 .with_time_zone_name("America/New_York");
1376 let got = to_string(p().precision(Some(9)), pieces);
1377 assert_eq!(
1378 got,
1379 "2024-03-10T05:34:45.000000000-04:00[America/New_York]"
1380 );
1381
1382 let pieces =
1383 Pieces::from(date(-9999, 3, 1).at(23, 59, 59, 999_999_999))
1384 .with_offset(Offset::constant(-4))
1385 .with_time_zone_name("America/Argentina/ComodRivadavia");
1386 let got = to_string(p().precision(Some(9)), pieces);
1387 assert_eq!(
1388 got,
1389 "-009999-03-01T23:59:59.999999999-04:00[America/Argentina/ComodRivadavia]",
1390 );
1391
1392 let pieces =
1393 Pieces::parse(
1394 "-009999-03-01T23:59:59.999999999-04:00[!America/Argentina/ComodRivadavia]",
1395 ).unwrap();
1396 let got = to_string(p().precision(Some(9)), pieces);
1397 assert_eq!(
1398 got,
1399 "-009999-03-01T23:59:59.999999999-04:00[!America/Argentina/ComodRivadavia]",
1400 );
1401
1402 let name = "Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz/Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz/Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz";
1404 let pieces =
1405 Pieces::from(date(-9999, 3, 1).at(23, 59, 59, 999_999_999))
1406 .with_offset(Offset::constant(-4))
1407 .with_time_zone_name(name);
1408 let got = to_string(p().precision(Some(9)), pieces);
1409 assert_eq!(
1410 got,
1411 "-009999-03-01T23:59:59.999999999-04:00[Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz/Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz/Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz]",
1412 );
1413 }
1414
1415 #[test]
1416 fn print_time_zone() {
1417 let p = || DateTimePrinter::new();
1418 let via_io = |dtp: DateTimePrinter, tz| {
1419 let mut buf = String::new();
1420 dtp.print_time_zone(&tz, &mut StdFmtWrite(&mut buf)).unwrap();
1421 buf
1422 };
1423 let to_string = |dtp: DateTimePrinter, tz| {
1424 let mut buf = String::new();
1425 dtp.print_time_zone(&tz, &mut buf).unwrap();
1426 let got_via_io = via_io(dtp, tz);
1427 assert_eq!(
1428 buf, got_via_io,
1429 "expected writes to `&mut String` to match `&mut StdFmtWrite`"
1430 );
1431 buf
1432 };
1433
1434 let tztest =
1435 crate::tz::testdata::TzifTestFile::get("America/New_York");
1436 let tz = TimeZone::tzif(tztest.name, tztest.data).unwrap();
1437 let got = to_string(p(), tz);
1438 assert_eq!(got, "America/New_York");
1439
1440 let tz = TimeZone::tzif(
1443 "Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz/Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz/Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz",
1444 tztest.data,
1445 ).unwrap();
1446 let got = to_string(p(), tz);
1447 assert_eq!(
1448 got,
1449 "Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz/Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz/Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz",
1450 );
1451
1452 let tz = TimeZone::UTC;
1453 let got = to_string(p(), tz);
1454 assert_eq!(got, "UTC");
1455
1456 let tz = TimeZone::unknown();
1457 let got = to_string(p(), tz);
1458 assert_eq!(got, "Etc/Unknown");
1459
1460 let tz = TimeZone::fixed(Offset::MIN);
1461 let got = to_string(p(), tz);
1462 assert_eq!(got, "-25:59:59");
1463
1464 let tz = TimeZone::fixed(Offset::MAX);
1465 let got = to_string(p(), tz);
1466 assert_eq!(got, "+25:59:59");
1467
1468 let tz = TimeZone::posix("EST5EDT,M3.2.0,M11.1.0").unwrap();
1469 let got = to_string(p(), tz);
1470 assert_eq!(got, "EST5EDT,M3.2.0,M11.1.0");
1471
1472 let tz = TimeZone::posix(
1473 "ABCDEFGHIJKLMNOPQRSTUVWXY5ABCDEFGHIJKLMNOPQRSTUVWXYT,M3.2.0,M11.1.0",
1474 ).unwrap();
1475 let got = to_string(p(), tz);
1476 assert_eq!(
1477 got,
1478 "ABCDEFGHIJKLMNOPQRSTUVWXY5ABCDEFGHIJKLMNOPQRSTUVWXYT,M3.2.0,M11.1.0",
1479 );
1480
1481 #[cfg(feature = "tz-system")]
1484 {
1485 let tz = TimeZone::tzif_system(tztest.data).unwrap();
1486 let mut buf = String::new();
1487 let err = p().print_time_zone(&tz, &mut buf).unwrap_err();
1488 assert_eq!(
1489 err.to_string(),
1490 "time zones without IANA identifiers that aren't \
1491 either fixed offsets or a POSIX time zone can't be \
1492 serialized (this typically occurs when this is a \
1493 system time zone derived from `/etc/localtime` on \
1494 Unix systems that isn't symlinked to an entry in \
1495 `/usr/share/zoneinfo`)",
1496 );
1497 }
1498 }
1499
1500 #[cfg(not(miri))]
1501 #[test]
1502 fn print_span_basic() {
1503 let p = |span: Span| -> String {
1504 let mut buf = String::new();
1505 SpanPrinter::new().print_span(&span, &mut buf).unwrap();
1506 buf
1507 };
1508
1509 insta::assert_snapshot!(p(Span::new()), @"PT0S");
1510 insta::assert_snapshot!(p(1.second()), @"PT1S");
1511 insta::assert_snapshot!(p(-1.second()), @"-PT1S");
1512 insta::assert_snapshot!(p(
1513 1.second().milliseconds(1).microseconds(1).nanoseconds(1),
1514 ), @"PT1.001001001S");
1515 insta::assert_snapshot!(p(
1516 0.second().milliseconds(999).microseconds(999).nanoseconds(999),
1517 ), @"PT0.999999999S");
1518 insta::assert_snapshot!(p(
1519 1.year().months(1).weeks(1).days(1)
1520 .hours(1).minutes(1).seconds(1)
1521 .milliseconds(1).microseconds(1).nanoseconds(1),
1522 ), @"P1Y1M1W1DT1H1M1.001001001S");
1523 insta::assert_snapshot!(p(
1524 -1.year().months(1).weeks(1).days(1)
1525 .hours(1).minutes(1).seconds(1)
1526 .milliseconds(1).microseconds(1).nanoseconds(1),
1527 ), @"-P1Y1M1W1DT1H1M1.001001001S");
1528 }
1529
1530 #[cfg(not(miri))]
1531 #[test]
1532 fn print_span_subsecond_positive() {
1533 let p = |span: Span| -> String {
1534 let mut buf = String::new();
1535 SpanPrinter::new().print_span(&span, &mut buf).unwrap();
1536 buf
1537 };
1538
1539 insta::assert_snapshot!(p(
1541 0.second().milliseconds(1000).microseconds(1000).nanoseconds(1000),
1542 ), @"PT1.001001S");
1543 insta::assert_snapshot!(p(
1544 1.second().milliseconds(1000).microseconds(1000).nanoseconds(1000),
1545 ), @"PT2.001001S");
1546 insta::assert_snapshot!(p(
1547 0.second()
1548 .milliseconds(b::SpanMilliseconds::MAX),
1549 ), @"PT631107417600S");
1550 insta::assert_snapshot!(p(
1551 0.second()
1552 .microseconds(b::SpanMicroseconds::MAX),
1553 ), @"PT631107417600S");
1554 insta::assert_snapshot!(p(
1555 0.second()
1556 .nanoseconds(b::SpanNanoseconds::MAX),
1557 ), @"PT9223372036.854775807S");
1558
1559 insta::assert_snapshot!(p(
1560 0.second()
1561 .milliseconds(b::SpanMilliseconds::MAX)
1562 .microseconds(999_999),
1563 ), @"PT631107417600.999999S");
1564 insta::assert_snapshot!(p(
1567 0.second()
1568 .milliseconds(b::SpanMilliseconds::MAX)
1569 .microseconds(1_000_000),
1570 ), @"PT631107417601S");
1571 insta::assert_snapshot!(p(
1572 0.second()
1573 .milliseconds(b::SpanMilliseconds::MAX)
1574 .microseconds(1_000_001),
1575 ), @"PT631107417601.000001S");
1576 insta::assert_snapshot!(p(
1579 0.second()
1580 .milliseconds(b::SpanMilliseconds::MAX)
1581 .nanoseconds(1_000_000_000),
1582 ), @"PT631107417601S");
1583 insta::assert_snapshot!(p(
1584 0.second()
1585 .milliseconds(b::SpanMilliseconds::MAX)
1586 .nanoseconds(1_000_000_001),
1587 ), @"PT631107417601.000000001S");
1588
1589 insta::assert_snapshot!(p(
1591 0.second()
1592 .milliseconds(b::SpanMilliseconds::MAX)
1593 .microseconds(b::SpanMicroseconds::MAX)
1594 .nanoseconds(b::SpanNanoseconds::MAX),
1595 ), @"PT1271438207236.854775807S");
1596 insta::assert_snapshot!(p(
1598 Span::new()
1599 .seconds(b::SpanSeconds::MAX)
1600 .milliseconds(b::SpanMilliseconds::MAX)
1601 .microseconds(b::SpanMicroseconds::MAX)
1602 .nanoseconds(b::SpanNanoseconds::MAX),
1603 ), @"PT1902545624836.854775807S");
1604 }
1605
1606 #[cfg(not(miri))]
1607 #[test]
1608 fn print_span_subsecond_negative() {
1609 let p = |span: Span| -> String {
1610 let mut buf = String::new();
1611 SpanPrinter::new().print_span(&span, &mut buf).unwrap();
1612 buf
1613 };
1614
1615 insta::assert_snapshot!(p(
1617 -0.second().milliseconds(1000).microseconds(1000).nanoseconds(1000),
1618 ), @"-PT1.001001S");
1619 insta::assert_snapshot!(p(
1620 -1.second().milliseconds(1000).microseconds(1000).nanoseconds(1000),
1621 ), @"-PT2.001001S");
1622 insta::assert_snapshot!(p(
1623 0.second()
1624 .milliseconds(b::SpanMilliseconds::MIN),
1625 ), @"-PT631107417600S");
1626 insta::assert_snapshot!(p(
1627 0.second()
1628 .microseconds(b::SpanMicroseconds::MIN),
1629 ), @"-PT631107417600S");
1630 insta::assert_snapshot!(p(
1631 0.second()
1632 .nanoseconds(b::SpanNanoseconds::MIN),
1633 ), @"-PT9223372036.854775807S");
1634
1635 insta::assert_snapshot!(p(
1636 0.second()
1637 .milliseconds(b::SpanMilliseconds::MIN)
1638 .microseconds(999_999),
1639 ), @"-PT631107417600.999999S");
1640 insta::assert_snapshot!(p(
1643 0.second()
1644 .milliseconds(b::SpanMilliseconds::MIN)
1645 .microseconds(1_000_000),
1646 ), @"-PT631107417601S");
1647 insta::assert_snapshot!(p(
1648 0.second()
1649 .milliseconds(b::SpanMilliseconds::MIN)
1650 .microseconds(1_000_001),
1651 ), @"-PT631107417601.000001S");
1652 insta::assert_snapshot!(p(
1655 0.second()
1656 .milliseconds(b::SpanMilliseconds::MIN)
1657 .nanoseconds(1_000_000_000),
1658 ), @"-PT631107417601S");
1659 insta::assert_snapshot!(p(
1660 0.second()
1661 .milliseconds(b::SpanMilliseconds::MIN)
1662 .nanoseconds(1_000_000_001),
1663 ), @"-PT631107417601.000000001S");
1664
1665 insta::assert_snapshot!(p(
1667 0.second()
1668 .milliseconds(b::SpanMilliseconds::MIN)
1669 .microseconds(b::SpanMicroseconds::MIN)
1670 .nanoseconds(b::SpanNanoseconds::MIN),
1671 ), @"-PT1271438207236.854775807S");
1672 insta::assert_snapshot!(p(
1674 Span::new()
1675 .seconds(b::SpanSeconds::MIN)
1676 .milliseconds(b::SpanMilliseconds::MIN)
1677 .microseconds(b::SpanMicroseconds::MIN)
1678 .nanoseconds(b::SpanNanoseconds::MIN),
1679 ), @"-PT1902545624836.854775807S");
1680 }
1681
1682 #[cfg(not(miri))]
1683 #[test]
1684 fn print_signed_duration() {
1685 let p = |secs, nanos| -> String {
1686 let dur = SignedDuration::new(secs, nanos);
1687 let mut buf = String::new();
1688 SpanPrinter::new().print_signed_duration(&dur, &mut buf).unwrap();
1689 buf
1690 };
1691
1692 insta::assert_snapshot!(p(0, 0), @"PT0S");
1693 insta::assert_snapshot!(p(0, 1), @"PT0.000000001S");
1694 insta::assert_snapshot!(p(1, 0), @"PT1S");
1695 insta::assert_snapshot!(p(59, 0), @"PT59S");
1696 insta::assert_snapshot!(p(60, 0), @"PT1M");
1697 insta::assert_snapshot!(p(60, 1), @"PT1M0.000000001S");
1698 insta::assert_snapshot!(p(61, 1), @"PT1M1.000000001S");
1699 insta::assert_snapshot!(p(3_600, 0), @"PT1H");
1700 insta::assert_snapshot!(p(3_600, 1), @"PT1H0.000000001S");
1701 insta::assert_snapshot!(p(3_660, 0), @"PT1H1M");
1702 insta::assert_snapshot!(p(3_660, 1), @"PT1H1M0.000000001S");
1703 insta::assert_snapshot!(p(3_661, 0), @"PT1H1M1S");
1704 insta::assert_snapshot!(p(3_661, 1), @"PT1H1M1.000000001S");
1705
1706 insta::assert_snapshot!(p(0, -1), @"-PT0.000000001S");
1707 insta::assert_snapshot!(p(-1, 0), @"-PT1S");
1708 insta::assert_snapshot!(p(-59, 0), @"-PT59S");
1709 insta::assert_snapshot!(p(-60, 0), @"-PT1M");
1710 insta::assert_snapshot!(p(-60, -1), @"-PT1M0.000000001S");
1711 insta::assert_snapshot!(p(-61, -1), @"-PT1M1.000000001S");
1712 insta::assert_snapshot!(p(-3_600, 0), @"-PT1H");
1713 insta::assert_snapshot!(p(-3_600, -1), @"-PT1H0.000000001S");
1714 insta::assert_snapshot!(p(-3_660, 0), @"-PT1H1M");
1715 insta::assert_snapshot!(p(-3_660, -1), @"-PT1H1M0.000000001S");
1716 insta::assert_snapshot!(p(-3_661, 0), @"-PT1H1M1S");
1717 insta::assert_snapshot!(p(-3_661, -1), @"-PT1H1M1.000000001S");
1718
1719 insta::assert_snapshot!(
1720 p(i64::MIN, -999_999_999),
1721 @"-PT2562047788015215H30M8.999999999S",
1722 );
1723 insta::assert_snapshot!(
1724 p(i64::MAX, 999_999_999),
1725 @"PT2562047788015215H30M7.999999999S",
1726 );
1727 }
1728
1729 #[cfg(not(miri))]
1730 #[test]
1731 fn print_unsigned_duration() {
1732 let p = |secs, nanos| -> String {
1733 let dur = Duration::new(secs, nanos);
1734 let mut buf = String::new();
1735 SpanPrinter::new()
1736 .print_unsigned_duration(&dur, &mut buf)
1737 .unwrap();
1738 buf
1739 };
1740
1741 insta::assert_snapshot!(p(0, 0), @"PT0S");
1742 insta::assert_snapshot!(p(0, 1), @"PT0.000000001S");
1743 insta::assert_snapshot!(p(1, 0), @"PT1S");
1744 insta::assert_snapshot!(p(59, 0), @"PT59S");
1745 insta::assert_snapshot!(p(60, 0), @"PT1M");
1746 insta::assert_snapshot!(p(60, 1), @"PT1M0.000000001S");
1747 insta::assert_snapshot!(p(61, 1), @"PT1M1.000000001S");
1748 insta::assert_snapshot!(p(3_600, 0), @"PT1H");
1749 insta::assert_snapshot!(p(3_600, 1), @"PT1H0.000000001S");
1750 insta::assert_snapshot!(p(3_660, 0), @"PT1H1M");
1751 insta::assert_snapshot!(p(3_660, 1), @"PT1H1M0.000000001S");
1752 insta::assert_snapshot!(p(3_661, 0), @"PT1H1M1S");
1753 insta::assert_snapshot!(p(3_661, 1), @"PT1H1M1.000000001S");
1754
1755 insta::assert_snapshot!(
1756 p(u64::MAX, 999_999_999),
1757 @"PT5124095576030431H15.999999999S",
1758 );
1759 }
1760}