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)]
133#[cfg_attr(feature = "defmt", derive(defmt::Format))]
134pub enum Era {
135 /// The "before common era" era.
136 ///
137 /// This corresponds to all years less than or equal to `0`.
138 ///
139 /// This is precisely equivalent to the "BC" or "before Christ" era.
140 BCE,
141 /// The "common era" era.
142 ///
143 /// This corresponds to all years greater than `0`.
144 ///
145 /// This is precisely equivalent to the "AD" or "anno Domini" or "in the
146 /// year of the Lord" era.
147 CE,
148}
149
150/// Creates a new `DateTime` value in a `const` context.
151///
152/// This is a convenience free function for [`DateTime::constant`]. It is
153/// intended to provide a terse syntax for constructing `DateTime` values from
154/// parameters that are known to be valid.
155///
156/// # Panics
157///
158/// This routine panics when [`DateTime::new`] would return an error. That
159/// is, when the given components do not correspond to a valid datetime.
160/// Namely, all of the following must be true:
161///
162/// * The year must be in the range `-9999..=9999`.
163/// * The month must be in the range `1..=12`.
164/// * The day must be at least `1` and must be at most the number of days
165/// in the corresponding month. So for example, `2024-02-29` is valid but
166/// `2023-02-29` is not.
167/// * `0 <= hour <= 23`
168/// * `0 <= minute <= 59`
169/// * `0 <= second <= 59`
170/// * `0 <= subsec_nanosecond <= 999,999,999`
171///
172/// Similarly, when used in a const context, invalid parameters will prevent
173/// your Rust program from compiling.
174///
175/// # Example
176///
177/// ```
178/// use jiff::civil::datetime;
179///
180/// let dt = datetime(2024, 2, 29, 21, 30, 5, 123_456_789);
181/// assert_eq!(dt.date().year(), 2024);
182/// assert_eq!(dt.date().month(), 2);
183/// assert_eq!(dt.date().day(), 29);
184/// assert_eq!(dt.time().hour(), 21);
185/// assert_eq!(dt.time().minute(), 30);
186/// assert_eq!(dt.time().second(), 5);
187/// assert_eq!(dt.time().millisecond(), 123);
188/// assert_eq!(dt.time().microsecond(), 456);
189/// assert_eq!(dt.time().nanosecond(), 789);
190/// ```
191#[inline]
192pub const fn datetime(
193 year: i16,
194 month: i8,
195 day: i8,
196 hour: i8,
197 minute: i8,
198 second: i8,
199 subsec_nanosecond: i32,
200) -> DateTime {
201 DateTime::constant(
202 year,
203 month,
204 day,
205 hour,
206 minute,
207 second,
208 subsec_nanosecond,
209 )
210}
211
212/// Creates a new `Date` value in a `const` context.
213///
214/// This is a convenience free function for [`Date::constant`]. It is intended
215/// to provide a terse syntax for constructing `Date` values from parameters
216/// that are known to be valid.
217///
218/// # Panics
219///
220/// This routine panics when [`Date::new`] would return an error. That is,
221/// when the given year-month-day does not correspond to a valid date.
222/// Namely, all of the following must be true:
223///
224/// * The year must be in the range `-9999..=9999`.
225/// * The month must be in the range `1..=12`.
226/// * The day must be at least `1` and must be at most the number of days
227/// in the corresponding month. So for example, `2024-02-29` is valid but
228/// `2023-02-29` is not.
229///
230/// Similarly, when used in a const context, invalid parameters will prevent
231/// your Rust program from compiling.
232///
233/// # Example
234///
235/// ```
236/// use jiff::civil::date;
237///
238/// let d = date(2024, 2, 29);
239/// assert_eq!(d.year(), 2024);
240/// assert_eq!(d.month(), 2);
241/// assert_eq!(d.day(), 29);
242/// ```
243#[inline]
244pub const fn date(year: i16, month: i8, day: i8) -> Date {
245 Date::constant(year, month, day)
246}
247
248/// Creates a new `Time` value in a `const` context.
249///
250/// This is a convenience free function for [`Time::constant`]. It is intended
251/// to provide a terse syntax for constructing `Time` values from parameters
252/// that are known to be valid.
253///
254/// # Panics
255///
256/// This panics if the given values do not correspond to a valid `Time`.
257/// All of the following conditions must be true:
258///
259/// * `0 <= hour <= 23`
260/// * `0 <= minute <= 59`
261/// * `0 <= second <= 59`
262/// * `0 <= subsec_nanosecond <= 999,999,999`
263///
264/// Similarly, when used in a const context, invalid parameters will
265/// prevent your Rust program from compiling.
266///
267/// # Example
268///
269/// This shows an example of a valid time in a `const` context:
270///
271/// ```
272/// use jiff::civil::time;
273///
274/// let t = time(21, 30, 5, 123_456_789);
275/// assert_eq!(t.hour(), 21);
276/// assert_eq!(t.minute(), 30);
277/// assert_eq!(t.second(), 5);
278/// assert_eq!(t.millisecond(), 123);
279/// assert_eq!(t.microsecond(), 456);
280/// assert_eq!(t.nanosecond(), 789);
281/// assert_eq!(t.subsec_nanosecond(), 123_456_789);
282/// ```
283#[inline]
284pub const fn time(
285 hour: i8,
286 minute: i8,
287 second: i8,
288 subsec_nanosecond: i32,
289) -> Time {
290 Time::constant(hour, minute, second, subsec_nanosecond)
291}