jiff/civil/date.rs
1#![allow(warnings)]
2
3use core::time::Duration as UnsignedDuration;
4
5use crate::{
6 civil::{DateTime, Era, ISOWeekDate, Time, Weekday},
7 duration::{Duration, SDuration},
8 error::{err, Error, ErrorContext},
9 fmt::{
10 self,
11 temporal::{DEFAULT_DATETIME_PARSER, DEFAULT_DATETIME_PRINTER},
12 },
13 tz::TimeZone,
14 util::{
15 common,
16 rangeint::{ri16, ri8, RFrom, RInto, TryRFrom},
17 t::{self, Constant, Day, Month, Sign, UnixEpochDay, Year, C},
18 },
19 RoundMode, SignedDuration, Span, SpanRound, Unit, Zoned,
20};
21
22/// A representation of a civil date in the Gregorian calendar.
23///
24/// A `Date` value corresponds to a triple of year, month and day. Every `Date`
25/// value is guaranteed to be a valid Gregorian calendar date. For example,
26/// both `2023-02-29` and `2023-11-31` are invalid and cannot be represented by
27/// a `Date`.
28///
29/// # Civil dates
30///
31/// A `Date` value behaves without regard to daylight saving time or time
32/// zones in general. When doing arithmetic on dates with spans defined in
33/// units of time (such as with [`Date::checked_add`]), days are considered to
34/// always be precisely `86,400` seconds long.
35///
36/// # Parsing and printing
37///
38/// The `Date` type provides convenient trait implementations of
39/// [`std::str::FromStr`] and [`std::fmt::Display`]:
40///
41/// ```
42/// use jiff::civil::Date;
43///
44/// let date: Date = "2024-06-19".parse()?;
45/// assert_eq!(date.to_string(), "2024-06-19");
46///
47/// # Ok::<(), Box<dyn std::error::Error>>(())
48/// ```
49///
50/// A civil `Date` can also be parsed from something that _contains_ a date,
51/// but with perhaps other data (such as an offset or time zone):
52///
53/// ```
54/// use jiff::civil::Date;
55///
56/// let date: Date = "2024-06-19T15:22:45-04[America/New_York]".parse()?;
57/// assert_eq!(date.to_string(), "2024-06-19");
58///
59/// # Ok::<(), Box<dyn std::error::Error>>(())
60/// ```
61///
62/// For more information on the specific format supported, see the
63/// [`fmt::temporal`](crate::fmt::temporal) module documentation.
64///
65/// # Default value
66///
67/// For convenience, this type implements the `Default` trait. Its default
68/// value corresponds to `0000-01-01`. One can also access this value via the
69/// `Date::ZERO` constant.
70///
71/// # Comparisons
72///
73/// The `Date` type provides both `Eq` and `Ord` trait implementations to
74/// facilitate easy comparisons. When a date `d1` occurs before a date `d2`,
75/// then `d1 < d2`. For example:
76///
77/// ```
78/// use jiff::civil::date;
79///
80/// let d1 = date(2024, 3, 11);
81/// let d2 = date(2025, 1, 31);
82/// assert!(d1 < d2);
83/// ```
84///
85/// # Arithmetic
86///
87/// This type provides routines for adding and subtracting spans of time, as
88/// well as computing the span of time between two `Date` values.
89///
90/// For adding or subtracting spans of time, one can use any of the following
91/// routines:
92///
93/// * [`Date::checked_add`] or [`Date::checked_sub`] for checked arithmetic.
94/// * [`Date::saturating_add`] or [`Date::saturating_sub`] for saturating
95/// arithmetic.
96///
97/// Additionally, checked arithmetic is available via the `Add` and `Sub`
98/// trait implementations. When the result overflows, a panic occurs.
99///
100/// ```
101/// use jiff::{civil::date, ToSpan};
102///
103/// let start = date(2024, 2, 25);
104/// let one_week_later = start + 1.weeks();
105/// assert_eq!(one_week_later, date(2024, 3, 3));
106/// ```
107///
108/// One can compute the span of time between two dates using either
109/// [`Date::until`] or [`Date::since`]. It's also possible to subtract two
110/// `Date` values directly via a `Sub` trait implementation:
111///
112/// ```
113/// use jiff::{civil::date, ToSpan};
114///
115/// let date1 = date(2024, 3, 3);
116/// let date2 = date(2024, 2, 25);
117/// assert_eq!(date1 - date2, 7.days().fieldwise());
118/// ```
119///
120/// The `until` and `since` APIs are polymorphic and allow re-balancing and
121/// rounding the span returned. For example, the default largest unit is days
122/// (as exemplified above), but we can ask for bigger units:
123///
124/// ```
125/// use jiff::{civil::date, ToSpan, Unit};
126///
127/// let date1 = date(2024, 5, 3);
128/// let date2 = date(2024, 2, 25);
129/// assert_eq!(
130/// date1.since((Unit::Year, date2))?,
131/// 2.months().days(7).fieldwise(),
132/// );
133///
134/// # Ok::<(), Box<dyn std::error::Error>>(())
135/// ```
136///
137/// Or even round the span returned:
138///
139/// ```
140/// use jiff::{civil::{DateDifference, date}, RoundMode, ToSpan, Unit};
141///
142/// let date1 = date(2024, 5, 15);
143/// let date2 = date(2024, 2, 25);
144/// assert_eq!(
145/// date1.since(
146/// DateDifference::new(date2)
147/// .smallest(Unit::Month)
148/// .largest(Unit::Year),
149/// )?,
150/// 2.months().fieldwise(),
151/// );
152/// // `DateDifference` uses truncation as a rounding mode by default,
153/// // but you can set the rounding mode to break ties away from zero:
154/// assert_eq!(
155/// date1.since(
156/// DateDifference::new(date2)
157/// .smallest(Unit::Month)
158/// .largest(Unit::Year)
159/// .mode(RoundMode::HalfExpand),
160/// )?,
161/// // Rounds up to 8 days.
162/// 3.months().fieldwise(),
163/// );
164///
165/// # Ok::<(), Box<dyn std::error::Error>>(())
166/// ```
167///
168/// # Rounding
169///
170/// Rounding dates is currently not supported. If you want this functionality,
171/// please participate in the [issue tracking its support][add-date-rounding].
172///
173/// [add-date-rounding]: https://github.com/BurntSushi/jiff/issues/1
174#[derive(Clone, Copy, Hash)]
175pub struct Date {
176 year: Year,
177 month: Month,
178 day: Day,
179}
180
181impl Date {
182 /// The minimum representable Gregorian date.
183 ///
184 /// The minimum is chosen such that any [`Timestamp`](crate::Timestamp)
185 /// combined with any valid time zone offset can be infallibly converted to
186 /// this type. This means that the minimum `Timestamp` is guaranteed to be
187 /// bigger than the minimum `Date`.
188 pub const MIN: Date = Date::constant(-9999, 1, 1);
189
190 /// The maximum representable Gregorian date.
191 ///
192 /// The maximum is chosen such that any [`Timestamp`](crate::Timestamp)
193 /// combined with any valid time zone offset can be infallibly converted to
194 /// this type. This means that the maximum `Timestamp` is guaranteed to be
195 /// smaller than the maximum `Date`.
196 pub const MAX: Date = Date::constant(9999, 12, 31);
197
198 /// The first day of the zeroth year.
199 ///
200 /// This is guaranteed to be equivalent to `Date::default()`.
201 ///
202 /// # Example
203 ///
204 /// ```
205 /// use jiff::civil::Date;
206 ///
207 /// assert_eq!(Date::ZERO, Date::default());
208 /// ```
209 pub const ZERO: Date = Date::constant(0, 1, 1);
210
211 /// Creates a new `Date` value from its component year, month and day
212 /// values.
213 ///
214 /// To set the component values of a date after creating it, use
215 /// [`DateWith`] via [`Date::with`] to build a new [`Date`] from the fields
216 /// of an existing date.
217 ///
218 /// # Errors
219 ///
220 /// This returns an error when the given year-month-day does not
221 /// correspond to a valid date. Namely, all of the following must be
222 /// 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 /// # Example
231 ///
232 /// This shows an example of a valid date:
233 ///
234 /// ```
235 /// use jiff::civil::Date;
236 ///
237 /// let d = Date::new(2024, 2, 29).unwrap();
238 /// assert_eq!(d.year(), 2024);
239 /// assert_eq!(d.month(), 2);
240 /// assert_eq!(d.day(), 29);
241 /// ```
242 ///
243 /// This shows an example of an invalid date:
244 ///
245 /// ```
246 /// use jiff::civil::Date;
247 ///
248 /// assert!(Date::new(2023, 2, 29).is_err());
249 /// ```
250 #[inline]
251 pub fn new(year: i16, month: i8, day: i8) -> Result<Date, Error> {
252 let year = Year::try_new("year", year)?;
253 let month = Month::try_new("month", month)?;
254 let day = Day::try_new("day", day)?;
255 Date::new_ranged(year, month, day)
256 }
257
258 /// Creates a new `Date` value in a `const` context.
259 ///
260 /// # Panics
261 ///
262 /// This routine panics when [`Date::new`] would return an error. That is,
263 /// when the given year-month-day does not correspond to a valid date.
264 /// Namely, all of the following must be true:
265 ///
266 /// * The year must be in the range `-9999..=9999`.
267 /// * The month must be in the range `1..=12`.
268 /// * The day must be at least `1` and must be at most the number of days
269 /// in the corresponding month. So for example, `2024-02-29` is valid but
270 /// `2023-02-29` is not.
271 ///
272 /// # Example
273 ///
274 /// ```
275 /// use jiff::civil::Date;
276 ///
277 /// let d = Date::constant(2024, 2, 29);
278 /// assert_eq!(d.year(), 2024);
279 /// assert_eq!(d.month(), 2);
280 /// assert_eq!(d.day(), 29);
281 /// ```
282 #[inline]
283 pub const fn constant(year: i16, month: i8, day: i8) -> Date {
284 if !Year::contains(year) {
285 panic!("invalid year");
286 }
287 if !Month::contains(month) {
288 panic!("invalid month");
289 }
290 if day > common::days_in_month(year, month) {
291 panic!("invalid day");
292 }
293 let year = Year::new_unchecked(year);
294 let month = Month::new_unchecked(month);
295 let day = Day::new_unchecked(day);
296 Date { year, month, day }
297 }
298
299 /// Construct a Gregorian date from an [ISO 8601 week date].
300 ///
301 /// The [`ISOWeekDate`] type describes itself in more detail, but in
302 /// breif, the ISO week date calendar system eschews months in favor of
303 /// weeks.
304 ///
305 /// The minimum and maximum values of an `ISOWeekDate` correspond
306 /// precisely to the minimum and maximum values of a `Date`. Therefore,
307 /// converting between them is lossless and infallible.
308 ///
309 /// This routine is equivalent to [`ISOWeekDate::date`]. It is also
310 /// available via a `From<ISOWeekDate>` trait implementation for `Date`.
311 ///
312 /// [ISO 8601 week date]: https://en.wikipedia.org/wiki/ISO_week_date
313 ///
314 /// # Example
315 ///
316 /// This shows a number of examples demonstrating the conversion from an
317 /// ISO 8601 week date to a Gregorian date.
318 ///
319 /// ```
320 /// use jiff::civil::{Date, ISOWeekDate, Weekday, date};
321 ///
322 /// let weekdate = ISOWeekDate::new(1994, 52, Weekday::Sunday).unwrap();
323 /// let d = Date::from_iso_week_date(weekdate);
324 /// assert_eq!(d, date(1995, 1, 1));
325 ///
326 /// let weekdate = ISOWeekDate::new(1997, 1, Weekday::Tuesday).unwrap();
327 /// let d = Date::from_iso_week_date(weekdate);
328 /// assert_eq!(d, date(1996, 12, 31));
329 ///
330 /// let weekdate = ISOWeekDate::new(2020, 1, Weekday::Monday).unwrap();
331 /// let d = Date::from_iso_week_date(weekdate);
332 /// assert_eq!(d, date(2019, 12, 30));
333 ///
334 /// let weekdate = ISOWeekDate::new(2024, 10, Weekday::Saturday).unwrap();
335 /// let d = Date::from_iso_week_date(weekdate);
336 /// assert_eq!(d, date(2024, 3, 9));
337 ///
338 /// let weekdate = ISOWeekDate::new(9999, 52, Weekday::Friday).unwrap();
339 /// let d = Date::from_iso_week_date(weekdate);
340 /// assert_eq!(d, date(9999, 12, 31));
341 /// ```
342 #[inline]
343 pub fn from_iso_week_date(weekdate: ISOWeekDate) -> Date {
344 let mut days = iso_week_start_from_year(weekdate.year_ranged());
345 let year = t::NoUnits16::rfrom(weekdate.year_ranged());
346 let week = t::NoUnits16::rfrom(weekdate.week_ranged());
347 let weekday = t::NoUnits16::rfrom(
348 weekdate.weekday().to_monday_zero_offset_ranged(),
349 );
350 let [week, weekday] = t::NoUnits16::vary_many(
351 [year, week, weekday],
352 |[year, week, weekday]| {
353 // This is weird, but because the max ISO week date is actually
354 // 9999-W52-4, we need to explicitly cap our maximum computed
355 // values here. This is only required because the maximums of
356 // each component of an ISO week date combine to represent an
357 // out-of-bounds Gregorian date.
358 //
359 // Note that this is purely done at the service of ranged
360 // integers. Otherwise, our ranged integers will compute a
361 // max value bigger than what can really occur, and then panic.
362 // So we use these caps to say, "no range integer, it truly
363 // won't exceed 9999-W52-4."
364 if year == 9999 {
365 if week >= 52 {
366 [week.min(C(52)), weekday.min(C(4))]
367 } else {
368 [week, weekday]
369 }
370 } else {
371 [week, weekday]
372 }
373 },
374 );
375 days += (UnixEpochDay::rfrom(week) - C(1)) * C(7);
376 days += weekday;
377 Date::from_unix_epoch_day(days)
378 }
379
380 /// Create a builder for constructing a `Date` from the fields of this
381 /// date.
382 ///
383 /// See the methods on [`DateWith`] for the different ways one can set the
384 /// fields of a new `Date`.
385 ///
386 /// # Example
387 ///
388 /// The builder ensures one can chain together the individual components
389 /// of a date without it failing at an intermediate step. For example,
390 /// if you had a date of `2024-10-31` and wanted to change both the day
391 /// and the month, and each setting was validated independent of the other,
392 /// you would need to be careful to set the day first and then the month.
393 /// In some cases, you would need to set the month first and then the day!
394 ///
395 /// But with the builder, you can set values in any order:
396 ///
397 /// ```
398 /// use jiff::civil::date;
399 ///
400 /// let d1 = date(2024, 10, 31);
401 /// let d2 = d1.with().month(11).day(30).build()?;
402 /// assert_eq!(d2, date(2024, 11, 30));
403 ///
404 /// let d1 = date(2024, 4, 30);
405 /// let d2 = d1.with().day(31).month(7).build()?;
406 /// assert_eq!(d2, date(2024, 7, 31));
407 ///
408 /// # Ok::<(), Box<dyn std::error::Error>>(())
409 /// ```
410 #[inline]
411 pub fn with(self) -> DateWith {
412 DateWith::new(self)
413 }
414
415 /// Returns the year for this date.
416 ///
417 /// The value returned is guaranteed to be in the range `-9999..=9999`.
418 ///
419 /// # Example
420 ///
421 /// ```
422 /// use jiff::civil::date;
423 ///
424 /// let d1 = date(2024, 3, 9);
425 /// assert_eq!(d1.year(), 2024);
426 ///
427 /// let d2 = date(-2024, 3, 9);
428 /// assert_eq!(d2.year(), -2024);
429 ///
430 /// let d3 = date(0, 3, 9);
431 /// assert_eq!(d3.year(), 0);
432 /// ```
433 #[inline]
434 pub fn year(self) -> i16 {
435 self.year_ranged().get()
436 }
437
438 /// Returns the year and its era.
439 ///
440 /// This crate specifically allows years to be negative or `0`, where as
441 /// years written for the Gregorian calendar are always positive and
442 /// greater than `0`. In the Gregorian calendar, the era labels `BCE` and
443 /// `CE` are used to disambiguate between years less than or equal to `0`
444 /// and years greater than `0`, respectively.
445 ///
446 /// The crate is designed this way so that years in the latest era (that
447 /// is, `CE`) are aligned with years in this crate.
448 ///
449 /// The year returned is guaranteed to be in the range `1..=10000`.
450 ///
451 /// # Example
452 ///
453 /// ```
454 /// use jiff::civil::{Era, date};
455 ///
456 /// let d = date(2024, 10, 3);
457 /// assert_eq!(d.era_year(), (2024, Era::CE));
458 ///
459 /// let d = date(1, 10, 3);
460 /// assert_eq!(d.era_year(), (1, Era::CE));
461 ///
462 /// let d = date(0, 10, 3);
463 /// assert_eq!(d.era_year(), (1, Era::BCE));
464 ///
465 /// let d = date(-1, 10, 3);
466 /// assert_eq!(d.era_year(), (2, Era::BCE));
467 ///
468 /// let d = date(-10, 10, 3);
469 /// assert_eq!(d.era_year(), (11, Era::BCE));
470 ///
471 /// let d = date(-9_999, 10, 3);
472 /// assert_eq!(d.era_year(), (10_000, Era::BCE));
473 /// ```
474 #[inline]
475 pub fn era_year(self) -> (i16, Era) {
476 let year = self.year_ranged();
477 if year >= 1 {
478 (year.get(), Era::CE)
479 } else {
480 // We specifically ensure our min/max bounds on `Year` always leave
481 // room in its representation to add or subtract 1, so this will
482 // never fail.
483 let year = -t::YearBCE::rfrom(year.min(C(0)));
484 let era_year = year + C(1);
485 (era_year.get(), Era::BCE)
486 }
487 }
488
489 /// Returns the month for this date.
490 ///
491 /// The value returned is guaranteed to be in the range `1..=12`.
492 ///
493 /// # Example
494 ///
495 /// ```
496 /// use jiff::civil::date;
497 ///
498 /// let d1 = date(2024, 3, 9);
499 /// assert_eq!(d1.month(), 3);
500 /// ```
501 #[inline]
502 pub fn month(self) -> i8 {
503 self.month_ranged().get()
504 }
505
506 /// Returns the day for this date.
507 ///
508 /// The value returned is guaranteed to be in the range `1..=31`.
509 ///
510 /// # Example
511 ///
512 /// ```
513 /// use jiff::civil::date;
514 ///
515 /// let d1 = date(2024, 2, 29);
516 /// assert_eq!(d1.day(), 29);
517 /// ```
518 #[inline]
519 pub fn day(self) -> i8 {
520 self.day_ranged().get()
521 }
522
523 /// Returns the weekday corresponding to this date.
524 ///
525 /// # Example
526 ///
527 /// ```
528 /// use jiff::civil::{Weekday, date};
529 ///
530 /// // The Unix epoch was on a Thursday.
531 /// let d1 = date(1970, 1, 1);
532 /// assert_eq!(d1.weekday(), Weekday::Thursday);
533 /// // One can also get the weekday as an offset in a variety of schemes.
534 /// assert_eq!(d1.weekday().to_monday_zero_offset(), 3);
535 /// assert_eq!(d1.weekday().to_monday_one_offset(), 4);
536 /// assert_eq!(d1.weekday().to_sunday_zero_offset(), 4);
537 /// assert_eq!(d1.weekday().to_sunday_one_offset(), 5);
538 /// ```
539 #[inline]
540 pub fn weekday(self) -> Weekday {
541 weekday_from_unix_epoch_days(self.to_unix_epoch_day())
542 }
543
544 /// Returns the ordinal day of the year that this date resides in.
545 ///
546 /// For leap years, this always returns a value in the range `1..=366`.
547 /// Otherwise, the value is in the range `1..=365`.
548 ///
549 /// # Example
550 ///
551 /// ```
552 /// use jiff::civil::date;
553 ///
554 /// let d = date(2006, 8, 24);
555 /// assert_eq!(d.day_of_year(), 236);
556 ///
557 /// let d = date(2023, 12, 31);
558 /// assert_eq!(d.day_of_year(), 365);
559 ///
560 /// let d = date(2024, 12, 31);
561 /// assert_eq!(d.day_of_year(), 366);
562 /// ```
563 #[inline]
564 pub fn day_of_year(self) -> i16 {
565 static DAYS_BY_MONTH_NO_LEAP: [i16; 14] =
566 [0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365];
567 static DAYS_BY_MONTH_LEAP: [i16; 14] =
568 [0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366];
569 static TABLES: [[i16; 14]; 2] =
570 [DAYS_BY_MONTH_NO_LEAP, DAYS_BY_MONTH_LEAP];
571 TABLES[self.in_leap_year() as usize][self.month() as usize]
572 + i16::from(self.day())
573 }
574
575 /// Returns the ordinal day of the year that this date resides in, but
576 /// ignores leap years.
577 ///
578 /// That is, the range of possible values returned by this routine is
579 /// `1..=365`, even if this date resides in a leap year. If this date is
580 /// February 29, then this routine returns `None`.
581 ///
582 /// The value `365` always corresponds to the last day in the year,
583 /// December 31, even for leap years.
584 ///
585 /// # Example
586 ///
587 /// ```
588 /// use jiff::civil::date;
589 ///
590 /// let d = date(2006, 8, 24);
591 /// assert_eq!(d.day_of_year_no_leap(), Some(236));
592 ///
593 /// let d = date(2023, 12, 31);
594 /// assert_eq!(d.day_of_year_no_leap(), Some(365));
595 ///
596 /// let d = date(2024, 12, 31);
597 /// assert_eq!(d.day_of_year_no_leap(), Some(365));
598 ///
599 /// let d = date(2024, 2, 29);
600 /// assert_eq!(d.day_of_year_no_leap(), None);
601 /// ```
602 #[inline]
603 pub fn day_of_year_no_leap(self) -> Option<i16> {
604 let mut days = self.day_of_year();
605 if self.in_leap_year() {
606 // day=60 is Feb 29
607 if days == 60 {
608 return None;
609 } else if days > 60 {
610 days -= 1;
611 }
612 }
613 Some(days)
614 }
615
616 /// Returns the first date of the month that this date resides in.
617 ///
618 /// # Example
619 ///
620 /// ```
621 /// use jiff::civil::date;
622 ///
623 /// let d = date(2024, 2, 29);
624 /// assert_eq!(d.first_of_month(), date(2024, 2, 1));
625 /// ```
626 #[inline]
627 pub fn first_of_month(self) -> Date {
628 Date::new_ranged_unchecked(
629 self.year_ranged(),
630 self.month_ranged(),
631 C(1).rinto(),
632 )
633 }
634
635 /// Returns the last date of the month that this date resides in.
636 ///
637 /// # Example
638 ///
639 /// ```
640 /// use jiff::civil::date;
641 ///
642 /// let d = date(2024, 2, 5);
643 /// assert_eq!(d.last_of_month(), date(2024, 2, 29));
644 /// ```
645 #[inline]
646 pub fn last_of_month(self) -> Date {
647 let max_day = self.days_in_month_ranged();
648 Date::new_ranged_unchecked(
649 self.year_ranged(),
650 self.month_ranged(),
651 max_day,
652 )
653 }
654
655 /// Returns the total number of days in the the month in which this date
656 /// resides.
657 ///
658 /// This is guaranteed to always return one of the following values,
659 /// depending on the year and the month: 28, 29, 30 or 31.
660 ///
661 /// # Example
662 ///
663 /// ```
664 /// use jiff::civil::date;
665 ///
666 /// let d = date(2024, 2, 10);
667 /// assert_eq!(d.days_in_month(), 29);
668 ///
669 /// let d = date(2023, 2, 10);
670 /// assert_eq!(d.days_in_month(), 28);
671 ///
672 /// let d = date(2024, 8, 15);
673 /// assert_eq!(d.days_in_month(), 31);
674 /// ```
675 #[inline]
676 pub fn days_in_month(self) -> i8 {
677 self.days_in_month_ranged().get()
678 }
679
680 /// Returns the first date of the year that this date resides in.
681 ///
682 /// # Example
683 ///
684 /// ```
685 /// use jiff::civil::date;
686 ///
687 /// let d = date(2024, 2, 29);
688 /// assert_eq!(d.first_of_year(), date(2024, 1, 1));
689 /// ```
690 #[inline]
691 pub fn first_of_year(self) -> Date {
692 Date::new_ranged_unchecked(
693 self.year_ranged(),
694 C(1).rinto(),
695 C(1).rinto(),
696 )
697 }
698
699 /// Returns the last date of the year that this date resides in.
700 ///
701 /// # Example
702 ///
703 /// ```
704 /// use jiff::civil::date;
705 ///
706 /// let d = date(2024, 2, 5);
707 /// assert_eq!(d.last_of_year(), date(2024, 12, 31));
708 /// ```
709 #[inline]
710 pub fn last_of_year(self) -> Date {
711 Date::new_ranged_unchecked(
712 self.year_ranged(),
713 C(12).rinto(),
714 C(31).rinto(),
715 )
716 }
717
718 /// Returns the total number of days in the the year in which this date
719 /// resides.
720 ///
721 /// This is guaranteed to always return either `365` or `366`.
722 ///
723 /// # Example
724 ///
725 /// ```
726 /// use jiff::civil::date;
727 ///
728 /// let d = date(2024, 7, 10);
729 /// assert_eq!(d.days_in_year(), 366);
730 ///
731 /// let d = date(2023, 7, 10);
732 /// assert_eq!(d.days_in_year(), 365);
733 /// ```
734 #[inline]
735 pub fn days_in_year(self) -> i16 {
736 if self.in_leap_year() {
737 366
738 } else {
739 365
740 }
741 }
742
743 /// Returns true if and only if the year in which this date resides is a
744 /// leap year.
745 ///
746 /// # Example
747 ///
748 /// ```
749 /// use jiff::civil::date;
750 ///
751 /// assert!(date(2024, 1, 1).in_leap_year());
752 /// assert!(!date(2023, 12, 31).in_leap_year());
753 /// ```
754 #[inline]
755 pub fn in_leap_year(self) -> bool {
756 is_leap_year(self.year_ranged())
757 }
758
759 /// Returns the date immediately following this one.
760 ///
761 /// # Errors
762 ///
763 /// This returns an error when this date is the maximum value.
764 ///
765 /// # Example
766 ///
767 /// ```
768 /// use jiff::civil::{Date, date};
769 ///
770 /// let d = date(2024, 2, 28);
771 /// assert_eq!(d.tomorrow()?, date(2024, 2, 29));
772 ///
773 /// // The max doesn't have a tomorrow.
774 /// assert!(Date::MAX.tomorrow().is_err());
775 ///
776 /// # Ok::<(), Box<dyn std::error::Error>>(())
777 /// ```
778 #[inline]
779 pub fn tomorrow(self) -> Result<Date, Error> {
780 if self.day() >= 28 && self.day() == self.days_in_month() {
781 if self.month() == 12 {
782 let year = self.year_ranged().try_checked_add("year", C(1))?;
783 let month = Month::new_unchecked(1);
784 let day = Day::new_unchecked(1);
785 return Ok(Date::new_ranged_unchecked(year, month, day));
786 }
787 let year = self.year_ranged();
788 let month = Month::new_unchecked(self.month() + 1);
789 let day = Day::new_unchecked(1);
790 return Ok(Date::new_ranged_unchecked(year, month, day));
791 }
792 let year = self.year_ranged();
793 let month = self.month_ranged();
794 let day = Day::new_unchecked(self.day() + 1);
795 Ok(Date::new_ranged_unchecked(year, month, day))
796 }
797
798 /// Returns the date immediately preceding this one.
799 ///
800 /// # Errors
801 ///
802 /// This returns an error when this date is the minimum value.
803 ///
804 /// # Example
805 ///
806 /// ```
807 /// use jiff::civil::{Date, date};
808 ///
809 /// let d = date(2024, 3, 1);
810 /// assert_eq!(d.yesterday()?, date(2024, 2, 29));
811 ///
812 /// // The min doesn't have a yesterday.
813 /// assert!(Date::MIN.yesterday().is_err());
814 ///
815 /// # Ok::<(), Box<dyn std::error::Error>>(())
816 /// ```
817 #[inline]
818 pub fn yesterday(self) -> Result<Date, Error> {
819 if self.day() == 1 {
820 if self.month() == 1 {
821 let year = self.year_ranged().try_checked_sub("year", C(1))?;
822 let month = Month::new_unchecked(12);
823 let day = Day::new_unchecked(31);
824 return Ok(Date::new_ranged_unchecked(year, month, day));
825 }
826 let year = self.year_ranged();
827 let month = Month::new_unchecked(self.month() - 1);
828 let day = days_in_month(year, month);
829 return Ok(Date::new_ranged_unchecked(year, month, day));
830 }
831 let year = self.year_ranged();
832 let month = self.month_ranged();
833 let day = Day::new_unchecked(self.day() - 1);
834 Ok(Date::new_ranged_unchecked(year, month, day))
835 }
836
837 /// Returns the "nth" weekday from the beginning or end of the month in
838 /// which this date resides.
839 ///
840 /// The `nth` parameter can be positive or negative. A positive value
841 /// computes the "nth" weekday from the beginning of the month. A negative
842 /// value computes the "nth" weekday from the end of the month. So for
843 /// example, use `-1` to "find the last weekday" in this date's month.
844 ///
845 /// # Errors
846 ///
847 /// This returns an error when `nth` is `0`, or if it is `5` or `-5` and
848 /// there is no 5th weekday from the beginning or end of the month.
849 ///
850 /// # Example
851 ///
852 /// This shows how to get the nth weekday in a month, starting from the
853 /// beginning of the month:
854 ///
855 /// ```
856 /// use jiff::civil::{Weekday, date};
857 ///
858 /// let month = date(2017, 3, 1);
859 /// let second_friday = month.nth_weekday_of_month(2, Weekday::Friday)?;
860 /// assert_eq!(second_friday, date(2017, 3, 10));
861 ///
862 /// # Ok::<(), Box<dyn std::error::Error>>(())
863 /// ```
864 ///
865 /// This shows how to do the reverse of the above. That is, the nth _last_
866 /// weekday in a month:
867 ///
868 /// ```
869 /// use jiff::civil::{Weekday, date};
870 ///
871 /// let month = date(2024, 3, 1);
872 /// let last_thursday = month.nth_weekday_of_month(-1, Weekday::Thursday)?;
873 /// assert_eq!(last_thursday, date(2024, 3, 28));
874 /// let second_last_thursday = month.nth_weekday_of_month(
875 /// -2,
876 /// Weekday::Thursday,
877 /// )?;
878 /// assert_eq!(second_last_thursday, date(2024, 3, 21));
879 ///
880 /// # Ok::<(), Box<dyn std::error::Error>>(())
881 /// ```
882 ///
883 /// This routine can return an error if there isn't an `nth` weekday
884 /// for this month. For example, March 2024 only has 4 Mondays:
885 ///
886 /// ```
887 /// use jiff::civil::{Weekday, date};
888 ///
889 /// let month = date(2024, 3, 25);
890 /// let fourth_monday = month.nth_weekday_of_month(4, Weekday::Monday)?;
891 /// assert_eq!(fourth_monday, date(2024, 3, 25));
892 /// // There is no 5th Monday.
893 /// assert!(month.nth_weekday_of_month(5, Weekday::Monday).is_err());
894 /// // Same goes for counting backwards.
895 /// assert!(month.nth_weekday_of_month(-5, Weekday::Monday).is_err());
896 ///
897 /// # Ok::<(), Box<dyn std::error::Error>>(())
898 /// ```
899 #[inline]
900 pub fn nth_weekday_of_month(
901 self,
902 nth: i8,
903 weekday: Weekday,
904 ) -> Result<Date, Error> {
905 type Nth = ri8<-5, 5>;
906
907 let nth = Nth::try_new("nth", nth)?;
908 if nth == 0 {
909 Err(err!("nth weekday of month cannot be `0`"))
910 } else if nth > 0 {
911 let nth = nth.max(C(1));
912 let first_weekday = self.first_of_month().weekday();
913 let diff = weekday.since_ranged(first_weekday);
914 let day = Day::rfrom(diff) + C(1) + (nth - C(1)) * C(7);
915 Date::new_ranged(self.year_ranged(), self.month_ranged(), day)
916 } else {
917 let nth = nth.min(C(-1));
918 let last = self.last_of_month();
919 let last_weekday = last.weekday();
920 let diff = last_weekday.since_ranged(weekday);
921 let day = last.day_ranged()
922 - Day::rfrom(diff)
923 - (nth.abs() - C(1)) * C(7);
924 // Our math can go below 1 when nth is -5 and there is no "5th from
925 // last" weekday in this month. Since this is outside the bounds
926 // of `Day`, we can't let this boundary condition escape. So we
927 // check it here.
928 if day < 1 {
929 return Err(day.to_error_with_bounds(
930 "day",
931 1,
932 self.days_in_month(),
933 ));
934 }
935 Date::new_ranged(self.year_ranged(), self.month_ranged(), day)
936 }
937 }
938
939 /// Returns the "nth" weekday from this date, not including itself.
940 ///
941 /// The `nth` parameter can be positive or negative. A positive value
942 /// computes the "nth" weekday starting at the day after this date and
943 /// going forwards in time. A negative value computes the "nth" weekday
944 /// starting at the day before this date and going backwards in time.
945 ///
946 /// For example, if this date's weekday is a Sunday and the first Sunday is
947 /// asked for (that is, `date.nth_weekday(1, Weekday::Sunday)`), then the
948 /// result is a week from this date corresponding to the following Sunday.
949 ///
950 /// # Errors
951 ///
952 /// This returns an error when `nth` is `0`, or if it would otherwise
953 /// result in a date that overflows the minimum/maximum values of `Date`.
954 ///
955 /// # Example
956 ///
957 /// This example shows how to find the "nth" weekday going forwards in
958 /// time:
959 ///
960 /// ```
961 /// use jiff::civil::{Weekday, date};
962 ///
963 /// // Use a Sunday in March as our start date.
964 /// let d = date(2024, 3, 10);
965 /// assert_eq!(d.weekday(), Weekday::Sunday);
966 ///
967 /// // The first next Monday is tomorrow!
968 /// let next_monday = d.nth_weekday(1, Weekday::Monday)?;
969 /// assert_eq!(next_monday, date(2024, 3, 11));
970 ///
971 /// // But the next Sunday is a week away, because this doesn't
972 /// // include the current weekday.
973 /// let next_sunday = d.nth_weekday(1, Weekday::Sunday)?;
974 /// assert_eq!(next_sunday, date(2024, 3, 17));
975 ///
976 /// // "not this Thursday, but next Thursday"
977 /// let next_next_thursday = d.nth_weekday(2, Weekday::Thursday)?;
978 /// assert_eq!(next_next_thursday, date(2024, 3, 21));
979 ///
980 /// # Ok::<(), Box<dyn std::error::Error>>(())
981 /// ```
982 ///
983 /// This example shows how to find the "nth" weekday going backwards in
984 /// time:
985 ///
986 /// ```
987 /// use jiff::civil::{Weekday, date};
988 ///
989 /// // Use a Sunday in March as our start date.
990 /// let d = date(2024, 3, 10);
991 /// assert_eq!(d.weekday(), Weekday::Sunday);
992 ///
993 /// // "last Saturday" was yesterday!
994 /// let last_saturday = d.nth_weekday(-1, Weekday::Saturday)?;
995 /// assert_eq!(last_saturday, date(2024, 3, 9));
996 ///
997 /// // "last Sunday" was a week ago.
998 /// let last_sunday = d.nth_weekday(-1, Weekday::Sunday)?;
999 /// assert_eq!(last_sunday, date(2024, 3, 3));
1000 ///
1001 /// // "not last Thursday, but the one before"
1002 /// let prev_prev_thursday = d.nth_weekday(-2, Weekday::Thursday)?;
1003 /// assert_eq!(prev_prev_thursday, date(2024, 2, 29));
1004 ///
1005 /// # Ok::<(), Box<dyn std::error::Error>>(())
1006 /// ```
1007 ///
1008 /// This example shows that overflow results in an error in either
1009 /// direction:
1010 ///
1011 /// ```
1012 /// use jiff::civil::{Date, Weekday};
1013 ///
1014 /// let d = Date::MAX;
1015 /// assert_eq!(d.weekday(), Weekday::Friday);
1016 /// assert!(d.nth_weekday(1, Weekday::Saturday).is_err());
1017 ///
1018 /// let d = Date::MIN;
1019 /// assert_eq!(d.weekday(), Weekday::Monday);
1020 /// assert!(d.nth_weekday(-1, Weekday::Sunday).is_err());
1021 /// ```
1022 ///
1023 /// # Example: the start of Israeli summer time
1024 ///
1025 /// Israeli law says (at present, as of 2024-03-11) that DST or "summer
1026 /// time" starts on the Friday before the last Sunday in March. We can find
1027 /// that date using both `nth_weekday` and [`Date::nth_weekday_of_month`]:
1028 ///
1029 /// ```
1030 /// use jiff::civil::{Weekday, date};
1031 ///
1032 /// let march = date(2024, 3, 1);
1033 /// let last_sunday = march.nth_weekday_of_month(-1, Weekday::Sunday)?;
1034 /// let dst_starts_on = last_sunday.nth_weekday(-1, Weekday::Friday)?;
1035 /// assert_eq!(dst_starts_on, date(2024, 3, 29));
1036 ///
1037 /// # Ok::<(), Box<dyn std::error::Error>>(())
1038 /// ```
1039 ///
1040 /// # Example: getting the start of the week
1041 ///
1042 /// Given a date, one can use `nth_weekday` to determine the start of the
1043 /// week in which the date resides in. This might vary based on whether
1044 /// the weeks start on Sunday or Monday. This example shows how to handle
1045 /// both.
1046 ///
1047 /// ```
1048 /// use jiff::civil::{Weekday, date};
1049 ///
1050 /// let d = date(2024, 3, 15);
1051 /// // For weeks starting with Sunday.
1052 /// let start_of_week = d.tomorrow()?.nth_weekday(-1, Weekday::Sunday)?;
1053 /// assert_eq!(start_of_week, date(2024, 3, 10));
1054 /// // For weeks starting with Monday.
1055 /// let start_of_week = d.tomorrow()?.nth_weekday(-1, Weekday::Monday)?;
1056 /// assert_eq!(start_of_week, date(2024, 3, 11));
1057 ///
1058 /// # Ok::<(), Box<dyn std::error::Error>>(())
1059 /// ```
1060 ///
1061 /// In the above example, we first get the date after the current one
1062 /// because `nth_weekday` does not consider itself when counting. This
1063 /// works as expected even at the boundaries of a week:
1064 ///
1065 /// ```
1066 /// use jiff::civil::{Weekday, date};
1067 ///
1068 /// // The start of the week.
1069 /// let d = date(2024, 3, 10);
1070 /// let start_of_week = d.tomorrow()?.nth_weekday(-1, Weekday::Sunday)?;
1071 /// assert_eq!(start_of_week, date(2024, 3, 10));
1072 /// // The end of the week.
1073 /// let d = date(2024, 3, 16);
1074 /// let start_of_week = d.tomorrow()?.nth_weekday(-1, Weekday::Sunday)?;
1075 /// assert_eq!(start_of_week, date(2024, 3, 10));
1076 ///
1077 /// # Ok::<(), Box<dyn std::error::Error>>(())
1078 /// ```
1079 #[inline]
1080 pub fn nth_weekday(
1081 self,
1082 nth: i32,
1083 weekday: Weekday,
1084 ) -> Result<Date, Error> {
1085 // ref: http://howardhinnant.github.io/date_algorithms.html#next_weekday
1086
1087 let nth = t::SpanWeeks::try_new("nth weekday", nth)?;
1088 if nth == 0 {
1089 Err(err!("nth weekday cannot be `0`"))
1090 } else if nth > 0 {
1091 let nth = nth.max(C(1));
1092 let weekday_diff = weekday.since_ranged(self.weekday().next());
1093 let diff = (nth - C(1)) * C(7) + weekday_diff;
1094 let start = self.tomorrow()?.to_unix_epoch_day();
1095 let end = start.try_checked_add("days", diff)?;
1096 Ok(Date::from_unix_epoch_day(end))
1097 } else {
1098 let nth: t::SpanWeeks = nth.min(C(-1)).abs();
1099 let weekday_diff = self.weekday().previous().since_ranged(weekday);
1100 let diff = (nth - C(1)) * C(7) + weekday_diff;
1101 let start = self.yesterday()?.to_unix_epoch_day();
1102 let end = start.try_checked_sub("days", diff)?;
1103 Ok(Date::from_unix_epoch_day(end))
1104 }
1105 }
1106
1107 /// Construct an [ISO 8601 week date] from this Gregorian date.
1108 ///
1109 /// The [`ISOWeekDate`] type describes itself in more detail, but in
1110 /// brief, the ISO week date calendar system eschews months in favor of
1111 /// weeks.
1112 ///
1113 /// The minimum and maximum values of an `ISOWeekDate` correspond
1114 /// precisely to the minimum and maximum values of a `Date`. Therefore,
1115 /// converting between them is lossless and infallible.
1116 ///
1117 /// This routine is equivalent to [`ISOWeekDate::from_date`].
1118 ///
1119 /// [ISO 8601 week date]: https://en.wikipedia.org/wiki/ISO_week_date
1120 ///
1121 /// # Example
1122 ///
1123 /// This shows a number of examples demonstrating the conversion from a
1124 /// Gregorian date to an ISO 8601 week date:
1125 ///
1126 /// ```
1127 /// use jiff::civil::{Date, Weekday, date};
1128 ///
1129 /// let weekdate = date(1995, 1, 1).iso_week_date();
1130 /// assert_eq!(weekdate.year(), 1994);
1131 /// assert_eq!(weekdate.week(), 52);
1132 /// assert_eq!(weekdate.weekday(), Weekday::Sunday);
1133 ///
1134 /// let weekdate = date(1996, 12, 31).iso_week_date();
1135 /// assert_eq!(weekdate.year(), 1997);
1136 /// assert_eq!(weekdate.week(), 1);
1137 /// assert_eq!(weekdate.weekday(), Weekday::Tuesday);
1138 ///
1139 /// let weekdate = date(2019, 12, 30).iso_week_date();
1140 /// assert_eq!(weekdate.year(), 2020);
1141 /// assert_eq!(weekdate.week(), 1);
1142 /// assert_eq!(weekdate.weekday(), Weekday::Monday);
1143 ///
1144 /// let weekdate = date(2024, 3, 9).iso_week_date();
1145 /// assert_eq!(weekdate.year(), 2024);
1146 /// assert_eq!(weekdate.week(), 10);
1147 /// assert_eq!(weekdate.weekday(), Weekday::Saturday);
1148 ///
1149 /// let weekdate = Date::MIN.iso_week_date();
1150 /// assert_eq!(weekdate.year(), -9999);
1151 /// assert_eq!(weekdate.week(), 1);
1152 /// assert_eq!(weekdate.weekday(), Weekday::Monday);
1153 ///
1154 /// let weekdate = Date::MAX.iso_week_date();
1155 /// assert_eq!(weekdate.year(), 9999);
1156 /// assert_eq!(weekdate.week(), 52);
1157 /// assert_eq!(weekdate.weekday(), Weekday::Friday);
1158 /// ```
1159 #[inline]
1160 pub fn iso_week_date(self) -> ISOWeekDate {
1161 let days = t::NoUnits32::rfrom(self.to_unix_epoch_day());
1162 let year = t::NoUnits32::rfrom(self.year_ranged());
1163 let week_start = t::NoUnits32::vary([days, year], |[days, year]| {
1164 let mut week_start =
1165 t::NoUnits32::rfrom(iso_week_start_from_year(year));
1166 if days < week_start {
1167 week_start =
1168 t::NoUnits32::rfrom(iso_week_start_from_year(year - C(1)));
1169 } else {
1170 let next_year_week_start =
1171 t::NoUnits32::rfrom(iso_week_start_from_year(year + C(1)));
1172 if days >= next_year_week_start {
1173 week_start = next_year_week_start;
1174 }
1175 }
1176 week_start
1177 });
1178
1179 let weekday = weekday_from_unix_epoch_days(days);
1180 let week = ((days - week_start) / C(7)) + C(1);
1181
1182 let unix_epoch_day = week_start
1183 + t::NoUnits32::rfrom(
1184 Weekday::Thursday.since_ranged(Weekday::Monday),
1185 );
1186 let year =
1187 Date::from_unix_epoch_day(unix_epoch_day.rinto()).year_ranged();
1188 ISOWeekDate::new_ranged(year, week, weekday)
1189 .expect("all Dates infallibly convert to ISOWeekDates")
1190 }
1191
1192 /// Converts a civil date to a [`Zoned`] datetime by adding the given
1193 /// time zone and setting the clock time to midnight.
1194 ///
1195 /// This is a convenience function for
1196 /// `date.to_datetime(midnight).in_tz(name)`. See [`DateTime::to_zoned`]
1197 /// for more details. Note that ambiguous datetimes are handled in the
1198 /// same way as `DateTime::to_zoned`.
1199 ///
1200 /// # Errors
1201 ///
1202 /// This returns an error when the given time zone name could not be found
1203 /// in the default time zone database.
1204 ///
1205 /// This also returns an error if this date could not be represented as
1206 /// a timestamp. This can occur in some cases near the minimum and maximum
1207 /// boundaries of a `Date`.
1208 ///
1209 /// # Example
1210 ///
1211 /// This is a simple example of converting a civil date (a "wall" or
1212 /// "local" or "naive" date) to a precise instant in time that is aware of
1213 /// its time zone:
1214 ///
1215 /// ```
1216 /// use jiff::civil::date;
1217 ///
1218 /// let zdt = date(2024, 6, 20).in_tz("America/New_York")?;
1219 /// assert_eq!(zdt.to_string(), "2024-06-20T00:00:00-04:00[America/New_York]");
1220 ///
1221 /// # Ok::<(), Box<dyn std::error::Error>>(())
1222 /// ```
1223 ///
1224 /// # Example: dealing with ambiguity
1225 ///
1226 /// Since a [`Zoned`] corresponds to a precise instant in time (to
1227 /// nanosecond precision) and a `Date` can be many possible such instants,
1228 /// this routine chooses one for this date: the first one, or midnight.
1229 ///
1230 /// Interestingly, some regions implement their daylight saving time
1231 /// transitions at midnight. This means there are some places in the world
1232 /// where, once a year, midnight does not exist on their clocks. As a
1233 /// result, it's possible for the datetime string representing a [`Zoned`]
1234 /// to be something other than midnight. For example:
1235 ///
1236 /// ```
1237 /// use jiff::civil::date;
1238 ///
1239 /// let zdt = date(2024, 3, 10).in_tz("Cuba")?;
1240 /// assert_eq!(zdt.to_string(), "2024-03-10T01:00:00-04:00[Cuba]");
1241 ///
1242 /// # Ok::<(), Box<dyn std::error::Error>>(())
1243 /// ```
1244 ///
1245 /// Since this uses
1246 /// [`Disambiguation::Compatible`](crate::tz::Disambiguation::Compatible),
1247 /// and since that also chooses the "later" time in a forward transition,
1248 /// it follows that the date of the returned `Zoned` will always match
1249 /// this civil date. (Unless there is a pathological time zone with a 24+
1250 /// hour transition forward.)
1251 ///
1252 /// But if a different disambiguation strategy is used, even when only
1253 /// dealing with standard one hour transitions, the date returned can be
1254 /// different:
1255 ///
1256 /// ```
1257 /// use jiff::{civil::date, tz::TimeZone};
1258 ///
1259 /// let tz = TimeZone::get("Cuba")?;
1260 /// let dt = date(2024, 3, 10).at(0, 0, 0, 0);
1261 /// let zdt = tz.to_ambiguous_zoned(dt).earlier()?;
1262 /// assert_eq!(zdt.to_string(), "2024-03-09T23:00:00-05:00[Cuba]");
1263 ///
1264 /// # Ok::<(), Box<dyn std::error::Error>>(())
1265 /// ```
1266 #[inline]
1267 pub fn in_tz(self, time_zone_name: &str) -> Result<Zoned, Error> {
1268 let tz = crate::tz::db().get(time_zone_name)?;
1269 self.to_zoned(tz)
1270 }
1271
1272 /// Converts a civil datetime to a [`Zoned`] datetime by adding the given
1273 /// [`TimeZone`] and setting the clock time to midnight.
1274 ///
1275 /// This is a convenience function for
1276 /// `date.to_datetime(midnight).to_zoned(tz)`. See [`DateTime::to_zoned`]
1277 /// for more details. Note that ambiguous datetimes are handled in the same
1278 /// way as `DateTime::to_zoned`.
1279 ///
1280 /// In the common case of a time zone being represented as a name string,
1281 /// like `Australia/Tasmania`, consider using [`Date::in_tz`]
1282 /// instead.
1283 ///
1284 /// # Errors
1285 ///
1286 /// This returns an error if this date could not be represented as a
1287 /// timestamp. This can occur in some cases near the minimum and maximum
1288 /// boundaries of a `Date`.
1289 ///
1290 /// # Example
1291 ///
1292 /// This example shows how to create a zoned value with a fixed time zone
1293 /// offset:
1294 ///
1295 /// ```
1296 /// use jiff::{civil::date, tz};
1297 ///
1298 /// let tz = tz::offset(-4).to_time_zone();
1299 /// let zdt = date(2024, 6, 20).to_zoned(tz)?;
1300 /// // A time zone annotation is still included in the printable version
1301 /// // of the Zoned value, but it is fixed to a particular offset.
1302 /// assert_eq!(zdt.to_string(), "2024-06-20T00:00:00-04:00[-04:00]");
1303 ///
1304 /// # Ok::<(), Box<dyn std::error::Error>>(())
1305 /// ```
1306 #[inline]
1307 pub fn to_zoned(self, tz: TimeZone) -> Result<Zoned, Error> {
1308 DateTime::from(self).to_zoned(tz)
1309 }
1310
1311 /// Given a [`Time`], this constructs a [`DateTime`] value with its time
1312 /// component equal to this time.
1313 ///
1314 /// This is a convenience function for [`DateTime::from_parts`].
1315 ///
1316 /// # Example
1317 ///
1318 /// ```
1319 /// use jiff::civil::{DateTime, date, time};
1320 ///
1321 /// let date = date(2010, 3, 14);
1322 /// let time = time(2, 30, 0, 0);
1323 /// assert_eq!(DateTime::from_parts(date, time), date.to_datetime(time));
1324 /// ```
1325 #[inline]
1326 pub const fn to_datetime(self, time: Time) -> DateTime {
1327 DateTime::from_parts(self, time)
1328 }
1329
1330 /// A convenience function for constructing a [`DateTime`] from this date
1331 /// at the time given by its components.
1332 ///
1333 /// # Example
1334 ///
1335 /// ```
1336 /// use jiff::civil::date;
1337 ///
1338 /// assert_eq!(
1339 /// date(2010, 3, 14).at(2, 30, 0, 0).to_string(),
1340 /// "2010-03-14T02:30:00",
1341 /// );
1342 /// ```
1343 ///
1344 /// One can also flip the order by making use of [`Time::on`]:
1345 ///
1346 /// ```
1347 /// use jiff::civil::time;
1348 ///
1349 /// assert_eq!(
1350 /// time(2, 30, 0, 0).on(2010, 3, 14).to_string(),
1351 /// "2010-03-14T02:30:00",
1352 /// );
1353 /// ```
1354 #[inline]
1355 pub const fn at(
1356 self,
1357 hour: i8,
1358 minute: i8,
1359 second: i8,
1360 subsec_nanosecond: i32,
1361 ) -> DateTime {
1362 DateTime::from_parts(
1363 self,
1364 Time::constant(hour, minute, second, subsec_nanosecond),
1365 )
1366 }
1367
1368 /// Add the given span of time to this date. If the sum would overflow the
1369 /// minimum or maximum date values, then an error is returned.
1370 ///
1371 /// This operation accepts three different duration types: [`Span`],
1372 /// [`SignedDuration`] or [`std::time::Duration`]. This is achieved via
1373 /// `From` trait implementations for the [`DateArithmetic`] type.
1374 ///
1375 /// # Properties
1376 ///
1377 /// When adding a [`Span`] duration, this routine is _not_ reversible
1378 /// because some additions may be ambiguous. For example, adding `1 month`
1379 /// to the date `2024-03-31` will produce `2024-04-30` since April has only
1380 /// 30 days in a month. Conversely, subtracting `1 month` from `2024-04-30`
1381 /// will produce `2024-03-30`, which is not the date we started with.
1382 ///
1383 /// If spans of time are limited to units of days (or less), then this
1384 /// routine _is_ reversible. This also implies that all operations with
1385 /// a [`SignedDuration`] or a [`std::time::Duration`] are reversible.
1386 ///
1387 /// # Errors
1388 ///
1389 /// If the span added to this date would result in a date that exceeds the
1390 /// range of a `Date`, then this will return an error.
1391 ///
1392 /// # Examples
1393 ///
1394 /// This shows a few examples of adding spans of time to various dates.
1395 /// We make use of the [`ToSpan`](crate::ToSpan) trait for convenient
1396 /// creation of spans.
1397 ///
1398 /// ```
1399 /// use jiff::{civil::date, ToSpan};
1400 ///
1401 /// let d = date(2024, 3, 31);
1402 /// assert_eq!(d.checked_add(1.months())?, date(2024, 4, 30));
1403 /// // Adding two months gives us May 31, not May 30.
1404 /// let d = date(2024, 3, 31);
1405 /// assert_eq!(d.checked_add(2.months())?, date(2024, 5, 31));
1406 /// // Any time in the span that does not exceed a day is ignored.
1407 /// let d = date(2024, 3, 31);
1408 /// assert_eq!(d.checked_add(23.hours())?, date(2024, 3, 31));
1409 /// // But if the time exceeds a day, that is accounted for!
1410 /// let d = date(2024, 3, 31);
1411 /// assert_eq!(d.checked_add(28.hours())?, date(2024, 4, 1));
1412 ///
1413 /// # Ok::<(), Box<dyn std::error::Error>>(())
1414 /// ```
1415 ///
1416 /// # Example: available via addition operator
1417 ///
1418 /// This routine can be used via the `+` operator. Note though that if it
1419 /// fails, it will result in a panic.
1420 ///
1421 /// ```
1422 /// use jiff::{civil::date, ToSpan};
1423 ///
1424 /// let d = date(2024, 3, 31);
1425 /// assert_eq!(d + 1.months(), date(2024, 4, 30));
1426 /// ```
1427 ///
1428 /// # Example: negative spans are supported
1429 ///
1430 /// ```
1431 /// use jiff::{civil::date, ToSpan};
1432 ///
1433 /// let d = date(2024, 3, 31);
1434 /// assert_eq!(
1435 /// d.checked_add(-1.months())?,
1436 /// date(2024, 2, 29),
1437 /// );
1438 /// # Ok::<(), Box<dyn std::error::Error>>(())
1439 /// ```
1440 ///
1441 /// # Example: error on overflow
1442 ///
1443 /// ```
1444 /// use jiff::{civil::date, ToSpan};
1445 ///
1446 /// let d = date(2024, 3, 31);
1447 /// assert!(d.checked_add(9000.years()).is_err());
1448 /// assert!(d.checked_add(-19000.years()).is_err());
1449 /// ```
1450 ///
1451 /// # Example: adding absolute durations
1452 ///
1453 /// This shows how to add signed and unsigned absolute durations to a
1454 /// `Date`. Only whole numbers of days are considered. Since this is a
1455 /// civil date unaware of time zones, days are always 24 hours.
1456 ///
1457 /// ```
1458 /// use std::time::Duration;
1459 ///
1460 /// use jiff::{civil::date, SignedDuration};
1461 ///
1462 /// let d = date(2024, 2, 29);
1463 ///
1464 /// let dur = SignedDuration::from_hours(24);
1465 /// assert_eq!(d.checked_add(dur)?, date(2024, 3, 1));
1466 /// assert_eq!(d.checked_add(-dur)?, date(2024, 2, 28));
1467 ///
1468 /// // Any leftover time is truncated. That is, only
1469 /// // whole days from the duration are considered.
1470 /// let dur = Duration::from_secs((24 * 60 * 60) + (23 * 60 * 60));
1471 /// assert_eq!(d.checked_add(dur)?, date(2024, 3, 1));
1472 ///
1473 /// # Ok::<(), Box<dyn std::error::Error>>(())
1474 /// ```
1475 #[inline]
1476 pub fn checked_add<A: Into<DateArithmetic>>(
1477 self,
1478 duration: A,
1479 ) -> Result<Date, Error> {
1480 let duration: DateArithmetic = duration.into();
1481 duration.checked_add(self)
1482 }
1483
1484 #[inline]
1485 fn checked_add_span(self, span: Span) -> Result<Date, Error> {
1486 if span.is_zero() {
1487 return Ok(self);
1488 }
1489 if span.units().contains_only(Unit::Day) {
1490 let epoch_days = self.to_unix_epoch_day();
1491 let days = epoch_days.try_checked_add(
1492 "days",
1493 UnixEpochDay::rfrom(span.get_days_ranged()),
1494 )?;
1495 return Ok(Date::from_unix_epoch_day(days));
1496 }
1497
1498 let (month, years) =
1499 month_add_overflowing(self.month, span.get_months_ranged());
1500 let year = self
1501 .year
1502 .try_checked_add("years", years)?
1503 .try_checked_add("years", span.get_years_ranged())?;
1504 let date = Date::constrain_ranged(year, month, self.day);
1505 let epoch_days = date.to_unix_epoch_day();
1506 let mut days = epoch_days
1507 .try_checked_add(
1508 "days",
1509 C(7) * UnixEpochDay::rfrom(span.get_weeks_ranged()),
1510 )?
1511 .try_checked_add(
1512 "days",
1513 UnixEpochDay::rfrom(span.get_days_ranged()),
1514 )?;
1515 if !span.units().only_time().is_empty() {
1516 let time_days = span
1517 .only_lower(Unit::Day)
1518 .to_invariant_nanoseconds()
1519 .div_ceil(t::NANOS_PER_CIVIL_DAY);
1520 days = days.try_checked_add("time", time_days)?;
1521 }
1522 Ok(Date::from_unix_epoch_day(days))
1523 }
1524
1525 #[inline]
1526 fn checked_add_duration(
1527 self,
1528 duration: SignedDuration,
1529 ) -> Result<Date, Error> {
1530 // OK because 24!={-1,0}.
1531 let days = duration.as_hours() / 24;
1532 let days = UnixEpochDay::try_new("days", days).with_context(|| {
1533 err!(
1534 "{days} computed from duration {duration:?} overflows \
1535 Jiff's datetime limits",
1536 )
1537 })?;
1538 let days = self.to_unix_epoch_day().try_checked_add("days", days)?;
1539 Ok(Date::from_unix_epoch_day(days))
1540 }
1541
1542 /// This routine is identical to [`Date::checked_add`] with the duration
1543 /// negated.
1544 ///
1545 /// # Errors
1546 ///
1547 /// This has the same error conditions as [`Date::checked_add`].
1548 ///
1549 /// # Example
1550 ///
1551 /// ```
1552 /// use std::time::Duration;
1553 ///
1554 /// use jiff::{civil::date, SignedDuration, ToSpan};
1555 ///
1556 /// let d = date(2024, 2, 29);
1557 /// assert_eq!(d.checked_sub(1.year())?, date(2023, 2, 28));
1558 ///
1559 /// let dur = SignedDuration::from_hours(24);
1560 /// assert_eq!(d.checked_sub(dur)?, date(2024, 2, 28));
1561 ///
1562 /// let dur = Duration::from_secs(24 * 60 * 60);
1563 /// assert_eq!(d.checked_sub(dur)?, date(2024, 2, 28));
1564 ///
1565 /// # Ok::<(), Box<dyn std::error::Error>>(())
1566 /// ```
1567 #[inline]
1568 pub fn checked_sub<A: Into<DateArithmetic>>(
1569 self,
1570 duration: A,
1571 ) -> Result<Date, Error> {
1572 let duration: DateArithmetic = duration.into();
1573 duration.checked_neg().and_then(|da| da.checked_add(self))
1574 }
1575
1576 /// This routine is identical to [`Date::checked_add`], except the
1577 /// result saturates on overflow. That is, instead of overflow, either
1578 /// [`Date::MIN`] or [`Date::MAX`] is returned.
1579 ///
1580 /// # Example
1581 ///
1582 /// ```
1583 /// use jiff::{civil::{Date, date}, SignedDuration, ToSpan};
1584 ///
1585 /// let d = date(2024, 3, 31);
1586 /// assert_eq!(Date::MAX, d.saturating_add(9000.years()));
1587 /// assert_eq!(Date::MIN, d.saturating_add(-19000.years()));
1588 /// assert_eq!(Date::MAX, d.saturating_add(SignedDuration::MAX));
1589 /// assert_eq!(Date::MIN, d.saturating_add(SignedDuration::MIN));
1590 /// assert_eq!(Date::MAX, d.saturating_add(std::time::Duration::MAX));
1591 /// ```
1592 #[inline]
1593 pub fn saturating_add<A: Into<DateArithmetic>>(self, duration: A) -> Date {
1594 let duration: DateArithmetic = duration.into();
1595 self.checked_add(duration).unwrap_or_else(|_| {
1596 if duration.is_negative() {
1597 Date::MIN
1598 } else {
1599 Date::MAX
1600 }
1601 })
1602 }
1603
1604 /// This routine is identical to [`Date::saturating_add`] with the span
1605 /// parameter negated.
1606 ///
1607 /// # Example
1608 ///
1609 /// ```
1610 /// use jiff::{civil::{Date, date}, SignedDuration, ToSpan};
1611 ///
1612 /// let d = date(2024, 3, 31);
1613 /// assert_eq!(Date::MIN, d.saturating_sub(19000.years()));
1614 /// assert_eq!(Date::MAX, d.saturating_sub(-9000.years()));
1615 /// assert_eq!(Date::MIN, d.saturating_sub(SignedDuration::MAX));
1616 /// assert_eq!(Date::MAX, d.saturating_sub(SignedDuration::MIN));
1617 /// assert_eq!(Date::MIN, d.saturating_sub(std::time::Duration::MAX));
1618 /// ```
1619 #[inline]
1620 pub fn saturating_sub<A: Into<DateArithmetic>>(self, duration: A) -> Date {
1621 let duration: DateArithmetic = duration.into();
1622 let Ok(duration) = duration.checked_neg() else { return Date::MIN };
1623 self.saturating_add(duration)
1624 }
1625
1626 /// Returns a span representing the elapsed time from this date until
1627 /// the given `other` date.
1628 ///
1629 /// When `other` occurs before this date, then the span returned will be
1630 /// negative.
1631 ///
1632 /// Depending on the input provided, the span returned is rounded. It may
1633 /// also be balanced up to bigger units than the default. By default, the
1634 /// span returned is balanced such that the biggest and smallest possible
1635 /// unit is days.
1636 ///
1637 /// This operation is configured by providing a [`DateDifference`]
1638 /// value. Since this routine accepts anything that implements
1639 /// `Into<DateDifference>`, once can pass a `Date` directly. One
1640 /// can also pass a `(Unit, Date)`, where `Unit` is treated as
1641 /// [`DateDifference::largest`].
1642 ///
1643 /// # Properties
1644 ///
1645 /// It is guaranteed that if the returned span is subtracted from `other`,
1646 /// and if no rounding is requested, and if the largest unit request is at
1647 /// most `Unit::Day`, then the original date will be returned.
1648 ///
1649 /// This routine is equivalent to `self.since(other).map(|span| -span)`
1650 /// if no rounding options are set. If rounding options are set, then
1651 /// it's equivalent to
1652 /// `self.since(other_without_rounding_options).map(|span| -span)`,
1653 /// followed by a call to [`Span::round`] with the appropriate rounding
1654 /// options set. This is because the negation of a span can result in
1655 /// different rounding results depending on the rounding mode.
1656 ///
1657 /// # Errors
1658 ///
1659 /// An error can occur if `DateDifference` is misconfigured. For example,
1660 /// if the smallest unit provided is bigger than the largest unit.
1661 ///
1662 /// It is guaranteed that if one provides a date with the default
1663 /// [`DateDifference`] configuration, then this routine will never fail.
1664 ///
1665 /// # Examples
1666 ///
1667 /// ```
1668 /// use jiff::{civil::date, ToSpan};
1669 ///
1670 /// let earlier = date(2006, 8, 24);
1671 /// let later = date(2019, 1, 31);
1672 /// assert_eq!(earlier.until(later)?, 4543.days().fieldwise());
1673 ///
1674 /// // Flipping the dates is fine, but you'll get a negative span.
1675 /// let earlier = date(2006, 8, 24);
1676 /// let later = date(2019, 1, 31);
1677 /// assert_eq!(later.until(earlier)?, -4543.days().fieldwise());
1678 ///
1679 /// # Ok::<(), Box<dyn std::error::Error>>(())
1680 /// ```
1681 ///
1682 /// # Example: using bigger units
1683 ///
1684 /// This example shows how to expand the span returned to bigger units.
1685 /// This makes use of a `From<(Unit, Date)> for DateDifference` trait
1686 /// implementation.
1687 ///
1688 /// ```
1689 /// use jiff::{civil::date, Unit, ToSpan};
1690 ///
1691 /// let d1 = date(1995, 12, 07);
1692 /// let d2 = date(2019, 01, 31);
1693 ///
1694 /// // The default limits durations to using "days" as the biggest unit.
1695 /// let span = d1.until(d2)?;
1696 /// assert_eq!(span.to_string(), "P8456D");
1697 ///
1698 /// // But we can ask for units all the way up to years.
1699 /// let span = d1.until((Unit::Year, d2))?;
1700 /// assert_eq!(span.to_string(), "P23Y1M24D");
1701 ///
1702 /// # Ok::<(), Box<dyn std::error::Error>>(())
1703 /// ```
1704 ///
1705 /// # Example: rounding the result
1706 ///
1707 /// This shows how one might find the difference between two dates and
1708 /// have the result rounded to the nearest month.
1709 ///
1710 /// In this case, we need to hand-construct a [`DateDifference`]
1711 /// in order to gain full configurability.
1712 ///
1713 /// ```
1714 /// use jiff::{civil::{date, DateDifference}, Unit, ToSpan};
1715 ///
1716 /// let d1 = date(1995, 12, 07);
1717 /// let d2 = date(2019, 02, 06);
1718 ///
1719 /// let span = d1.until(DateDifference::from(d2).smallest(Unit::Month))?;
1720 /// assert_eq!(span, 277.months().fieldwise());
1721 ///
1722 /// // Or even include years to make the span a bit more comprehensible.
1723 /// let span = d1.until(
1724 /// DateDifference::from(d2)
1725 /// .smallest(Unit::Month)
1726 /// .largest(Unit::Year),
1727 /// )?;
1728 /// // Notice that we are one day shy of 23y2m. Rounding spans computed
1729 /// // between dates uses truncation by default.
1730 /// assert_eq!(span, 23.years().months(1).fieldwise());
1731 ///
1732 /// # Ok::<(), Box<dyn std::error::Error>>(())
1733 /// ```
1734 ///
1735 /// # Example: units biggers than days inhibit reversibility
1736 ///
1737 /// If you ask for units bigger than days, then adding the span
1738 /// returned to the `other` date is not guaranteed to result in the
1739 /// original date. For example:
1740 ///
1741 /// ```
1742 /// use jiff::{civil::date, Unit, ToSpan};
1743 ///
1744 /// let d1 = date(2024, 3, 2);
1745 /// let d2 = date(2024, 5, 1);
1746 ///
1747 /// let span = d1.until((Unit::Month, d2))?;
1748 /// assert_eq!(span, 1.month().days(29).fieldwise());
1749 /// let maybe_original = d2.checked_sub(span)?;
1750 /// // Not the same as the original datetime!
1751 /// assert_eq!(maybe_original, date(2024, 3, 3));
1752 ///
1753 /// // But in the default configuration, days are always the biggest unit
1754 /// // and reversibility is guaranteed.
1755 /// let span = d1.until(d2)?;
1756 /// assert_eq!(span, 60.days().fieldwise());
1757 /// let is_original = d2.checked_sub(span)?;
1758 /// assert_eq!(is_original, d1);
1759 ///
1760 /// # Ok::<(), Box<dyn std::error::Error>>(())
1761 /// ```
1762 ///
1763 /// This occurs because spans are added as if by adding the biggest units
1764 /// first, and then the smaller units. Because months vary in length,
1765 /// their meaning can change depending on how the span is added. In this
1766 /// case, adding one month to `2024-03-02` corresponds to 31 days, but
1767 /// subtracting one month from `2024-05-01` corresponds to 30 days.
1768 #[inline]
1769 pub fn until<A: Into<DateDifference>>(
1770 self,
1771 other: A,
1772 ) -> Result<Span, Error> {
1773 let args: DateDifference = other.into();
1774 let span = args.until_with_largest_unit(self)?;
1775 if args.rounding_may_change_span() {
1776 span.round(args.round.relative(self))
1777 } else {
1778 Ok(span)
1779 }
1780 }
1781
1782 /// This routine is identical to [`Date::until`], but the order of the
1783 /// parameters is flipped.
1784 ///
1785 /// # Errors
1786 ///
1787 /// This has the same error conditions as [`Date::until`].
1788 ///
1789 /// # Example
1790 ///
1791 /// This routine can be used via the `-` operator. Since the default
1792 /// configuration is used and because a `Span` can represent the difference
1793 /// between any two possible dates, it will never panic.
1794 ///
1795 /// ```
1796 /// use jiff::{civil::date, ToSpan};
1797 ///
1798 /// let earlier = date(2006, 8, 24);
1799 /// let later = date(2019, 1, 31);
1800 /// assert_eq!(later - earlier, 4543.days().fieldwise());
1801 /// // Equivalent to:
1802 /// assert_eq!(later.since(earlier).unwrap(), 4543.days().fieldwise());
1803 /// ```
1804 #[inline]
1805 pub fn since<A: Into<DateDifference>>(
1806 self,
1807 other: A,
1808 ) -> Result<Span, Error> {
1809 let args: DateDifference = other.into();
1810 let span = -args.until_with_largest_unit(self)?;
1811 if args.rounding_may_change_span() {
1812 span.round(args.round.relative(self))
1813 } else {
1814 Ok(span)
1815 }
1816 }
1817
1818 /// Returns an absolute duration representing the elapsed time from this
1819 /// date until the given `other` date.
1820 ///
1821 /// When `other` occurs before this date, then the duration returned will
1822 /// be negative.
1823 ///
1824 /// Unlike [`Date::until`], this returns a duration corresponding to a
1825 /// 96-bit integer of nanoseconds between two dates. In this case of
1826 /// computing durations between civil dates where all days are assumed to
1827 /// be 24 hours long, the duration returned will always be divisible by
1828 /// 24 hours. (That is, `24 * 60 * 60 * 1_000_000_000` nanoseconds.)
1829 ///
1830 /// # Fallibility
1831 ///
1832 /// This routine never panics or returns an error. Since there are no
1833 /// configuration options that can be incorrectly provided, no error is
1834 /// possible when calling this routine. In contrast, [`Date::until`] can
1835 /// return an error in some cases due to misconfiguration. But like this
1836 /// routine, [`Date::until`] never panics or returns an error in its
1837 /// default configuration.
1838 ///
1839 /// # When should I use this versus [`Date::until`]?
1840 ///
1841 /// See the type documentation for [`SignedDuration`] for the section on
1842 /// when one should use [`Span`] and when one should use `SignedDuration`.
1843 /// In short, use `Span` (and therefore `Date::until`) unless you have a
1844 /// specific reason to do otherwise.
1845 ///
1846 /// # Example
1847 ///
1848 /// ```
1849 /// use jiff::{civil::date, SignedDuration};
1850 ///
1851 /// let earlier = date(2006, 8, 24);
1852 /// let later = date(2019, 1, 31);
1853 /// assert_eq!(
1854 /// earlier.duration_until(later),
1855 /// SignedDuration::from_hours(4543 * 24),
1856 /// );
1857 /// ```
1858 ///
1859 /// # Example: difference with [`Date::until`]
1860 ///
1861 /// The main difference between this routine and `Date::until` is that the
1862 /// latter can return units other than a 96-bit integer of nanoseconds.
1863 /// While a 96-bit integer of nanoseconds can be converted into other
1864 /// units like hours, this can only be done for uniform units. (Uniform
1865 /// units are units for which each individual unit always corresponds to
1866 /// the same elapsed time regardless of the datetime it is relative to.)
1867 /// This can't be done for units like years, months or days without a
1868 /// relative date.
1869 ///
1870 /// ```
1871 /// use jiff::{civil::date, SignedDuration, Span, SpanRound, ToSpan, Unit};
1872 ///
1873 /// let d1 = date(2024, 1, 1);
1874 /// let d2 = date(2025, 4, 1);
1875 ///
1876 /// let span = d1.until((Unit::Year, d2))?;
1877 /// assert_eq!(span, 1.year().months(3).fieldwise());
1878 ///
1879 /// let duration = d1.duration_until(d2);
1880 /// assert_eq!(duration, SignedDuration::from_hours(456 * 24));
1881 /// // There's no way to extract years or months from the signed
1882 /// // duration like one might extract hours (because every hour
1883 /// // is the same length). Instead, you actually have to convert
1884 /// // it to a span and then balance it by providing a relative date!
1885 /// let options = SpanRound::new().largest(Unit::Year).relative(d1);
1886 /// let span = Span::try_from(duration)?.round(options)?;
1887 /// assert_eq!(span, 1.year().months(3).fieldwise());
1888 ///
1889 /// # Ok::<(), Box<dyn std::error::Error>>(())
1890 /// ```
1891 ///
1892 /// # Example: getting an unsigned duration
1893 ///
1894 /// If you're looking to find the duration between two dates as a
1895 /// [`std::time::Duration`], you'll need to use this method to get a
1896 /// [`SignedDuration`] and then convert it to a `std::time::Duration`:
1897 ///
1898 /// ```
1899 /// use std::time::Duration;
1900 ///
1901 /// use jiff::{civil::date, SignedDuration};
1902 ///
1903 /// let d1 = date(2024, 7, 1);
1904 /// let d2 = date(2024, 8, 1);
1905 /// let duration = Duration::try_from(d1.duration_until(d2))?;
1906 /// assert_eq!(duration, Duration::from_secs(31 * 24 * 60 * 60));
1907 ///
1908 /// // Note that unsigned durations cannot represent all
1909 /// // possible differences! If the duration would be negative,
1910 /// // then the conversion fails:
1911 /// assert!(Duration::try_from(d2.duration_until(d1)).is_err());
1912 ///
1913 /// # Ok::<(), Box<dyn std::error::Error>>(())
1914 /// ```
1915 #[inline]
1916 pub fn duration_until(self, other: Date) -> SignedDuration {
1917 SignedDuration::date_until(self, other)
1918 }
1919
1920 /// This routine is identical to [`Date::duration_until`], but the order of
1921 /// the parameters is flipped.
1922 ///
1923 /// # Example
1924 ///
1925 /// ```
1926 /// use jiff::{civil::date, SignedDuration};
1927 ///
1928 /// let earlier = date(2006, 8, 24);
1929 /// let later = date(2019, 1, 31);
1930 /// assert_eq!(
1931 /// later.duration_since(earlier),
1932 /// SignedDuration::from_hours(4543 * 24),
1933 /// );
1934 /// ```
1935 #[inline]
1936 pub fn duration_since(self, other: Date) -> SignedDuration {
1937 SignedDuration::date_until(other, self)
1938 }
1939
1940 /// Return an iterator of periodic dates determined by the given span.
1941 ///
1942 /// The given span may be negative, in which case, the iterator will move
1943 /// backwards through time. The iterator won't stop until either the span
1944 /// itself overflows, or it would otherwise exceed the minimum or maximum
1945 /// `Date` value.
1946 ///
1947 /// # Example: Halloween day of the week
1948 ///
1949 /// As a kid, I always hoped for Halloween to fall on a weekend. With this
1950 /// program, we can print the day of the week for all Halloweens in the
1951 /// 2020s.
1952 ///
1953 /// ```
1954 /// use jiff::{civil::{Weekday, date}, ToSpan};
1955 ///
1956 /// let start = date(2020, 10, 31);
1957 /// let mut halloween_days_of_week = vec![];
1958 /// for halloween in start.series(1.years()).take(10) {
1959 /// halloween_days_of_week.push(
1960 /// (halloween.year(), halloween.weekday()),
1961 /// );
1962 /// }
1963 /// assert_eq!(halloween_days_of_week, vec![
1964 /// (2020, Weekday::Saturday),
1965 /// (2021, Weekday::Sunday),
1966 /// (2022, Weekday::Monday),
1967 /// (2023, Weekday::Tuesday),
1968 /// (2024, Weekday::Thursday),
1969 /// (2025, Weekday::Friday),
1970 /// (2026, Weekday::Saturday),
1971 /// (2027, Weekday::Sunday),
1972 /// (2028, Weekday::Tuesday),
1973 /// (2029, Weekday::Wednesday),
1974 /// ]);
1975 /// ```
1976 ///
1977 /// # Example: how many times do I mow the lawn in a year?
1978 ///
1979 /// I mow the lawn about every week and a half from the beginning of May
1980 /// to the end of October. About how many times will I mow the lawn in
1981 /// 2024?
1982 ///
1983 /// ```
1984 /// use jiff::{ToSpan, civil::date};
1985 ///
1986 /// let start = date(2024, 5, 1);
1987 /// let end = date(2024, 10, 31);
1988 /// let mows = start
1989 /// .series(1.weeks().days(3).hours(12))
1990 /// .take_while(|&d| d <= end)
1991 /// .count();
1992 /// assert_eq!(mows, 18);
1993 /// ```
1994 ///
1995 /// # Example: a period less than a day
1996 ///
1997 /// Using a period less than a day works, but since this type exists at the
1998 /// granularity of a day, some dates may be repeated.
1999 ///
2000 /// ```
2001 /// use jiff::{civil::{Date, date}, ToSpan};
2002 ///
2003 /// let start = date(2024, 3, 11);
2004 /// let every_five_hours: Vec<Date> =
2005 /// start.series(15.hours()).take(7).collect();
2006 /// assert_eq!(every_five_hours, vec![
2007 /// date(2024, 3, 11),
2008 /// date(2024, 3, 11),
2009 /// date(2024, 3, 12),
2010 /// date(2024, 3, 12),
2011 /// date(2024, 3, 13),
2012 /// date(2024, 3, 14),
2013 /// date(2024, 3, 14),
2014 /// ]);
2015 /// ```
2016 ///
2017 /// # Example: finding the most recent Friday the 13th
2018 ///
2019 /// When did the most recent Friday the 13th occur?
2020 ///
2021 /// ```
2022 /// use jiff::{civil::{Weekday, date}, ToSpan};
2023 ///
2024 /// let start = date(2024, 3, 13);
2025 /// let mut found = None;
2026 /// for date in start.series(-1.months()) {
2027 /// if date.weekday() == Weekday::Friday {
2028 /// found = Some(date);
2029 /// break;
2030 /// }
2031 /// }
2032 /// assert_eq!(found, Some(date(2023, 10, 13)));
2033 /// ```
2034 #[inline]
2035 pub fn series(self, period: Span) -> DateSeries {
2036 DateSeries { start: self, period, step: 0 }
2037 }
2038}
2039
2040/// Parsing and formatting using a "printf"-style API.
2041impl Date {
2042 /// Parses a civil date in `input` matching the given `format`.
2043 ///
2044 /// The format string uses a "printf"-style API where conversion
2045 /// specifiers can be used as place holders to match components of
2046 /// a datetime. For details on the specifiers supported, see the
2047 /// [`fmt::strtime`] module documentation.
2048 ///
2049 /// # Errors
2050 ///
2051 /// This returns an error when parsing failed. This might happen because
2052 /// the format string itself was invalid, or because the input didn't match
2053 /// the format string.
2054 ///
2055 /// This also returns an error if there wasn't sufficient information to
2056 /// construct a civil date. For example, if an offset wasn't parsed.
2057 ///
2058 /// # Example
2059 ///
2060 /// This example shows how to parse a civil date:
2061 ///
2062 /// ```
2063 /// use jiff::civil::Date;
2064 ///
2065 /// // Parse an American date with a two-digit year.
2066 /// let date = Date::strptime("%m/%d/%y", "7/14/24")?;
2067 /// assert_eq!(date.to_string(), "2024-07-14");
2068 ///
2069 /// # Ok::<(), Box<dyn std::error::Error>>(())
2070 /// ```
2071 #[inline]
2072 pub fn strptime(
2073 format: impl AsRef<[u8]>,
2074 input: impl AsRef<[u8]>,
2075 ) -> Result<Date, Error> {
2076 fmt::strtime::parse(format, input).and_then(|tm| tm.to_date())
2077 }
2078
2079 /// Formats this civil date according to the given `format`.
2080 ///
2081 /// The format string uses a "printf"-style API where conversion
2082 /// specifiers can be used as place holders to format components of
2083 /// a datetime. For details on the specifiers supported, see the
2084 /// [`fmt::strtime`] module documentation.
2085 ///
2086 /// # Errors and panics
2087 ///
2088 /// While this routine itself does not error or panic, using the value
2089 /// returned may result in a panic if formatting fails. See the
2090 /// documentation on [`fmt::strtime::Display`] for more information.
2091 ///
2092 /// To format in a way that surfaces errors without panicking, use either
2093 /// [`fmt::strtime::format`] or [`fmt::strtime::BrokenDownTime::format`].
2094 ///
2095 /// # Example
2096 ///
2097 /// This example shows how to format a civil date:
2098 ///
2099 /// ```
2100 /// use jiff::civil::date;
2101 ///
2102 /// let date = date(2024, 7, 15);
2103 /// let string = date.strftime("%Y-%m-%d is a %A").to_string();
2104 /// assert_eq!(string, "2024-07-15 is a Monday");
2105 /// ```
2106 #[inline]
2107 pub fn strftime<'f, F: 'f + ?Sized + AsRef<[u8]>>(
2108 &self,
2109 format: &'f F,
2110 ) -> fmt::strtime::Display<'f> {
2111 fmt::strtime::Display { fmt: format.as_ref(), tm: (*self).into() }
2112 }
2113}
2114
2115// Constants used for converting between Gregorian calendar dates and Unix
2116// epoch days.
2117//
2118// See: http://howardhinnant.github.io/date_algorithms.html
2119const DAYS_IN_ERA: Constant = Constant(146_097);
2120const DAYS_FROM_0000_01_01_TO_1970_01_01: Constant = Constant(719_468);
2121
2122/// Internal APIs using ranged integers.
2123impl Date {
2124 #[inline]
2125 pub(crate) fn new_ranged(
2126 year: impl RInto<Year>,
2127 month: impl RInto<Month>,
2128 day: impl RInto<Day>,
2129 ) -> Result<Date, Error> {
2130 let (year, month, day) = (year.rinto(), month.rinto(), day.rinto());
2131 let max_day = days_in_month(year, month);
2132 if day > max_day {
2133 return Err(day.to_error_with_bounds("day", 1, max_day));
2134 }
2135 Ok(Date::new_ranged_unchecked(year, month, day))
2136 }
2137
2138 #[inline]
2139 pub(crate) fn new_ranged_unchecked(
2140 year: Year,
2141 month: Month,
2142 day: Day,
2143 ) -> Date {
2144 Date { year, month, day }
2145 }
2146
2147 #[inline]
2148 fn constrain_ranged(
2149 year: impl RInto<Year>,
2150 month: impl RInto<Month>,
2151 day: impl RInto<Day>,
2152 ) -> Date {
2153 let (year, month, mut day) =
2154 (year.rinto(), month.rinto(), day.rinto());
2155 day = saturate_day_in_month(year, month, day);
2156 Date { year, month, day }
2157 }
2158
2159 #[inline]
2160 pub(crate) fn year_ranged(self) -> Year {
2161 self.year
2162 }
2163
2164 #[inline]
2165 pub(crate) fn month_ranged(self) -> Month {
2166 self.month
2167 }
2168
2169 #[inline]
2170 pub(crate) fn day_ranged(self) -> Day {
2171 self.day
2172 }
2173
2174 #[inline]
2175 pub(crate) fn days_in_month_ranged(self) -> Day {
2176 days_in_month(self.year_ranged(), self.month_ranged())
2177 }
2178
2179 #[inline]
2180 pub(crate) fn since_days_ranged(self, other: Date) -> t::SpanDays {
2181 -self.until_days_ranged(other)
2182 }
2183
2184 #[inline]
2185 pub(crate) fn until_days_ranged(self, other: Date) -> t::SpanDays {
2186 if self == other {
2187 return C(0).rinto();
2188 }
2189 let start = self.to_unix_epoch_day();
2190 let end = other.to_unix_epoch_day();
2191 (end - start).rinto()
2192 }
2193
2194 #[inline(always)]
2195 pub(crate) fn to_unix_epoch_day(self) -> UnixEpochDay {
2196 #[cfg(not(debug_assertions))]
2197 {
2198 UnixEpochDay {
2199 val: common::to_unix_epoch_day(
2200 self.year.val,
2201 self.month.val,
2202 self.day.val,
2203 ),
2204 }
2205 }
2206 #[cfg(debug_assertions)]
2207 {
2208 let val = common::to_unix_epoch_day(
2209 self.year.val,
2210 self.month.val,
2211 self.day.val,
2212 );
2213 let min = common::to_unix_epoch_day(
2214 self.year.min,
2215 self.month.min,
2216 self.day.min,
2217 );
2218 let max = common::to_unix_epoch_day(
2219 self.year.max,
2220 self.month.max,
2221 self.day.max,
2222 );
2223 UnixEpochDay { val, min, max }
2224 }
2225 }
2226
2227 #[inline(always)]
2228 pub(crate) fn from_unix_epoch_day(unix_epoch_day: UnixEpochDay) -> Date {
2229 #[cfg(not(debug_assertions))]
2230 {
2231 let (year, month, day) =
2232 common::from_unix_epoch_day(unix_epoch_day.val);
2233 Date {
2234 year: Year { val: year },
2235 month: Month { val: month },
2236 day: Day { val: day },
2237 }
2238 }
2239 #[cfg(debug_assertions)]
2240 {
2241 let (year, month, day) =
2242 common::from_unix_epoch_day(unix_epoch_day.val);
2243 let (min_year, min_month, min_day) =
2244 common::from_unix_epoch_day(unix_epoch_day.min);
2245 let (max_year, max_month, max_day) =
2246 common::from_unix_epoch_day(unix_epoch_day.max);
2247
2248 let year = Year { val: year, min: min_year, max: max_year };
2249 let month = Month { val: month, min: min_month, max: max_month };
2250 let day = Day { val: day, min: min_day, max: max_day };
2251
2252 Date { year, month, day }
2253 }
2254 }
2255}
2256
2257impl Eq for Date {}
2258
2259impl PartialEq for Date {
2260 #[inline]
2261 fn eq(&self, other: &Date) -> bool {
2262 // We roll our own PartialEq impl so that we call 'get' on the
2263 // underlying ranged integer. This forces bugs in boundary conditions
2264 // to result in panics when 'debug_assertions' is enabled.
2265 self.day.get() == other.day.get()
2266 && self.month.get() == other.month.get()
2267 && self.year.get() == other.year.get()
2268 }
2269}
2270
2271impl Ord for Date {
2272 #[inline]
2273 fn cmp(&self, other: &Date) -> core::cmp::Ordering {
2274 (self.year.get(), self.month.get(), self.day.get()).cmp(&(
2275 other.year.get(),
2276 other.month.get(),
2277 other.day.get(),
2278 ))
2279 }
2280}
2281
2282impl PartialOrd for Date {
2283 #[inline]
2284 fn partial_cmp(&self, other: &Date) -> Option<core::cmp::Ordering> {
2285 Some(self.cmp(other))
2286 }
2287}
2288
2289impl Default for Date {
2290 fn default() -> Date {
2291 Date::ZERO
2292 }
2293}
2294
2295impl core::fmt::Debug for Date {
2296 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
2297 core::fmt::Display::fmt(self, f)
2298 }
2299}
2300
2301impl core::fmt::Display for Date {
2302 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
2303 use crate::fmt::StdFmtWrite;
2304
2305 DEFAULT_DATETIME_PRINTER
2306 .print_date(self, StdFmtWrite(f))
2307 .map_err(|_| core::fmt::Error)
2308 }
2309}
2310
2311impl core::str::FromStr for Date {
2312 type Err = Error;
2313
2314 fn from_str(string: &str) -> Result<Date, Error> {
2315 DEFAULT_DATETIME_PARSER.parse_date(string)
2316 }
2317}
2318
2319impl From<ISOWeekDate> for Date {
2320 #[inline]
2321 fn from(weekdate: ISOWeekDate) -> Date {
2322 Date::from_iso_week_date(weekdate)
2323 }
2324}
2325
2326impl From<DateTime> for Date {
2327 #[inline]
2328 fn from(dt: DateTime) -> Date {
2329 dt.date()
2330 }
2331}
2332
2333impl From<Zoned> for Date {
2334 #[inline]
2335 fn from(zdt: Zoned) -> Date {
2336 zdt.datetime().date()
2337 }
2338}
2339
2340impl<'a> From<&'a Zoned> for Date {
2341 #[inline]
2342 fn from(zdt: &'a Zoned) -> Date {
2343 zdt.datetime().date()
2344 }
2345}
2346
2347/// Adds a span of time to a date.
2348///
2349/// This uses checked arithmetic and panics on overflow. To handle overflow
2350/// without panics, use [`Date::checked_add`].
2351impl core::ops::Add<Span> for Date {
2352 type Output = Date;
2353
2354 #[inline]
2355 fn add(self, rhs: Span) -> Date {
2356 self.checked_add(rhs).expect("adding span to date overflowed")
2357 }
2358}
2359
2360/// Adds a span of time to a date in place.
2361///
2362/// This uses checked arithmetic and panics on overflow. To handle overflow
2363/// without panics, use [`Date::checked_add`].
2364impl core::ops::AddAssign<Span> for Date {
2365 #[inline]
2366 fn add_assign(&mut self, rhs: Span) {
2367 *self = *self + rhs;
2368 }
2369}
2370
2371/// Subtracts a span of time from a date.
2372///
2373/// This uses checked arithmetic and panics on overflow. To handle overflow
2374/// without panics, use [`Date::checked_sub`].
2375impl core::ops::Sub<Span> for Date {
2376 type Output = Date;
2377
2378 #[inline]
2379 fn sub(self, rhs: Span) -> Date {
2380 self.checked_sub(rhs).expect("subing span to date overflowed")
2381 }
2382}
2383
2384/// Subtracts a span of time from a date in place.
2385///
2386/// This uses checked arithmetic and panics on overflow. To handle overflow
2387/// without panics, use [`Date::checked_sub`].
2388impl core::ops::SubAssign<Span> for Date {
2389 #[inline]
2390 fn sub_assign(&mut self, rhs: Span) {
2391 *self = *self - rhs;
2392 }
2393}
2394
2395/// Computes the span of time between two dates.
2396///
2397/// This will return a negative span when the date being subtracted is greater.
2398///
2399/// Since this uses the default configuration for calculating a span between
2400/// two date (no rounding and largest units is days), this will never panic or
2401/// fail in any way.
2402///
2403/// To configure the largest unit or enable rounding, use [`Date::since`].
2404impl core::ops::Sub for Date {
2405 type Output = Span;
2406
2407 #[inline]
2408 fn sub(self, rhs: Date) -> Span {
2409 self.since(rhs).expect("since never fails when given Date")
2410 }
2411}
2412
2413/// Adds a signed duration of time to a date.
2414///
2415/// This uses checked arithmetic and panics on overflow. To handle overflow
2416/// without panics, use [`Date::checked_add`].
2417impl core::ops::Add<SignedDuration> for Date {
2418 type Output = Date;
2419
2420 #[inline]
2421 fn add(self, rhs: SignedDuration) -> Date {
2422 self.checked_add(rhs)
2423 .expect("adding signed duration to date overflowed")
2424 }
2425}
2426
2427/// Adds a signed duration of time to a date in place.
2428///
2429/// This uses checked arithmetic and panics on overflow. To handle overflow
2430/// without panics, use [`Date::checked_add`].
2431impl core::ops::AddAssign<SignedDuration> for Date {
2432 #[inline]
2433 fn add_assign(&mut self, rhs: SignedDuration) {
2434 *self = *self + rhs;
2435 }
2436}
2437
2438/// Subtracts a signed duration of time from a date.
2439///
2440/// This uses checked arithmetic and panics on overflow. To handle overflow
2441/// without panics, use [`Date::checked_sub`].
2442impl core::ops::Sub<SignedDuration> for Date {
2443 type Output = Date;
2444
2445 #[inline]
2446 fn sub(self, rhs: SignedDuration) -> Date {
2447 self.checked_sub(rhs)
2448 .expect("subing signed duration to date overflowed")
2449 }
2450}
2451
2452/// Subtracts a signed duration of time from a date in place.
2453///
2454/// This uses checked arithmetic and panics on overflow. To handle overflow
2455/// without panics, use [`Date::checked_sub`].
2456impl core::ops::SubAssign<SignedDuration> for Date {
2457 #[inline]
2458 fn sub_assign(&mut self, rhs: SignedDuration) {
2459 *self = *self - rhs;
2460 }
2461}
2462
2463/// Adds an unsigned duration of time to a date.
2464///
2465/// This uses checked arithmetic and panics on overflow. To handle overflow
2466/// without panics, use [`Date::checked_add`].
2467impl core::ops::Add<UnsignedDuration> for Date {
2468 type Output = Date;
2469
2470 #[inline]
2471 fn add(self, rhs: UnsignedDuration) -> Date {
2472 self.checked_add(rhs)
2473 .expect("adding unsigned duration to date overflowed")
2474 }
2475}
2476
2477/// Adds an unsigned duration of time to a date in place.
2478///
2479/// This uses checked arithmetic and panics on overflow. To handle overflow
2480/// without panics, use [`Date::checked_add`].
2481impl core::ops::AddAssign<UnsignedDuration> for Date {
2482 #[inline]
2483 fn add_assign(&mut self, rhs: UnsignedDuration) {
2484 *self = *self + rhs;
2485 }
2486}
2487
2488/// Subtracts an unsigned duration of time from a date.
2489///
2490/// This uses checked arithmetic and panics on overflow. To handle overflow
2491/// without panics, use [`Date::checked_sub`].
2492impl core::ops::Sub<UnsignedDuration> for Date {
2493 type Output = Date;
2494
2495 #[inline]
2496 fn sub(self, rhs: UnsignedDuration) -> Date {
2497 self.checked_sub(rhs)
2498 .expect("subing unsigned duration to date overflowed")
2499 }
2500}
2501
2502/// Subtracts an unsigned duration of time from a date in place.
2503///
2504/// This uses checked arithmetic and panics on overflow. To handle overflow
2505/// without panics, use [`Date::checked_sub`].
2506impl core::ops::SubAssign<UnsignedDuration> for Date {
2507 #[inline]
2508 fn sub_assign(&mut self, rhs: UnsignedDuration) {
2509 *self = *self - rhs;
2510 }
2511}
2512
2513#[cfg(feature = "serde")]
2514impl serde::Serialize for Date {
2515 #[inline]
2516 fn serialize<S: serde::Serializer>(
2517 &self,
2518 serializer: S,
2519 ) -> Result<S::Ok, S::Error> {
2520 serializer.collect_str(self)
2521 }
2522}
2523
2524#[cfg(feature = "serde")]
2525impl<'de> serde::Deserialize<'de> for Date {
2526 #[inline]
2527 fn deserialize<D: serde::Deserializer<'de>>(
2528 deserializer: D,
2529 ) -> Result<Date, D::Error> {
2530 use serde::de;
2531
2532 struct DateVisitor;
2533
2534 impl<'de> de::Visitor<'de> for DateVisitor {
2535 type Value = Date;
2536
2537 fn expecting(
2538 &self,
2539 f: &mut core::fmt::Formatter,
2540 ) -> core::fmt::Result {
2541 f.write_str("a date string")
2542 }
2543
2544 #[inline]
2545 fn visit_bytes<E: de::Error>(
2546 self,
2547 value: &[u8],
2548 ) -> Result<Date, E> {
2549 DEFAULT_DATETIME_PARSER
2550 .parse_date(value)
2551 .map_err(de::Error::custom)
2552 }
2553
2554 #[inline]
2555 fn visit_str<E: de::Error>(self, value: &str) -> Result<Date, E> {
2556 self.visit_bytes(value.as_bytes())
2557 }
2558 }
2559
2560 deserializer.deserialize_str(DateVisitor)
2561 }
2562}
2563
2564#[cfg(test)]
2565impl quickcheck::Arbitrary for Date {
2566 fn arbitrary(g: &mut quickcheck::Gen) -> Date {
2567 let year = Year::arbitrary(g);
2568 let month = Month::arbitrary(g);
2569 let day = Day::arbitrary(g);
2570 Date::constrain_ranged(year, month, day)
2571 }
2572
2573 fn shrink(&self) -> alloc::boxed::Box<dyn Iterator<Item = Date>> {
2574 alloc::boxed::Box::new(
2575 (self.year_ranged(), self.month_ranged(), self.day_ranged())
2576 .shrink()
2577 .map(|(year, month, day)| {
2578 Date::constrain_ranged(year, month, day)
2579 }),
2580 )
2581 }
2582}
2583
2584/// An iterator over periodic dates, created by [`Date::series`].
2585///
2586/// It is exhausted when the next value would exceed a [`Span`] or [`Date`]
2587/// value.
2588#[derive(Clone, Debug)]
2589pub struct DateSeries {
2590 start: Date,
2591 period: Span,
2592 step: i64,
2593}
2594
2595impl Iterator for DateSeries {
2596 type Item = Date;
2597
2598 #[inline]
2599 fn next(&mut self) -> Option<Date> {
2600 let span = self.period.checked_mul(self.step).ok()?;
2601 self.step = self.step.checked_add(1)?;
2602 let date = self.start.checked_add(span).ok()?;
2603 Some(date)
2604 }
2605}
2606
2607/// Options for [`Date::checked_add`] and [`Date::checked_sub`].
2608///
2609/// This type provides a way to ergonomically add one of a few different
2610/// duration types to a [`Date`].
2611///
2612/// The main way to construct values of this type is with its `From` trait
2613/// implementations:
2614///
2615/// * `From<Span> for DateArithmetic` adds (or subtracts) the given span to the
2616/// receiver date.
2617/// * `From<SignedDuration> for DateArithmetic` adds (or subtracts)
2618/// the given signed duration to the receiver date.
2619/// * `From<std::time::Duration> for DateArithmetic` adds (or subtracts)
2620/// the given unsigned duration to the receiver date.
2621///
2622/// # Example
2623///
2624/// ```
2625/// use std::time::Duration;
2626///
2627/// use jiff::{civil::date, SignedDuration, ToSpan};
2628///
2629/// let d = date(2024, 2, 29);
2630/// assert_eq!(d.checked_add(1.year())?, date(2025, 2, 28));
2631/// assert_eq!(d.checked_add(SignedDuration::from_hours(24))?, date(2024, 3, 1));
2632/// assert_eq!(d.checked_add(Duration::from_secs(24 * 60 * 60))?, date(2024, 3, 1));
2633///
2634/// # Ok::<(), Box<dyn std::error::Error>>(())
2635/// ```
2636#[derive(Clone, Copy, Debug)]
2637pub struct DateArithmetic {
2638 duration: Duration,
2639}
2640
2641impl DateArithmetic {
2642 #[inline]
2643 fn checked_add(self, date: Date) -> Result<Date, Error> {
2644 match self.duration.to_signed()? {
2645 SDuration::Span(span) => date.checked_add_span(span),
2646 SDuration::Absolute(sdur) => date.checked_add_duration(sdur),
2647 }
2648 }
2649
2650 #[inline]
2651 fn checked_neg(self) -> Result<DateArithmetic, Error> {
2652 let duration = self.duration.checked_neg()?;
2653 Ok(DateArithmetic { duration })
2654 }
2655
2656 #[inline]
2657 fn is_negative(&self) -> bool {
2658 self.duration.is_negative()
2659 }
2660}
2661
2662impl From<Span> for DateArithmetic {
2663 fn from(span: Span) -> DateArithmetic {
2664 let duration = Duration::from(span);
2665 DateArithmetic { duration }
2666 }
2667}
2668
2669impl From<SignedDuration> for DateArithmetic {
2670 fn from(sdur: SignedDuration) -> DateArithmetic {
2671 let duration = Duration::from(sdur);
2672 DateArithmetic { duration }
2673 }
2674}
2675
2676impl From<UnsignedDuration> for DateArithmetic {
2677 fn from(udur: UnsignedDuration) -> DateArithmetic {
2678 let duration = Duration::from(udur);
2679 DateArithmetic { duration }
2680 }
2681}
2682
2683impl<'a> From<&'a Span> for DateArithmetic {
2684 fn from(span: &'a Span) -> DateArithmetic {
2685 DateArithmetic::from(*span)
2686 }
2687}
2688
2689impl<'a> From<&'a SignedDuration> for DateArithmetic {
2690 fn from(sdur: &'a SignedDuration) -> DateArithmetic {
2691 DateArithmetic::from(*sdur)
2692 }
2693}
2694
2695impl<'a> From<&'a UnsignedDuration> for DateArithmetic {
2696 fn from(udur: &'a UnsignedDuration) -> DateArithmetic {
2697 DateArithmetic::from(*udur)
2698 }
2699}
2700
2701/// Options for [`Date::since`] and [`Date::until`].
2702///
2703/// This type provides a way to configure the calculation of spans between two
2704/// [`Date`] values. In particular, both `Date::since` and `Date::until` accept
2705/// anything that implements `Into<DateDifference>`. There are a few key trait
2706/// implementations that make this convenient:
2707///
2708/// * `From<Date> for DateDifference` will construct a configuration consisting
2709/// of just the date. So for example, `date1.until(date2)` will return the span
2710/// from `date1` to `date2`.
2711/// * `From<DateTime> for DateDifference` will construct a configuration
2712/// consisting of just the date from the given datetime. So for example,
2713/// `date.since(datetime)` returns the span from `datetime.date()` to `date`.
2714/// * `From<(Unit, Date)>` is a convenient way to specify the largest units
2715/// that should be present on the span returned. By default, the largest units
2716/// are days. Using this trait implementation is equivalent to
2717/// `DateDifference::new(date).largest(unit)`.
2718/// * `From<(Unit, DateTime)>` is like the one above, but with the date from
2719/// the given datetime.
2720///
2721/// One can also provide a `DateDifference` value directly. Doing so is
2722/// necessary to use the rounding features of calculating a span. For example,
2723/// setting the smallest unit (defaults to [`Unit::Day`]), the rounding mode
2724/// (defaults to [`RoundMode::Trunc`]) and the rounding increment (defaults to
2725/// `1`). The defaults are selected such that no rounding occurs.
2726///
2727/// Rounding a span as part of calculating it is provided as a convenience.
2728/// Callers may choose to round the span as a distinct step via
2729/// [`Span::round`], but callers may need to provide a reference date
2730/// for rounding larger units. By coupling rounding with routines like
2731/// [`Date::since`], the reference date can be set automatically based on
2732/// the input to `Date::since`.
2733///
2734/// # Example
2735///
2736/// This example shows how to round a span between two date to the nearest
2737/// year, with ties breaking away from zero.
2738///
2739/// ```
2740/// use jiff::{civil::{Date, DateDifference}, RoundMode, ToSpan, Unit};
2741///
2742/// let d1 = "2024-03-15".parse::<Date>()?;
2743/// let d2 = "2030-09-13".parse::<Date>()?;
2744/// let span = d1.until(
2745/// DateDifference::new(d2)
2746/// .smallest(Unit::Year)
2747/// .mode(RoundMode::HalfExpand),
2748/// )?;
2749/// assert_eq!(span, 6.years().fieldwise());
2750///
2751/// // If the span were one day longer, it would round up to 7 years.
2752/// let d2 = "2030-09-14".parse::<Date>()?;
2753/// let span = d1.until(
2754/// DateDifference::new(d2)
2755/// .smallest(Unit::Year)
2756/// .mode(RoundMode::HalfExpand),
2757/// )?;
2758/// assert_eq!(span, 7.years().fieldwise());
2759///
2760/// # Ok::<(), Box<dyn std::error::Error>>(())
2761/// ```
2762#[derive(Clone, Copy, Debug)]
2763pub struct DateDifference {
2764 date: Date,
2765 round: SpanRound<'static>,
2766}
2767
2768impl DateDifference {
2769 /// Create a new default configuration for computing the span between
2770 /// the given date and some other date (specified as the receiver in
2771 /// [`Date::since`] or [`Date::until`]).
2772 #[inline]
2773 pub fn new(date: Date) -> DateDifference {
2774 // We use truncation rounding by default since it seems that's
2775 // what is generally expected when computing the difference between
2776 // datetimes.
2777 //
2778 // See: https://github.com/tc39/proposal-temporal/issues/1122
2779 let round = SpanRound::new().mode(RoundMode::Trunc);
2780 DateDifference { date, round }
2781 }
2782
2783 /// Set the smallest units allowed in the span returned.
2784 ///
2785 /// When a largest unit is not specified, then the largest unit is
2786 /// automatically set to be equal to the smallest unit.
2787 ///
2788 /// # Errors
2789 ///
2790 /// The smallest units must be no greater than the largest units. If this
2791 /// is violated, then computing a span with this configuration will result
2792 /// in an error.
2793 ///
2794 /// # Example
2795 ///
2796 /// This shows how to round a span between two date to the nearest
2797 /// number of weeks.
2798 ///
2799 /// ```
2800 /// use jiff::{civil::{Date, DateDifference}, RoundMode, ToSpan, Unit};
2801 ///
2802 /// let d1 = "2024-03-15".parse::<Date>()?;
2803 /// let d2 = "2030-11-22".parse::<Date>()?;
2804 /// let span = d1.until(
2805 /// DateDifference::new(d2)
2806 /// .smallest(Unit::Week)
2807 /// .largest(Unit::Week)
2808 /// .mode(RoundMode::HalfExpand),
2809 /// )?;
2810 /// assert_eq!(span, 349.weeks().fieldwise());
2811 ///
2812 /// # Ok::<(), Box<dyn std::error::Error>>(())
2813 /// ```
2814 #[inline]
2815 pub fn smallest(self, unit: Unit) -> DateDifference {
2816 DateDifference { round: self.round.smallest(unit), ..self }
2817 }
2818
2819 /// Set the largest units allowed in the span returned.
2820 ///
2821 /// When a largest unit is not specified, then the largest unit is
2822 /// automatically set to be equal to the smallest unit. Otherwise, when the
2823 /// largest unit is not specified, it is set to days.
2824 ///
2825 /// Once a largest unit is set, there is no way to change this rounding
2826 /// configuration back to using the "automatic" default. Instead, callers
2827 /// must create a new configuration.
2828 ///
2829 /// # Errors
2830 ///
2831 /// The largest units, when set, must be at least as big as the smallest
2832 /// units (which defaults to [`Unit::Day`]). If this is violated, then
2833 /// computing a span with this configuration will result in an error.
2834 ///
2835 /// # Example
2836 ///
2837 /// This shows how to round a span between two date to units no
2838 /// bigger than months.
2839 ///
2840 /// ```
2841 /// use jiff::{civil::{Date, DateDifference}, ToSpan, Unit};
2842 ///
2843 /// let d1 = "2024-03-15".parse::<Date>()?;
2844 /// let d2 = "2030-11-22".parse::<Date>()?;
2845 /// let span = d1.until(
2846 /// DateDifference::new(d2).largest(Unit::Month),
2847 /// )?;
2848 /// assert_eq!(span, 80.months().days(7).fieldwise());
2849 ///
2850 /// # Ok::<(), Box<dyn std::error::Error>>(())
2851 /// ```
2852 #[inline]
2853 pub fn largest(self, unit: Unit) -> DateDifference {
2854 DateDifference { round: self.round.largest(unit), ..self }
2855 }
2856
2857 /// Set the rounding mode.
2858 ///
2859 /// This defaults to [`RoundMode::Trunc`] since it's plausible that
2860 /// rounding "up" in the context of computing the span between two date
2861 /// could be surprising in a number of cases. The [`RoundMode::HalfExpand`]
2862 /// mode corresponds to typical rounding you might have learned about in
2863 /// school. But a variety of other rounding modes exist.
2864 ///
2865 /// # Example
2866 ///
2867 /// This shows how to always round "up" towards positive infinity.
2868 ///
2869 /// ```
2870 /// use jiff::{civil::{Date, DateDifference}, RoundMode, ToSpan, Unit};
2871 ///
2872 /// let d1 = "2024-01-15".parse::<Date>()?;
2873 /// let d2 = "2024-08-16".parse::<Date>()?;
2874 /// let span = d1.until(
2875 /// DateDifference::new(d2)
2876 /// .smallest(Unit::Month)
2877 /// .mode(RoundMode::Ceil),
2878 /// )?;
2879 /// // Only 7 months and 1 day elapsed, but we asked to always round up!
2880 /// assert_eq!(span, 8.months().fieldwise());
2881 ///
2882 /// // Since `Ceil` always rounds toward positive infinity, the behavior
2883 /// // flips for a negative span.
2884 /// let span = d1.since(
2885 /// DateDifference::new(d2)
2886 /// .smallest(Unit::Month)
2887 /// .mode(RoundMode::Ceil),
2888 /// )?;
2889 /// assert_eq!(span, -7.months().fieldwise());
2890 ///
2891 /// # Ok::<(), Box<dyn std::error::Error>>(())
2892 /// ```
2893 #[inline]
2894 pub fn mode(self, mode: RoundMode) -> DateDifference {
2895 DateDifference { round: self.round.mode(mode), ..self }
2896 }
2897
2898 /// Set the rounding increment for the smallest unit.
2899 ///
2900 /// The default value is `1`. Other values permit rounding the smallest
2901 /// unit to the nearest integer increment specified. For example, if the
2902 /// smallest unit is set to [`Unit::Month`], then a rounding increment of
2903 /// `2` would result in rounding in increments of every other month.
2904 ///
2905 /// # Example
2906 ///
2907 /// This shows how to round the span between two date to the nearest even
2908 /// month.
2909 ///
2910 /// ```
2911 /// use jiff::{civil::{Date, DateDifference}, RoundMode, ToSpan, Unit};
2912 ///
2913 /// let d1 = "2024-01-15".parse::<Date>()?;
2914 /// let d2 = "2024-08-15".parse::<Date>()?;
2915 /// let span = d1.until(
2916 /// DateDifference::new(d2)
2917 /// .smallest(Unit::Month)
2918 /// .increment(2)
2919 /// .mode(RoundMode::HalfExpand),
2920 /// )?;
2921 /// assert_eq!(span, 8.months().fieldwise());
2922 ///
2923 /// // If our second date was just one day less, rounding would truncate
2924 /// // down to 6 months!
2925 /// let d2 = "2024-08-14".parse::<Date>()?;
2926 /// let span = d1.until(
2927 /// DateDifference::new(d2)
2928 /// .smallest(Unit::Month)
2929 /// .increment(2)
2930 /// .mode(RoundMode::HalfExpand),
2931 /// )?;
2932 /// assert_eq!(span, 6.months().fieldwise());
2933 ///
2934 /// # Ok::<(), Box<dyn std::error::Error>>(())
2935 /// ```
2936 #[inline]
2937 pub fn increment(self, increment: i64) -> DateDifference {
2938 DateDifference { round: self.round.increment(increment), ..self }
2939 }
2940
2941 /// Returns true if and only if this configuration could change the span
2942 /// via rounding.
2943 #[inline]
2944 fn rounding_may_change_span(&self) -> bool {
2945 self.round.rounding_may_change_span_ignore_largest()
2946 }
2947
2948 /// Returns the span of time from `d1` to the date in this configuration.
2949 /// The biggest units allowed are determined by the `smallest` and
2950 /// `largest` settings, but defaults to `Unit::Day`.
2951 #[inline]
2952 fn until_with_largest_unit(&self, d1: Date) -> Result<Span, Error> {
2953 let d2 = self.date;
2954 let largest = self
2955 .round
2956 .get_largest()
2957 .unwrap_or_else(|| self.round.get_smallest().max(Unit::Day));
2958 if largest < Unit::Day {
2959 // This is the only error case when not rounding! Somewhat
2960 // unfortunate. I did consider making this a panic instead, because
2961 // we're so close to it being infallible (I think), but I decided
2962 // that would be too inconsistent with how we handle invalid units
2963 // in other places. (It's just that, in other places, invalid units
2964 // are one of a few different kinds of possible errors.)
2965 //
2966 // Another option would be to just assume `largest` is `Unit::Day`
2967 // when it's a smaller unit.
2968 //
2969 // Yet another option is to split `Unit` into `DateUnit` and
2970 // `TimeUnit`, but I found that to be quite awkward (it was the
2971 // design I started with).
2972 //
2973 // NOTE: I take the above back. It's actually possible for the
2974 // months component to overflow when largest=month.
2975 return Err(err!(
2976 "rounding the span between two dates must use days \
2977 or bigger for its units, but found {units}",
2978 units = largest.plural(),
2979 ));
2980 }
2981 if largest <= Unit::Week {
2982 let mut weeks = t::SpanWeeks::rfrom(C(0));
2983 let mut days = d1.until_days_ranged(d2);
2984 if largest == Unit::Week {
2985 weeks = days.div_ceil(C(7)).rinto();
2986 days = days.rem_ceil(C(7));
2987 }
2988 return Ok(Span::new().weeks_ranged(weeks).days_ranged(days));
2989 }
2990
2991 let year1 = d1.year_ranged();
2992 let month1 = d1.month_ranged();
2993 let day1 = d1.day_ranged();
2994 let mut year2 = d2.year_ranged();
2995 let mut month2 = d2.month_ranged();
2996 let day2 = d2.day_ranged();
2997
2998 let mut years =
2999 t::SpanYears::rfrom(year2) - t::SpanYears::rfrom(year1);
3000 let mut months =
3001 t::SpanMonths::rfrom(month2) - t::SpanMonths::rfrom(month1);
3002 let mut days = t::SpanDays::rfrom(day2) - t::SpanMonths::rfrom(day1);
3003 if years != 0 || months != 0 {
3004 let sign = if years != 0 {
3005 Sign::rfrom(years.signum())
3006 } else {
3007 Sign::rfrom(months.signum())
3008 };
3009 let mut days_in_month1 =
3010 t::SpanDays::rfrom(days_in_month(year2, month2));
3011 let mut day_correct = t::SpanDays::N::<0>();
3012 if days.signum() == -sign {
3013 let original_days_in_month1 = days_in_month1;
3014 let (y, m) = month_add_one(year2, month2, -sign).unwrap();
3015 year2 = y;
3016 month2 = m;
3017
3018 years =
3019 t::SpanYears::rfrom(year2) - t::SpanYears::rfrom(year1);
3020 months = t::SpanMonths::rfrom(month2)
3021 - t::SpanMonths::rfrom(month1);
3022 days_in_month1 = days_in_month(year2, month2).rinto();
3023 day_correct = if sign < 0 {
3024 -original_days_in_month1
3025 } else {
3026 days_in_month1
3027 };
3028 }
3029
3030 let day0_trunc = t::SpanDays::rfrom(day1.min(days_in_month1));
3031 days = t::SpanDays::rfrom(day2) - day0_trunc + day_correct;
3032
3033 if years != 0 {
3034 months = t::SpanMonths::rfrom(month2)
3035 - t::SpanMonths::rfrom(month1);
3036 if months.signum() == -sign {
3037 let month_correct = if sign < 0 {
3038 -t::MONTHS_PER_YEAR
3039 } else {
3040 t::MONTHS_PER_YEAR
3041 };
3042 year2 -= sign;
3043 years = t::SpanYears::rfrom(year2)
3044 - t::SpanYears::rfrom(year1);
3045
3046 months = t::SpanMonths::rfrom(month2)
3047 - t::SpanMonths::rfrom(month1)
3048 + month_correct;
3049 }
3050 }
3051 }
3052 if largest == Unit::Month && years != 0 {
3053 months = months.try_checked_add(
3054 "months",
3055 t::SpanMonths::rfrom(years) * t::MONTHS_PER_YEAR,
3056 )?;
3057 years = C(0).rinto();
3058 }
3059 Ok(Span::new()
3060 .years_ranged(years)
3061 .months_ranged(months)
3062 .days_ranged(days))
3063 }
3064}
3065
3066impl From<Date> for DateDifference {
3067 #[inline]
3068 fn from(date: Date) -> DateDifference {
3069 DateDifference::new(date)
3070 }
3071}
3072
3073impl From<DateTime> for DateDifference {
3074 #[inline]
3075 fn from(dt: DateTime) -> DateDifference {
3076 DateDifference::from(Date::from(dt))
3077 }
3078}
3079
3080impl From<Zoned> for DateDifference {
3081 #[inline]
3082 fn from(zdt: Zoned) -> DateDifference {
3083 DateDifference::from(Date::from(zdt))
3084 }
3085}
3086
3087impl<'a> From<&'a Zoned> for DateDifference {
3088 #[inline]
3089 fn from(zdt: &'a Zoned) -> DateDifference {
3090 DateDifference::from(zdt.datetime())
3091 }
3092}
3093
3094impl From<(Unit, Date)> for DateDifference {
3095 #[inline]
3096 fn from((largest, date): (Unit, Date)) -> DateDifference {
3097 DateDifference::from(date).largest(largest)
3098 }
3099}
3100
3101impl From<(Unit, DateTime)> for DateDifference {
3102 #[inline]
3103 fn from((largest, dt): (Unit, DateTime)) -> DateDifference {
3104 DateDifference::from((largest, Date::from(dt)))
3105 }
3106}
3107
3108impl From<(Unit, Zoned)> for DateDifference {
3109 #[inline]
3110 fn from((largest, zdt): (Unit, Zoned)) -> DateDifference {
3111 DateDifference::from((largest, Date::from(zdt)))
3112 }
3113}
3114
3115impl<'a> From<(Unit, &'a Zoned)> for DateDifference {
3116 #[inline]
3117 fn from((largest, zdt): (Unit, &'a Zoned)) -> DateDifference {
3118 DateDifference::from((largest, zdt.datetime()))
3119 }
3120}
3121
3122/// A builder for setting the fields on a [`Date`].
3123///
3124/// This builder is constructed via [`Date::with`].
3125///
3126/// # Example
3127///
3128/// The builder ensures one can chain together the individual components
3129/// of a date without it failing at an intermediate step. For example,
3130/// if you had a date of `2024-10-31` and wanted to change both the day
3131/// and the month, and each setting was validated independent of the other,
3132/// you would need to be careful to set the day first and then the month.
3133/// In some cases, you would need to set the month first and then the day!
3134///
3135/// But with the builder, you can set values in any order:
3136///
3137/// ```
3138/// use jiff::civil::date;
3139///
3140/// let d1 = date(2024, 10, 31);
3141/// let d2 = d1.with().month(11).day(30).build()?;
3142/// assert_eq!(d2, date(2024, 11, 30));
3143///
3144/// let d1 = date(2024, 4, 30);
3145/// let d2 = d1.with().day(31).month(7).build()?;
3146/// assert_eq!(d2, date(2024, 7, 31));
3147///
3148/// # Ok::<(), Box<dyn std::error::Error>>(())
3149/// ```
3150#[derive(Clone, Copy, Debug)]
3151pub struct DateWith {
3152 original: Date,
3153 year: Option<DateWithYear>,
3154 month: Option<i8>,
3155 day: Option<DateWithDay>,
3156}
3157
3158impl DateWith {
3159 #[inline]
3160 fn new(original: Date) -> DateWith {
3161 DateWith { original, year: None, month: None, day: None }
3162 }
3163
3164 /// Create a new `Date` from the fields set on this configuration.
3165 ///
3166 /// An error occurs when the fields combine to an invalid date.
3167 ///
3168 /// For any fields not set on this configuration, the values are taken from
3169 /// the [`Date`] that originally created this configuration. When no values
3170 /// are set, this routine is guaranteed to succeed and will always return
3171 /// the original date without modification.
3172 ///
3173 /// # Example
3174 ///
3175 /// This creates a date corresponding to the last day in the year:
3176 ///
3177 /// ```
3178 /// use jiff::civil::date;
3179 ///
3180 /// assert_eq!(
3181 /// date(2023, 1, 1).with().day_of_year_no_leap(365).build()?,
3182 /// date(2023, 12, 31),
3183 /// );
3184 /// // It also works with leap years for the same input:
3185 /// assert_eq!(
3186 /// date(2024, 1, 1).with().day_of_year_no_leap(365).build()?,
3187 /// date(2024, 12, 31),
3188 /// );
3189 ///
3190 /// # Ok::<(), Box<dyn std::error::Error>>(())
3191 /// ```
3192 ///
3193 /// # Example: error for invalid date
3194 ///
3195 /// If the fields combine to form an invalid date, then an error is
3196 /// returned:
3197 ///
3198 /// ```
3199 /// use jiff::civil::date;
3200 ///
3201 /// let d = date(2024, 11, 30);
3202 /// assert!(d.with().day(31).build().is_err());
3203 ///
3204 /// let d = date(2024, 2, 29);
3205 /// assert!(d.with().year(2023).build().is_err());
3206 /// ```
3207 #[inline]
3208 pub fn build(self) -> Result<Date, Error> {
3209 let year = match self.year {
3210 None => self.original.year_ranged(),
3211 Some(DateWithYear::Jiff(year)) => Year::try_new("year", year)?,
3212 Some(DateWithYear::EraYear(year, Era::CE)) => {
3213 let year_ce = t::YearCE::try_new("CE year", year)?;
3214 t::Year::try_rfrom("CE year", year_ce)?
3215 }
3216 Some(DateWithYear::EraYear(year, Era::BCE)) => {
3217 let year_bce = t::YearBCE::try_new("BCE year", year)?;
3218 t::Year::try_rfrom("BCE year", -year_bce + C(1))?
3219 }
3220 };
3221 let month = match self.month {
3222 None => self.original.month_ranged(),
3223 Some(month) => Month::try_new("month", month)?,
3224 };
3225 let day = match self.day {
3226 None => self.original.day_ranged(),
3227 Some(DateWithDay::OfMonth(day)) => Day::try_new("day", day)?,
3228 Some(DateWithDay::OfYear(day)) => {
3229 return day_of_year(year, day);
3230 }
3231 Some(DateWithDay::OfYearNoLeap(mut day)) => {
3232 type DayOfYear = ri16<1, 365>;
3233
3234 let _ = DayOfYear::try_new("day-of-year", day)?;
3235 if is_leap_year(year) && day >= 60 {
3236 day += 1;
3237 }
3238 return day_of_year(year, day);
3239 }
3240 };
3241 Date::new_ranged(year, month, day)
3242 }
3243
3244 /// Set the year field on a [`Date`].
3245 ///
3246 /// One can access this value via [`Date::year`].
3247 ///
3248 /// This overrides any previous year settings.
3249 ///
3250 /// # Errors
3251 ///
3252 /// This returns an error when [`DateWith::build`] is called if the given
3253 /// year is outside the range `-9999..=9999`. This can also return an error
3254 /// if the resulting date is otherwise invalid.
3255 ///
3256 /// # Example
3257 ///
3258 /// This shows how to create a new date with a different year:
3259 ///
3260 /// ```
3261 /// use jiff::civil::date;
3262 ///
3263 /// let d1 = date(2005, 11, 5);
3264 /// assert_eq!(d1.year(), 2005);
3265 /// let d2 = d1.with().year(2007).build()?;
3266 /// assert_eq!(d2.year(), 2007);
3267 ///
3268 /// # Ok::<(), Box<dyn std::error::Error>>(())
3269 /// ```
3270 ///
3271 /// # Example: only changing the year can fail
3272 ///
3273 /// For example, while `2024-02-29` is valid, `2023-02-29` is not:
3274 ///
3275 /// ```
3276 /// use jiff::civil::date;
3277 ///
3278 /// let d1 = date(2024, 2, 29);
3279 /// assert!(d1.with().year(2023).build().is_err());
3280 /// ```
3281 #[inline]
3282 pub fn year(self, year: i16) -> DateWith {
3283 DateWith { year: Some(DateWithYear::Jiff(year)), ..self }
3284 }
3285
3286 /// Set year of a date via its era and its non-negative numeric component.
3287 ///
3288 /// One can access this value via [`Date::era_year`].
3289 ///
3290 /// # Errors
3291 ///
3292 /// This returns an error when [`DateWith::build`] is called if the year is
3293 /// outside the range for the era specified. For [`Era::BCE`], the range is
3294 /// `1..=10000`. For [`Era::CE`], the range is `1..=9999`.
3295 ///
3296 /// # Example
3297 ///
3298 /// This shows that `CE` years are equivalent to the years used by this
3299 /// crate:
3300 ///
3301 /// ```
3302 /// use jiff::civil::{Era, date};
3303 ///
3304 /// let d1 = date(2005, 11, 5);
3305 /// assert_eq!(d1.year(), 2005);
3306 /// let d2 = d1.with().era_year(2007, Era::CE).build()?;
3307 /// assert_eq!(d2.year(), 2007);
3308 ///
3309 /// // CE years are always positive and can be at most 9999:
3310 /// assert!(d1.with().era_year(-5, Era::CE).build().is_err());
3311 /// assert!(d1.with().era_year(10_000, Era::CE).build().is_err());
3312 ///
3313 /// # Ok::<(), Box<dyn std::error::Error>>(())
3314 /// ```
3315 ///
3316 /// But `BCE` years always correspond to years less than or equal to `0`
3317 /// in this crate:
3318 ///
3319 /// ```
3320 /// use jiff::civil::{Era, date};
3321 ///
3322 /// let d1 = date(-27, 7, 1);
3323 /// assert_eq!(d1.year(), -27);
3324 /// assert_eq!(d1.era_year(), (28, Era::BCE));
3325 ///
3326 /// let d2 = d1.with().era_year(509, Era::BCE).build()?;
3327 /// assert_eq!(d2.year(), -508);
3328 /// assert_eq!(d2.era_year(), (509, Era::BCE));
3329 ///
3330 /// let d2 = d1.with().era_year(10_000, Era::BCE).build()?;
3331 /// assert_eq!(d2.year(), -9_999);
3332 /// assert_eq!(d2.era_year(), (10_000, Era::BCE));
3333 ///
3334 /// // BCE years are always positive and can be at most 10000:
3335 /// assert!(d1.with().era_year(-5, Era::BCE).build().is_err());
3336 /// assert!(d1.with().era_year(10_001, Era::BCE).build().is_err());
3337 ///
3338 /// # Ok::<(), Box<dyn std::error::Error>>(())
3339 /// ```
3340 ///
3341 /// # Example: overrides `DateWith::year`
3342 ///
3343 /// Setting this option will override any previous `DateWith::year`
3344 /// option:
3345 ///
3346 /// ```
3347 /// use jiff::civil::{Era, date};
3348 ///
3349 /// let d1 = date(2024, 7, 2);
3350 /// let d2 = d1.with().year(2000).era_year(1900, Era::CE).build()?;
3351 /// assert_eq!(d2, date(1900, 7, 2));
3352 ///
3353 /// # Ok::<(), Box<dyn std::error::Error>>(())
3354 /// ```
3355 ///
3356 /// Similarly, `DateWith::year` will override any previous call to
3357 /// `DateWith::era_year`:
3358 ///
3359 /// ```
3360 /// use jiff::civil::{Era, date};
3361 ///
3362 /// let d1 = date(2024, 7, 2);
3363 /// let d2 = d1.with().era_year(1900, Era::CE).year(2000).build()?;
3364 /// assert_eq!(d2, date(2000, 7, 2));
3365 ///
3366 /// # Ok::<(), Box<dyn std::error::Error>>(())
3367 /// ```
3368 #[inline]
3369 pub fn era_year(self, year: i16, era: Era) -> DateWith {
3370 DateWith { year: Some(DateWithYear::EraYear(year, era)), ..self }
3371 }
3372
3373 /// Set the month field on a [`Date`].
3374 ///
3375 /// One can access this value via [`Date::month`].
3376 ///
3377 /// This overrides any previous month settings.
3378 ///
3379 /// # Errors
3380 ///
3381 /// This returns an error when [`DateWith::build`] is called if the given
3382 /// month is outside the range `1..=12`. This can also return an error if
3383 /// the resulting date is otherwise invalid.
3384 ///
3385 /// # Example
3386 ///
3387 /// This shows how to create a new date with a different month:
3388 ///
3389 /// ```
3390 /// use jiff::civil::date;
3391 ///
3392 /// let d1 = date(2005, 11, 5);
3393 /// assert_eq!(d1.month(), 11);
3394 /// let d2 = d1.with().month(6).build()?;
3395 /// assert_eq!(d2.month(), 6);
3396 ///
3397 /// # Ok::<(), Box<dyn std::error::Error>>(())
3398 /// ```
3399 ///
3400 /// # Example: only changing the month can fail
3401 ///
3402 /// For example, while `2024-10-31` is valid, `2024-11-31` is not:
3403 ///
3404 /// ```
3405 /// use jiff::civil::date;
3406 ///
3407 /// let d = date(2024, 10, 31);
3408 /// assert!(d.with().month(11).build().is_err());
3409 /// ```
3410 #[inline]
3411 pub fn month(self, month: i8) -> DateWith {
3412 DateWith { month: Some(month), ..self }
3413 }
3414
3415 /// Set the day field on a [`Date`].
3416 ///
3417 /// One can access this value via [`Date::day`].
3418 ///
3419 /// This overrides any previous day settings.
3420 ///
3421 /// # Errors
3422 ///
3423 /// This returns an error when [`DateWith::build`] is called if the given
3424 /// given day is outside of allowable days for the corresponding year and
3425 /// month fields.
3426 ///
3427 /// # Example
3428 ///
3429 /// This shows some examples of setting the day, including a leap day:
3430 ///
3431 /// ```
3432 /// use jiff::civil::date;
3433 ///
3434 /// let d1 = date(2024, 2, 5);
3435 /// assert_eq!(d1.day(), 5);
3436 /// let d2 = d1.with().day(10).build()?;
3437 /// assert_eq!(d2.day(), 10);
3438 /// let d3 = d1.with().day(29).build()?;
3439 /// assert_eq!(d3.day(), 29);
3440 ///
3441 /// # Ok::<(), Box<dyn std::error::Error>>(())
3442 /// ```
3443 ///
3444 /// # Example: changing only the day can fail
3445 ///
3446 /// This shows some examples that will fail:
3447 ///
3448 /// ```
3449 /// use jiff::civil::date;
3450 ///
3451 /// let d1 = date(2023, 2, 5);
3452 /// // 2023 is not a leap year
3453 /// assert!(d1.with().day(29).build().is_err());
3454 ///
3455 /// // September has 30 days, not 31.
3456 /// let d1 = date(2023, 9, 5);
3457 /// assert!(d1.with().day(31).build().is_err());
3458 /// ```
3459 #[inline]
3460 pub fn day(self, day: i8) -> DateWith {
3461 DateWith { day: Some(DateWithDay::OfMonth(day)), ..self }
3462 }
3463
3464 /// Set the day field on a [`Date`] via the ordinal number of a day within
3465 /// a year.
3466 ///
3467 /// When used, any settings for month are ignored since the month is
3468 /// determined by the day of the year.
3469 ///
3470 /// The valid values for `day` are `1..=366`. Note though that `366` is
3471 /// only valid for leap years.
3472 ///
3473 /// This overrides any previous day settings.
3474 ///
3475 /// # Errors
3476 ///
3477 /// This returns an error when [`DateWith::build`] is called if the given
3478 /// day is outside the allowed range of `1..=366`, or when a value of `366`
3479 /// is given for a non-leap year.
3480 ///
3481 /// # Example
3482 ///
3483 /// This demonstrates that if a year is a leap year, then `60` corresponds
3484 /// to February 29:
3485 ///
3486 /// ```
3487 /// use jiff::civil::date;
3488 ///
3489 /// let d = date(2024, 1, 1);
3490 /// assert_eq!(d.with().day_of_year(60).build()?, date(2024, 2, 29));
3491 ///
3492 /// # Ok::<(), Box<dyn std::error::Error>>(())
3493 /// ```
3494 ///
3495 /// But for non-leap years, day 60 is March 1:
3496 ///
3497 /// ```
3498 /// use jiff::civil::date;
3499 ///
3500 /// let d = date(2023, 1, 1);
3501 /// assert_eq!(d.with().day_of_year(60).build()?, date(2023, 3, 1));
3502 ///
3503 /// # Ok::<(), Box<dyn std::error::Error>>(())
3504 /// ```
3505 ///
3506 /// And using `366` for a non-leap year will result in an error, since
3507 /// non-leap years only have 365 days:
3508 ///
3509 /// ```
3510 /// use jiff::civil::date;
3511 ///
3512 /// let d = date(2023, 1, 1);
3513 /// assert!(d.with().day_of_year(366).build().is_err());
3514 /// // The maximal year is not a leap year, so it returns an error too.
3515 /// let d = date(9999, 1, 1);
3516 /// assert!(d.with().day_of_year(366).build().is_err());
3517 /// ```
3518 #[inline]
3519 pub fn day_of_year(self, day: i16) -> DateWith {
3520 DateWith { day: Some(DateWithDay::OfYear(day)), ..self }
3521 }
3522
3523 /// Set the day field on a [`Date`] via the ordinal number of a day within
3524 /// a year, but ignoring leap years.
3525 ///
3526 /// When used, any settings for month are ignored since the month is
3527 /// determined by the day of the year.
3528 ///
3529 /// The valid values for `day` are `1..=365`. The value `365` always
3530 /// corresponds to the last day of the year, even for leap years. It is
3531 /// impossible for this routine to return a date corresponding to February
3532 /// 29.
3533 ///
3534 /// This overrides any previous day settings.
3535 ///
3536 /// # Errors
3537 ///
3538 /// This returns an error when [`DateWith::build`] is called if the given
3539 /// day is outside the allowed range of `1..=365`.
3540 ///
3541 /// # Example
3542 ///
3543 /// This demonstrates that `60` corresponds to March 1, regardless of
3544 /// whether the year is a leap year or not:
3545 ///
3546 /// ```
3547 /// use jiff::civil::date;
3548 ///
3549 /// assert_eq!(
3550 /// date(2023, 1, 1).with().day_of_year_no_leap(60).build()?,
3551 /// date(2023, 3, 1),
3552 /// );
3553 ///
3554 /// assert_eq!(
3555 /// date(2024, 1, 1).with().day_of_year_no_leap(60).build()?,
3556 /// date(2024, 3, 1),
3557 /// );
3558 ///
3559 /// # Ok::<(), Box<dyn std::error::Error>>(())
3560 /// ```
3561 ///
3562 /// And using `365` for any year will always yield the last day of the
3563 /// year:
3564 ///
3565 /// ```
3566 /// use jiff::civil::date;
3567 ///
3568 /// let d = date(2023, 1, 1);
3569 /// assert_eq!(
3570 /// d.with().day_of_year_no_leap(365).build()?,
3571 /// d.last_of_year(),
3572 /// );
3573 ///
3574 /// let d = date(2024, 1, 1);
3575 /// assert_eq!(
3576 /// d.with().day_of_year_no_leap(365).build()?,
3577 /// d.last_of_year(),
3578 /// );
3579 ///
3580 /// let d = date(9999, 1, 1);
3581 /// assert_eq!(
3582 /// d.with().day_of_year_no_leap(365).build()?,
3583 /// d.last_of_year(),
3584 /// );
3585 ///
3586 /// # Ok::<(), Box<dyn std::error::Error>>(())
3587 /// ```
3588 ///
3589 /// A value of `366` is out of bounds, even for leap years:
3590 ///
3591 /// ```
3592 /// use jiff::civil::date;
3593 ///
3594 /// let d = date(2024, 1, 1);
3595 /// assert!(d.with().day_of_year_no_leap(366).build().is_err());
3596 /// ```
3597 #[inline]
3598 pub fn day_of_year_no_leap(self, day: i16) -> DateWith {
3599 DateWith { day: Some(DateWithDay::OfYearNoLeap(day)), ..self }
3600 }
3601}
3602
3603/// Encodes the "with year" option of [`DateWith`].
3604///
3605/// This encodes the invariant that `DateWith::year` and `DateWith::era_year`
3606/// are mutually exclusive and override each other.
3607#[derive(Clone, Copy, Debug)]
3608enum DateWithYear {
3609 Jiff(i16),
3610 EraYear(i16, Era),
3611}
3612
3613/// Encodes the "with day" option of [`DateWith`].
3614///
3615/// This encodes the invariant that `DateWith::day`, `DateWith::day_of_year`
3616/// and `DateWith::day_of_year_no_leap` are all mutually exclusive and override
3617/// each other.
3618///
3619/// Note that when "day of year" or "day of year no leap" are used, then if a
3620/// day of month is set, it is ignored.
3621#[derive(Clone, Copy, Debug)]
3622enum DateWithDay {
3623 OfMonth(i8),
3624 OfYear(i16),
3625 OfYearNoLeap(i16),
3626}
3627
3628/// Returns the Unix epoch day corresponding to the first day in the ISO 8601
3629/// week year given.
3630///
3631/// Ref: http://howardhinnant.github.io/date_algorithms.html
3632fn iso_week_start_from_year(year: impl RInto<t::ISOYear>) -> UnixEpochDay {
3633 let year = year.rinto();
3634 // A week's year always corresponds to the Gregorian year in which the
3635 // Thursday of that week falls. Therefore, Jan 4 is *always* in the first
3636 // week of any ISO week year.
3637 let date_in_first_week = Date::new_ranged(year, C(1), C(4))
3638 .expect("Jan 4 is valid for all valid years");
3639 // The start of the first week is a Monday, so find the number of days
3640 // since Monday from a date that we know is in the first ISO week of
3641 // `year`.
3642 let diff_from_monday =
3643 date_in_first_week.weekday().since_ranged(Weekday::Monday);
3644 date_in_first_week.to_unix_epoch_day() - diff_from_monday
3645}
3646
3647/// Returns the weekday for the given number of days since the Unix epoch.
3648fn weekday_from_unix_epoch_days(days: impl RInto<UnixEpochDay>) -> Weekday {
3649 // Based on Hinnant's approach here, although we use ISO weekday numbering
3650 // by default. Basically, this works by using the knowledge that 1970-01-01
3651 // was a Thursday.
3652 //
3653 // Ref: http://howardhinnant.github.io/date_algorithms.html
3654 let days = days.rinto();
3655 Weekday::from_monday_zero_offset_ranged((days + C(3)) % C(7))
3656}
3657
3658/// Adds or subtracts `sign` from the given `year`/`month`.
3659///
3660/// If month overflows in either direction, then the `year` returned is
3661/// adjusted as appropriate.
3662fn month_add_one(
3663 year: impl RInto<Year>,
3664 month: impl RInto<Month>,
3665 sign: impl RInto<Sign>,
3666) -> Result<(Year, Month), Error> {
3667 let mut year = year.rinto();
3668 let mut month = month.rinto();
3669 let delta = sign.rinto();
3670
3671 month += delta;
3672 if month < 1 {
3673 year -= C(1);
3674 month += t::MONTHS_PER_YEAR;
3675 } else if month > t::MONTHS_PER_YEAR {
3676 year += C(1);
3677 month -= t::MONTHS_PER_YEAR;
3678 }
3679 let year = Year::try_rfrom("year", year)?;
3680 let month = Month::try_rfrom("year", month)?;
3681 Ok((year, month))
3682}
3683
3684/// Adds the given span of months to the `month` given.
3685///
3686/// If adding (or subtracting) would result in overflowing the `month` value,
3687/// then the amount by which it overflowed, in units of years, is returned. For
3688/// example, adding 14 months to the month `3` (March) will result in returning
3689/// the month `5` (May) with `1` year of overflow.
3690fn month_add_overflowing(
3691 month: impl RInto<t::Month>,
3692 span: impl RInto<t::SpanMonths>,
3693) -> (t::Month, t::SpanYears) {
3694 let month = t::SpanMonths::rfrom(month.rinto());
3695 let span = span.rinto();
3696 let total = month - C(1) + span;
3697 let years = total / C(12);
3698 let month = (total % C(12)) + C(1);
3699 (month.rinto(), years.rinto())
3700}
3701
3702fn day_of_year(year: Year, day: i16) -> Result<Date, Error> {
3703 let day = t::DayOfYear::try_new("day-of-year", day)?;
3704 let span = Span::new().days_ranged(day - C(1));
3705 let start = Date::new_ranged(year, C(1), C(1))?;
3706 let end = start.checked_add(span)?;
3707 // If we overflowed into the next year, then `day` is too big.
3708 if start.year() != end.year() {
3709 // Can only happen given day=366 and this is a leap year.
3710 debug_assert_eq!(day, 366);
3711 debug_assert!(!start.in_leap_year());
3712 return Err(Error::range("day-of-year", day, 1, 365));
3713 }
3714 Ok(end)
3715}
3716
3717/// Returns true if and only if the given year is a leap year.
3718///
3719/// A leap year is a year with 366 days. Typical years have 365 days.
3720#[inline]
3721fn is_leap_year(year: Year) -> bool {
3722 common::is_leap_year(year.get())
3723}
3724
3725/// Saturates the given day in the month.
3726///
3727/// That is, if the day exceeds the maximum number of days in the given year
3728/// and month, then this returns the maximum. Otherwise, it returns the day
3729/// given.
3730#[inline]
3731fn saturate_day_in_month(year: Year, month: Month, day: Day) -> Day {
3732 day.min(days_in_month(year, month))
3733}
3734
3735/// Returns the number of days in the given year and month.
3736///
3737/// This correctly returns `29` when the year is a leap year and the month is
3738/// February.
3739#[inline]
3740fn days_in_month(year: Year, month: Month) -> Day {
3741 #[cfg(not(debug_assertions))]
3742 {
3743 Day::new_unchecked(common::days_in_month(year.get(), month.get()))
3744 }
3745 #[cfg(debug_assertions)]
3746 {
3747 let days = common::days_in_month(year.val, month.val);
3748 let min_days = common::days_in_month(year.min, month.min);
3749 let max_days = common::days_in_month(year.max, month.max);
3750 Day { val: days, min: min_days, max: max_days }
3751 }
3752}
3753
3754#[cfg(test)]
3755mod tests {
3756 use std::io::Cursor;
3757
3758 use crate::{civil::date, span::span_eq, tz::TimeZone, Timestamp, ToSpan};
3759
3760 use super::*;
3761
3762 #[test]
3763 fn t_from_unix() {
3764 fn date_from_timestamp(timestamp: Timestamp) -> Date {
3765 timestamp.to_zoned(TimeZone::UTC).datetime().date()
3766 }
3767
3768 assert_eq!(
3769 date(1970, 1, 1),
3770 date_from_timestamp(Timestamp::new(0, 0).unwrap()),
3771 );
3772 assert_eq!(
3773 date(1969, 12, 31),
3774 date_from_timestamp(Timestamp::new(-1, 0).unwrap()),
3775 );
3776 assert_eq!(
3777 date(1969, 12, 31),
3778 date_from_timestamp(Timestamp::new(-86_400, 0).unwrap()),
3779 );
3780 assert_eq!(
3781 date(1969, 12, 30),
3782 date_from_timestamp(Timestamp::new(-86_401, 0).unwrap()),
3783 );
3784 assert_eq!(
3785 date(-9999, 1, 2),
3786 date_from_timestamp(
3787 Timestamp::new(t::UnixSeconds::MIN_REPR, 0).unwrap()
3788 ),
3789 );
3790 assert_eq!(
3791 date(9999, 12, 30),
3792 date_from_timestamp(
3793 Timestamp::new(t::UnixSeconds::MAX_REPR, 0).unwrap()
3794 ),
3795 );
3796 }
3797
3798 #[test]
3799 fn all_days_to_date_roundtrip() {
3800 for rd in -100_000..=100_000 {
3801 let rd = UnixEpochDay::new(rd).unwrap();
3802 let date = Date::from_unix_epoch_day(rd);
3803 let got = date.to_unix_epoch_day();
3804 assert_eq!(rd, got, "for date {date:?}");
3805 }
3806 }
3807
3808 #[test]
3809 fn all_date_to_days_roundtrip() {
3810 let year_range = 2000..=2500;
3811 // let year_range = -9999..=9999;
3812 for year in year_range {
3813 let year = Year::new(year).unwrap();
3814 for month in Month::MIN_REPR..=Month::MAX_REPR {
3815 let month = Month::new(month).unwrap();
3816 for day in 1..=days_in_month(year, month).get() {
3817 let date = date(year.get(), month.get(), day);
3818 let rd = date.to_unix_epoch_day();
3819 let got = Date::from_unix_epoch_day(rd);
3820 assert_eq!(date, got, "for date {date:?}");
3821 }
3822 }
3823 }
3824 }
3825
3826 #[test]
3827 fn all_date_to_iso_week_date_roundtrip() {
3828 let year_range = 2000..=2500;
3829 for year in year_range {
3830 let year = Year::new(year).unwrap();
3831 for month in [1, 2, 4] {
3832 let month = Month::new(month).unwrap();
3833 for day in 20..=days_in_month(year, month).get() {
3834 let date = date(year.get(), month.get(), day);
3835 let wd = date.iso_week_date();
3836 let got = wd.date();
3837 assert_eq!(
3838 date, got,
3839 "for date {date:?}, and ISO week date {wd:?}"
3840 );
3841 }
3842 }
3843 }
3844 }
3845
3846 #[test]
3847 fn add_constrained() {
3848 use crate::ToSpan;
3849
3850 let d1 = date(2023, 3, 31);
3851 let d2 = d1.checked_add(1.months().days(1)).unwrap();
3852 assert_eq!(d2, date(2023, 5, 1));
3853 }
3854
3855 #[test]
3856 fn since_years() {
3857 let d1 = date(2023, 4, 15);
3858 let d2 = date(2019, 2, 22);
3859 let span = d1.since((Unit::Year, d2)).unwrap();
3860 span_eq!(span, 4.years().months(1).days(21));
3861 let span = d2.since((Unit::Year, d1)).unwrap();
3862 span_eq!(span, -4.years().months(1).days(24));
3863
3864 let d1 = date(2023, 2, 22);
3865 let d2 = date(2019, 4, 15);
3866 let span = d1.since((Unit::Year, d2)).unwrap();
3867 span_eq!(span, 3.years().months(10).days(7));
3868 let span = d2.since((Unit::Year, d1)).unwrap();
3869 span_eq!(span, -3.years().months(10).days(7));
3870
3871 let d1 = date(9999, 12, 31);
3872 let d2 = date(-9999, 1, 1);
3873 let span = d1.since((Unit::Year, d2)).unwrap();
3874 span_eq!(span, 19998.years().months(11).days(30));
3875 let span = d2.since((Unit::Year, d1)).unwrap();
3876 span_eq!(span, -19998.years().months(11).days(30));
3877 }
3878
3879 #[test]
3880 fn since_months() {
3881 let d1 = date(2024, 7, 24);
3882 let d2 = date(2024, 2, 22);
3883 let span = d1.since((Unit::Month, d2)).unwrap();
3884 span_eq!(span, 5.months().days(2));
3885 let span = d2.since((Unit::Month, d1)).unwrap();
3886 span_eq!(span, -5.months().days(2));
3887 assert_eq!(d2, d1.checked_sub(5.months().days(2)).unwrap());
3888 assert_eq!(d1, d2.checked_sub(-5.months().days(2)).unwrap());
3889
3890 let d1 = date(2024, 7, 15);
3891 let d2 = date(2024, 2, 22);
3892 let span = d1.since((Unit::Month, d2)).unwrap();
3893 span_eq!(span, 4.months().days(22));
3894 let span = d2.since((Unit::Month, d1)).unwrap();
3895 span_eq!(span, -4.months().days(23));
3896 assert_eq!(d2, d1.checked_sub(4.months().days(22)).unwrap());
3897 assert_eq!(d1, d2.checked_sub(-4.months().days(23)).unwrap());
3898
3899 let d1 = date(2023, 4, 15);
3900 let d2 = date(2023, 2, 22);
3901 let span = d1.since((Unit::Month, d2)).unwrap();
3902 span_eq!(span, 1.month().days(21));
3903 let span = d2.since((Unit::Month, d1)).unwrap();
3904 span_eq!(span, -1.month().days(24));
3905 assert_eq!(d2, d1.checked_sub(1.month().days(21)).unwrap());
3906 assert_eq!(d1, d2.checked_sub(-1.month().days(24)).unwrap());
3907
3908 let d1 = date(2023, 4, 15);
3909 let d2 = date(2019, 2, 22);
3910 let span = d1.since((Unit::Month, d2)).unwrap();
3911 span_eq!(span, 49.months().days(21));
3912 let span = d2.since((Unit::Month, d1)).unwrap();
3913 span_eq!(span, -49.months().days(24));
3914 }
3915
3916 #[test]
3917 fn since_weeks() {
3918 let d1 = date(2024, 7, 15);
3919 let d2 = date(2024, 6, 22);
3920 let span = d1.since((Unit::Week, d2)).unwrap();
3921 span_eq!(span, 3.weeks().days(2));
3922 let span = d2.since((Unit::Week, d1)).unwrap();
3923 span_eq!(span, -3.weeks().days(2));
3924 }
3925
3926 #[test]
3927 fn since_days() {
3928 let d1 = date(2024, 7, 15);
3929 let d2 = date(2024, 2, 22);
3930 let span = d1.since((Unit::Day, d2)).unwrap();
3931 span_eq!(span, 144.days());
3932 let span = d2.since((Unit::Day, d1)).unwrap();
3933 span_eq!(span, -144.days());
3934 }
3935
3936 #[test]
3937 fn until_month_lengths() {
3938 let jan1 = date(2020, 1, 1);
3939 let feb1 = date(2020, 2, 1);
3940 let mar1 = date(2020, 3, 1);
3941
3942 span_eq!(jan1.until(feb1).unwrap(), 31.days());
3943 span_eq!(jan1.until((Unit::Month, feb1)).unwrap(), 1.month());
3944 span_eq!(feb1.until(mar1).unwrap(), 29.days());
3945 span_eq!(feb1.until((Unit::Month, mar1)).unwrap(), 1.month());
3946 span_eq!(jan1.until(mar1).unwrap(), 60.days());
3947 span_eq!(jan1.until((Unit::Month, mar1)).unwrap(), 2.months());
3948 }
3949
3950 // Ref: https://github.com/tc39/proposal-temporal/issues/2845#issuecomment-2121057896
3951 #[test]
3952 fn since_until_not_commutative() {
3953 // Temporal.PlainDate.from("2020-04-30").since("2020-02-29", {largestUnit: "months"})
3954 // // => P2M
3955 // Temporal.PlainDate.from("2020-02-29").until("2020-04-30", {largestUnit: "months"})
3956 // // => P2M1D
3957 let d1 = date(2020, 4, 30);
3958 let d2 = date(2020, 2, 29);
3959
3960 let since = d1.since((Unit::Month, d2)).unwrap();
3961 span_eq!(since, 2.months());
3962
3963 let until = d2.until((Unit::Month, d1)).unwrap();
3964 span_eq!(until, 2.months().days(1));
3965 }
3966
3967 // Ref: https://github.com/tc39/proposal-temporal/issues/2893
3968 #[test]
3969 fn until_weeks_round() {
3970 use crate::{RoundMode, SpanRound};
3971
3972 let earlier = date(2019, 1, 8);
3973 let later = date(2021, 9, 7);
3974 let span = earlier.until((Unit::Week, later)).unwrap();
3975 span_eq!(span, 139.weeks());
3976
3977 let options = SpanRound::new()
3978 .smallest(Unit::Week)
3979 .mode(RoundMode::HalfExpand)
3980 .relative(earlier.to_datetime(Time::midnight()));
3981 let rounded = span.round(options).unwrap();
3982 span_eq!(rounded, 139.weeks());
3983 }
3984
3985 // This test checks current behavior, but I think it's wrong. I think the
3986 // results below should be 11 months and 1 month.
3987 //
3988 // Ref: https://github.com/tc39/proposal-temporal/issues/2919
3989 #[test]
3990 fn until_months_no_balance() {
3991 let sp =
3992 date(2023, 5, 31).until((Unit::Month, date(2024, 4, 30))).unwrap();
3993 span_eq!(sp, 10.months().days(30));
3994
3995 let sp =
3996 date(2023, 5, 31).until((Unit::Month, date(2023, 6, 30))).unwrap();
3997 span_eq!(sp, 30.days());
3998 }
3999
4000 #[test]
4001 fn test_month_add() {
4002 let add =
4003 |year: i16, month: i8, delta: i8| -> Result<(i16, i8), Error> {
4004 let year = Year::new(year).unwrap();
4005 let month = Month::new(month).unwrap();
4006 let delta = Sign::new(delta).unwrap();
4007 let (year, month) = month_add_one(year, month, delta)?;
4008 Ok((year.get(), month.get()))
4009 };
4010
4011 assert_eq!(add(2024, 1, 1).unwrap(), (2024, 2));
4012 assert_eq!(add(2024, 1, -1).unwrap(), (2023, 12));
4013 assert_eq!(add(2024, 12, 1).unwrap(), (2025, 1));
4014 assert_eq!(add(9999, 12, -1).unwrap(), (9999, 11));
4015 assert_eq!(add(-9999, 1, 1).unwrap(), (-9999, 2));
4016
4017 assert!(add(9999, 12, 1).is_err());
4018 assert!(add(-9999, 1, -1).is_err());
4019 }
4020
4021 #[test]
4022 fn test_month_add_overflowing() {
4023 let month_add = |month, span| {
4024 let month = t::Month::new(month).unwrap();
4025 let span = t::SpanMonths::new(span).unwrap();
4026 let (month, years) = month_add_overflowing(month, span);
4027 (month.get(), years.get())
4028 };
4029
4030 assert_eq!((1, 0), month_add(1, 0));
4031 assert_eq!((12, 0), month_add(1, 11));
4032 assert_eq!((1, 1), month_add(1, 12));
4033 assert_eq!((2, 1), month_add(1, 13));
4034 assert_eq!((9, 1), month_add(1, 20));
4035 assert_eq!((12, 19998), month_add(12, t::SpanMonths::MAX_REPR));
4036
4037 assert_eq!((12, -1), month_add(1, -1));
4038 assert_eq!((11, -1), month_add(1, -2));
4039 assert_eq!((1, -1), month_add(1, -12));
4040 assert_eq!((12, -2), month_add(1, -13));
4041 }
4042
4043 #[test]
4044 fn date_size() {
4045 #[cfg(debug_assertions)]
4046 {
4047 assert_eq!(12, core::mem::size_of::<Date>());
4048 }
4049 #[cfg(not(debug_assertions))]
4050 {
4051 assert_eq!(4, core::mem::size_of::<Date>());
4052 }
4053 }
4054
4055 quickcheck::quickcheck! {
4056 fn prop_checked_add_then_sub(
4057 d1: Date,
4058 span: Span
4059 ) -> quickcheck::TestResult {
4060 // Force our span to have no units greater than days.
4061 let span = if span.largest_unit() <= Unit::Day {
4062 span
4063 } else {
4064 let round = SpanRound::new().largest(Unit::Day).relative(d1);
4065 let Ok(span) = span.round(round) else {
4066 return quickcheck::TestResult::discard();
4067 };
4068 span
4069 };
4070 let Ok(d2) = d1.checked_add(span) else {
4071 return quickcheck::TestResult::discard();
4072 };
4073 let got = d2.checked_sub(span).unwrap();
4074 quickcheck::TestResult::from_bool(d1 == got)
4075 }
4076
4077 fn prop_checked_sub_then_add(
4078 d1: Date,
4079 span: Span
4080 ) -> quickcheck::TestResult {
4081 // Force our span to have no units greater than days.
4082 let span = if span.largest_unit() <= Unit::Day {
4083 span
4084 } else {
4085 let round = SpanRound::new().largest(Unit::Day).relative(d1);
4086 let Ok(span) = span.round(round) else {
4087 return quickcheck::TestResult::discard();
4088 };
4089 span
4090 };
4091 let Ok(d2) = d1.checked_sub(span) else {
4092 return quickcheck::TestResult::discard();
4093 };
4094 let got = d2.checked_add(span).unwrap();
4095 quickcheck::TestResult::from_bool(d1 == got)
4096 }
4097
4098 fn prop_since_then_add(d1: Date, d2: Date) -> bool {
4099 let span = d1.since(d2).unwrap();
4100 let got = d2.checked_add(span).unwrap();
4101 d1 == got
4102 }
4103
4104 fn prop_until_then_sub(d1: Date, d2: Date) -> bool {
4105 let span = d1.until(d2).unwrap();
4106 let got = d2.checked_sub(span).unwrap();
4107 d1 == got
4108 }
4109 }
4110
4111 /// # `serde` deserializer compatibility test
4112 ///
4113 /// Serde YAML used to be unable to deserialize `jiff` types,
4114 /// as deserializing from bytes is not supported by the deserializer.
4115 ///
4116 /// - <https://github.com/BurntSushi/jiff/issues/138>
4117 /// - <https://github.com/BurntSushi/jiff/discussions/148>
4118 #[test]
4119 fn civil_date_deserialize_yaml() {
4120 let expected = date(2024, 10, 31);
4121
4122 let deserialized: Date = serde_yaml::from_str("2024-10-31").unwrap();
4123
4124 assert_eq!(deserialized, expected);
4125
4126 let deserialized: Date =
4127 serde_yaml::from_slice("2024-10-31".as_bytes()).unwrap();
4128
4129 assert_eq!(deserialized, expected);
4130
4131 let cursor = Cursor::new(b"2024-10-31");
4132 let deserialized: Date = serde_yaml::from_reader(cursor).unwrap();
4133
4134 assert_eq!(deserialized, expected);
4135 }
4136}