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