jiff/civil/mod.rs
1/*!
2Facilities for dealing with inexact dates and times.
3
4# Overview
5
6The 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
12Moreover, the [`date`](date()) and [`time`](time()) free functions can be used
13to conveniently create values of any of three types above:
14
15```
16use jiff::civil::{date, time};
17
18assert_eq!(date(2024, 7, 31).to_string(), "2024-07-31");
19assert_eq!(time(15, 20, 0, 123).to_string(), "15:20:00.000000123");
20assert_eq!(
21 date(2024, 7, 31).at(15, 20, 0, 123).to_string(),
22 "2024-07-31T15:20:00.000000123",
23);
24assert_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
32A civil datetime is a calendar date and a clock time. It also goes by the
33names "naive," "local" or "plain." The most important thing to understand
34about civil time is that it does not correspond to a precise instant in
35time. 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
37nanosecond precision).
38
39Because a civil datetime _never_ has a time zone associated with it, and
40because some time zones have transitions that skip or repeat clock times, it
41follows that not all civil datetimes precisely map to a single instant in time.
42For example, `2024-03-10 02:30` never existed on a clock in `America/New_York`
43because the 2 o'clock hour was skipped when the clocks were "moved forward"
44for 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
49In contrast, an instant in time (that is, `Timestamp` or `Zoned`) can _always_
50be converted to a civil datetime. And, when a civil datetime is combined
51with its time zone identifier _and_ its offset, the resulting machine readable
52string is unambiguous 100% of the time:
53
54```
55use jiff::{civil::date, tz::TimeZone};
56
57let tz = TimeZone::get("America/New_York")?;
58let dt = date(2024, 11, 3).at(1, 30, 0, 0);
59// It's ambiguous, so asking for an unambiguous instant presents an error!
60assert!(tz.to_ambiguous_zoned(dt).unambiguous().is_err());
61// Gives you the earlier time in a fold, i.e., before DST ends:
62assert_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!
68assert_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"
73assert_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
83Here is a likely non-exhaustive list of reasons why you might want to use
84civil time:
85
86* When you want or need to deal with calendar and clock units as an
87intermediate step before and/or after associating it with a time zone. For
88example, perhaps you need to parse strings like `2000-01-01T00:00:00` from a
89CSV file that have no time zone or offset information, but the time zone is
90implied through some out-of-band mechanism.
91* When time zone is actually irrelevant. For example, a fitness tracking app
92that reminds you to work-out at 6am local time, regardless of which time zone
93you're in.
94* When you need to perform arithmetic that deliberately ignores daylight
95saving time.
96* When interacting with legacy systems or systems that specifically do not
97support time zones.
98*/
99
100pub 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
113mod date;
114mod datetime;
115mod iso_week_date;
116mod time;
117mod 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)]
133pub 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]
191pub 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]
243pub 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]
283pub 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}