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