1use crate::{civil::Weekday, error, tz::Offset, util::escape};
2
3#[derive(Clone, Debug)]
4pub(crate) enum Error {
5 ColonCount {
6 directive: u8,
7 },
8 DirectiveFailure {
9 directive: u8,
10 colons: u8,
11 },
12 DirectiveFailureDot {
13 directive: u8,
14 },
15 ExpectedDirectiveAfterColons,
16 ExpectedDirectiveAfterFlag {
17 flag: u8,
18 },
19 ExpectedDirectiveAfterWidth,
20 FailedStrftime,
21 FailedStrptime,
22 FailedWidth,
23 InvalidDate,
24 InvalidISOWeekDate,
25 InvalidWeekdayMonday {
26 got: Weekday,
27 },
28 InvalidWeekdaySunday {
29 got: Weekday,
30 },
31 MismatchOffset {
32 parsed: Offset,
33 got: Offset,
34 },
35 MismatchWeekday {
36 parsed: Weekday,
37 got: Weekday,
38 },
39 MissingTimeHourForFractional,
40 MissingTimeHourForMinute,
41 MissingTimeHourForSecond,
42 MissingTimeMinuteForFractional,
43 MissingTimeMinuteForSecond,
44 MissingTimeSecondForFractional,
45 RangeTimestamp,
46 RangeWidth,
47 RequiredDateForDateTime,
48 RequiredDateTimeForTimestamp,
49 RequiredDateTimeForZoned,
50 RequiredOffsetForTimestamp,
51 RequiredSomeDayForDate,
52 RequiredTimeForDateTime,
53 RequiredYearForDate,
54 UnconsumedStrptime {
55 #[cfg(feature = "alloc")]
56 remaining: alloc::boxed::Box<[u8]>,
57 },
58 UnexpectedEndAfterDot,
59 UnexpectedEndAfterPercent,
60 UnknownDirectiveAfterDot {
61 directive: u8,
62 },
63 UnknownDirective {
64 directive: u8,
65 },
66 ZonedOffsetOrTz,
67}
68
69impl Error {
70 pub(crate) fn unconsumed(_remaining: &[u8]) -> Error {
71 Error::UnconsumedStrptime {
72 #[cfg(feature = "alloc")]
73 remaining: _remaining.into(),
74 }
75 }
76}
77
78impl error::IntoError for Error {
79 fn into_error(self) -> error::Error {
80 self.into()
81 }
82}
83
84impl From<Error> for error::Error {
85 #[cold]
86 #[inline(never)]
87 fn from(err: Error) -> error::Error {
88 error::ErrorKind::FmtStrtime(err).into()
89 }
90}
91
92impl core::fmt::Display for Error {
93 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
94 use self::Error::*;
95
96 match *self {
97 ColonCount { directive } => write!(
98 f,
99 "invalid number of `:` in `%{directive}` directive",
100 directive = escape::Byte(directive),
101 ),
102 DirectiveFailure { directive, colons } => write!(
103 f,
104 "%{colons}{directive} failed",
105 colons = escape::RepeatByte { byte: b':', count: colons },
106 directive = escape::Byte(directive),
107 ),
108 DirectiveFailureDot { directive } => write!(
109 f,
110 "%.{directive} failed",
111 directive = escape::Byte(directive),
112 ),
113 ExpectedDirectiveAfterColons => f.write_str(
114 "expected to find specifier directive after colons, \
115 but found end of format string",
116 ),
117 ExpectedDirectiveAfterFlag { flag } => write!(
118 f,
119 "expected to find specifier directive after flag \
120 `{flag}`, but found end of format string",
121 flag = escape::Byte(flag),
122 ),
123 ExpectedDirectiveAfterWidth => f.write_str(
124 "expected to find specifier directive after parsed width, \
125 but found end of format string",
126 ),
127 FailedStrftime => f.write_str("strftime formatting failed"),
128 FailedStrptime => f.write_str("strptime parsing failed"),
129 FailedWidth => {
130 f.write_str("failed to parse conversion specifier width")
131 }
132 InvalidDate => f.write_str("invalid date"),
133 InvalidISOWeekDate => f.write_str("invalid ISO 8601 week date"),
134 InvalidWeekdayMonday { got } => write!(
135 f,
136 "weekday `{got:?}` is not valid for \
137 Monday based week number",
138 ),
139 InvalidWeekdaySunday { got } => write!(
140 f,
141 "weekday `{got:?}` is not valid for \
142 Sunday based week number",
143 ),
144 MissingTimeHourForFractional => f.write_str(
145 "parsing format did not include hour directive, \
146 but did include fractional second directive (cannot have \
147 smaller time units with bigger time units missing)",
148 ),
149 MissingTimeHourForMinute => f.write_str(
150 "parsing format did not include hour directive, \
151 but did include minute directive (cannot have \
152 smaller time units with bigger time units missing)",
153 ),
154 MissingTimeHourForSecond => f.write_str(
155 "parsing format did not include hour directive, \
156 but did include second directive (cannot have \
157 smaller time units with bigger time units missing)",
158 ),
159 MissingTimeMinuteForFractional => f.write_str(
160 "parsing format did not include minute directive, \
161 but did include fractional second directive (cannot have \
162 smaller time units with bigger time units missing)",
163 ),
164 MissingTimeMinuteForSecond => f.write_str(
165 "parsing format did not include minute directive, \
166 but did include second directive (cannot have \
167 smaller time units with bigger time units missing)",
168 ),
169 MissingTimeSecondForFractional => f.write_str(
170 "parsing format did not include second directive, \
171 but did include fractional second directive (cannot have \
172 smaller time units with bigger time units missing)",
173 ),
174 MismatchOffset { parsed, got } => write!(
175 f,
176 "parsed time zone offset `{parsed}`, but \
177 offset from timestamp and time zone is `{got}`",
178 ),
179 MismatchWeekday { parsed, got } => write!(
180 f,
181 "parsed weekday `{parsed:?}` does not match \
182 weekday `{got:?}` from parsed date",
183 ),
184 RangeTimestamp => f.write_str(
185 "parsed datetime and offset, \
186 but combining them into a zoned datetime \
187 is outside Jiff's supported timestamp range",
188 ),
189 RangeWidth => write!(
190 f,
191 "parsed width is too big, max is {max}",
192 max = u8::MAX
193 ),
194 RequiredDateForDateTime => {
195 f.write_str("date required to parse datetime")
196 }
197 RequiredDateTimeForTimestamp => {
198 f.write_str("datetime required to parse timestamp")
199 }
200 RequiredDateTimeForZoned => {
201 f.write_str("datetime required to parse zoned datetime")
202 }
203 RequiredOffsetForTimestamp => {
204 f.write_str("offset required to parse timestamp")
205 }
206 RequiredSomeDayForDate => f.write_str(
207 "a month/day, day-of-year or week date must be \
208 present to create a date, but none were found",
209 ),
210 RequiredTimeForDateTime => {
211 f.write_str("time required to parse datetime")
212 }
213 RequiredYearForDate => f.write_str("year required to parse date"),
214 #[cfg(feature = "alloc")]
215 UnconsumedStrptime { ref remaining } => write!(
216 f,
217 "strptime expects to consume the entire input, but \
218 `{remaining}` remains unparsed",
219 remaining = escape::Bytes(remaining),
220 ),
221 #[cfg(not(feature = "alloc"))]
222 UnconsumedStrptime {} => f.write_str(
223 "strptime expects to consume the entire input, but \
224 there is unparsed input remaining",
225 ),
226 UnexpectedEndAfterDot => f.write_str(
227 "invalid format string, expected directive after `%.`",
228 ),
229 UnexpectedEndAfterPercent => f.write_str(
230 "invalid format string, expected byte after `%`, \
231 but found end of format string",
232 ),
233 UnknownDirective { directive } => write!(
234 f,
235 "found unrecognized specifier directive `{directive}`",
236 directive = escape::Byte(directive),
237 ),
238 UnknownDirectiveAfterDot { directive } => write!(
239 f,
240 "found unrecognized specifier directive `{directive}` \
241 following `%.`",
242 directive = escape::Byte(directive),
243 ),
244 ZonedOffsetOrTz => f.write_str(
245 "either offset (from `%z`) or IANA time zone identifier \
246 (from `%Q`) is required for parsing zoned datetime",
247 ),
248 }
249 }
250}
251
252#[derive(Clone, Debug)]
253pub(crate) enum ParseError {
254 ExpectedAmPm,
255 ExpectedAmPmTooShort,
256 ExpectedIanaTz,
257 ExpectedIanaTzEndOfInput,
258 ExpectedMonthAbbreviation,
259 ExpectedMonthAbbreviationTooShort,
260 ExpectedWeekdayAbbreviation,
261 ExpectedWeekdayAbbreviationTooShort,
262 ExpectedChoice {
263 available: &'static [&'static [u8]],
264 },
265 ExpectedFractionalDigit,
266 ExpectedMatchLiteralByte {
267 expected: u8,
268 got: u8,
269 },
270 ExpectedMatchLiteralEndOfInput {
271 expected: u8,
272 },
273 ExpectedNonEmpty {
274 directive: u8,
275 },
276 #[cfg(not(feature = "alloc"))]
277 NotAllowedAlloc {
278 directive: u8,
279 colons: u8,
280 },
281 NotAllowedLocaleClockTime,
282 NotAllowedLocaleDate,
283 NotAllowedLocaleDateAndTime,
284 NotAllowedLocaleTwelveHourClockTime,
285 NotAllowedTimeZoneAbbreviation,
286 ParseDay,
287 ParseDayOfYear,
288 ParseCentury,
289 ParseFractionalSeconds,
290 ParseHour,
291 ParseIsoWeekNumber,
292 ParseIsoWeekYear,
293 ParseIsoWeekYearTwoDigit,
294 ParseMinute,
295 ParseMondayWeekNumber,
296 ParseMonth,
297 ParseSecond,
298 ParseSundayWeekNumber,
299 ParseTimestamp,
300 ParseWeekdayNumber,
301 ParseYear,
302 ParseYearTwoDigit,
303 UnknownMonthName,
304 UnknownWeekdayAbbreviation,
305}
306
307impl error::IntoError for ParseError {
308 fn into_error(self) -> error::Error {
309 self.into()
310 }
311}
312
313impl From<ParseError> for error::Error {
314 #[cold]
315 #[inline(never)]
316 fn from(err: ParseError) -> error::Error {
317 error::ErrorKind::FmtStrtimeParse(err).into()
318 }
319}
320
321impl core::fmt::Display for ParseError {
322 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
323 use self::ParseError::*;
324
325 match *self {
326 ExpectedAmPm => f.write_str("expected to find `AM` or `PM`"),
327 ExpectedAmPmTooShort => f.write_str(
328 "expected to find `AM` or `PM`, \
329 but the remaining input is too short \
330 to contain one",
331 ),
332 ExpectedIanaTz => f.write_str(
333 "expected to find the start of an IANA time zone \
334 identifier name or component",
335 ),
336 ExpectedIanaTzEndOfInput => f.write_str(
337 "expected to find the start of an IANA time zone \
338 identifier name or component, \
339 but found end of input instead",
340 ),
341 ExpectedMonthAbbreviation => {
342 f.write_str("expected to find month name abbreviation")
343 }
344 ExpectedMonthAbbreviationTooShort => f.write_str(
345 "expected to find month name abbreviation, \
346 but the remaining input is too short \
347 to contain one",
348 ),
349 ExpectedWeekdayAbbreviation => {
350 f.write_str("expected to find weekday abbreviation")
351 }
352 ExpectedWeekdayAbbreviationTooShort => f.write_str(
353 "expected to find weekday abbreviation, \
354 but the remaining input is too short \
355 to contain one",
356 ),
357 ExpectedChoice { available } => {
358 f.write_str(
359 "failed to find expected value, available choices are: ",
360 )?;
361 for (i, choice) in available.iter().enumerate() {
362 if i > 0 {
363 f.write_str(", ")?;
364 }
365 write!(f, "{}", escape::Bytes(choice))?;
366 }
367 Ok(())
368 }
369 ExpectedFractionalDigit => f.write_str(
370 "expected at least one fractional decimal digit, \
371 but did not find any",
372 ),
373 ExpectedMatchLiteralByte { expected, got } => write!(
374 f,
375 "expected to match literal byte `{expected}` from \
376 format string, but found byte `{got}` in input",
377 expected = escape::Byte(expected),
378 got = escape::Byte(got),
379 ),
380 ExpectedMatchLiteralEndOfInput { expected } => write!(
381 f,
382 "expected to match literal byte `{expected}` from \
383 format string, but found end of input",
384 expected = escape::Byte(expected),
385 ),
386 ExpectedNonEmpty { directive } => write!(
387 f,
388 "expected non-empty input for directive `%{directive}`, \
389 but found end of input",
390 directive = escape::Byte(directive),
391 ),
392 #[cfg(not(feature = "alloc"))]
393 NotAllowedAlloc { directive, colons } => write!(
394 f,
395 "cannot parse `%{colons}{directive}` \
396 without Jiff's `alloc` feature enabled",
397 colons = escape::RepeatByte { byte: b':', count: colons },
398 directive = escape::Byte(directive),
399 ),
400 NotAllowedLocaleClockTime => {
401 f.write_str("parsing locale clock time is not allowed")
402 }
403 NotAllowedLocaleDate => {
404 f.write_str("parsing locale date is not allowed")
405 }
406 NotAllowedLocaleDateAndTime => {
407 f.write_str("parsing locale date and time is not allowed")
408 }
409 NotAllowedLocaleTwelveHourClockTime => {
410 f.write_str("parsing locale 12-hour clock time is not allowed")
411 }
412 NotAllowedTimeZoneAbbreviation => {
413 f.write_str("parsing time zone abbreviation is not allowed")
414 }
415 ParseCentury => {
416 f.write_str("failed to parse year number for century")
417 }
418 ParseDay => f.write_str("failed to parse day number"),
419 ParseDayOfYear => {
420 f.write_str("failed to parse day of year number")
421 }
422 ParseFractionalSeconds => f.write_str(
423 "failed to parse fractional second component \
424 (up to 9 digits, nanosecond precision)",
425 ),
426 ParseHour => f.write_str("failed to parse hour number"),
427 ParseMinute => f.write_str("failed to parse minute number"),
428 ParseWeekdayNumber => {
429 f.write_str("failed to parse weekday number")
430 }
431 ParseIsoWeekNumber => {
432 f.write_str("failed to parse ISO 8601 week number")
433 }
434 ParseIsoWeekYear => {
435 f.write_str("failed to parse ISO 8601 week year")
436 }
437 ParseIsoWeekYearTwoDigit => {
438 f.write_str("failed to parse 2-digit ISO 8601 week year")
439 }
440 ParseMondayWeekNumber => {
441 f.write_str("failed to parse Monday-based week number")
442 }
443 ParseMonth => f.write_str("failed to parse month number"),
444 ParseSecond => f.write_str("failed to parse second number"),
445 ParseSundayWeekNumber => {
446 f.write_str("failed to parse Sunday-based week number")
447 }
448 ParseTimestamp => {
449 f.write_str("failed to parse Unix timestamp (in seconds)")
450 }
451 ParseYear => f.write_str("failed to parse year"),
452 ParseYearTwoDigit => f.write_str("failed to parse 2-digit year"),
453 UnknownMonthName => f.write_str("unrecognized month name"),
454 UnknownWeekdayAbbreviation => {
455 f.write_str("unrecognized weekday abbreviation")
456 }
457 }
458 }
459}
460
461#[derive(Clone, Debug)]
462pub(crate) enum FormatError {
463 RequiresDate,
464 RequiresInstant,
465 RequiresOffset,
466 RequiresTime,
467 RequiresTimeZone,
468 RequiresTimeZoneOrOffset,
469 InvalidUtf8,
470 ZeroPrecisionFloat,
471 ZeroPrecisionNano,
472}
473
474impl error::IntoError for FormatError {
475 fn into_error(self) -> error::Error {
476 self.into()
477 }
478}
479
480impl From<FormatError> for error::Error {
481 #[cold]
482 #[inline(never)]
483 fn from(err: FormatError) -> error::Error {
484 error::ErrorKind::FmtStrtimeFormat(err).into()
485 }
486}
487
488impl core::fmt::Display for FormatError {
489 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
490 use self::FormatError::*;
491
492 match *self {
493 RequiresDate => f.write_str("requires date to format"),
494 RequiresInstant => f.write_str(
495 "requires instant (a timestamp or a date, time and offset)",
496 ),
497 RequiresTime => f.write_str("requires time to format"),
498 RequiresOffset => f.write_str("requires time zone offset"),
499 RequiresTimeZone => {
500 f.write_str("requires IANA time zone identifier")
501 }
502 RequiresTimeZoneOrOffset => f.write_str(
503 "requires IANA time zone identifier or \
504 time zone offset, but neither were present",
505 ),
506 InvalidUtf8 => {
507 f.write_str("invalid format string, it must be valid UTF-8")
508 }
509 ZeroPrecisionFloat => {
510 f.write_str("zero precision with %f is not allowed")
511 }
512 ZeroPrecisionNano => {
513 f.write_str("zero precision with %N is not allowed")
514 }
515 }
516 }
517}