BDE 4.14.0 Production release
Loading...
Searching...
No Matches

Detailed Description

Outline

Purpose

Provide a value-semantic type to represent dates.

Classes

See also
bdlt_dayofweek, bdlt_serialdateimputil

Description

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):

http://pubs.opengroup.org/onlinepubs/9699919799/utilities/cal.html

"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.

Valid Date Values and Their Representations

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:

http://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar
https://en.wikipedia.org/wiki/Calendar_(New_Style)_Act_1750

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:

assert(++bdlt::Date(1752, 9, 2) == bdlt::Date(1752, 9, 14));
assert( bdlt::Date(1752, 9, 2) == --bdlt::Date(1752, 9, 14));
Definition bdlt_date.h:294

Note that two static (class) methods:

bool isValidYearDay(int year, int dayOfYear);
bool isValidYearMonthDay(int year, int month, int day);

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).

Ensuring bdlt::Date Validity

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:

void setYearDay(int year, int dayOfYear);
void setYearMonthDay(int year, int month, int day);

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:

int setYearDayIfValid(int year, int dayOfYear);
int setYearMonthDayIfValid(int year, int month, int day);

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.

BDEX Compatibility with Legacy POSIX-Based Date

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.

ISO Standard Text Representation

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.

Usage

This section illustrates intended use of this component.

Example 1: Basic Use of bdlt::Date

The following snippets of code illustrate how to create and use a bdlt::Date object.

First, we create a default date d1:

bdlt::Date d1; assert( 1 == d1.year());
assert( 1 == d1.month());
assert( 1 == d1.day());
int day() const
Return the day of the month in the range [1 .. 31] of this date.
Definition bdlt_date.h:927
int year() const
Return the year in the range [1 .. 9999] of this date.
Definition bdlt_date.h:977
int month() const
Return the month of the year in the range [1 .. 12] of this date.
Definition bdlt_date.h:965

Next, we set d1 to July 4, 1776:

d1.setYearMonthDay(1776, 7, 4);
assert(1776 == d1.year());
assert( 7 == d1.month());
assert( 4 == d1.day());
void setYearMonthDay(int year, int month, int day)
Definition bdlt_date.h:871

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:

int ret = d1.setYearMonthDayIfValid(1900, 2, 29);
assert( 0 != ret); // 1900 not leap year
assert(1776 == d1.year()); // no effect on 'd1'
assert( 7 == d1.month());
assert( 4 == d1.day());
int setYearMonthDayIfValid(int year, int month, int day)
Definition bdlt_date.h:881

Then, from d1, we can determine the day of the year, and the day of the week, of July 4, 1776:

int dayOfYear = d1.dayOfYear();
assert( 186 == dayOfYear);
assert(bdlt::DayOfWeek::e_THU == dayOfWeek);
int dayOfYear() const
Return the day of the year in the range [1 .. 366] of this date.
Definition bdlt_date.h:940
DayOfWeek::Enum dayOfWeek() const
Definition bdlt_date.h:933
Enum
Enumerated day-of-week values.
Definition bdlt_dayofweek.h:123
@ e_THU
Definition bdlt_dayofweek.h:129

Next, we create a bdlt::Date object, d2, using the year/day-of-year representation for dates:

bdlt::Date d2(1776, dayOfYear);
assert(1776 == d2.year());
assert( 186 == d2.dayOfYear());
assert( 7 == d2.month());
assert( 4 == d2.day());
assert( d1 == d2);

Then, we add six days to the value of d2:

d2 += 6; assert(1776 == d2.year());
assert( 7 == d2.month());
assert( 10 == d2.day());

Now, we subtract d1 from d2, storing the (signed) difference in days (a.k.a. actual difference) in daysDiff:

int daysDiff = d2 - d1; assert( 6 == daysDiff);

Finally, we stream the value of d2 to stdout:

bsl::cout << d2 << bsl::endl;

The streaming operator produces:

10JUL1776

on stdout.