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