speedate/lib.rs
1#![doc = include_str ! ("../README.md")]
2extern crate core;
3extern crate strum;
4
5use strum::{Display, EnumMessage};
6
7mod config;
8mod date;
9mod datetime;
10mod duration;
11mod numbers;
12mod time;
13mod util;
14
15pub use config::{
16 DateConfig, DateConfigBuilder, DateTimeConfig, DateTimeConfigBuilder, TimeConfig, TimeConfigBuilder, TimestampUnit,
17};
18pub use date::Date;
19pub use datetime::DateTime;
20pub use duration::Duration;
21pub use time::{MicrosecondsPrecisionOverflowBehavior, Time};
22
23pub use numbers::{float_parse_bytes, float_parse_str, int_parse_bytes, int_parse_str, IntFloat};
24
25// Parsing datetime, date, time & duration values
26
27// get a character from the bytes as as a decimal
28macro_rules! get_digit {
29 ($bytes:ident, $index:expr, $error:ident) => {
30 match $bytes.get($index) {
31 Some(c) if c.is_ascii_digit() => c - b'0',
32 _ => return Err(ParseError::$error),
33 }
34 };
35}
36pub(crate) use get_digit;
37
38// as above without bounds check, requires length to checked first!
39macro_rules! get_digit_unchecked {
40 ($bytes:ident, $index:expr, $error:ident) => {
41 match $bytes.get_unchecked($index) {
42 c if c.is_ascii_digit() => c - b'0',
43 _ => return Err(ParseError::$error),
44 }
45 };
46}
47pub(crate) use get_digit_unchecked;
48
49/// Details about errors when parsing datetime, date, time & duration values
50///
51/// As well as comparing enum values, machine and human readable representations of
52/// errors are provided.
53///
54/// # Examples
55/// (Note: the `strum::EnumMessage` trait must be used to support `.get_documentation()`)
56/// ```
57/// use strum::EnumMessage;
58/// use speedate::{Date, ParseError};
59///
60/// match Date::parse_str("invalid") {
61/// Ok(_) => println!("Parsed successfully"),
62/// Err(error) => {
63/// assert_eq!(error, ParseError::TooShort);
64/// assert_eq!(error.to_string(), "too_short");
65/// assert_eq!(error.get_documentation(), Some("input is too short"));
66/// }
67/// };
68/// ```
69#[derive(Debug, Display, EnumMessage, PartialEq, Eq, Clone)]
70#[strum(serialize_all = "snake_case")]
71pub enum ParseError {
72 /// input is too short
73 TooShort,
74 /// unexpected extra characters at the end of the input
75 ExtraCharacters,
76 /// invalid datetime separator, expected `T`, `t`, `_` or space
77 InvalidCharDateTimeSep,
78 /// invalid date separator, expected `-`
79 InvalidCharDateSep,
80 /// Timestamp is not an exact date
81 DateNotExact,
82 /// invalid character in year
83 InvalidCharYear,
84 /// invalid character in month
85 InvalidCharMonth,
86 /// invalid character in day
87 InvalidCharDay,
88 /// invalid time separator, expected `:`
89 InvalidCharTimeSep,
90 /// invalid character in hour
91 InvalidCharHour,
92 /// invalid character in minute
93 InvalidCharMinute,
94 /// invalid character in second
95 InvalidCharSecond,
96 /// invalid character in second fraction
97 InvalidCharSecondFraction,
98 /// invalid timezone sign
99 InvalidCharTzSign,
100 /// invalid timezone hour
101 InvalidCharTzHour,
102 /// invalid timezone minute
103 InvalidCharTzMinute,
104 /// timezone minute value is outside expected range of 0-59
105 OutOfRangeTzMinute,
106 /// timezone offset must be less than 24 hours
107 OutOfRangeTz,
108 /// timezone is required to adjust to a new timezone
109 TzRequired,
110 /// Error getting system time
111 SystemTimeError,
112 /// month value is outside expected range of 1-12
113 OutOfRangeMonth,
114 /// day value is outside expected range
115 OutOfRangeDay,
116 /// hour value is outside expected range of 0-23
117 OutOfRangeHour,
118 /// minute value is outside expected range of 0-59
119 OutOfRangeMinute,
120 /// second value is outside expected range of 0-59
121 OutOfRangeSecond,
122 /// second fraction value is more than 6 digits long
123 SecondFractionTooLong,
124 /// second fraction digits missing after `.`
125 SecondFractionMissing,
126 /// millisecond fraction value is more than 3 digits long
127 MillisecondFractionTooLong,
128 /// invalid digit in duration
129 DurationInvalidNumber,
130 /// `t` character repeated in duration
131 DurationTRepeated,
132 /// quantity fraction invalid in duration
133 DurationInvalidFraction,
134 /// quantity invalid in time part of duration
135 DurationInvalidTimeUnit,
136 /// quantity invalid in date part of duration
137 DurationInvalidDateUnit,
138 /// "day" identifier in duration not correctly formatted
139 DurationInvalidDays,
140 /// a numeric value in the duration is too large
141 DurationValueTooLarge,
142 /// durations may not exceed 999,999,999 hours
143 DurationHourValueTooLarge,
144 /// durations may not exceed 999,999,999 days
145 DurationDaysTooLarge,
146 /// dates before 0000 are not supported as unix timestamps
147 DateTooSmall,
148 /// dates after 9999 are not supported as unix timestamps
149 DateTooLarge,
150 /// numeric times may not exceed 86,399 seconds
151 TimeTooLarge,
152}
153
154#[derive(Debug, Display, EnumMessage, PartialEq, Eq, Clone)]
155#[strum(serialize_all = "snake_case")]
156pub enum ConfigError {
157 // SecondsPrecisionOverflowBehavior string representation, must be one of "error" or "truncate"
158 UnknownMicrosecondsPrecisionOverflowBehaviorString,
159 // TimestampUnit string representation, must be one of "s", "ms" or "infer"
160 UnknownTimestampUnitString,
161}
162
163/// Used internally to write numbers to a buffer for `Display` of speedate types
164fn display_num_buf(num: usize, start: usize, value: u32, buf: &mut [u8]) {
165 for i in 0..num {
166 if (i + 1) == num {
167 buf[i + start] = b'0' + (value % 10) as u8;
168 } else if num <= 2 {
169 buf[i + start] = b'0' + (value / (10i32.pow((num - 1 - i) as u32)) as u32) as u8;
170 } else {
171 buf[i + start] = b'0' + (value / (10i32.pow((num - 1 - i) as u32)) as u32 % 10) as u8;
172 }
173 }
174}