@@ -25,7 +25,7 @@ use crate::format::{Item, Numeric, Pad};
2525use crate :: month:: Months ;
2626use crate :: naive:: { IsoWeek , NaiveDateTime , NaiveTime } ;
2727use crate :: oldtime:: Duration as OldDuration ;
28- use crate :: { Datelike , Duration , Weekday } ;
28+ use crate :: { Datelike , Duration , Month , Weekday } ;
2929
3030use super :: internals:: { self , DateImpl , Mdf , Of , YearFlags } ;
3131use super :: isoweek;
@@ -236,6 +236,49 @@ fn test_date_bounds() {
236236}
237237
238238impl 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