BDE 4.14.0 Production release
|
Provide a value-semantic type to represent dates.
This component defines a value-semantic class, bdlt::Date
, capable of representing any valid date that is consistent with the Unix (POSIX) calendar restricted to the years 1 through 9999 (inclusive):
"Actual" (i.e., natural) day and date calculations are supported directly by bdlt::Date
and its associated free operators. Calculations involving business days (or holidays), and day-count conventions (e.g., "ISMA30360"), can be found elsewhere.
See bdlt_calendar and the bbldc
day-count convention package.
A bdlt::Date
object always represents a valid date value as defined by the standard Unix calendar. The value of a bdlt::Date
object can be expressed in the interface as either (year, month, day)
, the canonical representation of dates, or (year, dayOfYear)
. For example, (1959, 3, 8)
represents the same valid bdlt::Date
value as (1959, 67)
because the 8th day of the 3rd month of the year 1959 is also the 67th day of the year 1959.
Of course, not all combinations of (year, month, day)
and (year, dayOfYear)
constitute valid values for bdlt::Date
objects. A (year, dayOfYear)
pair does not represent a valid bdlt::Date
value unless 1 <= year <= 9999
and 1 <= dayOfYear <= 366
. Additionally, if year
is not a leap year, then the representation is not valid unless 1 <= dayOfYear <= 365
.
In a leap year, February has 29 days instead of the usual 28. (Thus, leap years have 366 days instead of the usual 365.) Prior to 1752, the Unix calendar follows the convention of the Julian calendar: every year divisible by 4 is a leap year. After 1752, the Unix calendar follows the (more accurate) Gregorian calendar: a year is leap year if it is divisible by 4, but not divisible by 100, unless it is also divisible by 400. Note that 1752 is the year that Britain and its empire (including the colonies that later became the United States) switched from the Julian to the Gregorian calendar. See:
Moreover, the Unix calendar lacks the dates [3 .. 13]
in September 1752, days that were dropped to align British dates with those used on the European continent. Thus:
Note that two static
(class) methods:
are provided within bdlt::Date
to indicate whether the given pair or triple of integers would represent a valid bdlt::Date
value (e.g., prior to using them to construct a bdlt::Date
object).
Note that it is incumbent on the client of a bdlt::Date
object never to cause it, directly or indirectly, to hold an invalid value, which can occur only by violating explicitly stated preconditions. For example, invoking operator++
on a date object that represents the valid bdlt::Date
value 9999/12/31 (December 31, 9999) is a contract violation that can lead to undefined behavior. Similarly, attempting to set the value of an existing date using the setYearMonthDay
manipulator such that invoking isValidYearMonthDay
would return false
on the same (year, month, day)
arguments is another contract violation.
When setting a bdlt::Date
object to a particular value, there are two forms of methods provided for both of the (year, month, day)
and (year, dayOfYear)
representations of date values. When you are certain that the value you are trying to set is valid, either of the following two runtime-efficient methods can be used safely:
If, however, the integral date attributes at hand are not known to represent a valid date, they must first be validated, e.g., by calling one of the two static
isValid*
methods, or by calling the appropriate set method having the IfValid
suffix, which will always verify validity before either setting the value and returning 0, or returning a non-zero status with no effect on the object:
Note that if the value is "known" to be valid, and these latter IfValid
variants are called without checking their return status, we run the risk of a "double fault" in that if the value is not actually valid, there is no way for a robust implementation (such as this one) to check for the error in a defensive (e.g., "DEBUG" or "SAFE") build mode.
The version 1 format supported by bdlt::Date
for BDEX streaming is expressly intended for maintaining some degree of "compatibility" with versions of this date class that are built to use the proleptic Gregorian calendar.
WARNING: Use of the proleptic Gregorian version of this class is disallowed in Bloomberg code.
Over the range of dates supported by bdlt::Date
([0001JAN01 .. 9999DEC31]
), the proleptic Gregorian calendar (used by bdlt::Date
) has two fewer days than cal
, and some dates that exist in one calendar do not exist in the other; therefore, true compatibility is not possible. The compatibility guaranteed by BDEX streaming version 1 is such that all dates in the range [1752SEP14 .. 9999DEC31]
, as well as the default value (0001JAN01
), can be successfully exchanged, via BDEX, between bdlt::Date
classes built to use the POSIX calendar and those built to use the proleptic Gregorian calendar.
A common standard text representation of a date and time value is described by ISO 8601. BDE provides the bdlt_iso8601util component for conversion to and from the standard ISO8601 format.
This section illustrates intended use of this component.
The following snippets of code illustrate how to create and use a bdlt::Date
object.
First, we create a default date d1
:
Next, we set d1
to July 4, 1776:
We can also use setYearMonthDayIfValid
if we are not sure whether a particular year/month/day combination constitutes a valid bdlt::Date
. For example, if we want to set d1
to 1900/02/29
, and it turns out that year 1900 was not a leap year (it wasn't), there will be no effect on the current value of the object:
Then, from d1
, we can determine the day of the year, and the day of the week, of July 4, 1776:
Next, we create a bdlt::Date
object, d2
, using the year/day-of-year representation for dates:
Then, we add six days to the value of d2
:
Now, we subtract d1
from d2
, storing the (signed) difference in days (a.k.a. actual difference) in daysDiff
:
Finally, we stream the value of d2
to stdout
:
The streaming operator produces:
on stdout
.