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