jiff/civil/
mod.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
/*!
Facilities for dealing with inexact dates and times.

# Overview

The essential types in this module are:

* [`Date`] is a specific day in the Gregorian calendar.
* [`Time`] is a specific wall clock time.
* [`DateTime`] is a combination of a day and a time.

Moreover, the [`date`](date()) and [`time`](time()) free functions can be used
to conveniently create values of any of three types above:

```
use jiff::civil::{date, time};

assert_eq!(date(2024, 7, 31).to_string(), "2024-07-31");
assert_eq!(time(15, 20, 0, 123).to_string(), "15:20:00.000000123");
assert_eq!(
    date(2024, 7, 31).at(15, 20, 0, 123).to_string(),
    "2024-07-31T15:20:00.000000123",
);
assert_eq!(
    time(15, 20, 0, 123).on(2024, 7, 31).to_string(),
    "2024-07-31T15:20:00.000000123",
);
```

# What is "civil" time?

A civil datetime is a calendar date and a clock time. It also goes by the
names "naive," "local" or "plain." The most important thing to understand
about civil time is that it does not correspond to a precise instant in
time. This is in contrast to types like [`Timestamp`](crate::Timestamp) and
[`Zoned`](crate::Zoned), which _do_ correspond to a precise instant in time (to
nanosecond precision).

Because a civil datetime _never_ has a time zone associated with it, and
because some time zones have transitions that skip or repeat clock times, it
follows that not all civil datetimes precisely map to a single instant in time.
For example, `2024-03-10 02:30` never existed on a clock in `America/New_York`
because the 2 o'clock hour was skipped when the clocks were "moved forward"
for daylight saving time. Conversely, `2024-11-03 01:30` occurred twice in
`America/New_York` because the 1 o'clock hour was repeated when clocks were
"moved backward" for daylight saving time. (When time is skipped, it's called a
"gap." When time is repeated, it's called a "fold.")

In contrast, an instant in time (that is, `Timestamp` or `Zoned`) can _always_
be converted to a civil datetime. And, when a civil datetime is combined
with its time zone identifier _and_ its offset, the resulting machine readable
string is unambiguous 100% of the time:

```
use jiff::{civil::date, tz::TimeZone};

let tz = TimeZone::get("America/New_York")?;
let dt = date(2024, 11, 3).at(1, 30, 0, 0);
// It's ambiguous, so asking for an unambiguous instant presents an error!
assert!(tz.to_ambiguous_zoned(dt).unambiguous().is_err());
// Gives you the earlier time in a fold, i.e., before DST ends:
assert_eq!(
    tz.to_ambiguous_zoned(dt).earlier()?.to_string(),
    "2024-11-03T01:30:00-04:00[America/New_York]",
);
// Gives you the later time in a fold, i.e., after DST ends.
// Notice the offset change from the previous example!
assert_eq!(
    tz.to_ambiguous_zoned(dt).later()?.to_string(),
    "2024-11-03T01:30:00-05:00[America/New_York]",
);
// "Just give me something reasonable"
assert_eq!(
    tz.to_ambiguous_zoned(dt).compatible()?.to_string(),
    "2024-11-03T01:30:00-04:00[America/New_York]",
);

# Ok::<(), Box<dyn std::error::Error>>(())
```

# When should I use civil time?

Here is a likely non-exhaustive list of reasons why you might want to use
civil time:

* When you want or need to deal with calendar and clock units as an
intermediate step before and/or after associating it with a time zone. For
example, perhaps you need to parse strings like `2000-01-01T00:00:00` from a
CSV file that have no time zone or offset information, but the time zone is
implied through some out-of-band mechanism.
* When time zone is actually irrelevant. For example, a fitness tracking app
that reminds you to work-out at 6am local time, regardless of which time zone
you're in.
* When you need to perform arithmetic that deliberately ignores daylight
saving time.
* When interacting with legacy systems or systems that specifically do not
support time zones.
*/

pub use self::{
    date::{Date, DateArithmetic, DateDifference, DateSeries, DateWith},
    datetime::{
        DateTime, DateTimeArithmetic, DateTimeDifference, DateTimeRound,
        DateTimeSeries, DateTimeWith,
    },
    iso_week_date::ISOWeekDate,
    time::{
        Time, TimeArithmetic, TimeDifference, TimeRound, TimeSeries, TimeWith,
    },
    weekday::{Weekday, WeekdaysForward, WeekdaysReverse},
};

mod date;
mod datetime;
mod iso_week_date;
mod time;
mod weekday;

/// The era corresponding to a particular year.
///
/// The BCE era corresponds to years less than or equal to `0`, while the CE
/// era corresponds to years greater than `0`.
///
/// In particular, this crate allows years to be negative and also to be `0`,
/// which is contrary to the common practice of excluding the year `0` when
/// writing dates for the Gregorian calendar. Moreover, common practice eschews
/// negative years in favor of labeling a year with an era notation. That is,
/// the year `1 BCE` is year `0` in this crate. The year `2 BCE` is the year
/// `-1` in this crate.
///
/// To get the year in its era format, use [`Date::era_year`].
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Era {
    /// The "before common era" era.
    ///
    /// This corresponds to all years less than or equal to `0`.
    ///
    /// This is precisely equivalent to the "BC" or "before Christ" era.
    BCE,
    /// The "common era" era.
    ///
    /// This corresponds to all years greater than `0`.
    ///
    /// This is precisely equivalent to the "AD" or "anno Domini" or "in the
    /// year of the Lord" era.
    CE,
}

/// Creates a new `DateTime` value in a `const` context.
///
/// This is a convenience free function for [`DateTime::constant`]. It is
/// intended to provide a terse syntax for constructing `DateTime` values from
/// parameters that are known to be valid.
///
/// # Panics
///
/// This routine panics when [`DateTime::new`] would return an error. That
/// is, when the given components do not correspond to a valid datetime.
/// Namely, all of the following must be true:
///
/// * The year must be in the range `-9999..=9999`.
/// * The month must be in the range `1..=12`.
/// * The day must be at least `1` and must be at most the number of days
/// in the corresponding month. So for example, `2024-02-29` is valid but
/// `2023-02-29` is not.
/// * `0 <= hour <= 23`
/// * `0 <= minute <= 59`
/// * `0 <= second <= 59`
/// * `0 <= subsec_nanosecond <= 999,999,999`
///
/// Similarly, when used in a const context, invalid parameters will prevent
/// your Rust program from compiling.
///
/// # Example
///
/// ```
/// use jiff::civil::DateTime;
///
/// let d = DateTime::constant(2024, 2, 29, 21, 30, 5, 123_456_789);
/// assert_eq!(d.date().year(), 2024);
/// assert_eq!(d.date().month(), 2);
/// assert_eq!(d.date().day(), 29);
/// assert_eq!(d.time().hour(), 21);
/// assert_eq!(d.time().minute(), 30);
/// assert_eq!(d.time().second(), 5);
/// assert_eq!(d.time().millisecond(), 123);
/// assert_eq!(d.time().microsecond(), 456);
/// assert_eq!(d.time().nanosecond(), 789);
/// ```
#[inline]
pub const fn datetime(
    year: i16,
    month: i8,
    day: i8,
    hour: i8,
    minute: i8,
    second: i8,
    subsec_nanosecond: i32,
) -> DateTime {
    DateTime::constant(
        year,
        month,
        day,
        hour,
        minute,
        second,
        subsec_nanosecond,
    )
}

/// Creates a new `Date` value in a `const` context.
///
/// This is a convenience free function for [`Date::constant`]. It is intended
/// to provide a terse syntax for constructing `Date` values from parameters
/// that are known to be valid.
///
/// # Panics
///
/// This routine panics when [`Date::new`] would return an error. That is,
/// when the given year-month-day does not correspond to a valid date.
/// Namely, all of the following must be true:
///
/// * The year must be in the range `-9999..=9999`.
/// * The month must be in the range `1..=12`.
/// * The day must be at least `1` and must be at most the number of days
/// in the corresponding month. So for example, `2024-02-29` is valid but
/// `2023-02-29` is not.
///
/// Similarly, when used in a const context, invalid parameters will prevent
/// your Rust program from compiling.
///
/// # Example
///
/// ```
/// use jiff::civil::date;
///
/// let d = date(2024, 2, 29);
/// assert_eq!(d.year(), 2024);
/// assert_eq!(d.month(), 2);
/// assert_eq!(d.day(), 29);
/// ```
#[inline]
pub const fn date(year: i16, month: i8, day: i8) -> Date {
    Date::constant(year, month, day)
}

/// Creates a new `Time` value in a `const` context.
///
/// This is a convenience free function for [`Time::constant`]. It is intended
/// to provide a terse syntax for constructing `Time` values from parameters
/// that are known to be valid.
///
/// # Panics
///
/// This panics if the given values do not correspond to a valid `Time`.
/// All of the following conditions must be true:
///
/// * `0 <= hour <= 23`
/// * `0 <= minute <= 59`
/// * `0 <= second <= 59`
/// * `0 <= subsec_nanosecond <= 999,999,999`
///
/// Similarly, when used in a const context, invalid parameters will
/// prevent your Rust program from compiling.
///
/// # Example
///
/// This shows an example of a valid time in a `const` context:
///
/// ```
/// use jiff::civil::Time;
///
/// const BEDTIME: Time = Time::constant(21, 30, 5, 123_456_789);
/// assert_eq!(BEDTIME.hour(), 21);
/// assert_eq!(BEDTIME.minute(), 30);
/// assert_eq!(BEDTIME.second(), 5);
/// assert_eq!(BEDTIME.millisecond(), 123);
/// assert_eq!(BEDTIME.microsecond(), 456);
/// assert_eq!(BEDTIME.nanosecond(), 789);
/// assert_eq!(BEDTIME.subsec_nanosecond(), 123_456_789);
/// ```
#[inline]
pub const fn time(
    hour: i8,
    minute: i8,
    second: i8,
    subsec_nanosecond: i32,
) -> Time {
    Time::constant(hour, minute, second, subsec_nanosecond)
}