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