use crate::error::ErrorMessage;
use crate::parsed_intermediates::ParsedDate;
use crate::{
builtins::{
calendar::{CalendarFields, YearMonthCalendarFields},
core::{
calendar::Calendar, duration::DateDuration, Duration, PlainDateTime, PlainTime,
ZonedDateTime,
},
},
iso::{IsoDate, IsoDateTime, IsoTime},
options::{
DifferenceOperation, DifferenceSettings, Disambiguation, DisplayCalendar, Overflow,
ResolvedRoundingOptions, Unit, UnitGroup,
},
parsers::IxdtfStringBuilder,
provider::{NeverProvider, TimeZoneProvider},
unix_time::EpochNanoseconds,
MonthCode, TemporalError, TemporalResult, TimeZone,
};
use alloc::string::String;
use core::{cmp::Ordering, str::FromStr};
use icu_calendar::AnyCalendarKind;
use writeable::Writeable;
use super::{duration::normalized::InternalDurationRecord, PlainMonthDay, PlainYearMonth};
use tinystr::TinyAsciiStr;
#[derive(Debug, Default, Clone, PartialEq)]
pub struct PartialDate {
pub calendar_fields: CalendarFields,
pub calendar: Calendar,
}
impl PartialDate {
pub const fn new() -> Self {
Self {
calendar_fields: CalendarFields {
year: None,
month: None,
month_code: None,
day: None,
era: None,
era_year: None,
},
calendar: Calendar::new(AnyCalendarKind::Iso),
}
}
pub const fn with_era(mut self, era: Option<TinyAsciiStr<19>>) -> Self {
self.calendar_fields.era = era;
self
}
pub const fn with_era_year(mut self, era_year: Option<i32>) -> Self {
self.calendar_fields.era_year = era_year;
self
}
pub const fn with_year(mut self, year: Option<i32>) -> Self {
self.calendar_fields.year = year;
self
}
pub const fn with_month(mut self, month: Option<u8>) -> Self {
self.calendar_fields.month = month;
self
}
pub const fn with_month_code(mut self, month_code: Option<MonthCode>) -> Self {
self.calendar_fields.month_code = month_code;
self
}
pub const fn with_day(mut self, day: Option<u8>) -> Self {
self.calendar_fields.day = day;
self
}
pub const fn with_calendar(mut self, calendar: Calendar) -> Self {
self.calendar = calendar;
self
}
}
#[non_exhaustive]
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct PlainDate {
pub(crate) iso: IsoDate,
calendar: Calendar,
}
impl core::fmt::Display for PlainDate {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(&self.to_ixdtf_string(DisplayCalendar::Auto))
}
}
impl PlainDate {
#[inline]
#[must_use]
pub(crate) fn new_unchecked(iso: IsoDate, calendar: Calendar) -> Self {
Self { iso, calendar }
}
#[inline]
pub(crate) fn add_duration_to_date(
&self,
duration: &Duration,
overflow: Option<Overflow>,
) -> TemporalResult<Self> {
let date_duration = duration.to_date_duration_record_without_time()?;
let overflow = overflow.unwrap_or(Overflow::Constrain);
self.calendar()
.date_add(&self.iso, &date_duration, overflow)
}
#[inline]
pub(crate) fn internal_diff_date(
&self,
other: &Self,
largest_unit: Unit,
) -> TemporalResult<Duration> {
if self.iso.year == other.iso.year
&& self.iso.month == other.iso.month
&& self.iso.day == other.iso.day
{
return Ok(Duration::default());
}
if largest_unit == Unit::Day {
let days = self.days_until(other);
return Ok(Duration::from(DateDuration::new(0, 0, 0, days.into())?));
}
self.calendar()
.date_until(&self.iso, &other.iso, largest_unit)
}
pub(crate) fn diff_date(
&self,
op: DifferenceOperation,
other: &Self,
settings: DifferenceSettings,
) -> TemporalResult<Duration> {
if self.calendar().identifier() != other.calendar().identifier() {
return Err(TemporalError::range()
.with_message("Calendars are for difference operation are not the same."));
}
let resolved = ResolvedRoundingOptions::from_diff_settings(
settings,
op,
UnitGroup::Date,
Unit::Day,
Unit::Day,
)?;
if self.iso == other.iso {
return Ok(Duration::default());
}
let result = self.internal_diff_date(other, resolved.largest_unit)?;
let mut duration = InternalDurationRecord::from_date_duration(result.date())?;
let rounding_granularity_is_noop =
resolved.smallest_unit == Unit::Day && resolved.increment.get() == 1;
if !rounding_granularity_is_noop {
let iso_date_time = IsoDateTime::new_unchecked(self.iso, IsoTime::default());
let origin_epoch_ns = iso_date_time.as_nanoseconds();
let dest_epoch_ns = other.iso.as_nanoseconds();
let dt = PlainDateTime::new_unchecked(iso_date_time, self.calendar.clone());
duration = duration.round_relative_duration(
origin_epoch_ns,
dest_epoch_ns.0,
&dt,
Option::<(&TimeZone, &NeverProvider)>::None,
resolved,
)?
}
let result = Duration::from_internal(duration, Unit::Day)?;
match op {
DifferenceOperation::Until => Ok(result),
DifferenceOperation::Since => Ok(result.negated()),
}
}
#[inline]
#[must_use]
fn days_until(&self, other: &Self) -> i32 {
debug_assert!(self.iso.check_within_limits().is_ok());
debug_assert!(other.iso.check_within_limits().is_ok());
(other.iso.to_epoch_days() - self.iso.to_epoch_days()) as i32
}
#[inline]
#[must_use]
pub(crate) const fn iso_year(&self) -> i32 {
self.iso.year
}
#[inline]
#[must_use]
pub(crate) const fn iso_month(&self) -> u8 {
self.iso.month
}
#[inline]
#[must_use]
pub(crate) const fn iso_day(&self) -> u8 {
self.iso.day
}
}
impl PlainDate {
#[inline]
pub fn new(year: i32, month: u8, day: u8, calendar: Calendar) -> TemporalResult<Self> {
Self::new_with_overflow(year, month, day, calendar, Overflow::Constrain)
}
#[inline]
pub fn new_iso(year: i32, month: u8, day: u8) -> TemporalResult<Self> {
Self::new(year, month, day, Calendar::default())
}
#[inline]
pub fn try_new(year: i32, month: u8, day: u8, calendar: Calendar) -> TemporalResult<Self> {
Self::new_with_overflow(year, month, day, calendar, Overflow::Reject)
}
#[inline]
pub fn try_new_iso(year: i32, month: u8, day: u8) -> TemporalResult<Self> {
Self::try_new(year, month, day, Calendar::default())
}
#[inline]
pub fn new_with_overflow(
year: i32,
month: u8,
day: u8,
calendar: Calendar,
overflow: Overflow,
) -> TemporalResult<Self> {
let iso = IsoDate::new_with_overflow(year, month, day, overflow)?;
Ok(Self::new_unchecked(iso, calendar))
}
#[inline]
pub fn from_partial(partial: PartialDate, overflow: Option<Overflow>) -> TemporalResult<Self> {
let year_check = partial.calendar_fields.year.is_some()
|| (partial.calendar_fields.era.is_some()
&& partial.calendar_fields.era_year.is_some());
let month_check =
partial.calendar_fields.month.is_some() || partial.calendar_fields.month_code.is_some();
if !year_check || !month_check || partial.calendar_fields.day.is_none() {
return Err(TemporalError::r#type().with_message("Invalid PlainDate fields provided."));
}
let overflow = overflow.unwrap_or_default();
partial
.calendar
.date_from_fields(partial.calendar_fields, overflow)
}
pub fn from_utf8(s: &[u8]) -> TemporalResult<Self> {
let parsed = ParsedDate::from_utf8(s)?;
Self::from_parsed(parsed)
}
pub fn from_parsed(parsed: ParsedDate) -> TemporalResult<Self> {
Self::try_new(
parsed.record.year,
parsed.record.month,
parsed.record.day,
Calendar::new(parsed.calendar),
)
}
pub fn with(&self, fields: CalendarFields, overflow: Option<Overflow>) -> TemporalResult<Self> {
if fields.is_empty() {
return Err(TemporalError::r#type().with_enum(ErrorMessage::EmptyFieldsIsInvalid));
}
let overflow = overflow.unwrap_or(Overflow::Constrain);
self.calendar.date_from_fields(
fields.with_fallback_date(self, self.calendar.kind())?,
overflow,
)
}
pub fn with_calendar(&self, calendar: Calendar) -> Self {
Self::new_unchecked(self.iso, calendar)
}
#[inline]
#[must_use]
pub fn calendar(&self) -> &Calendar {
&self.calendar
}
#[must_use]
pub fn is_valid(&self) -> bool {
self.iso.is_valid()
}
#[inline]
#[must_use]
pub fn compare_iso(&self, other: &Self) -> Ordering {
self.iso.cmp(&other.iso)
}
#[inline]
pub fn add(&self, duration: &Duration, overflow: Option<Overflow>) -> TemporalResult<Self> {
self.add_duration_to_date(duration, overflow)
}
#[inline]
pub fn subtract(
&self,
duration: &Duration,
overflow: Option<Overflow>,
) -> TemporalResult<Self> {
self.add_duration_to_date(&duration.negated(), overflow)
}
#[inline]
pub fn until(&self, other: &Self, settings: DifferenceSettings) -> TemporalResult<Duration> {
self.diff_date(DifferenceOperation::Until, other, settings)
}
#[inline]
pub fn since(&self, other: &Self, settings: DifferenceSettings) -> TemporalResult<Duration> {
self.diff_date(DifferenceOperation::Since, other, settings)
}
}
impl PlainDate {
pub fn year(&self) -> i32 {
self.calendar.year(&self.iso)
}
pub fn month(&self) -> u8 {
self.calendar.month(&self.iso)
}
pub fn month_code(&self) -> MonthCode {
self.calendar.month_code(&self.iso)
}
pub fn day(&self) -> u8 {
self.calendar.day(&self.iso)
}
pub fn day_of_week(&self) -> u16 {
self.calendar.day_of_week(&self.iso)
}
pub fn day_of_year(&self) -> u16 {
self.calendar.day_of_year(&self.iso)
}
pub fn week_of_year(&self) -> Option<u8> {
self.calendar.week_of_year(&self.iso)
}
pub fn year_of_week(&self) -> Option<i32> {
self.calendar.year_of_week(&self.iso)
}
pub fn days_in_week(&self) -> u16 {
self.calendar.days_in_week(&self.iso)
}
pub fn days_in_month(&self) -> u16 {
self.calendar.days_in_month(&self.iso)
}
pub fn days_in_year(&self) -> u16 {
self.calendar.days_in_year(&self.iso)
}
pub fn months_in_year(&self) -> u16 {
self.calendar.months_in_year(&self.iso)
}
pub fn in_leap_year(&self) -> bool {
self.calendar.in_leap_year(&self.iso)
}
pub fn era(&self) -> Option<TinyAsciiStr<16>> {
self.calendar.era(&self.iso)
}
pub fn era_year(&self) -> Option<i32> {
self.calendar.era_year(&self.iso)
}
}
impl PlainDate {
#[inline]
pub fn to_plain_date_time(&self, time: Option<PlainTime>) -> TemporalResult<PlainDateTime> {
let time = time.unwrap_or_default();
let iso = IsoDateTime::new(self.iso, time.iso)?;
Ok(PlainDateTime::new_unchecked(iso, self.calendar().clone()))
}
#[inline]
pub fn to_plain_year_month(&self) -> TemporalResult<PlainYearMonth> {
let era = self
.era()
.map(|e| {
TinyAsciiStr::<19>::try_from_utf8(e.as_bytes())
.map_err(|_| TemporalError::general("Parsing era failed"))
})
.transpose()?;
let fields = YearMonthCalendarFields::new()
.with_year(self.year())
.with_era(era)
.with_era_year(self.era_year())
.with_month(self.month())
.with_month_code(self.month_code());
self.calendar()
.year_month_from_fields(fields, Overflow::Constrain)
}
#[inline]
pub fn to_plain_month_day(&self) -> TemporalResult<PlainMonthDay> {
let overflow = Overflow::Constrain;
self.calendar().month_day_from_fields(
CalendarFields::default().with_fallback_date(self, self.calendar.kind())?,
overflow,
)
}
#[inline]
pub fn to_ixdtf_string(&self, display_calendar: DisplayCalendar) -> String {
self.to_ixdtf_writeable(display_calendar)
.write_to_string()
.into()
}
#[inline]
pub fn to_ixdtf_writeable(&self, display_calendar: DisplayCalendar) -> impl Writeable + '_ {
IxdtfStringBuilder::default()
.with_date(self.iso)
.with_calendar(self.calendar.identifier(), display_calendar)
.build()
}
#[inline]
pub fn to_zoned_date_time_with_provider(
&self,
time_zone: TimeZone,
plain_time: Option<PlainTime>,
provider: &(impl TimeZoneProvider + ?Sized),
) -> TemporalResult<ZonedDateTime> {
let epoch_ns = if let Some(time) = plain_time {
let result_iso = IsoDateTime::new(self.iso, time.iso)?;
time_zone.get_epoch_nanoseconds_for(result_iso, Disambiguation::Compatible, provider)?
} else {
time_zone.get_start_of_day(&self.iso, provider)?
};
ZonedDateTime::try_new_with_cached_offset(
epoch_ns.ns.0,
time_zone,
self.calendar.clone(),
epoch_ns.offset,
)
}
pub fn epoch_ns_for_utc(&self) -> EpochNanoseconds {
let iso = IsoDateTime::new(self.iso, IsoTime::noon());
debug_assert!(iso.is_ok());
iso.unwrap_or_default().as_nanoseconds()
}
}
impl From<PlainDateTime> for PlainDate {
fn from(pdt: PlainDateTime) -> Self {
pdt.to_plain_date()
}
}
impl FromStr for PlainDate {
type Err = TemporalError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::from_utf8(s.as_bytes())
}
}
#[cfg(test)]
mod tests {
use tinystr::tinystr;
use crate::options::{RoundingIncrement, RoundingMode};
use super::*;
#[test]
fn new_date_limits() {
let err = PlainDate::try_new(-271_821, 4, 18, Calendar::default());
assert!(err.is_err());
let err = PlainDate::try_new(275_760, 9, 14, Calendar::default());
assert!(err.is_err());
let ok = PlainDate::try_new(-271_821, 4, 19, Calendar::default());
assert_eq!(
ok,
Ok(PlainDate {
iso: IsoDate {
year: -271_821,
month: 4,
day: 19,
},
calendar: Calendar::default(),
})
);
let ok = PlainDate::try_new(275_760, 9, 13, Calendar::default());
assert_eq!(
ok,
Ok(PlainDate {
iso: IsoDate {
year: 275760,
month: 9,
day: 13,
},
calendar: Calendar::default(),
})
);
}
#[test]
fn simple_date_add() {
let base = PlainDate::from_str("1976-11-18").unwrap();
let result = base
.add(&Duration::from_str("P43Y").unwrap(), None)
.unwrap();
assert_eq!(
result.iso,
IsoDate {
year: 2019,
month: 11,
day: 18,
}
);
let result = base.add(&Duration::from_str("P3M").unwrap(), None).unwrap();
assert_eq!(
result.iso,
IsoDate {
year: 1977,
month: 2,
day: 18,
}
);
let result = base
.add(&Duration::from_str("P20D").unwrap(), None)
.unwrap();
assert_eq!(
result.iso,
IsoDate {
year: 1976,
month: 12,
day: 8,
}
)
}
#[test]
fn date_add_limits() {
let max = PlainDate::try_new(275_760, 9, 13, Calendar::default()).unwrap();
let result = max.add(&Duration::from_str("P1D").unwrap(), None);
assert!(result.is_err());
let max = PlainDate::try_new(275_760, 9, 12, Calendar::default()).unwrap();
let result = max.add(&Duration::from_str("P1D").unwrap(), None);
assert_eq!(
result,
Ok(PlainDate {
iso: IsoDate {
year: 275760,
month: 9,
day: 13
},
calendar: Calendar::default(),
})
);
let min = PlainDate::try_new(-271_821, 4, 19, Calendar::default()).unwrap();
let result = min.add(&Duration::from_str("-P1D").unwrap(), None);
assert!(result.is_err());
let min = PlainDate::try_new(-271_821, 4, 20, Calendar::default()).unwrap();
let result = min.add(&Duration::from_str("-P1D").unwrap(), None);
assert_eq!(
result,
Ok(PlainDate {
iso: IsoDate {
year: -271_821,
month: 4,
day: 19
},
calendar: Calendar::default(),
})
);
}
#[test]
fn simple_date_subtract() {
let base = PlainDate::from_str("2019-11-18").unwrap();
let result = base
.subtract(&Duration::from_str("P43Y").unwrap(), None)
.unwrap();
assert_eq!(
result.iso,
IsoDate {
year: 1976,
month: 11,
day: 18,
}
);
let result = base
.subtract(&Duration::from_str("P11M").unwrap(), None)
.unwrap();
assert_eq!(
result.iso,
IsoDate {
year: 2018,
month: 12,
day: 18,
}
);
let result = base
.subtract(&Duration::from_str("P20D").unwrap(), None)
.unwrap();
assert_eq!(
result.iso,
IsoDate {
year: 2019,
month: 10,
day: 29,
}
)
}
#[test]
fn simple_date_until() {
let earlier = PlainDate::from_str("1969-07-24").unwrap();
let later = PlainDate::from_str("1969-10-05").unwrap();
let result = earlier
.until(&later, DifferenceSettings::default())
.unwrap();
assert_eq!(result.days(), 73,);
let later = PlainDate::from_str("1996-03-03").unwrap();
let result = earlier
.until(&later, DifferenceSettings::default())
.unwrap();
assert_eq!(result.days(), 9719,);
}
#[test]
fn simple_date_since() {
let earlier = PlainDate::from_str("1969-07-24").unwrap();
let later = PlainDate::from_str("1969-10-05").unwrap();
let result = later
.since(&earlier, DifferenceSettings::default())
.unwrap();
assert_eq!(result.days(), 73,);
let later = PlainDate::from_str("1996-03-03").unwrap();
let result = later
.since(&earlier, DifferenceSettings::default())
.unwrap();
assert_eq!(result.days(), 9719,);
}
#[test]
fn date_with_empty_error() {
let base = PlainDate::new(1976, 11, 18, Calendar::default()).unwrap();
let err = base.with(CalendarFields::default(), None);
assert!(err.is_err());
}
#[test]
fn basic_date_with() {
let base = PlainDate::new(1976, 11, 18, Calendar::default()).unwrap();
let fields = CalendarFields::new().with_year(2019);
let with_year = base.with(fields, None).unwrap();
assert_eq!(with_year.year(), 2019);
assert_eq!(with_year.month(), 11);
assert_eq!(with_year.month_code(), MonthCode::from_str("M11").unwrap());
assert_eq!(with_year.day(), 18);
let fields = CalendarFields::new().with_month(5);
let with_month = base.with(fields, None).unwrap();
assert_eq!(with_month.year(), 1976);
assert_eq!(with_month.month(), 5);
assert_eq!(with_month.month_code(), MonthCode::from_str("M05").unwrap());
assert_eq!(with_month.day(), 18);
let fields = CalendarFields::new().with_month_code(MonthCode(tinystr!(4, "M05")));
let with_mc = base.with(fields, None).unwrap();
assert_eq!(with_mc.year(), 1976);
assert_eq!(with_mc.month(), 5);
assert_eq!(with_mc.month_code(), MonthCode::from_str("M05").unwrap());
assert_eq!(with_mc.day(), 18);
let fields = CalendarFields::new().with_day(17);
let with_day = base.with(fields, None).unwrap();
assert_eq!(with_day.year(), 1976);
assert_eq!(with_day.month(), 11);
assert_eq!(with_day.month_code(), MonthCode::from_str("M11").unwrap());
assert_eq!(with_day.day(), 17);
}
#[cfg(feature = "tzdb")]
#[test]
fn to_zoned_date_time() {
use timezone_provider::tzif::FsTzdbProvider;
let provider = &FsTzdbProvider::default();
let date = PlainDate::from_str("2020-01-01").unwrap();
let time_zone = TimeZone::try_from_str_with_provider("UTC", provider).unwrap();
let zdt = date
.to_zoned_date_time_with_provider(time_zone, None, provider)
.unwrap();
assert_eq!(zdt.year(), 2020);
assert_eq!(zdt.month(), 1);
assert_eq!(zdt.day(), 1);
assert_eq!(zdt.hour(), 0);
assert_eq!(zdt.minute(), 0);
assert_eq!(zdt.second(), 0);
assert_eq!(zdt.millisecond(), 0);
assert_eq!(zdt.microsecond(), 0);
assert_eq!(zdt.nanosecond(), 0);
}
#[cfg(feature = "tzdb")]
#[test]
fn to_zoned_date_time_error() {
use timezone_provider::tzif::FsTzdbProvider;
let provider = &FsTzdbProvider::default();
let date = PlainDate::try_new_iso(-271_821, 4, 19).unwrap();
let time_zone = TimeZone::try_from_str_with_provider("+00", provider).unwrap();
let zdt = date.to_zoned_date_time_with_provider(time_zone, None, provider);
assert!(zdt.is_err())
}
#[test]
fn invalid_strings() {
const INVALID_STRINGS: &[&str] = &[
"",
"invalid iso8601",
"2020-01-00",
"2020-01-32",
"2020-02-30",
"2021-02-29",
"2020-00-01",
"2020-13-01",
"2020-01-01T",
"2020-01-01T25:00:00",
"2020-01-01T01:60:00",
"2020-01-01T01:60:61",
"2020-01-01junk",
"2020-01-01T00:00:00junk",
"2020-01-01T00:00:00+00:00junk",
"2020-01-01T00:00:00+00:00[UTC]junk",
"2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk",
"02020-01-01",
"2020-001-01",
"2020-01-001",
"2020-01-01T001",
"2020-01-01T01:001",
"2020-01-01T01:01:001",
"2020-W01-1",
"2020-001",
"+0002020-01-01",
"2020-01-01[u-ca=notexist]",
"2020-01",
"+002020-01",
"01-01",
"2020-W01",
"P1Y",
"-P12Y",
"-999999-01-01",
"+999999-01-01",
"1970-01-01T00:00:00.1234567891",
"1970-01-01T00:00:00.1234567890",
"1970-01-01T00+00:00:00.1234567891",
"1970-01-01T00+00:00:00.1234567890",
];
for s in INVALID_STRINGS {
assert!(PlainDate::from_str(s).is_err(), "{} should not parse", s)
}
}
#[test]
fn argument_string_critical_unknown_annotation() {
const INVALID_STRINGS: [&str; 6] = [
"1970-01-01[!foo=bar]",
"1970-01-01T00:00[!foo=bar]",
"1970-01-01T00:00[UTC][!foo=bar]",
"1970-01-01T00:00[u-ca=iso8601][!foo=bar]",
"1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]",
"1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
];
for s in INVALID_STRINGS {
assert!(PlainDate::from_str(s).is_err())
}
}
#[test]
fn rounding_increment_observed() {
let earlier = PlainDate::try_new_iso(2019, 1, 8).unwrap();
let later = PlainDate::try_new_iso(2021, 9, 7).unwrap();
let settings = DifferenceSettings {
smallest_unit: Some(Unit::Year),
rounding_mode: Some(RoundingMode::HalfExpand),
increment: Some(RoundingIncrement::try_new(4).unwrap()),
..Default::default()
};
let result = later.since(&earlier, settings).unwrap();
assert_eq!(result.years(), 4);
let settings = DifferenceSettings {
smallest_unit: Some(Unit::Month),
rounding_mode: Some(RoundingMode::HalfExpand),
increment: Some(RoundingIncrement::try_new(10).unwrap()),
..Default::default()
};
let result = later.since(&earlier, settings).unwrap();
assert_eq!(result.months(), 30);
let settings = DifferenceSettings {
smallest_unit: Some(Unit::Week),
rounding_mode: Some(RoundingMode::HalfExpand),
increment: Some(RoundingIncrement::try_new(12).unwrap()),
..Default::default()
};
let result = later.since(&earlier, settings).unwrap();
assert_eq!(result.weeks(), 144);
let settings = DifferenceSettings {
smallest_unit: Some(Unit::Day),
rounding_mode: Some(RoundingMode::HalfExpand),
increment: Some(RoundingIncrement::try_new(100).unwrap()),
..Default::default()
};
let result = later.since(&earlier, settings).unwrap();
assert_eq!(result.days(), 1000);
}
}