BDE 4.14.0 Production release
|
Provide low-level support functions for date-value manipulation.
This component provides a utility struct
, bdlt::ProlepticDateImpUtil
, that defines a suite of low-level, date-related functions, which can be used to validate, manipulate, and convert among values in three different formats:
The supplied functionality can also be used (e.g.) for determining leap years, finding the last day in a given month, and for determining the day of the week for a given date. Note that in this component a "date" is understood to represent a valid day in the (YMD) range 0001/01/01
to 9999/12/31
according to the proleptic Gregorian calendar:
The "Calendar Date", or "year-month-day (ymd)", is the canonical representation and is denoted as "YYYY/MM/DD", with valid years being in the range [1 .. 9999]
. Within a valid year, valid months and valid days are confined to the respective ranges [1 .. 12]
and [1 .. 31]
. Valid dates in this representation range from 0001/01/01
to 9999/12/31
, governed by the proleptic Gregorian calendar. Specifically, within the 4th, 6th, 9th, and 11th months (respectively, April, June, September, and November) of any year, valid days are in the range [1 .. 30]
. Valid days for all other months of any year, with exception of the 2nd month (February), are in the range [1 .. 31]
.
In a leap year, February has 29 days instead of the usual 28. Thus, the range of valid days for February in a leap year is [1 .. 29]
; otherwise, the range of valid days for February is [1 .. 28]
. The proleptic Gregorian calendar retroactively applies the leap year rules instituted by the Gregorian Reformation to all years. In particular, a year in the range [1 .. 9999]
is a leap year if it is divisible by 4, but not divisible by 100, unless it is also divisible by 400. (Expressed conversely, all years not divisible by 4 are non-leap years, as are all century years not divisible by 400 (e.g., 1900).
The "Day-Of-Year Date", or "year-day (yd)" representation, denoted by "YYYY/DDD", represents dates by their year (again, in the range [1 .. 9999]
) and the day of year, in the range [1 .. 366]
. Valid date values in this representation range from 0001/001
to 9999/365
, with a day-of-year value of 366 permitted for leap years only.
The "Serial Date" representation depicts dates as consecutive integers beginning with 1 (representing 0001/01/01
). In this representation, valid date values are in the range [1 .. 3652059]
, with 3652059 representing 9999/12/31
.
To achieve maximal runtime performance, several of the functions in this component reserve the right to be implemented using statically cached (i.e., tabulated, pre-calculated) values (which is inherently thread-safe). For all functions where a cache may be used, bdlt::ProlepticDateImpUtil
also explicitly provides a NoCache
version (e.g., ymdToSerialNoCache
) that is guaranteed NOT to use a cache. Although the "normal" (potentially cached) functions typically gain huge performance advantages, the NoCache
versions may conceivably be preferred by the performance-minded user who is reasonably certain that the vast majority of date values of interest will miss the cache (thus incurring a small, but unnecessary overhead for the cache-hit tests). Note, however, that the NoCache
function variants are provided primarily for testing and for generating the cache in the first place (see this component's test driver).
This section illustrates intended use of this component.
The primary purpose of this component is to support the implementation of a general-purpose, value-semantic (vocabulary) "Date" type. However, it also provides many low-level utility functions suitable for direct use by other clients. In this example we employ several of the functions from this component to ask questions about particular dates in one of the three supported formats.
First, what day of the week was January 3, 2010?
Then, was the year 2000 a leap year?
Next, was February 29, 1900 a valid date in history?
Then, what was the last day of February in 1600?
Next, how many leap years occurred from 1959 to 2012, inclusive?
Now, on what day of the year will February 29, 2020 fall?
Finally, in what month did the 120th day of 2011 fall?
Using the functions supplied in this component, we can easily implement a C++ class that represents abstract (mathematical) date values and performs common operations on them. The internal representation could be any of the three supported by this component. In this example, we choose to represent the date value internally as a "serial date".
First, we define a partial interface of our date class, MyDate
, omitting many methods, free operators, and friend
declarations that do not contribute substantively to illustrating use of this component:
Then, we provide an implementation of the MyDate
methods and associated free operators declared above, using bsls_assert to identify preconditions and invariants where appropriate. Note the use of various bdlt::ProlepticDateImpUtil
functions in the code:
Next, we illustrate basic use of our MyDate
class, starting with the creation of a default object, d1
:
Now, we set d1
to July 4, 1776 via the setYearMonthDay
method, but we first verify that it is a valid date using isValid
:
Finally, using the value constructor, we create d2
to have the same value as d1
:
Note that equality comparison of MyDate
objects is very efficient, being comprised of a comparison of two int
values. Similarly, the MyDate
methods and free operators (not shown) that add a (signed) number of days to a date are also very efficient. However, one of the trade-offs of storing a date internally as a serial value is that operations involving conversion among the serial value and one or more of the year
, month
, and day
attributes (e.g., setYearMonthDay
, getYearMonthDay
) entail considerably more computation.