1 | /*! |
2 | Facilities for dealing with inexact dates and times. |
3 | |
4 | # Overview |
5 | |
6 | The essential types in this module are: |
7 | |
8 | * [`Date`] is a specific day in the Gregorian calendar. |
9 | * [`Time`] is a specific wall clock time. |
10 | * [`DateTime`] is a combination of a day and a time. |
11 | |
12 | Moreover, the [`date`](date()) and [`time`](time()) free functions can be used |
13 | to conveniently create values of any of three types above: |
14 | |
15 | ``` |
16 | use jiff::civil::{date, time}; |
17 | |
18 | assert_eq!(date(2024, 7, 31).to_string(), "2024-07-31" ); |
19 | assert_eq!(time(15, 20, 0, 123).to_string(), "15:20:00.000000123" ); |
20 | assert_eq!( |
21 | date(2024, 7, 31).at(15, 20, 0, 123).to_string(), |
22 | "2024-07-31T15:20:00.000000123" , |
23 | ); |
24 | assert_eq!( |
25 | time(15, 20, 0, 123).on(2024, 7, 31).to_string(), |
26 | "2024-07-31T15:20:00.000000123" , |
27 | ); |
28 | ``` |
29 | |
30 | # What is "civil" time? |
31 | |
32 | A civil datetime is a calendar date and a clock time. It also goes by the |
33 | names "naive," "local" or "plain." The most important thing to understand |
34 | about civil time is that it does not correspond to a precise instant in |
35 | time. This is in contrast to types like [`Timestamp`](crate::Timestamp) and |
36 | [`Zoned`](crate::Zoned), which _do_ correspond to a precise instant in time (to |
37 | nanosecond precision). |
38 | |
39 | Because a civil datetime _never_ has a time zone associated with it, and |
40 | because some time zones have transitions that skip or repeat clock times, it |
41 | follows that not all civil datetimes precisely map to a single instant in time. |
42 | For example, `2024-03-10 02:30` never existed on a clock in `America/New_York` |
43 | because the 2 o'clock hour was skipped when the clocks were "moved forward" |
44 | for daylight saving time. Conversely, `2024-11-03 01:30` occurred twice in |
45 | `America/New_York` because the 1 o'clock hour was repeated when clocks were |
46 | "moved backward" for daylight saving time. (When time is skipped, it's called a |
47 | "gap." When time is repeated, it's called a "fold.") |
48 | |
49 | In contrast, an instant in time (that is, `Timestamp` or `Zoned`) can _always_ |
50 | be converted to a civil datetime. And, when a civil datetime is combined |
51 | with its time zone identifier _and_ its offset, the resulting machine readable |
52 | string is unambiguous 100% of the time: |
53 | |
54 | ``` |
55 | use jiff::{civil::date, tz::TimeZone}; |
56 | |
57 | let tz = TimeZone::get("America/New_York" )?; |
58 | let dt = date(2024, 11, 3).at(1, 30, 0, 0); |
59 | // It's ambiguous, so asking for an unambiguous instant presents an error! |
60 | assert!(tz.to_ambiguous_zoned(dt).unambiguous().is_err()); |
61 | // Gives you the earlier time in a fold, i.e., before DST ends: |
62 | assert_eq!( |
63 | tz.to_ambiguous_zoned(dt).earlier()?.to_string(), |
64 | "2024-11-03T01:30:00-04:00[America/New_York]" , |
65 | ); |
66 | // Gives you the later time in a fold, i.e., after DST ends. |
67 | // Notice the offset change from the previous example! |
68 | assert_eq!( |
69 | tz.to_ambiguous_zoned(dt).later()?.to_string(), |
70 | "2024-11-03T01:30:00-05:00[America/New_York]" , |
71 | ); |
72 | // "Just give me something reasonable" |
73 | assert_eq!( |
74 | tz.to_ambiguous_zoned(dt).compatible()?.to_string(), |
75 | "2024-11-03T01:30:00-04:00[America/New_York]" , |
76 | ); |
77 | |
78 | # Ok::<(), Box<dyn std::error::Error>>(()) |
79 | ``` |
80 | |
81 | # When should I use civil time? |
82 | |
83 | Here is a likely non-exhaustive list of reasons why you might want to use |
84 | civil time: |
85 | |
86 | * When you want or need to deal with calendar and clock units as an |
87 | intermediate step before and/or after associating it with a time zone. For |
88 | example, perhaps you need to parse strings like `2000-01-01T00:00:00` from a |
89 | CSV file that have no time zone or offset information, but the time zone is |
90 | implied through some out-of-band mechanism. |
91 | * When time zone is actually irrelevant. For example, a fitness tracking app |
92 | that reminds you to work-out at 6am local time, regardless of which time zone |
93 | you're in. |
94 | * When you need to perform arithmetic that deliberately ignores daylight |
95 | saving time. |
96 | * When interacting with legacy systems or systems that specifically do not |
97 | support time zones. |
98 | */ |
99 | |
100 | pub use self::{ |
101 | date::{Date, DateArithmetic, DateDifference, DateSeries, DateWith}, |
102 | datetime::{ |
103 | DateTime, DateTimeArithmetic, DateTimeDifference, DateTimeRound, |
104 | DateTimeSeries, DateTimeWith, |
105 | }, |
106 | iso_week_date::ISOWeekDate, |
107 | time::{ |
108 | Time, TimeArithmetic, TimeDifference, TimeRound, TimeSeries, TimeWith, |
109 | }, |
110 | weekday::{Weekday, WeekdaysForward, WeekdaysReverse}, |
111 | }; |
112 | |
113 | mod date; |
114 | mod datetime; |
115 | mod iso_week_date; |
116 | mod time; |
117 | mod weekday; |
118 | |
119 | /// The era corresponding to a particular year. |
120 | /// |
121 | /// The BCE era corresponds to years less than or equal to `0`, while the CE |
122 | /// era corresponds to years greater than `0`. |
123 | /// |
124 | /// In particular, this crate allows years to be negative and also to be `0`, |
125 | /// which is contrary to the common practice of excluding the year `0` when |
126 | /// writing dates for the Gregorian calendar. Moreover, common practice eschews |
127 | /// negative years in favor of labeling a year with an era notation. That is, |
128 | /// the year `1 BCE` is year `0` in this crate. The year `2 BCE` is the year |
129 | /// `-1` in this crate. |
130 | /// |
131 | /// To get the year in its era format, use [`Date::era_year`]. |
132 | #[derive (Clone, Copy, Debug, Eq, Hash, PartialEq)] |
133 | pub enum Era { |
134 | /// The "before common era" era. |
135 | /// |
136 | /// This corresponds to all years less than or equal to `0`. |
137 | /// |
138 | /// This is precisely equivalent to the "BC" or "before Christ" era. |
139 | BCE, |
140 | /// The "common era" era. |
141 | /// |
142 | /// This corresponds to all years greater than `0`. |
143 | /// |
144 | /// This is precisely equivalent to the "AD" or "anno Domini" or "in the |
145 | /// year of the Lord" era. |
146 | CE, |
147 | } |
148 | |
149 | /// Creates a new `DateTime` value in a `const` context. |
150 | /// |
151 | /// This is a convenience free function for [`DateTime::constant`]. It is |
152 | /// intended to provide a terse syntax for constructing `DateTime` values from |
153 | /// parameters that are known to be valid. |
154 | /// |
155 | /// # Panics |
156 | /// |
157 | /// This routine panics when [`DateTime::new`] would return an error. That |
158 | /// is, when the given components do not correspond to a valid datetime. |
159 | /// Namely, all of the following must be true: |
160 | /// |
161 | /// * The year must be in the range `-9999..=9999`. |
162 | /// * The month must be in the range `1..=12`. |
163 | /// * The day must be at least `1` and must be at most the number of days |
164 | /// in the corresponding month. So for example, `2024-02-29` is valid but |
165 | /// `2023-02-29` is not. |
166 | /// * `0 <= hour <= 23` |
167 | /// * `0 <= minute <= 59` |
168 | /// * `0 <= second <= 59` |
169 | /// * `0 <= subsec_nanosecond <= 999,999,999` |
170 | /// |
171 | /// Similarly, when used in a const context, invalid parameters will prevent |
172 | /// your Rust program from compiling. |
173 | /// |
174 | /// # Example |
175 | /// |
176 | /// ``` |
177 | /// use jiff::civil::DateTime; |
178 | /// |
179 | /// let d = DateTime::constant(2024, 2, 29, 21, 30, 5, 123_456_789); |
180 | /// assert_eq!(d.date().year(), 2024); |
181 | /// assert_eq!(d.date().month(), 2); |
182 | /// assert_eq!(d.date().day(), 29); |
183 | /// assert_eq!(d.time().hour(), 21); |
184 | /// assert_eq!(d.time().minute(), 30); |
185 | /// assert_eq!(d.time().second(), 5); |
186 | /// assert_eq!(d.time().millisecond(), 123); |
187 | /// assert_eq!(d.time().microsecond(), 456); |
188 | /// assert_eq!(d.time().nanosecond(), 789); |
189 | /// ``` |
190 | #[inline ] |
191 | pub const fn datetime( |
192 | year: i16, |
193 | month: i8, |
194 | day: i8, |
195 | hour: i8, |
196 | minute: i8, |
197 | second: i8, |
198 | subsec_nanosecond: i32, |
199 | ) -> DateTime { |
200 | DateTime::constant( |
201 | year, |
202 | month, |
203 | day, |
204 | hour, |
205 | minute, |
206 | second, |
207 | subsec_nanosecond, |
208 | ) |
209 | } |
210 | |
211 | /// Creates a new `Date` value in a `const` context. |
212 | /// |
213 | /// This is a convenience free function for [`Date::constant`]. It is intended |
214 | /// to provide a terse syntax for constructing `Date` values from parameters |
215 | /// that are known to be valid. |
216 | /// |
217 | /// # Panics |
218 | /// |
219 | /// This routine panics when [`Date::new`] would return an error. That is, |
220 | /// when the given year-month-day does not correspond to a valid date. |
221 | /// Namely, all of the following must be true: |
222 | /// |
223 | /// * The year must be in the range `-9999..=9999`. |
224 | /// * The month must be in the range `1..=12`. |
225 | /// * The day must be at least `1` and must be at most the number of days |
226 | /// in the corresponding month. So for example, `2024-02-29` is valid but |
227 | /// `2023-02-29` is not. |
228 | /// |
229 | /// Similarly, when used in a const context, invalid parameters will prevent |
230 | /// your Rust program from compiling. |
231 | /// |
232 | /// # Example |
233 | /// |
234 | /// ``` |
235 | /// use jiff::civil::date; |
236 | /// |
237 | /// let d = date(2024, 2, 29); |
238 | /// assert_eq!(d.year(), 2024); |
239 | /// assert_eq!(d.month(), 2); |
240 | /// assert_eq!(d.day(), 29); |
241 | /// ``` |
242 | #[inline ] |
243 | pub const fn date(year: i16, month: i8, day: i8) -> Date { |
244 | Date::constant(year, month, day) |
245 | } |
246 | |
247 | /// Creates a new `Time` value in a `const` context. |
248 | /// |
249 | /// This is a convenience free function for [`Time::constant`]. It is intended |
250 | /// to provide a terse syntax for constructing `Time` values from parameters |
251 | /// that are known to be valid. |
252 | /// |
253 | /// # Panics |
254 | /// |
255 | /// This panics if the given values do not correspond to a valid `Time`. |
256 | /// All of the following conditions must be true: |
257 | /// |
258 | /// * `0 <= hour <= 23` |
259 | /// * `0 <= minute <= 59` |
260 | /// * `0 <= second <= 59` |
261 | /// * `0 <= subsec_nanosecond <= 999,999,999` |
262 | /// |
263 | /// Similarly, when used in a const context, invalid parameters will |
264 | /// prevent your Rust program from compiling. |
265 | /// |
266 | /// # Example |
267 | /// |
268 | /// This shows an example of a valid time in a `const` context: |
269 | /// |
270 | /// ``` |
271 | /// use jiff::civil::Time; |
272 | /// |
273 | /// const BEDTIME: Time = Time::constant(21, 30, 5, 123_456_789); |
274 | /// assert_eq!(BEDTIME.hour(), 21); |
275 | /// assert_eq!(BEDTIME.minute(), 30); |
276 | /// assert_eq!(BEDTIME.second(), 5); |
277 | /// assert_eq!(BEDTIME.millisecond(), 123); |
278 | /// assert_eq!(BEDTIME.microsecond(), 456); |
279 | /// assert_eq!(BEDTIME.nanosecond(), 789); |
280 | /// assert_eq!(BEDTIME.subsec_nanosecond(), 123_456_789); |
281 | /// ``` |
282 | #[inline ] |
283 | pub const fn time( |
284 | hour: i8, |
285 | minute: i8, |
286 | second: i8, |
287 | subsec_nanosecond: i32, |
288 | ) -> Time { |
289 | Time::constant(hour, minute, second, subsec_nanosecond) |
290 | } |
291 | |