1use std::io;
4
5#[allow(unused_imports, reason = "MSRV of 1.87")]
6use num_conv::prelude::*;
7
8use crate::convert::*;
9use crate::format_description::well_known::iso8601::{
10 DateKind, EncodedConfig, OffsetPrecision, TimePrecision,
11};
12use crate::format_description::well_known::Iso8601;
13use crate::formatting::{format_float, format_number_pad_zero, write, write_if, write_if_else};
14use crate::{error, Date, Time, UtcOffset};
15
16pub(super) fn format_date<const CONFIG: EncodedConfig>(
18 output: &mut (impl io::Write + ?Sized),
19 date: Date,
20) -> Result<usize, error::Format> {
21 let mut bytes = 0;
22
23 match Iso8601::<CONFIG>::DATE_KIND {
24 DateKind::Calendar => {
25 let (year, month, day) = date.to_calendar_date();
26 if Iso8601::<CONFIG>::YEAR_IS_SIX_DIGITS {
27 bytes += write_if_else(output, year < 0, b"-", b"+")?;
28 bytes += format_number_pad_zero::<6>(output, year.unsigned_abs())?;
29 } else if !(0..=9999).contains(&year) {
30 return Err(error::Format::InvalidComponent("year"));
31 } else {
32 bytes += format_number_pad_zero::<4>(output, year.cast_unsigned())?;
33 }
34 bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b"-")?;
35 bytes += format_number_pad_zero::<2>(output, u8::from(month))?;
36 bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b"-")?;
37 bytes += format_number_pad_zero::<2>(output, day)?;
38 }
39 DateKind::Week => {
40 let (year, week, day) = date.to_iso_week_date();
41 if Iso8601::<CONFIG>::YEAR_IS_SIX_DIGITS {
42 bytes += write_if_else(output, year < 0, b"-", b"+")?;
43 bytes += format_number_pad_zero::<6>(output, year.unsigned_abs())?;
44 } else if !(0..=9999).contains(&year) {
45 return Err(error::Format::InvalidComponent("year"));
46 } else {
47 bytes += format_number_pad_zero::<4>(output, year.cast_unsigned())?;
48 }
49 bytes += write_if_else(output, Iso8601::<CONFIG>::USE_SEPARATORS, b"-W", b"W")?;
50 bytes += format_number_pad_zero::<2>(output, week)?;
51 bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b"-")?;
52 bytes += format_number_pad_zero::<1>(output, day.number_from_monday())?;
53 }
54 DateKind::Ordinal => {
55 let (year, day) = date.to_ordinal_date();
56 if Iso8601::<CONFIG>::YEAR_IS_SIX_DIGITS {
57 bytes += write_if_else(output, year < 0, b"-", b"+")?;
58 bytes += format_number_pad_zero::<6>(output, year.unsigned_abs())?;
59 } else if !(0..=9999).contains(&year) {
60 return Err(error::Format::InvalidComponent("year"));
61 } else {
62 bytes += format_number_pad_zero::<4>(output, year.cast_unsigned())?;
63 }
64 bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b"-")?;
65 bytes += format_number_pad_zero::<3>(output, day)?;
66 }
67 }
68
69 Ok(bytes)
70}
71
72#[inline]
74pub(super) fn format_time<const CONFIG: EncodedConfig>(
75 output: &mut (impl io::Write + ?Sized),
76 time: Time,
77) -> Result<usize, error::Format> {
78 let mut bytes = 0;
79
80 bytes += write_if(
82 output,
83 Iso8601::<CONFIG>::USE_SEPARATORS || Iso8601::<CONFIG>::FORMAT_DATE,
84 b"T",
85 )?;
86
87 let (hours, minutes, seconds, nanoseconds) = time.as_hms_nano();
88
89 match Iso8601::<CONFIG>::TIME_PRECISION {
90 TimePrecision::Hour { decimal_digits } => {
91 let hours = (hours as f64)
92 + (minutes as f64) / Minute::per_t::<f64>(Hour)
93 + (seconds as f64) / Second::per_t::<f64>(Hour)
94 + (nanoseconds as f64) / Nanosecond::per_t::<f64>(Hour);
95 format_float(output, hours, 2, decimal_digits)?;
96 }
97 TimePrecision::Minute { decimal_digits } => {
98 bytes += format_number_pad_zero::<2>(output, hours)?;
99 bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b":")?;
100 let minutes = (minutes as f64)
101 + (seconds as f64) / Second::per_t::<f64>(Minute)
102 + (nanoseconds as f64) / Nanosecond::per_t::<f64>(Minute);
103 bytes += format_float(output, minutes, 2, decimal_digits)?;
104 }
105 TimePrecision::Second { decimal_digits } => {
106 bytes += format_number_pad_zero::<2>(output, hours)?;
107 bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b":")?;
108 bytes += format_number_pad_zero::<2>(output, minutes)?;
109 bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b":")?;
110 let seconds =
111 (seconds as f64) + (nanoseconds as f64) / Nanosecond::per_t::<f64>(Second);
112 bytes += format_float(output, seconds, 2, decimal_digits)?;
113 }
114 }
115
116 Ok(bytes)
117}
118
119#[inline]
121pub(super) fn format_offset<const CONFIG: EncodedConfig>(
122 output: &mut (impl io::Write + ?Sized),
123 offset: UtcOffset,
124) -> Result<usize, error::Format> {
125 if Iso8601::<CONFIG>::FORMAT_TIME && offset.is_utc() {
126 return Ok(write(output, b"Z")?);
127 }
128
129 let mut bytes = 0;
130
131 let (hours, minutes, seconds) = offset.as_hms();
132 if seconds != 0 {
133 return Err(error::Format::InvalidComponent("offset_second"));
134 }
135 bytes += write_if_else(output, offset.is_negative(), b"-", b"+")?;
136 bytes += format_number_pad_zero::<2>(output, hours.unsigned_abs())?;
137
138 if Iso8601::<CONFIG>::OFFSET_PRECISION == OffsetPrecision::Hour && minutes != 0 {
139 return Err(error::Format::InvalidComponent("offset_minute"));
140 } else if Iso8601::<CONFIG>::OFFSET_PRECISION == OffsetPrecision::Minute {
141 bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b":")?;
142 bytes += format_number_pad_zero::<2>(output, minutes.unsigned_abs())?;
143 }
144
145 Ok(bytes)
146}