BDE 4.39.x Production Release
Loading...
Searching...
No Matches
bdlt_dateutil.h
Go to the documentation of this file.
1/// @file bdlt_dateutil.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bdlt_dateutil.h -*-C++-*-
8#ifndef INCLUDED_BDLT_DATEUTIL
9#define INCLUDED_BDLT_DATEUTIL
10
11#include <bsls_ident.h>
12BSLS_IDENT("$Id: $")
13
14/// @defgroup bdlt_dateutil bdlt_dateutil
15/// @brief Provide common non-primitive operations on date objects.
16/// @addtogroup bdl
17/// @{
18/// @addtogroup bdlt
19/// @{
20/// @addtogroup bdlt_dateutil
21/// @{
22///
23/// <h1> Outline </h1>
24/// * <a href="#bdlt_dateutil-purpose"> Purpose</a>
25/// * <a href="#bdlt_dateutil-classes"> Classes </a>
26/// * <a href="#bdlt_dateutil-description"> Description </a>
27/// * <a href="#bdlt_dateutil-yyyymmdd-format"> "YYYYMMDD" Format </a>
28/// * <a href="#bdlt_dateutil-end-of-month-adjustment-conventions"> End-of-Month Adjustment Conventions </a>
29/// * <a href="#bdlt_dateutil-usage"> Usage </a>
30/// * <a href="#bdlt_dateutil-example-1-schedule-generation"> Example 1: Schedule Generation </a>
31///
32/// # Purpose {#bdlt_dateutil-purpose}
33/// Provide common non-primitive operations on date objects.
34///
35/// # Classes {#bdlt_dateutil-classes}
36///
37/// - bdlt::DateUtil: namespace for non-primitive operations on date objects
38///
39/// @see bdlt_date
40///
41/// # Description {#bdlt_dateutil-description}
42/// This component provides a `struct`, `bdlt::DateUtil`, that
43/// serves as a namespace for utility functions that operate on `bdlt::Date`
44/// objects.
45///
46/// The following list of methods are provided by `bdlt::DateUtil`:
47/// @code
48/// 'isValidYYYYMMDD' o Validate or convert to and from the
49/// 'convertFromYYYYMMDDRaw' "YYYYMMDD" format
50/// 'convertFromYYYYMMDD' (see {"YYYYMMDD" Format}).
51/// 'convertToYYYYMMDD'
52///
53/// 'nextDayOfWeek' o Move a date to the next or the previous
54/// 'nextDayOfWeekInclusive' specified day of week.
55/// 'previousDayOfWeek'
56/// 'previousDayOfWeekInclusive'
57///
58/// 'earliestDayOfWeekInMonth' o Find a specified day of the week in a
59/// 'nthDayOfWeekInMonth' specified year and month.
60/// 'lastDayOfWeekInMonth'
61/// 'lastDayInMonth'
62///
63/// 'addMonthsEom' o Add a specified number of months to a date
64/// 'addMonthsNoEom' using either the end-of-month or the
65/// 'addMonths' non-end-of-month convention (see
66/// {End-of-Month Adjustment Conventions}).
67///
68/// 'addYearsEom' o Add a specified number of years to a date
69/// 'addYearsNoEom' using either the end-of-month or the
70/// 'addYears' non-end-of-month convention (see
71/// {End-of-Month Adjustment Conventions}).
72///
73/// 'fromYmd' o Validate the input parameters and construct
74/// a `Date` object if they are valid.
75/// @endcode
76///
77/// ## "YYYYMMDD" Format {#bdlt_dateutil-yyyymmdd-format}
78///
79///
80/// The "YYYYMMDD" format is a common integral representation of a date that is
81/// human readable and maintains appropriate ordering when sorted using integer
82/// comparisons. The notation uses eight digits (from left to right): four
83/// digits for the year, two digits for the month, and two digits for the day of
84/// the month. For example, February 1, 2014, is represented by the number
85/// 20140201.
86///
87/// Note that the year is not restricted to values on or after 1000, so, for
88/// example, 10102 (or 00010102) represents the date January 2, 0001.
89///
90/// ## End-of-Month Adjustment Conventions {#bdlt_dateutil-end-of-month-adjustment-conventions}
91///
92///
93/// Two adjustment conventions are used to determine the behavior of the
94/// functions (`addMonths` and `addYears`) that adjust a date by a particular
95/// number of months or years: the end-of-month convention and the
96/// non-end-of-month convention. The difference between the two conventions is
97/// that the end-of-month convention adjusts the resulting date to the end of
98/// the month if the original date is the last day of the month, while the
99/// non-end-of-month convention does not perform this adjustment.
100///
101/// For example, if we add 3 months to February 28, 2013 using the
102/// non-end-of-month convention, then the resulting date will be May 28, 2013.
103/// If we do the same operation except using the end-of-month convention, then
104/// the resulting date will be May 31, 2013.
105///
106/// More formal definitions of the two conventions are provided below:
107///
108/// The End-of-Month Convention:
109/// If the original date to be adjusted is the last day of a month, or if
110/// the day of the month of the original date does not exist in the
111/// resulting date, then adjust the resulting date to be the last day of
112/// the month.
113///
114/// The Non-End-of-Month Convention:
115/// If the day of the month of the original date does not exist in the
116/// resulting date, then adjust the resulting date to be the last day of
117/// the month.
118///
119/// ## Usage {#bdlt_dateutil-usage}
120///
121///
122/// This section illustrates intended use of this component.
123///
124/// ### Example 1: Schedule Generation {#bdlt_dateutil-example-1-schedule-generation}
125///
126///
127/// Suppose that given a starting date in the "YYYYMMDD" format, we want to
128/// generate a schedule for an event that occurs on the same day of the month
129/// for 12 months.
130///
131/// First, we use the `bdlt::DateUtil::convertFromYYYYMMDD` function to convert
132/// the integer into a `bdlt::Date`:
133/// @code
134/// const int startingDateYYYYMMDD = 20130430;
135///
136/// bdlt::Date date;
137/// int rc = bdlt::DateUtil::convertFromYYYYMMDD(&date, startingDateYYYYMMDD);
138/// assert(0 == rc);
139/// @endcode
140/// Now, we use the `addMonthsEom` function to generate the schedule. Note that
141/// `addMonthsEom` adjusts the resulting date to be the last day of the month if
142/// the original date is the last day of the month, while `addMonthsNoEom` does
143/// not make this adjustment.
144/// @code
145/// bsl::vector<bdlt::Date> schedule;
146/// schedule.push_back(date);
147///
148/// for (int i = 1; i < 12; ++i) {
149/// schedule.push_back(bdlt::DateUtil::addMonthsEom(date, i));
150/// }
151/// @endcode
152/// Finally, we print the generated schedule to the console and observe the
153/// output:
154/// @code
155/// bsl::copy(schedule.begin(),
156/// schedule.end(),
157/// bsl::ostream_iterator<bdlt::Date>(bsl::cout, "\n"));
158///
159/// // Expected output on the console:
160/// //
161/// // 30APR2013
162/// // 31MAY2013
163/// // 30JUN2013
164/// // 31JUL2013
165/// // 31AUG2013
166/// // 30SEP2013
167/// // 31OCT2013
168/// // 30NOV2013
169/// // 31DEC2013
170/// // 31JAN2014
171/// // 28FEB2014
172/// // 31MAR2014
173/// @endcode
174/// Notice that the dates have been adjusted to the end of the month. If we had
175/// used `addMonthsNoEom` instead of `addMonthsEom`, this adjustment would not
176/// have occurred.
177/// @}
178/** @} */
179/** @} */
180
181/** @addtogroup bdl
182 * @{
183 */
184/** @addtogroup bdlt
185 * @{
186 */
187/** @addtogroup bdlt_dateutil
188 * @{
189 */
190
191#include <bdlscm_version.h>
192
193#include <bdlt_date.h>
194#include <bdlt_dayofweek.h>
196
197#include <bsls_assert.h>
198#include <bsls_review.h>
199
200#include <bsl_optional.h>
201
202
203namespace bdlt {
204
205 // ===============
206 // struct DateUtil
207 // ===============
208
209/// This `struct` provides a namespace for utility functions that provide
210/// non-primitive operations on dates.
211///
212/// See @ref bdlt_dateutil
213struct DateUtil {
214
215 private:
216 // PRIVATE CLASS METHODS
217
218 /// Return the date that is the specified `numYears` from the specified
219 /// `original` date (which must be either the 28th or 29th of February),
220 /// adjusted as necessary according to the following (end-of-month)
221 /// rules: (1) if `original` is the last day of a month, adjust the
222 /// result to be the last day of the month, and (2) if the day of the
223 /// month in `original` does not exist in the month of the result (e.g.,
224 /// February 29, 2001), move the resulting date to the last day of the month.
225 ///
226 /// \pre The behavior is undefined unless `original` is either the
227 /// 28th or 29th of February, and the resulting date results in a valid `Date` value.
228 ///
229 /// \note Note that `numYears` may be negative.
230 static Date addYearsEomEndOfFebruary(const Date& original, int numYears);
231
232 public:
233 // CLASS METHODS
234
235 /// Return the date that is the specified `numMonths` from the specified
236 /// `original` date, adjusted as necessary according to the specified
237 /// `eomFlag` (end-of-month flag). If `eomFlag` is `true` and
238 /// `original` is the last day of the month, then adjust the result to
239 /// be the last day of the month; if `eomFlag` is `false`, then no such
240 /// adjustment is performed. In any case, if the day of the month in
241 /// `original` does not exist in the month of the result (e.g., February
242 /// 29, 2001), move the resulting date to the last day of the month.
243 ///
244 /// \pre The behavior is undefined unless the operation results in a valid `Date` value.
245 ///
246 /// \note Note that `numMonths` may be negative.
247 static Date addMonths(const Date& original, int numMonths, bool eomFlag);
248
249 /// Return the date that is the specified `numMonths` from the specified
250 /// `original` date, adjusted as necessary according to the following
251 /// (end-of-month) rules: (1) if `original` is the last day of a month,
252 /// adjust the result to be the last day of the month, and (2) if the
253 /// day of the month in `original` does not exist in the month of the
254 /// result (e.g., February 30), move the resulting date to the last day of the month.
255 ///
256 /// \pre The behavior is undefined unless the operation results in a valid `Date` value.
257 ///
258 /// \note Note that `numMonths` may be
259 /// negative.
260 static Date addMonthsEom(const Date& original, int numMonths);
261
262 /// Return the date that is the specified `numMonths` from the specified
263 /// `original` date, adjusted as necessary according to the following
264 /// (non-end-of-month) rule: if the day of the month in `original` does
265 /// not exist in the month of the result (e.g., February 29, 2001), move
266 /// the resulting date to the last day of the month.
267 ///
268 /// \pre The behavior is undefined unless the operation results in a valid `Date` value.
269 ///
270 /// \note Note that `numMonths` may be negative.
271 static Date addMonthsNoEom(const Date& original, int numMonths);
272
273 /// Return the date that is the specified `numYears` from the specified
274 /// `original` date, adjusted as necessary according to the specified
275 /// `eomFlag` (end-of-month flag). If `eomFlag` is `true` and
276 /// `original` is the last day of the month, then adjust the result to
277 /// be the last day of the month; if `eomFlag` is `false`, then no such
278 /// adjustment is performed. In any case, if the day of the month in
279 /// `original` does not exist in the month of the result (e.g., February
280 /// 29, 2001), move the resulting date to the last day of the month.
281 ///
282 /// \pre The behavior is undefined unless the operation results in a valid `Date` value.
283 ///
284 /// \note Note that `numYears` may be negative.
285 static Date addYears(const Date& original, int numYears, bool eomFlag);
286
287 /// Return the date that is the specified `numYears` from the specified
288 /// `original` date, adjusted as necessary according to the following
289 /// (end-of-month) rules: (1) if `original` is the last day of a month,
290 /// adjust the result to be the last day of the month, and (2) if the
291 /// day of the month in `original` does not exist in the month of the
292 /// result (e.g., February 29, 2001), move the resulting date to the last day of the month.
293 ///
294 /// \pre The behavior is undefined unless the operation results in a valid `Date` value.
295 ///
296 /// \note Note that `numYears` may
297 /// be negative.
298 static Date addYearsEom(const Date& original, int numYears);
299
300 /// Return the date that is the specified `numYears` from the specified
301 /// `original` date, adjusted as necessary according to the following
302 /// (non-end-of-month) rule: if the day of the month in `original` does
303 /// not exist in the month of the result (e.g., February 30), move the
304 /// resulting date to the last day of the month.
305 ///
306 /// \pre The behavior is undefined unless the operation results in a valid `Date` value.
307 ///
308 /// \note Note that `numYears` may be negative.
309 static Date addYearsNoEom(const Date& original, int numYears);
310
311 /// Load, into the specified `result`, the `Date` value represented by
312 /// the specified `yyyymmddValue` in the "YYYYMMDD" format. Return 0 on
313 /// success, and a non-zero value, with no effect on `result`, if
314 /// `yyyymmddValue` does not represent a valid `Date`.
315 static int convertFromYYYYMMDD(Date *result, int yyyymmddValue);
316
317 /// Return the `Date` value represented by the specified `yyyymmddValue` in the "YYYYMMDD" format.
318 ///
319 /// \pre The behavior is undefined unless
320 /// `yyyymmddValue` represents a valid `Date`.
321 static Date convertFromYYYYMMDDRaw(int yyyymmddValue);
322
323 /// Return the integer value in the "YYYYMMDD" format that represents
324 /// the specified `date`.
325 static int convertToYYYYMMDD(const Date& date);
326
327 /// Return the earliest date in the specified `month` of the specified
328 /// `year` that falls on the specified `dayOfWeek`.
329 ///
330 /// \pre The behavior is undefined unless `1 <= year <= 9999` and `1 <= month <= 12`.
331 static Date earliestDayOfWeekInMonth(int year,
332 int month,
333 DayOfWeek::Enum dayOfWeek);
334
335 /// Return an `optional` having a `Date` with the specified `year`,
336 /// `month`, and `day`, if those form a valid `Date` (see `Date::isValid`);
337 /// otherwise return an `optional` without a value.
338 static bsl::optional<Date> fromYmd(int year, int month, int day);
339
340 /// Return `true` if the specified `yyyymmddValue` represents a valid
341 /// `Date` value in the "YYYYMMDD" format, and `false` otherwise.
342 static bool isValidYYYYMMDD(int yyyymmddValue);
343
344 /// Return the latest date in the specified `month` of the specified `year`.
345 ///
346 /// \pre The behavior is undefined unless `1 <= year <= 9999` and
347 /// `1 <= month <= 12`.
348 static Date lastDayInMonth(int year, int month);
349
350 /// Return the latest date in the specified `month` of the specified
351 /// `year` that falls on the specified `dayOfWeek`.
352 ///
353 /// \pre The behavior is undefined unless `1 <= year <= 9999` and `1 <= month <= 12`.
354 static Date lastDayOfWeekInMonth(int year,
355 int month,
356 DayOfWeek::Enum dayOfWeek);
357
358 /// Return the first date *after* the specified `date` that falls on the specified `dayOfWeek`.
359 ///
360 /// \pre The behavior is undefined unless the
361 /// resulting date is no later than 9999/12/31.
362 static Date nextDayOfWeek(DayOfWeek::Enum dayOfWeek, const Date& date);
363
364 /// Return the first date *on* or *after* the specified `date` that
365 /// falls on the specified `dayOfWeek`.
366 ///
367 /// \pre The behavior is undefined unless the resulting date is no later than 9999/12/31.
369 const Date& date);
370
371 /// Return the date in the specified `month` of the specified `year`
372 /// corresponding to the specified `n`th occurrence of the specified
373 /// `dayOfWeek`. If `n < 0`, return the date corresponding to the
374 /// `-n`th occurrence of the `dayOfWeek` counting from the end of the
375 /// `month` towards the first of the `month`. If `5 == n` and a result
376 /// cannot be found in `month`, then return the date of the first
377 /// `dayOfWeek` in the following month. If `-5 == n` and a result
378 /// cannot be found in `month`, then return the date of the last `dayOfWeek` in the previous month.
379 ///
380 /// \pre The behavior is undefined unless
381 /// `1 <= year <= 9999`, `1 <= month <= 12`, `n != 0`, `-5 <= n <= 5`,
382 /// and the resulting date is neither earlier than 0001/01/01 nor later
383 /// than 9999/12/31.
384 ///
385 /// For example:
386 /// @code
387 /// nthDayOfWeekInMonth(2004, 11, DayOfWeek::e_THURSDAY, 4);
388 /// @endcode
389 /// returns November 25, 2004, the fourth Thursday in November, 2004.
390 static Date nthDayOfWeekInMonth(int year,
391 int month,
392 DayOfWeek::Enum dayOfWeek,
393 int n);
394
395 /// Return the last date *before* the specified `date` that falls on the specified `dayOfWeek`.
396 ///
397 /// \pre The behavior is undefined unless the
398 /// resulting date is no earlier than 1/1/1.
399 static Date previousDayOfWeek(DayOfWeek::Enum dayOfWeek, const Date& date);
400
401 /// Return the last date *on* or *before* the specified `date` that
402 /// falls on the specified `dayOfWeek`.
403 ///
404 /// \pre The behavior is undefined unless the resulting date is no earlier than 1/1/1.
406 const Date& date);
407};
408
409// ============================================================================
410// INLINE DEFINITIONS
411// ============================================================================
412
413 // ---------------
414 // struct DateUtil
415 // ---------------
416
417// CLASS METHODS
418inline
419Date DateUtil::addMonths(const Date& original, int numMonths, bool eomFlag)
420{
421
422 return eomFlag ? addMonthsEom(original, numMonths)
423 : addMonthsNoEom(original, numMonths);
424}
425
426inline
427Date DateUtil::addYears(const Date& original, int numYears, bool eomFlag)
428{
429
430 return eomFlag ? addYearsEom(original, numYears)
431 : addYearsNoEom(original, numYears);
432}
433
434inline
435Date DateUtil::addYearsEom(const Date& original, int numYears)
436{
437 BSLS_REVIEW( 1 <= original.year() + numYears);
438 BSLS_REVIEW(9999 >= original.year() + numYears);
439
440 if (2 == original.month() && 28 <= original.day()) {
441 return addYearsEomEndOfFebruary(original, numYears); // RETURN
442 }
443 return Date(original.year() + numYears, original.month(), original.day());
444}
445
446inline
447Date DateUtil::addYearsNoEom(const Date& original, int numYears)
448{
449 BSLS_REVIEW( 1 <= original.year() + numYears);
450 BSLS_REVIEW(9999 >= original.year() + numYears);
451
452 const int newYear = original.year() + numYears;
453
454 if (2 == original.month() && 29 == original.day()) {
455 return Date(newYear,
456 original.month(),
457 SerialDateImpUtil::isLeapYear(newYear) ? 29 : 28);
458 // RETURN
459
460 }
461 return Date(newYear, original.month(), original.day());
462}
463
464inline
465int DateUtil::convertFromYYYYMMDD(Date *result, int yyyymmddValue)
466{
467 BSLS_REVIEW(result);
468
469 if (!isValidYYYYMMDD(yyyymmddValue)) {
470 return 1; // RETURN
471 }
472 *result = convertFromYYYYMMDDRaw(yyyymmddValue);
473
474 return 0;
475}
476
477inline
479{
480 BSLS_ASSERT_SAFE(isValidYYYYMMDD(yyyymmddValue));
481
482 return Date(yyyymmddValue / 10000,
483 (yyyymmddValue / 100) % 100,
484 yyyymmddValue % 100);
485}
486
487inline
489{
490 return date.year() * 10000 + date.month() * 100 + date.day();
491}
492
493inline
495 int month,
496 DayOfWeek::Enum dayOfWeek)
497{
498 BSLS_ASSERT_SAFE(1 <= year); BSLS_ASSERT_SAFE(year <= 9999);
499 BSLS_ASSERT_SAFE(1 <= month); BSLS_ASSERT_SAFE(month <= 12);
500
501 return nextDayOfWeekInclusive(dayOfWeek, Date(year, month, 1));
502}
503
504inline
505bsl::optional<Date> DateUtil::fromYmd(int year, int month, int day)
506{
507 return Date::isValid(year, month, day)
508 ? bsl::optional<Date>(bsl::in_place, year, month, day)
509 : bsl::nullopt;
510}
511
512inline
513bool DateUtil::isValidYYYYMMDD(int yyyymmddValue)
514{
515 const int day = yyyymmddValue % 100;
516 yyyymmddValue /= 100;
517 const int month = yyyymmddValue % 100;
518
519 return SerialDateImpUtil::isValidYearMonthDay(yyyymmddValue / 100,
520 month,
521 day);
522}
523
524inline
525Date DateUtil::lastDayInMonth(int year, int month)
526{
527 BSLS_REVIEW(1 <= year); BSLS_REVIEW(year <= 9999);
528 BSLS_REVIEW(1 <= month); BSLS_REVIEW(month <= 12);
529
530 return Date(year,
531 month,
533}
534
535} // close package namespace
536
537
538#endif
539
540// ----------------------------------------------------------------------------
541// Copyright 2016 Bloomberg Finance L.P.
542//
543// Licensed under the Apache License, Version 2.0 (the "License");
544// you may not use this file except in compliance with the License.
545// You may obtain a copy of the License at
546//
547// http://www.apache.org/licenses/LICENSE-2.0
548//
549// Unless required by applicable law or agreed to in writing, software
550// distributed under the License is distributed on an "AS IS" BASIS,
551// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
552// See the License for the specific language governing permissions and
553// limitations under the License.
554// ----------------------------- END-OF-FILE ----------------------------------
555
556/** @} */
557/** @} */
558/** @} */
Definition bdlt_date.h:294
static bool isValid(int year, int dayOfYear)
Definition bdlt_date.h:1044
int day() const
Return the day of the month in the range [1 .. 31] of this date.
Definition bdlt_date.h:955
int year() const
Return the year in the range [1 .. 9999] of this date.
Definition bdlt_date.h:1005
int month() const
Return the month of the year in the range [1 .. 12] of this date.
Definition bdlt_date.h:993
Definition bslstl_optional.h:2043
#define BSLS_ASSERT_SAFE(X)
Definition bsls_assert.h:1917
#define BSLS_IDENT(str)
BSLS_IDENT() - insert string into .comment binary segment (if supported)
Definition bsls_ident.h:238
#define BSLS_REVIEW(X)
Definition bsls_review.h:1019
Definition bbldc_basicisma30360.h:112
const nullopt_t nullopt
const in_place_t in_place
Definition bdlt_dateutil.h:213
static Date addYears(const Date &original, int numYears, bool eomFlag)
Definition bdlt_dateutil.h:427
static bsl::optional< Date > fromYmd(int year, int month, int day)
Definition bdlt_dateutil.h:505
static int convertToYYYYMMDD(const Date &date)
Definition bdlt_dateutil.h:488
static Date nthDayOfWeekInMonth(int year, int month, DayOfWeek::Enum dayOfWeek, int n)
static Date addMonthsEom(const Date &original, int numMonths)
static Date convertFromYYYYMMDDRaw(int yyyymmddValue)
Definition bdlt_dateutil.h:478
static int convertFromYYYYMMDD(Date *result, int yyyymmddValue)
Definition bdlt_dateutil.h:465
static Date lastDayInMonth(int year, int month)
Definition bdlt_dateutil.h:525
static Date addYearsEom(const Date &original, int numYears)
Definition bdlt_dateutil.h:435
static Date addMonths(const Date &original, int numMonths, bool eomFlag)
Definition bdlt_dateutil.h:419
static Date lastDayOfWeekInMonth(int year, int month, DayOfWeek::Enum dayOfWeek)
static Date previousDayOfWeek(DayOfWeek::Enum dayOfWeek, const Date &date)
static Date addYearsNoEom(const Date &original, int numYears)
Definition bdlt_dateutil.h:447
static Date previousDayOfWeekInclusive(DayOfWeek::Enum dayOfWeek, const Date &date)
static Date nextDayOfWeek(DayOfWeek::Enum dayOfWeek, const Date &date)
static bool isValidYYYYMMDD(int yyyymmddValue)
Definition bdlt_dateutil.h:513
static Date addMonthsNoEom(const Date &original, int numMonths)
static Date earliestDayOfWeekInMonth(int year, int month, DayOfWeek::Enum dayOfWeek)
Definition bdlt_dateutil.h:494
static Date nextDayOfWeekInclusive(DayOfWeek::Enum dayOfWeek, const Date &date)
Enum
Enumerated day-of-week values.
Definition bdlt_dayofweek.h:125
static bool isValidYearMonthDay(int year, int month, int day)
Definition bdlt_posixdateimputil.h:782
static bool isLeapYear(int year)
Definition bdlt_posixdateimputil.h:766
static int lastDayOfMonth(int year, int month)