Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 119 additions & 2 deletions src/naive/date.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use crate::format::{Item, Numeric, Pad};
use crate::month::Months;
use crate::naive::{IsoWeek, NaiveDateTime, NaiveTime};
use crate::oldtime::Duration as OldDuration;
use crate::{Datelike, Duration, Weekday};
use crate::{Datelike, Duration, Month, Weekday};

use super::internals::{self, DateImpl, Mdf, Of, YearFlags};
use super::isoweek;
Expand Down Expand Up @@ -236,6 +236,49 @@ fn test_date_bounds() {
}

impl NaiveDate {
/// Create a `NaiveDate` of the first day of the given year
///
/// This takes an `i16` rather than `i32` to ensure it is not
/// out of the allowed range
pub fn start_year(year: i16) -> NaiveDate {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using an i16 ties into your work in #882. But maybe it is best to accept an i32 until the decision is made to restrict the range of allowed years?

// ideally this unwrap can be later removed
Self::from_ymd_opt(year.into(), 1, 1).unwrap()
}

/// Create a `NaiveDate` of the last day of the given year
///
/// This takes an `i16` rather than `i32` to ensure it is not
/// out of the allowed range
pub fn end_year(year: i16) -> NaiveDate {
// ideally this unwrap can be later removed
Self::from_ymd_opt(i32::from(year) + 1, 1, 1).unwrap().pred_opt().unwrap()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might fail if year == MAX_YEAR.

}

/// Create a `NaiveDate` of the first day of the given year and month
///
/// This takes an `i16` rather than `i32` to ensure it is not
/// out of the allowed range
pub fn start_month(year: i16, month: Month) -> NaiveDate {
// ideally this unwrap can be later removed
Self::from_ymd_opt(year.into(), month.number_from_month(), 1).unwrap()
}

/// Create a `NaiveDate` of the ;ast day of the given year and month
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Create a `NaiveDate` of the ;ast day of the given year and month
/// Create a `NaiveDate` of the last day of the given year and month

///
/// This takes an `i16` rather than `i32` to ensure it is not
/// out of the allowed range
pub fn end_month(year: i16, month: Month) -> NaiveDate {
// ideally this unwrap can be later removed
let start = Self::start_month(year, month);

(start + Months::new(1)).pred_opt().unwrap()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might fail if year == MAX_YEAR && month == 12.

}

/// Create a `NaiveDateTime` at midnight on the given `NaiveDate`
pub fn start_time(&self) -> NaiveDateTime {
self.and_time(NaiveTime::MIN)
}

/// Makes a new `NaiveDate` from year and packed ordinal-flags, with a verification.
fn from_of(year: i32, of: Of) -> Option<NaiveDate> {
if (MIN_YEAR..=MAX_YEAR).contains(&year) && of.valid() {
Expand Down Expand Up @@ -2166,13 +2209,87 @@ mod tests {
use super::{
Days, Months, NaiveDate, MAX_DAYS_FROM_YEAR_0, MAX_YEAR, MIN_DAYS_FROM_YEAR_0, MIN_YEAR,
};
use crate::naive::internals::YearFlags;
use crate::oldtime::Duration;
use crate::{Datelike, Weekday};
use crate::{Datelike, Month, Weekday};
use std::{
convert::{TryFrom, TryInto},
i32, u32,
};

#[test]
fn test_helpers() {
let months = &[
Month::January,
Month::February,
Month::March,
Month::April,
Month::May,
Month::June,
Month::July,
Month::August,
Month::September,
Month::October,
Month::November,
Month::December,
];

fn days_in_month(month: Month, flags: YearFlags) -> u8 {
match month {
Month::January => 31,
Month::February if flags.ndays() == 366 => 29,
Month::February => 28,
Month::March => 31,
Month::April => 30,
Month::May => 31,
Month::June => 30,
Month::July => 31,
Month::August => 31,
Month::September => 30,
Month::October => 31,
Month::November => 30,
Month::December => 31,
}
}
for year in 0..=9999 {
assert_eq!(NaiveDate::start_year(year).to_string(), format!("{:04}-01-01", year));
assert_eq!(NaiveDate::end_year(year).to_string(), format!("{:04}-12-31", year));
assert_eq!(
NaiveDate::start_year(year).start_time().to_string(),
format!("{:04}-01-01 00:00:00", year)
);

for month in months {
assert_eq!(
NaiveDate::start_month(year, *month).to_string(),
format!("{:04}-{:02}-01", year, month.number_from_month())
);
assert_eq!(
NaiveDate::end_month(year, *month).to_string(),
format!(
"{:04}-{:02}-{:02}",
year,
month.number_from_month(),
days_in_month(*month, YearFlags::from_year(year.into()))
)
);
}
}
for year in std::i16::MIN..=std::i16::MAX {
NaiveDate::start_year(year);
NaiveDate::end_year(year);
assert_eq!(
NaiveDate::start_year(year),
NaiveDate::start_year(year).start_time().date()
);

for month in months {
NaiveDate::start_month(year, *month);
NaiveDate::end_month(year, *month);
}
}
}

#[test]
fn diff_months() {
// identity
Expand Down