1use core::cmp::min;
4use std::io;
5
6use deranged::{ru8, ru16, ru32};
7use num_conv::prelude::*;
8
9use crate::error;
10use crate::format_description::modifier::Padding;
11use crate::format_description::well_known::Iso8601;
12use crate::format_description::well_known::iso8601::{
13 DateKind, EncodedConfig, OffsetPrecision, TimePrecision,
14};
15use crate::formatting::{
16 ComponentProvider, format_float, format_four_digits_pad_zero, format_int_padded,
17 format_single_digit, format_six_digits_pad_zero, format_three_digits, format_two_digits, write,
18 write_if, write_if_else,
19};
20use crate::unit::*;
21
22pub(super) fn format_date<V, const CONFIG: EncodedConfig>(
24 output: &mut (impl io::Write + ?Sized),
25 value: &V,
26 state: &mut V::State,
27) -> Result<usize, error::Format>
28where
29 V: ComponentProvider,
30{
31 let mut bytes = 0;
32
33 match Iso8601::<CONFIG>::DATE_KIND {
34 DateKind::Calendar => {
35 let year = value.calendar_year(state).get();
36
37 if Iso8601::<CONFIG>::YEAR_IS_SIX_DIGITS {
38 bytes += write_if_else(output, year < 0, "-", "+")?;
39 bytes += format_six_digits_pad_zero(output, unsafe {
42 ru32::new_unchecked(year.unsigned_abs())
43 })?;
44 } else {
45 let year = ru16::new(year.cast_unsigned().truncate())
46 .ok_or(error::Format::InvalidComponent("year"))?;
47 bytes += format_four_digits_pad_zero(output, year)?;
48 }
49 bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, "-")?;
50 bytes += format_two_digits(
52 output,
53 unsafe { ru8::new_unchecked(u8::from(value.month(state))) },
54 Padding::Zero,
55 )?;
56 bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, "-")?;
57 bytes += format_two_digits(output, value.day(state).expand(), Padding::Zero)?;
58 }
59 DateKind::Week => {
60 let year = value.iso_year(state).get();
61
62 if Iso8601::<CONFIG>::YEAR_IS_SIX_DIGITS {
63 bytes += write_if_else(output, year < 0, "-", "+")?;
64 bytes += format_six_digits_pad_zero(output, unsafe {
67 ru32::new_unchecked(year.unsigned_abs())
68 })?;
69 } else {
70 let year = ru16::new(year.cast_unsigned().truncate())
71 .ok_or(error::Format::InvalidComponent("year"))?;
72 bytes += format_four_digits_pad_zero(output, year)?;
73 }
74 bytes += write_if_else(output, Iso8601::<CONFIG>::USE_SEPARATORS, "-W", "W")?;
75 bytes +=
76 format_two_digits(output, value.iso_week_number(state).expand(), Padding::Zero)?;
77 bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, "-")?;
78 bytes += format_single_digit(output, unsafe {
80 ru8::new_unchecked(value.weekday(state).number_from_monday())
81 })?;
82 }
83 DateKind::Ordinal => {
84 let year = value.calendar_year(state).get();
85
86 if Iso8601::<CONFIG>::YEAR_IS_SIX_DIGITS {
87 bytes += write_if_else(output, year < 0, "-", "+")?;
88 bytes += format_six_digits_pad_zero(output, unsafe {
91 ru32::new_unchecked(year.unsigned_abs())
92 })?;
93 } else {
94 let year = ru16::new(year.cast_unsigned().truncate())
95 .ok_or(error::Format::InvalidComponent("year"))?;
96 bytes += format_four_digits_pad_zero(output, year)?;
97 }
98 bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, "-")?;
99 bytes += format_three_digits(output, value.ordinal(state).expand(), Padding::Zero)?;
100 }
101 }
102
103 Ok(bytes)
104}
105
106#[inline]
108pub(super) fn format_time<V, const CONFIG: EncodedConfig>(
109 output: &mut (impl io::Write + ?Sized),
110 value: &V,
111 state: &mut V::State,
112) -> Result<usize, error::Format>
113where
114 V: ComponentProvider,
115{
116 let mut bytes = 0;
117
118 bytes += write_if(
120 output,
121 !Iso8601::<CONFIG>::USE_SEPARATORS || Iso8601::<CONFIG>::FORMAT_DATE,
122 "T",
123 )?;
124
125 match Iso8601::<CONFIG>::TIME_PRECISION {
126 TimePrecision::Hour { decimal_digits } => {
127 let hours = (value.hour(state).get() as f64)
128 + (value.minute(state).get() as f64) / Minute::per_t::<f64>(Hour)
129 + (value.second(state).get() as f64) / Second::per_t::<f64>(Hour)
130 + (value.nanosecond(state).get() as f64) / Nanosecond::per_t::<f64>(Hour);
131 format_float(output, hours, 2, decimal_digits)?;
132 }
133 TimePrecision::Minute { decimal_digits } => {
134 bytes += format_two_digits(output, value.hour(state).expand(), Padding::Zero)?;
135 bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, ":")?;
136 let minutes = (value.minute(state).get() as f64)
137 + (value.second(state).get() as f64) / Second::per_t::<f64>(Minute)
138 + (value.nanosecond(state).get() as f64) / Nanosecond::per_t::<f64>(Minute);
139 bytes += format_float(output, minutes, 2, decimal_digits)?;
140 }
141 TimePrecision::Second { decimal_digits } => {
142 bytes += format_two_digits(output, value.hour(state).expand(), Padding::Zero)?;
143 bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, ":")?;
144 bytes += format_two_digits(output, value.minute(state).expand(), Padding::Zero)?;
145 bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, ":")?;
146 bytes += format_two_digits(output, value.second(state).expand(), Padding::Zero)?;
147 if let Some(digits) = decimal_digits {
148 const POW_TABLE: [u64; 9] = [
149 1,
150 10,
151 100,
152 1_000,
153 10_000,
154 100_000,
155 1_000_000,
156 10_000_000,
157 100_000_000,
158 ];
159
160 bytes += write(output, ".")?;
161 let nano = value.nanosecond(state).get() as u64;
162 let sub_digits = min(digits.get(), 9);
163 let truncated = nano / POW_TABLE[9 - sub_digits as usize];
164 bytes += format_int_padded(output, truncated, sub_digits)?;
165 for _ in 9..digits.get() {
166 bytes += write(output, "0")?;
167 }
168 }
169 }
170 }
171
172 Ok(bytes)
173}
174
175#[inline]
177pub(super) fn format_offset<V, const CONFIG: EncodedConfig>(
178 output: &mut (impl io::Write + ?Sized),
179 value: &V,
180 state: &mut V::State,
181) -> Result<usize, error::Format>
182where
183 V: ComponentProvider,
184{
185 if Iso8601::<CONFIG>::FORMAT_TIME && value.offset_is_utc(state) {
186 return Ok(write(output, "Z")?);
187 }
188
189 let mut bytes = 0;
190
191 if value.offset_second(state).get() != 0 {
192 return Err(error::Format::InvalidComponent("offset_second"));
193 }
194 bytes += write_if_else(output, value.offset_is_negative(state), "-", "+")?;
195 bytes += format_two_digits(
197 output,
198 unsafe { ru8::new_unchecked(value.offset_hour(state).get().unsigned_abs()) },
199 Padding::Zero,
200 )?;
201
202 let minutes = value.offset_minute(state);
203
204 if Iso8601::<CONFIG>::OFFSET_PRECISION == OffsetPrecision::Hour && minutes.get() != 0 {
205 return Err(error::Format::InvalidComponent("offset_minute"));
206 } else if Iso8601::<CONFIG>::OFFSET_PRECISION == OffsetPrecision::Minute {
207 bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, ":")?;
208 bytes += format_two_digits(
210 output,
211 unsafe { ru8::new_unchecked(minutes.get().unsigned_abs()) },
212 Padding::Zero,
213 )?;
214 }
215
216 Ok(bytes)
217}