/rust/registry/src/index.crates.io-1949cf8c6b5b557f/time-0.3.37/src/parsing/parsed.rs
Line | Count | Source |
1 | | //! Information parsed from an input and format description. |
2 | | |
3 | | use core::num::{NonZeroU16, NonZeroU8}; |
4 | | |
5 | | use deranged::{ |
6 | | OptionRangedI128, OptionRangedI16, OptionRangedI32, OptionRangedI8, OptionRangedU16, |
7 | | OptionRangedU32, OptionRangedU8, RangedI128, RangedI16, RangedI32, RangedI8, RangedU16, |
8 | | RangedU32, RangedU8, |
9 | | }; |
10 | | use num_conv::prelude::*; |
11 | | |
12 | | use crate::convert::{Day, Hour, Minute, Nanosecond, Second}; |
13 | | use crate::date::{MAX_YEAR, MIN_YEAR}; |
14 | | use crate::error::TryFromParsed::InsufficientInformation; |
15 | | #[cfg(feature = "alloc")] |
16 | | use crate::format_description::OwnedFormatItem; |
17 | | use crate::format_description::{modifier, BorrowedFormatItem, Component}; |
18 | | use crate::internal_macros::{bug, const_try_opt}; |
19 | | use crate::parsing::component::{ |
20 | | parse_day, parse_end, parse_hour, parse_ignore, parse_minute, parse_month, parse_offset_hour, |
21 | | parse_offset_minute, parse_offset_second, parse_ordinal, parse_period, parse_second, |
22 | | parse_subsecond, parse_unix_timestamp, parse_week_number, parse_weekday, parse_year, Period, |
23 | | }; |
24 | | use crate::parsing::ParsedItem; |
25 | | use crate::{error, Date, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday}; |
26 | | |
27 | | /// Sealed to prevent downstream implementations. |
28 | | mod sealed { |
29 | | use super::*; |
30 | | |
31 | | /// A trait to allow `parse_item` to be generic. |
32 | | pub trait AnyFormatItem { |
33 | | /// Parse a single item, returning the remaining input on success. |
34 | | fn parse_item<'a>( |
35 | | &self, |
36 | | parsed: &mut Parsed, |
37 | | input: &'a [u8], |
38 | | ) -> Result<&'a [u8], error::ParseFromDescription>; |
39 | | } |
40 | | } |
41 | | |
42 | | impl sealed::AnyFormatItem for BorrowedFormatItem<'_> { |
43 | 0 | fn parse_item<'a>( |
44 | 0 | &self, |
45 | 0 | parsed: &mut Parsed, |
46 | 0 | input: &'a [u8], |
47 | 0 | ) -> Result<&'a [u8], error::ParseFromDescription> { |
48 | 0 | match self { |
49 | 0 | Self::Literal(literal) => Parsed::parse_literal(input, literal), |
50 | 0 | Self::Component(component) => parsed.parse_component(input, *component), |
51 | 0 | Self::Compound(compound) => parsed.parse_items(input, compound), |
52 | 0 | Self::Optional(item) => parsed.parse_item(input, *item).or(Ok(input)), |
53 | 0 | Self::First(items) => { |
54 | 0 | let mut first_err = None; |
55 | | |
56 | 0 | for item in items.iter() { |
57 | 0 | match parsed.parse_item(input, item) { |
58 | 0 | Ok(remaining_input) => return Ok(remaining_input), |
59 | 0 | Err(err) if first_err.is_none() => first_err = Some(err), |
60 | 0 | Err(_) => {} |
61 | | } |
62 | | } |
63 | | |
64 | 0 | match first_err { |
65 | 0 | Some(err) => Err(err), |
66 | | // This location will be reached if the slice is empty, skipping the `for` loop. |
67 | | // As this case is expected to be uncommon, there's no need to check up front. |
68 | 0 | None => Ok(input), |
69 | | } |
70 | | } |
71 | | } |
72 | 0 | } |
73 | | } |
74 | | |
75 | | #[cfg(feature = "alloc")] |
76 | | impl sealed::AnyFormatItem for OwnedFormatItem { |
77 | 0 | fn parse_item<'a>( |
78 | 0 | &self, |
79 | 0 | parsed: &mut Parsed, |
80 | 0 | input: &'a [u8], |
81 | 0 | ) -> Result<&'a [u8], error::ParseFromDescription> { |
82 | 0 | match self { |
83 | 0 | Self::Literal(literal) => Parsed::parse_literal(input, literal), |
84 | 0 | Self::Component(component) => parsed.parse_component(input, *component), |
85 | 0 | Self::Compound(compound) => parsed.parse_items(input, compound), |
86 | 0 | Self::Optional(item) => parsed.parse_item(input, item.as_ref()).or(Ok(input)), |
87 | 0 | Self::First(items) => { |
88 | 0 | let mut first_err = None; |
89 | | |
90 | 0 | for item in items.iter() { |
91 | 0 | match parsed.parse_item(input, item) { |
92 | 0 | Ok(remaining_input) => return Ok(remaining_input), |
93 | 0 | Err(err) if first_err.is_none() => first_err = Some(err), |
94 | 0 | Err(_) => {} |
95 | | } |
96 | | } |
97 | | |
98 | 0 | match first_err { |
99 | 0 | Some(err) => Err(err), |
100 | | // This location will be reached if the slice is empty, skipping the `for` loop. |
101 | | // As this case is expected to be uncommon, there's no need to check up front. |
102 | 0 | None => Ok(input), |
103 | | } |
104 | | } |
105 | | } |
106 | 0 | } |
107 | | } |
108 | | |
109 | | /// All information parsed. |
110 | | /// |
111 | | /// This information is directly used to construct the final values. |
112 | | /// |
113 | | /// Most users will not need think about this struct in any way. It is public to allow for manual |
114 | | /// control over values, in the instance that the default parser is insufficient. |
115 | | #[derive(Debug, Clone, Copy)] |
116 | | pub struct Parsed { |
117 | | /// Calendar year. |
118 | | year: OptionRangedI32<{ MIN_YEAR }, { MAX_YEAR }>, |
119 | | /// All digits except the last two of the calendar year. |
120 | | year_century: OptionRangedI16<{ (MIN_YEAR / 100) as i16 }, { (MAX_YEAR / 100) as i16 }>, |
121 | | /// The last two digits of the calendar year. |
122 | | year_last_two: OptionRangedU8<0, 99>, |
123 | | /// Year of the [ISO week date](https://en.wikipedia.org/wiki/ISO_week_date). |
124 | | iso_year: OptionRangedI32<{ MIN_YEAR }, { MAX_YEAR }>, |
125 | | /// All digits except the last two of the ISO week year. |
126 | | iso_year_century: OptionRangedI16<{ (MIN_YEAR / 100) as i16 }, { (MAX_YEAR / 100) as i16 }>, |
127 | | /// The last two digits of the ISO week year. |
128 | | iso_year_last_two: OptionRangedU8<0, 99>, |
129 | | /// Month of the year. |
130 | | month: Option<Month>, |
131 | | /// Week of the year, where week one begins on the first Sunday of the calendar year. |
132 | | sunday_week_number: OptionRangedU8<0, 53>, |
133 | | /// Week of the year, where week one begins on the first Monday of the calendar year. |
134 | | monday_week_number: OptionRangedU8<0, 53>, |
135 | | /// Week of the year, where week one is the Monday-to-Sunday period containing January 4. |
136 | | iso_week_number: OptionRangedU8<1, 53>, |
137 | | /// Day of the week. |
138 | | weekday: Option<Weekday>, |
139 | | /// Day of the year. |
140 | | ordinal: OptionRangedU16<1, 366>, |
141 | | /// Day of the month. |
142 | | day: OptionRangedU8<1, 31>, |
143 | | /// Hour within the day. |
144 | | hour_24: OptionRangedU8<0, { Hour::per(Day) - 1 }>, |
145 | | /// Hour within the 12-hour period (midnight to noon or vice versa). This is typically used in |
146 | | /// conjunction with AM/PM, which is indicated by the `hour_12_is_pm` field. |
147 | | hour_12: OptionRangedU8<1, 12>, |
148 | | /// Whether the `hour_12` field indicates a time that "PM". |
149 | | hour_12_is_pm: Option<bool>, |
150 | | /// Minute within the hour. |
151 | | minute: OptionRangedU8<0, { Minute::per(Hour) - 1 }>, |
152 | | /// Second within the minute. |
153 | | // do not subtract one, as leap seconds may be allowed |
154 | | second: OptionRangedU8<0, { Second::per(Minute) }>, |
155 | | /// Nanosecond within the second. |
156 | | subsecond: OptionRangedU32<0, { Nanosecond::per(Second) - 1 }>, |
157 | | /// Whole hours of the UTC offset. |
158 | | offset_hour: OptionRangedI8<-23, 23>, |
159 | | /// Minutes within the hour of the UTC offset. |
160 | | offset_minute: |
161 | | OptionRangedI8<{ -((Minute::per(Hour) - 1) as i8) }, { (Minute::per(Hour) - 1) as _ }>, |
162 | | /// Seconds within the minute of the UTC offset. |
163 | | offset_second: |
164 | | OptionRangedI8<{ -((Second::per(Minute) - 1) as i8) }, { (Second::per(Minute) - 1) as _ }>, |
165 | | /// The Unix timestamp in nanoseconds. |
166 | | unix_timestamp_nanos: OptionRangedI128< |
167 | | { |
168 | | OffsetDateTime::new_in_offset(Date::MIN, Time::MIDNIGHT, UtcOffset::UTC) |
169 | | .unix_timestamp_nanos() |
170 | | }, |
171 | | { |
172 | | OffsetDateTime::new_in_offset(Date::MAX, Time::MAX, UtcOffset::UTC) |
173 | | .unix_timestamp_nanos() |
174 | | }, |
175 | | >, |
176 | | /// Indicates whether the [`UtcOffset`] is negative. This information is obtained when parsing |
177 | | /// the offset hour, but may not otherwise be stored due to "-0" being equivalent to "0". |
178 | | offset_is_negative: bool, |
179 | | /// Indicates whether the `year_century` component is negative. This information is obtained |
180 | | /// when parsing, but may not otherwise be stored due to "-0" being equivalent to "0". |
181 | | year_century_is_negative: bool, |
182 | | /// Indicates whether the `iso_year_century` component is negative. This information is |
183 | | /// obtained when parsing, but may not otherwise be stored due to "-0" being equivalent to "0". |
184 | | iso_year_century_is_negative: bool, |
185 | | /// Indicates whether a leap second is permitted to be parsed. This is required by some |
186 | | /// well-known formats. |
187 | | pub(super) leap_second_allowed: bool, |
188 | | } |
189 | | |
190 | | impl Default for Parsed { |
191 | 0 | fn default() -> Self { |
192 | 0 | Self::new() |
193 | 0 | } |
194 | | } |
195 | | |
196 | | impl Parsed { |
197 | | /// Create a new instance of `Parsed` with no information known. |
198 | 0 | pub const fn new() -> Self { |
199 | 0 | Self { |
200 | 0 | year: OptionRangedI32::None, |
201 | 0 | year_century: OptionRangedI16::None, |
202 | 0 | year_last_two: OptionRangedU8::None, |
203 | 0 | iso_year: OptionRangedI32::None, |
204 | 0 | iso_year_century: OptionRangedI16::None, |
205 | 0 | iso_year_last_two: OptionRangedU8::None, |
206 | 0 | month: None, |
207 | 0 | sunday_week_number: OptionRangedU8::None, |
208 | 0 | monday_week_number: OptionRangedU8::None, |
209 | 0 | iso_week_number: OptionRangedU8::None, |
210 | 0 | weekday: None, |
211 | 0 | ordinal: OptionRangedU16::None, |
212 | 0 | day: OptionRangedU8::None, |
213 | 0 | hour_24: OptionRangedU8::None, |
214 | 0 | hour_12: OptionRangedU8::None, |
215 | 0 | hour_12_is_pm: None, |
216 | 0 | minute: OptionRangedU8::None, |
217 | 0 | second: OptionRangedU8::None, |
218 | 0 | subsecond: OptionRangedU32::None, |
219 | 0 | offset_hour: OptionRangedI8::None, |
220 | 0 | offset_minute: OptionRangedI8::None, |
221 | 0 | offset_second: OptionRangedI8::None, |
222 | 0 | unix_timestamp_nanos: OptionRangedI128::None, |
223 | 0 | offset_is_negative: false, |
224 | 0 | year_century_is_negative: false, |
225 | 0 | iso_year_century_is_negative: false, |
226 | 0 | leap_second_allowed: false, |
227 | 0 | } |
228 | 0 | } |
229 | | |
230 | | /// Parse a single [`BorrowedFormatItem`] or [`OwnedFormatItem`], mutating the struct. The |
231 | | /// remaining input is returned as the `Ok` value. |
232 | | /// |
233 | | /// If a [`BorrowedFormatItem::Optional`] or [`OwnedFormatItem::Optional`] is passed, parsing |
234 | | /// will not fail; the input will be returned as-is if the expected format is not present. |
235 | 0 | pub fn parse_item<'a>( |
236 | 0 | &mut self, |
237 | 0 | input: &'a [u8], |
238 | 0 | item: &impl sealed::AnyFormatItem, |
239 | 0 | ) -> Result<&'a [u8], error::ParseFromDescription> { |
240 | 0 | item.parse_item(self, input) |
241 | 0 | } Unexecuted instantiation: <time::parsing::parsed::Parsed>::parse_item::<time::format_description::owned_format_item::OwnedFormatItem> Unexecuted instantiation: <time::parsing::parsed::Parsed>::parse_item::<time::format_description::borrowed_format_item::BorrowedFormatItem> |
242 | | |
243 | | /// Parse a sequence of [`BorrowedFormatItem`]s or [`OwnedFormatItem`]s, mutating the struct. |
244 | | /// The remaining input is returned as the `Ok` value. |
245 | | /// |
246 | | /// This method will fail if any of the contained [`BorrowedFormatItem`]s or |
247 | | /// [`OwnedFormatItem`]s fail to parse. `self` will not be mutated in this instance. |
248 | 0 | pub fn parse_items<'a>( |
249 | 0 | &mut self, |
250 | 0 | mut input: &'a [u8], |
251 | 0 | items: &[impl sealed::AnyFormatItem], |
252 | 0 | ) -> Result<&'a [u8], error::ParseFromDescription> { |
253 | | // Make a copy that we can mutate. It will only be set to the user's copy if everything |
254 | | // succeeds. |
255 | 0 | let mut this = *self; |
256 | 0 | for item in items { |
257 | 0 | input = this.parse_item(input, item)?; |
258 | | } |
259 | 0 | *self = this; |
260 | 0 | Ok(input) |
261 | 0 | } Unexecuted instantiation: <time::parsing::parsed::Parsed>::parse_items::<time::format_description::owned_format_item::OwnedFormatItem> Unexecuted instantiation: <time::parsing::parsed::Parsed>::parse_items::<time::format_description::borrowed_format_item::BorrowedFormatItem> |
262 | | |
263 | | /// Parse a literal byte sequence. The remaining input is returned as the `Ok` value. |
264 | 0 | pub fn parse_literal<'a>( |
265 | 0 | input: &'a [u8], |
266 | 0 | literal: &[u8], |
267 | 0 | ) -> Result<&'a [u8], error::ParseFromDescription> { |
268 | 0 | input |
269 | 0 | .strip_prefix(literal) |
270 | 0 | .ok_or(error::ParseFromDescription::InvalidLiteral) |
271 | 0 | } |
272 | | |
273 | | /// Parse a single component, mutating the struct. The remaining input is returned as the `Ok` |
274 | | /// value. |
275 | 0 | pub fn parse_component<'a>( |
276 | 0 | &mut self, |
277 | 0 | input: &'a [u8], |
278 | 0 | component: Component, |
279 | 0 | ) -> Result<&'a [u8], error::ParseFromDescription> { |
280 | | use error::ParseFromDescription::InvalidComponent; |
281 | | |
282 | 0 | match component { |
283 | 0 | Component::Day(modifiers) => parse_day(input, modifiers) |
284 | 0 | .and_then(|parsed| parsed.consume_value(|value| self.set_day(value))) |
285 | 0 | .ok_or(InvalidComponent("day")), |
286 | 0 | Component::Month(modifiers) => parse_month(input, modifiers) |
287 | 0 | .and_then(|parsed| parsed.consume_value(|value| self.set_month(value))) |
288 | 0 | .ok_or(InvalidComponent("month")), |
289 | 0 | Component::Ordinal(modifiers) => parse_ordinal(input, modifiers) |
290 | 0 | .and_then(|parsed| parsed.consume_value(|value| self.set_ordinal(value))) |
291 | 0 | .ok_or(InvalidComponent("ordinal")), |
292 | 0 | Component::Weekday(modifiers) => parse_weekday(input, modifiers) |
293 | 0 | .and_then(|parsed| parsed.consume_value(|value| self.set_weekday(value))) |
294 | 0 | .ok_or(InvalidComponent("weekday")), |
295 | 0 | Component::WeekNumber(modifiers) => { |
296 | 0 | let ParsedItem(remaining, value) = |
297 | 0 | parse_week_number(input, modifiers).ok_or(InvalidComponent("week number"))?; |
298 | 0 | match modifiers.repr { |
299 | | modifier::WeekNumberRepr::Iso => { |
300 | 0 | NonZeroU8::new(value).and_then(|value| self.set_iso_week_number(value)) |
301 | | } |
302 | 0 | modifier::WeekNumberRepr::Sunday => self.set_sunday_week_number(value), |
303 | 0 | modifier::WeekNumberRepr::Monday => self.set_monday_week_number(value), |
304 | | } |
305 | 0 | .ok_or(InvalidComponent("week number"))?; |
306 | 0 | Ok(remaining) |
307 | | } |
308 | 0 | Component::Year(modifiers) => { |
309 | 0 | let ParsedItem(remaining, (value, is_negative)) = |
310 | 0 | parse_year(input, modifiers).ok_or(InvalidComponent("year"))?; |
311 | 0 | match (modifiers.iso_week_based, modifiers.repr) { |
312 | 0 | (false, modifier::YearRepr::Full) => self.set_year(value), |
313 | | (false, modifier::YearRepr::Century) => { |
314 | 0 | self.set_year_century(value.truncate(), is_negative) |
315 | | } |
316 | | (false, modifier::YearRepr::LastTwo) => { |
317 | 0 | self.set_year_last_two(value.cast_unsigned().truncate()) |
318 | | } |
319 | 0 | (true, modifier::YearRepr::Full) => self.set_iso_year(value), |
320 | | (true, modifier::YearRepr::Century) => { |
321 | 0 | self.set_iso_year_century(value.truncate(), is_negative) |
322 | | } |
323 | | (true, modifier::YearRepr::LastTwo) => { |
324 | 0 | self.set_iso_year_last_two(value.cast_unsigned().truncate()) |
325 | | } |
326 | | } |
327 | 0 | .ok_or(InvalidComponent("year"))?; |
328 | 0 | Ok(remaining) |
329 | | } |
330 | 0 | Component::Hour(modifiers) => { |
331 | 0 | let ParsedItem(remaining, value) = |
332 | 0 | parse_hour(input, modifiers).ok_or(InvalidComponent("hour"))?; |
333 | 0 | if modifiers.is_12_hour_clock { |
334 | 0 | NonZeroU8::new(value).and_then(|value| self.set_hour_12(value)) |
335 | | } else { |
336 | 0 | self.set_hour_24(value) |
337 | | } |
338 | 0 | .ok_or(InvalidComponent("hour"))?; |
339 | 0 | Ok(remaining) |
340 | | } |
341 | 0 | Component::Minute(modifiers) => parse_minute(input, modifiers) |
342 | 0 | .and_then(|parsed| parsed.consume_value(|value| self.set_minute(value))) |
343 | 0 | .ok_or(InvalidComponent("minute")), |
344 | 0 | Component::Period(modifiers) => parse_period(input, modifiers) |
345 | 0 | .and_then(|parsed| { |
346 | 0 | parsed.consume_value(|value| self.set_hour_12_is_pm(value == Period::Pm)) |
347 | 0 | }) |
348 | 0 | .ok_or(InvalidComponent("period")), |
349 | 0 | Component::Second(modifiers) => parse_second(input, modifiers) |
350 | 0 | .and_then(|parsed| parsed.consume_value(|value| self.set_second(value))) |
351 | 0 | .ok_or(InvalidComponent("second")), |
352 | 0 | Component::Subsecond(modifiers) => parse_subsecond(input, modifiers) |
353 | 0 | .and_then(|parsed| parsed.consume_value(|value| self.set_subsecond(value))) |
354 | 0 | .ok_or(InvalidComponent("subsecond")), |
355 | 0 | Component::OffsetHour(modifiers) => parse_offset_hour(input, modifiers) |
356 | 0 | .and_then(|parsed| { |
357 | 0 | parsed.consume_value(|(value, is_negative)| { |
358 | 0 | self.set_offset_hour(value)?; |
359 | 0 | self.offset_is_negative = is_negative; |
360 | 0 | Some(()) |
361 | 0 | }) |
362 | 0 | }) |
363 | 0 | .ok_or(InvalidComponent("offset hour")), |
364 | 0 | Component::OffsetMinute(modifiers) => parse_offset_minute(input, modifiers) |
365 | 0 | .and_then(|parsed| { |
366 | 0 | parsed.consume_value(|value| self.set_offset_minute_signed(value)) |
367 | 0 | }) |
368 | 0 | .ok_or(InvalidComponent("offset minute")), |
369 | 0 | Component::OffsetSecond(modifiers) => parse_offset_second(input, modifiers) |
370 | 0 | .and_then(|parsed| { |
371 | 0 | parsed.consume_value(|value| self.set_offset_second_signed(value)) |
372 | 0 | }) |
373 | 0 | .ok_or(InvalidComponent("offset second")), |
374 | 0 | Component::Ignore(modifiers) => parse_ignore(input, modifiers) |
375 | 0 | .map(ParsedItem::<()>::into_inner) |
376 | 0 | .ok_or(InvalidComponent("ignore")), |
377 | 0 | Component::UnixTimestamp(modifiers) => parse_unix_timestamp(input, modifiers) |
378 | 0 | .and_then(|parsed| { |
379 | 0 | parsed.consume_value(|value| self.set_unix_timestamp_nanos(value)) |
380 | 0 | }) |
381 | 0 | .ok_or(InvalidComponent("unix_timestamp")), |
382 | 0 | Component::End(modifiers) => parse_end(input, modifiers) |
383 | 0 | .map(ParsedItem::<()>::into_inner) |
384 | 0 | .ok_or(error::ParseFromDescription::UnexpectedTrailingCharacters), |
385 | | } |
386 | 0 | } |
387 | | } |
388 | | |
389 | | /// Getter methods |
390 | | impl Parsed { |
391 | | /// Obtain the `year` component. |
392 | 0 | pub const fn year(&self) -> Option<i32> { |
393 | 0 | self.year.get_primitive() |
394 | 0 | } |
395 | | |
396 | | /// Obtain the `year_century` component. |
397 | | /// |
398 | | /// If the year is zero, the sign of the century is not stored. To differentiate between |
399 | | /// positive and negative zero, use `year_century_is_negative`. |
400 | 0 | pub const fn year_century(&self) -> Option<i16> { |
401 | 0 | self.year_century.get_primitive() |
402 | 0 | } |
403 | | |
404 | | /// Obtain the `year_century_is_negative` component. |
405 | | /// |
406 | | /// This indicates whether the value returned from `year_century` is negative. If the year is |
407 | | /// zero, it is necessary to call this method for disambiguation. |
408 | 0 | pub const fn year_century_is_negative(&self) -> Option<bool> { |
409 | 0 | match self.year_century() { |
410 | 0 | Some(_) => Some(self.year_century_is_negative), |
411 | 0 | None => None, |
412 | | } |
413 | 0 | } |
414 | | |
415 | | /// Obtain the `year_last_two` component. |
416 | 0 | pub const fn year_last_two(&self) -> Option<u8> { |
417 | 0 | self.year_last_two.get_primitive() |
418 | 0 | } |
419 | | |
420 | | /// Obtain the `iso_year` component. |
421 | 0 | pub const fn iso_year(&self) -> Option<i32> { |
422 | 0 | self.iso_year.get_primitive() |
423 | 0 | } |
424 | | |
425 | | /// Obtain the `iso_year_century` component. |
426 | | /// |
427 | | /// If the year is zero, the sign of the century is not stored. To differentiate between |
428 | | /// positive and negative zero, use `iso_year_century_is_negative`. |
429 | 0 | pub const fn iso_year_century(&self) -> Option<i16> { |
430 | 0 | self.iso_year_century.get_primitive() |
431 | 0 | } |
432 | | |
433 | | /// Obtain the `iso_year_century_is_negative` component. |
434 | | /// |
435 | | /// This indicates whether the value returned from `iso_year_century` is negative. If the year |
436 | | /// is zero, it is necessary to call this method for disambiguation. |
437 | 0 | pub const fn iso_year_century_is_negative(&self) -> Option<bool> { |
438 | 0 | match self.iso_year_century() { |
439 | 0 | Some(_) => Some(self.iso_year_century_is_negative), |
440 | 0 | None => None, |
441 | | } |
442 | 0 | } |
443 | | |
444 | | /// Obtain the `iso_year_last_two` component. |
445 | 0 | pub const fn iso_year_last_two(&self) -> Option<u8> { |
446 | 0 | self.iso_year_last_two.get_primitive() |
447 | 0 | } |
448 | | |
449 | | /// Obtain the `month` component. |
450 | 0 | pub const fn month(&self) -> Option<Month> { |
451 | 0 | self.month |
452 | 0 | } |
453 | | |
454 | | /// Obtain the `sunday_week_number` component. |
455 | 0 | pub const fn sunday_week_number(&self) -> Option<u8> { |
456 | 0 | self.sunday_week_number.get_primitive() |
457 | 0 | } |
458 | | |
459 | | /// Obtain the `monday_week_number` component. |
460 | 0 | pub const fn monday_week_number(&self) -> Option<u8> { |
461 | 0 | self.monday_week_number.get_primitive() |
462 | 0 | } |
463 | | |
464 | | /// Obtain the `iso_week_number` component. |
465 | 0 | pub const fn iso_week_number(&self) -> Option<NonZeroU8> { |
466 | 0 | NonZeroU8::new(const_try_opt!(self.iso_week_number.get_primitive())) |
467 | 0 | } |
468 | | |
469 | | /// Obtain the `weekday` component. |
470 | 0 | pub const fn weekday(&self) -> Option<Weekday> { |
471 | 0 | self.weekday |
472 | 0 | } |
473 | | |
474 | | /// Obtain the `ordinal` component. |
475 | 0 | pub const fn ordinal(&self) -> Option<NonZeroU16> { |
476 | 0 | NonZeroU16::new(const_try_opt!(self.ordinal.get_primitive())) |
477 | 0 | } |
478 | | |
479 | | /// Obtain the `day` component. |
480 | 0 | pub const fn day(&self) -> Option<NonZeroU8> { |
481 | 0 | NonZeroU8::new(const_try_opt!(self.day.get_primitive())) |
482 | 0 | } |
483 | | |
484 | | /// Obtain the `hour_24` component. |
485 | 0 | pub const fn hour_24(&self) -> Option<u8> { |
486 | 0 | self.hour_24.get_primitive() |
487 | 0 | } |
488 | | |
489 | | /// Obtain the `hour_12` component. |
490 | 0 | pub const fn hour_12(&self) -> Option<NonZeroU8> { |
491 | 0 | NonZeroU8::new(const_try_opt!(self.hour_12.get_primitive())) |
492 | 0 | } |
493 | | |
494 | | /// Obtain the `hour_12_is_pm` component. |
495 | 0 | pub const fn hour_12_is_pm(&self) -> Option<bool> { |
496 | 0 | self.hour_12_is_pm |
497 | 0 | } |
498 | | |
499 | | /// Obtain the `minute` component. |
500 | 0 | pub const fn minute(&self) -> Option<u8> { |
501 | 0 | self.minute.get_primitive() |
502 | 0 | } |
503 | | |
504 | | /// Obtain the `second` component. |
505 | 0 | pub const fn second(&self) -> Option<u8> { |
506 | 0 | self.second.get_primitive() |
507 | 0 | } |
508 | | |
509 | | /// Obtain the `subsecond` component. |
510 | 0 | pub const fn subsecond(&self) -> Option<u32> { |
511 | 0 | self.subsecond.get_primitive() |
512 | 0 | } |
513 | | |
514 | | /// Obtain the `offset_hour` component. |
515 | 0 | pub const fn offset_hour(&self) -> Option<i8> { |
516 | 0 | self.offset_hour.get_primitive() |
517 | 0 | } |
518 | | |
519 | | /// Obtain the absolute value of the `offset_minute` component. |
520 | | #[doc(hidden)] |
521 | | #[deprecated(since = "0.3.8", note = "use `parsed.offset_minute_signed()` instead")] |
522 | 0 | pub const fn offset_minute(&self) -> Option<u8> { |
523 | 0 | Some(const_try_opt!(self.offset_minute_signed()).unsigned_abs()) |
524 | 0 | } |
525 | | |
526 | | /// Obtain the `offset_minute` component. |
527 | 0 | pub const fn offset_minute_signed(&self) -> Option<i8> { |
528 | 0 | match (self.offset_minute.get_primitive(), self.offset_is_negative) { |
529 | 0 | (Some(offset_minute), true) => Some(-offset_minute), |
530 | 0 | (Some(offset_minute), _) => Some(offset_minute), |
531 | 0 | (None, _) => None, |
532 | | } |
533 | 0 | } |
534 | | |
535 | | /// Obtain the absolute value of the `offset_second` component. |
536 | | #[doc(hidden)] |
537 | | #[deprecated(since = "0.3.8", note = "use `parsed.offset_second_signed()` instead")] |
538 | 0 | pub const fn offset_second(&self) -> Option<u8> { |
539 | 0 | Some(const_try_opt!(self.offset_second_signed()).unsigned_abs()) |
540 | 0 | } |
541 | | |
542 | | /// Obtain the `offset_second` component. |
543 | 0 | pub const fn offset_second_signed(&self) -> Option<i8> { |
544 | 0 | match (self.offset_second.get_primitive(), self.offset_is_negative) { |
545 | 0 | (Some(offset_second), true) => Some(-offset_second), |
546 | 0 | (Some(offset_second), _) => Some(offset_second), |
547 | 0 | (None, _) => None, |
548 | | } |
549 | 0 | } |
550 | | |
551 | | /// Obtain the `unix_timestamp_nanos` component. |
552 | 0 | pub const fn unix_timestamp_nanos(&self) -> Option<i128> { |
553 | 0 | self.unix_timestamp_nanos.get_primitive() |
554 | 0 | } |
555 | | } |
556 | | |
557 | | /// Generate setters based on the builders. |
558 | | macro_rules! setters { |
559 | | ($($name:ident $setter:ident $builder:ident $type:ty;)*) => {$( |
560 | | #[doc = concat!("Set the `", stringify!($setter), "` component.")] |
561 | 0 | pub fn $setter(&mut self, value: $type) -> Option<()> { |
562 | 0 | *self = self.$builder(value)?; |
563 | 0 | Some(()) |
564 | 0 | } Unexecuted instantiation: <time::parsing::parsed::Parsed>::set_minute Unexecuted instantiation: <time::parsing::parsed::Parsed>::set_second Unexecuted instantiation: <time::parsing::parsed::Parsed>::set_hour_12 Unexecuted instantiation: <time::parsing::parsed::Parsed>::set_hour_24 Unexecuted instantiation: <time::parsing::parsed::Parsed>::set_ordinal Unexecuted instantiation: <time::parsing::parsed::Parsed>::set_weekday Unexecuted instantiation: <time::parsing::parsed::Parsed>::set_iso_year Unexecuted instantiation: <time::parsing::parsed::Parsed>::set_subsecond Unexecuted instantiation: <time::parsing::parsed::Parsed>::set_offset_hour Unexecuted instantiation: <time::parsing::parsed::Parsed>::set_hour_12_is_pm Unexecuted instantiation: <time::parsing::parsed::Parsed>::set_year_last_two Unexecuted instantiation: <time::parsing::parsed::Parsed>::set_iso_week_number Unexecuted instantiation: <time::parsing::parsed::Parsed>::set_iso_year_last_two Unexecuted instantiation: <time::parsing::parsed::Parsed>::set_monday_week_number Unexecuted instantiation: <time::parsing::parsed::Parsed>::set_sunday_week_number Unexecuted instantiation: <time::parsing::parsed::Parsed>::set_offset_minute_signed Unexecuted instantiation: <time::parsing::parsed::Parsed>::set_offset_second_signed Unexecuted instantiation: <time::parsing::parsed::Parsed>::set_unix_timestamp_nanos Unexecuted instantiation: <time::parsing::parsed::Parsed>::set_day Unexecuted instantiation: <time::parsing::parsed::Parsed>::set_year Unexecuted instantiation: <time::parsing::parsed::Parsed>::set_month |
565 | | )*}; |
566 | | } |
567 | | |
568 | | /// Setter methods |
569 | | /// |
570 | | /// All setters return `Option<()>`, which is `Some` if the value was set, and `None` if not. The |
571 | | /// setters _may_ fail if the value is invalid, though behavior is not guaranteed. |
572 | | impl Parsed { |
573 | | setters! { |
574 | | year set_year with_year i32; |
575 | | } |
576 | | |
577 | | /// Set the `year_century` component. |
578 | | /// |
579 | | /// If the value is zero, the sign of the century is taken from the second parameter. Otherwise |
580 | | /// the sign is inferred from the value. |
581 | 0 | pub fn set_year_century(&mut self, value: i16, is_negative: bool) -> Option<()> { |
582 | 0 | self.year_century = OptionRangedI16::Some(const_try_opt!(RangedI16::new(value))); |
583 | 0 | if value != 0 { |
584 | 0 | self.year_century_is_negative = value.is_negative(); |
585 | 0 | } else { |
586 | 0 | self.year_century_is_negative = is_negative; |
587 | 0 | } |
588 | 0 | Some(()) |
589 | 0 | } |
590 | | |
591 | | setters! { |
592 | | year_last_two set_year_last_two with_year_last_two u8; |
593 | | iso_year set_iso_year with_iso_year i32; |
594 | | iso_year_last_two set_iso_year_last_two with_iso_year_last_two u8; |
595 | | } |
596 | | |
597 | | /// Set the `iso_year_century` component. |
598 | | /// |
599 | | /// If the value is zero, the sign of the century is taken from the second parameter. Otherwise |
600 | | /// the sign is inferred from the value. |
601 | 0 | pub fn set_iso_year_century(&mut self, value: i16, is_negative: bool) -> Option<()> { |
602 | 0 | self.iso_year_century = OptionRangedI16::Some(const_try_opt!(RangedI16::new(value))); |
603 | 0 | if value != 0 { |
604 | 0 | self.iso_year_century_is_negative = value.is_negative(); |
605 | 0 | } else { |
606 | 0 | self.iso_year_century_is_negative = is_negative; |
607 | 0 | } |
608 | 0 | Some(()) |
609 | 0 | } |
610 | | |
611 | | setters! { |
612 | | month set_month with_month Month; |
613 | | sunday_week_number set_sunday_week_number with_sunday_week_number u8; |
614 | | monday_week_number set_monday_week_number with_monday_week_number u8; |
615 | | iso_week_number set_iso_week_number with_iso_week_number NonZeroU8; |
616 | | weekday set_weekday with_weekday Weekday; |
617 | | ordinal set_ordinal with_ordinal NonZeroU16; |
618 | | day set_day with_day NonZeroU8; |
619 | | hour_24 set_hour_24 with_hour_24 u8; |
620 | | hour_12 set_hour_12 with_hour_12 NonZeroU8; |
621 | | hour_12_is_pm set_hour_12_is_pm with_hour_12_is_pm bool; |
622 | | minute set_minute with_minute u8; |
623 | | second set_second with_second u8; |
624 | | subsecond set_subsecond with_subsecond u32; |
625 | | offset_hour set_offset_hour with_offset_hour i8; |
626 | | offset_minute set_offset_minute_signed with_offset_minute_signed i8; |
627 | | offset_second set_offset_second_signed with_offset_second_signed i8; |
628 | | unix_timestamp_nanos set_unix_timestamp_nanos with_unix_timestamp_nanos i128; |
629 | | } |
630 | | |
631 | | /// Set the `offset_minute` component. |
632 | | #[doc(hidden)] |
633 | | #[deprecated( |
634 | | since = "0.3.8", |
635 | | note = "use `parsed.set_offset_minute_signed()` instead" |
636 | | )] |
637 | 0 | pub fn set_offset_minute(&mut self, value: u8) -> Option<()> { |
638 | 0 | if value > i8::MAX.cast_unsigned() { |
639 | 0 | None |
640 | | } else { |
641 | 0 | self.set_offset_minute_signed(value.cast_signed()) |
642 | | } |
643 | 0 | } |
644 | | |
645 | | /// Set the `offset_minute` component. |
646 | | #[doc(hidden)] |
647 | | #[deprecated( |
648 | | since = "0.3.8", |
649 | | note = "use `parsed.set_offset_second_signed()` instead" |
650 | | )] |
651 | 0 | pub fn set_offset_second(&mut self, value: u8) -> Option<()> { |
652 | 0 | if value > i8::MAX.cast_unsigned() { |
653 | 0 | None |
654 | | } else { |
655 | 0 | self.set_offset_second_signed(value.cast_signed()) |
656 | | } |
657 | 0 | } |
658 | | } |
659 | | |
660 | | /// Builder methods |
661 | | /// |
662 | | /// All builder methods return `Option<Self>`, which is `Some` if the value was set, and `None` if |
663 | | /// not. The builder methods _may_ fail if the value is invalid, though behavior is not guaranteed. |
664 | | impl Parsed { |
665 | | /// Set the `year` component and return `self`. |
666 | 0 | pub const fn with_year(mut self, value: i32) -> Option<Self> { |
667 | 0 | self.year = OptionRangedI32::Some(const_try_opt!(RangedI32::new(value))); |
668 | 0 | Some(self) |
669 | 0 | } |
670 | | |
671 | | /// Set the `year_century` component and return `self`. |
672 | | /// |
673 | | /// If the value is zero, the sign of the century is taken from the second parameter. Otherwise |
674 | | /// the sign is inferred from the value. |
675 | 0 | pub const fn with_year_century(mut self, value: i16, is_negative: bool) -> Option<Self> { |
676 | 0 | self.year_century = OptionRangedI16::Some(const_try_opt!(RangedI16::new(value))); |
677 | 0 | if value != 0 { |
678 | 0 | self.year_century_is_negative = value.is_negative(); |
679 | 0 | } else { |
680 | 0 | self.year_century_is_negative = is_negative; |
681 | 0 | } |
682 | 0 | Some(self) |
683 | 0 | } |
684 | | |
685 | | /// Set the `year_last_two` component and return `self`. |
686 | 0 | pub const fn with_year_last_two(mut self, value: u8) -> Option<Self> { |
687 | 0 | self.year_last_two = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value))); |
688 | 0 | Some(self) |
689 | 0 | } |
690 | | |
691 | | /// Set the `iso_year` component and return `self`. |
692 | 0 | pub const fn with_iso_year(mut self, value: i32) -> Option<Self> { |
693 | 0 | self.iso_year = OptionRangedI32::Some(const_try_opt!(RangedI32::new(value))); |
694 | 0 | Some(self) |
695 | 0 | } |
696 | | |
697 | | /// Set the `iso_year_century` component and return `self`. |
698 | | /// |
699 | | /// If the value is zero, the sign of the century is taken from the second parameter. Otherwise |
700 | | /// the sign is inferred from the value. |
701 | 0 | pub const fn with_iso_year_century(mut self, value: i16, is_negative: bool) -> Option<Self> { |
702 | 0 | self.iso_year_century = OptionRangedI16::Some(const_try_opt!(RangedI16::new(value))); |
703 | 0 | if value != 0 { |
704 | 0 | self.iso_year_century_is_negative = value.is_negative(); |
705 | 0 | } else { |
706 | 0 | self.iso_year_century_is_negative = is_negative; |
707 | 0 | } |
708 | 0 | Some(self) |
709 | 0 | } |
710 | | |
711 | | /// Set the `iso_year_last_two` component and return `self`. |
712 | 0 | pub const fn with_iso_year_last_two(mut self, value: u8) -> Option<Self> { |
713 | 0 | self.iso_year_last_two = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value))); |
714 | 0 | Some(self) |
715 | 0 | } |
716 | | |
717 | | /// Set the `month` component and return `self`. |
718 | 0 | pub const fn with_month(mut self, value: Month) -> Option<Self> { |
719 | 0 | self.month = Some(value); |
720 | 0 | Some(self) |
721 | 0 | } |
722 | | |
723 | | /// Set the `sunday_week_number` component and return `self`. |
724 | 0 | pub const fn with_sunday_week_number(mut self, value: u8) -> Option<Self> { |
725 | 0 | self.sunday_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value))); |
726 | 0 | Some(self) |
727 | 0 | } |
728 | | |
729 | | /// Set the `monday_week_number` component and return `self`. |
730 | 0 | pub const fn with_monday_week_number(mut self, value: u8) -> Option<Self> { |
731 | 0 | self.monday_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value))); |
732 | 0 | Some(self) |
733 | 0 | } |
734 | | |
735 | | /// Set the `iso_week_number` component and return `self`. |
736 | 0 | pub const fn with_iso_week_number(mut self, value: NonZeroU8) -> Option<Self> { |
737 | 0 | self.iso_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get()))); |
738 | 0 | Some(self) |
739 | 0 | } |
740 | | |
741 | | /// Set the `weekday` component and return `self`. |
742 | 0 | pub const fn with_weekday(mut self, value: Weekday) -> Option<Self> { |
743 | 0 | self.weekday = Some(value); |
744 | 0 | Some(self) |
745 | 0 | } |
746 | | |
747 | | /// Set the `ordinal` component and return `self`. |
748 | 0 | pub const fn with_ordinal(mut self, value: NonZeroU16) -> Option<Self> { |
749 | 0 | self.ordinal = OptionRangedU16::Some(const_try_opt!(RangedU16::new(value.get()))); |
750 | 0 | Some(self) |
751 | 0 | } |
752 | | |
753 | | /// Set the `day` component and return `self`. |
754 | 0 | pub const fn with_day(mut self, value: NonZeroU8) -> Option<Self> { |
755 | 0 | self.day = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get()))); |
756 | 0 | Some(self) |
757 | 0 | } |
758 | | |
759 | | /// Set the `hour_24` component and return `self`. |
760 | 0 | pub const fn with_hour_24(mut self, value: u8) -> Option<Self> { |
761 | 0 | self.hour_24 = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value))); |
762 | 0 | Some(self) |
763 | 0 | } |
764 | | |
765 | | /// Set the `hour_12` component and return `self`. |
766 | 0 | pub const fn with_hour_12(mut self, value: NonZeroU8) -> Option<Self> { |
767 | 0 | self.hour_12 = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get()))); |
768 | 0 | Some(self) |
769 | 0 | } |
770 | | |
771 | | /// Set the `hour_12_is_pm` component and return `self`. |
772 | 0 | pub const fn with_hour_12_is_pm(mut self, value: bool) -> Option<Self> { |
773 | 0 | self.hour_12_is_pm = Some(value); |
774 | 0 | Some(self) |
775 | 0 | } |
776 | | |
777 | | /// Set the `minute` component and return `self`. |
778 | 0 | pub const fn with_minute(mut self, value: u8) -> Option<Self> { |
779 | 0 | self.minute = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value))); |
780 | 0 | Some(self) |
781 | 0 | } |
782 | | |
783 | | /// Set the `second` component and return `self`. |
784 | 0 | pub const fn with_second(mut self, value: u8) -> Option<Self> { |
785 | 0 | self.second = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value))); |
786 | 0 | Some(self) |
787 | 0 | } |
788 | | |
789 | | /// Set the `subsecond` component and return `self`. |
790 | 0 | pub const fn with_subsecond(mut self, value: u32) -> Option<Self> { |
791 | 0 | self.subsecond = OptionRangedU32::Some(const_try_opt!(RangedU32::new(value))); |
792 | 0 | Some(self) |
793 | 0 | } |
794 | | |
795 | | /// Set the `offset_hour` component and return `self`. |
796 | 0 | pub const fn with_offset_hour(mut self, value: i8) -> Option<Self> { |
797 | 0 | self.offset_hour = OptionRangedI8::Some(const_try_opt!(RangedI8::new(value))); |
798 | 0 | Some(self) |
799 | 0 | } |
800 | | |
801 | | /// Set the `offset_minute` component and return `self`. |
802 | | #[doc(hidden)] |
803 | | #[deprecated( |
804 | | since = "0.3.8", |
805 | | note = "use `parsed.with_offset_minute_signed()` instead" |
806 | | )] |
807 | 0 | pub const fn with_offset_minute(self, value: u8) -> Option<Self> { |
808 | 0 | if value > i8::MAX as u8 { |
809 | 0 | None |
810 | | } else { |
811 | 0 | self.with_offset_minute_signed(value as _) |
812 | | } |
813 | 0 | } |
814 | | |
815 | | /// Set the `offset_minute` component and return `self`. |
816 | 0 | pub const fn with_offset_minute_signed(mut self, value: i8) -> Option<Self> { |
817 | 0 | self.offset_minute = OptionRangedI8::Some(const_try_opt!(RangedI8::new(value))); |
818 | 0 | Some(self) |
819 | 0 | } |
820 | | |
821 | | /// Set the `offset_minute` component and return `self`. |
822 | | #[doc(hidden)] |
823 | | #[deprecated( |
824 | | since = "0.3.8", |
825 | | note = "use `parsed.with_offset_second_signed()` instead" |
826 | | )] |
827 | 0 | pub const fn with_offset_second(self, value: u8) -> Option<Self> { |
828 | 0 | if value > i8::MAX as u8 { |
829 | 0 | None |
830 | | } else { |
831 | 0 | self.with_offset_second_signed(value as _) |
832 | | } |
833 | 0 | } |
834 | | |
835 | | /// Set the `offset_second` component and return `self`. |
836 | 0 | pub const fn with_offset_second_signed(mut self, value: i8) -> Option<Self> { |
837 | 0 | self.offset_second = OptionRangedI8::Some(const_try_opt!(RangedI8::new(value))); |
838 | 0 | Some(self) |
839 | 0 | } |
840 | | |
841 | | /// Set the `unix_timestamp_nanos` component and return `self`. |
842 | 0 | pub const fn with_unix_timestamp_nanos(mut self, value: i128) -> Option<Self> { |
843 | 0 | self.unix_timestamp_nanos = OptionRangedI128::Some(const_try_opt!(RangedI128::new(value))); |
844 | 0 | Some(self) |
845 | 0 | } |
846 | | } |
847 | | |
848 | | impl TryFrom<Parsed> for Date { |
849 | | type Error = error::TryFromParsed; |
850 | | |
851 | 0 | fn try_from(mut parsed: Parsed) -> Result<Self, Self::Error> { |
852 | | /// Match on the components that need to be present. |
853 | | macro_rules! match_ { |
854 | | (_ => $catch_all:expr $(,)?) => { |
855 | | $catch_all |
856 | | }; |
857 | | (($($name:ident),* $(,)?) => $arm:expr, $($rest:tt)*) => { |
858 | | if let ($(Some($name)),*) = ($(parsed.$name()),*) { |
859 | | $arm |
860 | | } else { |
861 | | match_!($($rest)*) |
862 | | } |
863 | | }; |
864 | | } |
865 | | |
866 | | /// Get the value needed to adjust the ordinal day for Sunday and Monday-based week |
867 | | /// numbering. |
868 | 0 | const fn adjustment(year: i32) -> i16 { |
869 | | // Safety: `ordinal` is not zero. |
870 | 0 | match unsafe { Date::__from_ordinal_date_unchecked(year, 1) }.weekday() { |
871 | 0 | Weekday::Monday => 7, |
872 | 0 | Weekday::Tuesday => 1, |
873 | 0 | Weekday::Wednesday => 2, |
874 | 0 | Weekday::Thursday => 3, |
875 | 0 | Weekday::Friday => 4, |
876 | 0 | Weekday::Saturday => 5, |
877 | 0 | Weekday::Sunday => 6, |
878 | | } |
879 | 0 | } |
880 | | |
881 | | // If we do not have the year but we have *both* the century and the last two digits, we can |
882 | | // construct the year. Likewise for the ISO year. |
883 | 0 | if let (None, Some(century), Some(is_negative), Some(last_two)) = ( |
884 | 0 | parsed.year(), |
885 | 0 | parsed.year_century(), |
886 | 0 | parsed.year_century_is_negative(), |
887 | 0 | parsed.year_last_two(), |
888 | | ) { |
889 | 0 | let year = if is_negative { |
890 | 0 | 100 * century.extend::<i32>() - last_two.cast_signed().extend::<i32>() |
891 | | } else { |
892 | 0 | 100 * century.extend::<i32>() + last_two.cast_signed().extend::<i32>() |
893 | | }; |
894 | 0 | parsed.year = OptionRangedI32::from(RangedI32::new(year)); |
895 | 0 | } |
896 | 0 | if let (None, Some(century), Some(is_negative), Some(last_two)) = ( |
897 | 0 | parsed.iso_year(), |
898 | 0 | parsed.iso_year_century(), |
899 | 0 | parsed.iso_year_century_is_negative(), |
900 | 0 | parsed.iso_year_last_two(), |
901 | | ) { |
902 | 0 | let iso_year = if is_negative { |
903 | 0 | 100 * century.extend::<i32>() - last_two.cast_signed().extend::<i32>() |
904 | | } else { |
905 | 0 | 100 * century.extend::<i32>() + last_two.cast_signed().extend::<i32>() |
906 | | }; |
907 | 0 | parsed.iso_year = OptionRangedI32::from(RangedI32::new(iso_year)); |
908 | 0 | } |
909 | | |
910 | 0 | match_! { |
911 | 0 | (year, ordinal) => Ok(Self::from_ordinal_date(year, ordinal.get())?), |
912 | 0 | (year, month, day) => Ok(Self::from_calendar_date(year, month, day.get())?), |
913 | 0 | (iso_year, iso_week_number, weekday) => Ok(Self::from_iso_week_date( |
914 | 0 | iso_year, |
915 | 0 | iso_week_number.get(), |
916 | 0 | weekday, |
917 | 0 | )?), |
918 | 0 | (year, sunday_week_number, weekday) => Ok(Self::from_ordinal_date( |
919 | 0 | year, |
920 | 0 | (sunday_week_number.cast_signed().extend::<i16>() * 7 |
921 | 0 | + weekday.number_days_from_sunday().cast_signed().extend::<i16>() |
922 | 0 | - adjustment(year) |
923 | 0 | + 1).cast_unsigned(), |
924 | 0 | )?), |
925 | 0 | (year, monday_week_number, weekday) => Ok(Self::from_ordinal_date( |
926 | 0 | year, |
927 | 0 | (monday_week_number.cast_signed().extend::<i16>() * 7 |
928 | 0 | + weekday.number_days_from_monday().cast_signed().extend::<i16>() |
929 | 0 | - adjustment(year) |
930 | 0 | + 1).cast_unsigned(), |
931 | 0 | )?), |
932 | 0 | _ => Err(InsufficientInformation), |
933 | | } |
934 | 0 | } |
935 | | } |
936 | | |
937 | | impl TryFrom<Parsed> for Time { |
938 | | type Error = error::TryFromParsed; |
939 | | |
940 | 0 | fn try_from(parsed: Parsed) -> Result<Self, Self::Error> { |
941 | 0 | let hour = match (parsed.hour_24(), parsed.hour_12(), parsed.hour_12_is_pm()) { |
942 | 0 | (Some(hour), _, _) => hour, |
943 | 0 | (_, Some(hour), Some(false)) if hour.get() == 12 => 0, |
944 | 0 | (_, Some(hour), Some(true)) if hour.get() == 12 => 12, |
945 | 0 | (_, Some(hour), Some(false)) => hour.get(), |
946 | 0 | (_, Some(hour), Some(true)) => hour.get() + 12, |
947 | 0 | _ => return Err(InsufficientInformation), |
948 | | }; |
949 | | |
950 | 0 | if parsed.hour_24().is_none() |
951 | 0 | && parsed.hour_12().is_some() |
952 | 0 | && parsed.hour_12_is_pm().is_some() |
953 | 0 | && parsed.minute().is_none() |
954 | 0 | && parsed.second().is_none() |
955 | 0 | && parsed.subsecond().is_none() |
956 | | { |
957 | 0 | return Ok(Self::from_hms_nano(hour, 0, 0, 0)?); |
958 | 0 | } |
959 | | |
960 | | // Reject combinations such as hour-second with minute omitted. |
961 | 0 | match (parsed.minute(), parsed.second(), parsed.subsecond()) { |
962 | 0 | (None, None, None) => Ok(Self::from_hms_nano(hour, 0, 0, 0)?), |
963 | 0 | (Some(minute), None, None) => Ok(Self::from_hms_nano(hour, minute, 0, 0)?), |
964 | 0 | (Some(minute), Some(second), None) => Ok(Self::from_hms_nano(hour, minute, second, 0)?), |
965 | 0 | (Some(minute), Some(second), Some(subsecond)) => { |
966 | 0 | Ok(Self::from_hms_nano(hour, minute, second, subsecond)?) |
967 | | } |
968 | 0 | _ => Err(InsufficientInformation), |
969 | | } |
970 | 0 | } |
971 | | } |
972 | | |
973 | | impl TryFrom<Parsed> for UtcOffset { |
974 | | type Error = error::TryFromParsed; |
975 | | |
976 | 0 | fn try_from(parsed: Parsed) -> Result<Self, Self::Error> { |
977 | 0 | let hour = parsed.offset_hour().ok_or(InsufficientInformation)?; |
978 | 0 | let minute = parsed.offset_minute_signed().unwrap_or(0); |
979 | 0 | let second = parsed.offset_second_signed().unwrap_or(0); |
980 | | |
981 | 0 | Self::from_hms(hour, minute, second).map_err(|mut err| { |
982 | | // Provide the user a more accurate error. |
983 | 0 | if err.name == "hours" { |
984 | 0 | err.name = "offset hour"; |
985 | 0 | } else if err.name == "minutes" { |
986 | 0 | err.name = "offset minute"; |
987 | 0 | } else if err.name == "seconds" { |
988 | 0 | err.name = "offset second"; |
989 | 0 | } |
990 | 0 | err.into() |
991 | 0 | }) |
992 | 0 | } |
993 | | } |
994 | | |
995 | | impl TryFrom<Parsed> for PrimitiveDateTime { |
996 | | type Error = error::TryFromParsed; |
997 | | |
998 | 0 | fn try_from(parsed: Parsed) -> Result<Self, Self::Error> { |
999 | 0 | Ok(Self::new(parsed.try_into()?, parsed.try_into()?)) |
1000 | 0 | } |
1001 | | } |
1002 | | |
1003 | | impl TryFrom<Parsed> for OffsetDateTime { |
1004 | | type Error = error::TryFromParsed; |
1005 | | |
1006 | 0 | fn try_from(mut parsed: Parsed) -> Result<Self, Self::Error> { |
1007 | 0 | if let Some(timestamp) = parsed.unix_timestamp_nanos() { |
1008 | 0 | let mut value = Self::from_unix_timestamp_nanos(timestamp)?; |
1009 | 0 | if let Some(subsecond) = parsed.subsecond() { |
1010 | 0 | value = value.replace_nanosecond(subsecond)?; |
1011 | 0 | } |
1012 | 0 | return Ok(value); |
1013 | 0 | } |
1014 | | |
1015 | | // Some well-known formats explicitly allow leap seconds. We don't currently support them, |
1016 | | // so treat it as the nearest preceding moment that can be represented. Because leap seconds |
1017 | | // always fall at the end of a month UTC, reject any that are at other times. |
1018 | 0 | let leap_second_input = if parsed.leap_second_allowed && parsed.second() == Some(60) { |
1019 | 0 | if parsed.set_second(59).is_none() { |
1020 | 0 | bug!("59 is a valid second"); |
1021 | 0 | } |
1022 | 0 | if parsed.set_subsecond(999_999_999).is_none() { |
1023 | 0 | bug!("999_999_999 is a valid subsecond"); |
1024 | 0 | } |
1025 | 0 | true |
1026 | | } else { |
1027 | 0 | false |
1028 | | }; |
1029 | | |
1030 | 0 | let dt = Self::new_in_offset( |
1031 | 0 | Date::try_from(parsed)?, |
1032 | 0 | Time::try_from(parsed)?, |
1033 | 0 | UtcOffset::try_from(parsed)?, |
1034 | | ); |
1035 | | |
1036 | 0 | if leap_second_input && !dt.is_valid_leap_second_stand_in() { |
1037 | 0 | return Err(error::TryFromParsed::ComponentRange( |
1038 | 0 | error::ComponentRange { |
1039 | 0 | name: "second", |
1040 | 0 | minimum: 0, |
1041 | 0 | maximum: 59, |
1042 | 0 | value: 60, |
1043 | 0 | conditional_range: true, |
1044 | 0 | }, |
1045 | 0 | )); |
1046 | 0 | } |
1047 | 0 | Ok(dt) |
1048 | 0 | } |
1049 | | } |