Skip to content

Commit bbe5709

Browse files
committed
convenience functions to create NaiveDates
1 parent ff370ae commit bbe5709

File tree

1 file changed

+119
-2
lines changed

1 file changed

+119
-2
lines changed

src/naive/date.rs

Lines changed: 119 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use crate::format::{Item, Numeric, Pad};
2525
use crate::month::Months;
2626
use crate::naive::{IsoWeek, NaiveDateTime, NaiveTime};
2727
use crate::oldtime::Duration as OldDuration;
28-
use crate::{Datelike, Duration, Weekday};
28+
use crate::{Datelike, Duration, Month, Weekday};
2929

3030
use super::internals::{self, DateImpl, Mdf, Of, YearFlags};
3131
use super::isoweek;
@@ -236,6 +236,49 @@ fn test_date_bounds() {
236236
}
237237

238238
impl NaiveDate {
239+
/// Create a `NaiveDate` of the first day of the given year
240+
///
241+
/// This takes an `i16` rather than `i32` to ensure it is not
242+
/// out of the allowed range
243+
pub fn start_year(year: i16) -> NaiveDate {
244+
// ideally this unwrap can be later removed
245+
Self::from_ymd_opt(year.into(), 1, 1).unwrap()
246+
}
247+
248+
/// Create a `NaiveDate` of the last day of the given year
249+
///
250+
/// This takes an `i16` rather than `i32` to ensure it is not
251+
/// out of the allowed range
252+
pub fn end_year(year: i16) -> NaiveDate {
253+
// ideally this unwrap can be later removed
254+
Self::from_ymd_opt(i32::from(year) + 1, 1, 1).unwrap().pred_opt().unwrap()
255+
}
256+
257+
/// Create a `NaiveDate` of the first day of the given year and month
258+
///
259+
/// This takes an `i16` rather than `i32` to ensure it is not
260+
/// out of the allowed range
261+
pub fn start_month(year: i16, month: Month) -> NaiveDate {
262+
// ideally this unwrap can be later removed
263+
Self::from_ymd_opt(year.into(), month.number_from_month(), 1).unwrap()
264+
}
265+
266+
/// Create a `NaiveDate` of the ;ast day of the given year and month
267+
///
268+
/// This takes an `i16` rather than `i32` to ensure it is not
269+
/// out of the allowed range
270+
pub fn end_month(year: i16, month: Month) -> NaiveDate {
271+
// ideally this unwrap can be later removed
272+
let start = Self::start_month(year, month);
273+
274+
(start + Months::new(1)).pred_opt().unwrap()
275+
}
276+
277+
/// Create a `NaiveDateTime` at midnight on the given `NaiveDate`
278+
pub fn start_time(&self) -> NaiveDateTime {
279+
self.and_time(NaiveTime::MIN)
280+
}
281+
239282
/// Makes a new `NaiveDate` from year and packed ordinal-flags, with a verification.
240283
fn from_of(year: i32, of: Of) -> Option<NaiveDate> {
241284
if (MIN_YEAR..=MAX_YEAR).contains(&year) && of.valid() {
@@ -2166,13 +2209,87 @@ mod tests {
21662209
use super::{
21672210
Days, Months, NaiveDate, MAX_DAYS_FROM_YEAR_0, MAX_YEAR, MIN_DAYS_FROM_YEAR_0, MIN_YEAR,
21682211
};
2212+
use crate::naive::internals::YearFlags;
21692213
use crate::oldtime::Duration;
2170-
use crate::{Datelike, Weekday};
2214+
use crate::{Datelike, Month, Weekday};
21712215
use std::{
21722216
convert::{TryFrom, TryInto},
21732217
i32, u32,
21742218
};
21752219

2220+
#[test]
2221+
fn test_helpers() {
2222+
let months = &[
2223+
Month::January,
2224+
Month::February,
2225+
Month::March,
2226+
Month::April,
2227+
Month::May,
2228+
Month::June,
2229+
Month::July,
2230+
Month::August,
2231+
Month::September,
2232+
Month::October,
2233+
Month::November,
2234+
Month::December,
2235+
];
2236+
2237+
fn days_in_month(month: Month, flags: YearFlags) -> u8 {
2238+
match month {
2239+
Month::January => 31,
2240+
Month::February if flags.ndays() == 366 => 29,
2241+
Month::February => 28,
2242+
Month::March => 31,
2243+
Month::April => 30,
2244+
Month::May => 31,
2245+
Month::June => 30,
2246+
Month::July => 31,
2247+
Month::August => 31,
2248+
Month::September => 30,
2249+
Month::October => 31,
2250+
Month::November => 30,
2251+
Month::December => 31,
2252+
}
2253+
}
2254+
for year in 0..=9999 {
2255+
assert_eq!(NaiveDate::start_year(year).to_string(), format!("{:04}-01-01", year));
2256+
assert_eq!(NaiveDate::end_year(year).to_string(), format!("{:04}-12-31", year));
2257+
assert_eq!(
2258+
NaiveDate::start_year(year).start_time().to_string(),
2259+
format!("{:04}-01-01 00:00:00", year)
2260+
);
2261+
2262+
for month in months {
2263+
assert_eq!(
2264+
NaiveDate::start_month(year, *month).to_string(),
2265+
format!("{:04}-{:02}-01", year, month.number_from_month())
2266+
);
2267+
assert_eq!(
2268+
NaiveDate::end_month(year, *month).to_string(),
2269+
format!(
2270+
"{:04}-{:02}-{:02}",
2271+
year,
2272+
month.number_from_month(),
2273+
days_in_month(*month, YearFlags::from_year(year.into()))
2274+
)
2275+
);
2276+
}
2277+
}
2278+
for year in i16::MIN..=i16::MAX {
2279+
NaiveDate::start_year(year);
2280+
NaiveDate::end_year(year);
2281+
assert_eq!(
2282+
NaiveDate::start_year(year),
2283+
NaiveDate::start_year(year).start_time().date()
2284+
);
2285+
2286+
for month in months {
2287+
NaiveDate::start_month(year, *month);
2288+
NaiveDate::end_month(year, *month);
2289+
}
2290+
}
2291+
}
2292+
21762293
#[test]
21772294
fn diff_months() {
21782295
// identity

0 commit comments

Comments
 (0)