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