1use crate::Version;
2use thiserror::Error;
3
4#[derive(Debug, Error, PartialEq, Eq)]
5pub enum TzifBuildError {
6 #[error("time zone designation must not be empty")]
7 EmptyDesignation,
8 #[error("time zone designation {designation:?} must be ASCII")]
9 NonAsciiDesignation { designation: String },
10 #[error("time zone designation {designation:?} contains unsupported character {character:?}")]
11 UnsupportedDesignationCharacter {
12 designation: String,
13 character: char,
14 },
15 #[error("time zone designation {designation:?} must be at least 3 characters")]
16 DesignationTooShort { designation: String },
17 #[error("time zone designation {designation:?} must be at most 6 characters")]
18 DesignationTooLong { designation: String },
19 #[error("duplicate time zone designation {0:?}")]
20 DuplicateDesignation(String),
21 #[error("unknown time zone designation {0:?}")]
22 UnknownDesignation(String),
23 #[error("explicit transition times must be sorted in ascending order")]
24 UnsortedTransitions,
25 #[error("UTC offset -2^31 is not valid in TZif local time types")]
26 InvalidUtcOffset,
27 #[error("UTC offset {seconds} cannot be represented in a POSIX TZ footer")]
28 PosixOffsetOutOfRange { seconds: i32 },
29 #[error("TZif version {version:?} cannot represent transition timestamp {timestamp}")]
30 TransitionOutOfRangeForVersion { version: Version, timestamp: i64 },
31 #[error("TZif version {version:?} cannot include a footer")]
32 VersionCannotIncludeFooter { version: Version },
33 #[error("TZif version {version:?} cannot represent the POSIX TZ string extension")]
34 VersionCannotRepresentFooterExtension { version: Version },
35 #[error("POSIX TZ month {month} is outside the range 1..=12")]
36 InvalidPosixMonth { month: u8 },
37 #[error("POSIX TZ week {week} is outside the range 1..=5")]
38 InvalidPosixWeek { week: u8 },
39 #[error("POSIX TZ weekday {weekday} is outside the range 0..=6")]
40 InvalidPosixWeekday { weekday: u8 },
41 #[error("POSIX TZ Julian day {day} is outside the range 1..=365")]
42 InvalidPosixJulianDay { day: u16 },
43 #[error("POSIX TZ zero-based day {day} is outside the range 0..=365")]
44 InvalidPosixZeroBasedDay { day: u16 },
45 #[error("POSIX TZ transition time {seconds} seconds is outside the supported range")]
46 InvalidPosixTransitionTime { seconds: i32 },
47 #[error(transparent)]
48 InvalidTzif(#[from] TzifError),
49}
50
51#[derive(Debug, Error, PartialEq, Eq)]
52pub enum TzdistError {
53 #[error(transparent)]
54 InvalidTzif(#[from] TzifError),
55 #[error("unsupported TZif media type {0}")]
56 UnsupportedMediaType(String),
57 #[error("TZDIST capabilities must advertise application/tzif when advertising application/tzif-leap")]
58 TzifLeapCapabilityRequiresTzif,
59 #[error("application/tzif MUST NOT contain leap-second records; use application/tzif-leap")]
60 LeapSecondsNotAllowedForApplicationTzif,
61 #[error("TZDIST truncation requires a version 2 or later TZif file")]
62 TruncationRequiresVersion2Plus,
63 #[error("TZDIST truncation requires at least one version 2+ transition")]
64 TruncationRequiresVersion2PlusTransitions,
65 #[error("start truncation transition mismatch: expected {expected}, got {actual}")]
66 StartTruncationTransitionMismatch { expected: i64, actual: i64 },
67 #[error("start truncation requires time type 0 to be a -00 placeholder")]
68 StartTruncationTypeZeroMustBePlaceholder,
69 #[error("end truncation transition mismatch: expected {expected}, got {actual}")]
70 EndTruncationTransitionMismatch { expected: i64, actual: i64 },
71 #[error("end truncation requires an empty TZ string footer")]
72 EndTruncationRequiresEmptyFooter,
73 #[error("end truncation requires the last transition type to be a -00 placeholder")]
74 EndTruncationLastTypeMustBePlaceholder,
75 #[error("invalid TZDIST truncation range: start {start} must be before end {end}")]
76 InvalidTruncationRange { start: i64, end: i64 },
77}
78
79#[derive(Debug, Error, PartialEq, Eq)]
80pub enum TzifError {
81 #[error("input ended unexpectedly at byte {offset} while reading {context}")]
82 UnexpectedEof {
83 offset: usize,
84 context: &'static str,
85 },
86 #[error("expected TZif magic at byte {offset}")]
87 InvalidMagic { offset: usize },
88 #[error("invalid TZif version byte 0x{0:02x}")]
89 InvalidVersion(u8),
90 #[error("version mismatch between first header {first:?} and second header {second:?}")]
91 VersionMismatch { first: Version, second: Version },
92 #[error("expected newline at byte {offset} before TZif footer")]
93 MissingFooterStart { offset: usize },
94 #[error("missing newline terminator for TZif footer starting at byte {offset}")]
95 MissingFooterEnd { offset: usize },
96 #[error("TZif footer is not valid UTF-8")]
97 InvalidFooterUtf8,
98 #[error("trailing data starts at byte {offset}")]
99 TrailingData { offset: usize },
100 #[error("{field} count {count} cannot fit in memory on this platform")]
101 CountTooLarge { field: &'static str, count: u32 },
102 #[error("data block byte length overflow while calculating {field}")]
103 DataBlockLengthOverflow { field: &'static str },
104 #[error("local time type {index} has invalid isdst value {value}")]
105 InvalidDstIndicator { index: usize, value: u8 },
106 #[error("{field} indicator {index} has invalid value {value}")]
107 InvalidBooleanIndicator {
108 field: &'static str,
109 index: usize,
110 value: u8,
111 },
112 #[error("version 1 files must not include a v2+ data block or footer")]
113 UnexpectedV2PlusData,
114 #[error("version {0:?} files must include a v2+ data block and footer")]
115 MissingV2PlusData(Version),
116 #[error("{field} has {actual} entries, but expected {expected}")]
117 CountMismatch {
118 field: &'static str,
119 expected: usize,
120 actual: usize,
121 },
122 #[error("{field} count {count} exceeds TZif u32 count range")]
123 CountOverflow { field: &'static str, count: usize },
124 #[error("local time type count must not be zero")]
125 EmptyLocalTimeTypes,
126 #[error("designation table must not be empty")]
127 EmptyDesignations,
128 #[error("local time type count {0} exceeds 256")]
129 TooManyLocalTimeTypes(usize),
130 #[error(
131 "transition type {transition_type} at index {index} does not reference a local time type"
132 )]
133 InvalidTransitionType { index: usize, transition_type: u8 },
134 #[error("designation index {designation_index} at local time type {index} is out of range")]
135 InvalidDesignationIndex { index: usize, designation_index: u8 },
136 #[error("transition time {value} at index {index} is outside the version 1 i32 range")]
137 Version1TransitionOutOfRange { index: usize, value: i64 },
138 #[error("leap-second occurrence {value} at index {index} is outside the version 1 i32 range")]
139 Version1LeapSecondOutOfRange { index: usize, value: i64 },
140 #[error("transition time at index {index} is not strictly ascending")]
141 TransitionTimesNotAscending { index: usize },
142 #[error(
143 "designation index {designation_index} at local time type {index} has no NUL terminator"
144 )]
145 UnterminatedDesignation { index: usize, designation_index: u8 },
146 #[error("local time type {index} has invalid UTC offset -2^31")]
147 InvalidUtcOffset { index: usize },
148 #[error("time zone designation at local time type {index} violates RFC 9636 designation requirements")]
149 InvalidDesignation { index: usize, designation: Vec<u8> },
150 #[error("UT/local indicator {index} is set without the corresponding standard/wall indicator")]
151 InvalidUtLocalIndicatorCombination { index: usize },
152 #[error("first leap-second occurrence {value} at index 0 must be non-negative")]
153 FirstLeapSecondOccurrenceNegative { value: i64 },
154 #[error("leap-second occurrence at index {index} is not strictly ascending")]
155 LeapSecondOccurrencesNotAscending { index: usize },
156 #[error("first leap-second correction {correction} must be +1 or -1 unless using version 4 truncation")]
157 InvalidFirstLeapSecondCorrection { correction: i32 },
158 #[error("leap-second correction at index {index} must differ from the previous correction by +1 or -1")]
159 InvalidLeapSecondCorrection { index: usize },
160 #[error("version {version:?} cannot contain a leap-second table truncated at the start")]
161 LeapSecondTruncationRequiresVersion4 { version: Version },
162 #[error("version {version:?} cannot contain a leap-second table expiration time")]
163 LeapSecondExpirationRequiresVersion4 { version: Version },
164 #[error("leap-second occurrence at index {index} does not occur at a UTC month boundary")]
165 LeapSecondOccurrenceNotAtMonthEnd { index: usize },
166 #[error("TZif footer must be ASCII")]
167 InvalidFooterAscii,
168 #[error("TZif footer must not contain NUL or newline bytes")]
169 InvalidFooterControlByte,
170 #[error("TZif version {version:?} cannot use the POSIX TZ string extension")]
171 FooterExtensionRequiresVersion3 { version: Version },
172 #[error("TZif footer is not a valid POSIX TZ string")]
173 InvalidFooterSyntax,
174 #[error("TZif footer is inconsistent with the last transition type")]
175 FooterInconsistentWithLastTransition,
176}