Outline
Purpose
Provide low-level support functions for date-value manipulation.
Classes
Description
This component implements a utility class, bdlt::PosixDateImpUtil
, that provides a suite of low-level, date-related functions that can be used to validate, manipulate, and convert among values in three different formats:
YMD: year/month/day
YD: year/dayOfYear
S: serialDate
The supplied functionality can also be used for determining leap years, finding the last day in a 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 range 0001JAN01
to 9999DEC31
, according to the standard UNIX date implementation.
Representations, Valid Dates, and Leap Years
The "Calendar Date", or "year-month-day (ymd)", is the primary 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 will range from 0001/01/01
to 9999/12/31
, but are also governed by the standard UNIX (Gregorian) calendar convention.
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 ]
. Note that valid date values in this representation range from 0001/01
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 0001JAN01
). In this representation, valid date value are in the range [ 1 .. 3652061 ]
.
In a leap year, February has 29 days instead of the usual 28. For years subsequent to 1752, a year is a leap year if it is divisible by 4, but not divisible by 100, unless it is also divisible by 400. Prior to 1752, all years divisible by 4 were leap years. Note that the year 1752 had only 366 - 11 = 355
days, as September 3rd through the 13th were omitted (in a one-time correction to the previous, less accurate, leap-year convention).
Caching
To achieve maximal runtime performance, several of the functions in this component reserve the right to be implemented using statically cached (i.e., tabulated) values (which is inherently thread-safe). In all cases where a cache may be used, bdlt::PosixDateImpUtil
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 that cache in the first place (see bdlt_posixdateimputil.t.cpp
).
Usage
This component was created primarily to support the implementation of a general-purpose, value-semantic (vocabulary) "Date" type, but also provides many low-level utility functions suitable for direct use by other clients.
Creating a General Purpose Utility
Many of the functions provided by this component can be used directly by clients that want to ask questions about a particular date in one of the three supported formats.
What day of the week was January 3, 2010?
static int ymdToDayOfWeek(int year, int month, int day)
Definition bdlt_posixdateimputil.h:856
Was the year 2000 a leap year?
static bool isLeapYear(int year)
Definition bdlt_posixdateimputil.h:726
Was February 29, 1900, a valid date in history?
static bool isValidYearMonthDay(int year, int month, int day)
Definition bdlt_posixdateimputil.h:742
What was the last day of February in 1600?
static int lastDayOfMonth(int year, int month)
How many leap years occur from 1959 to 2012 inclusive?
static int numLeapYears(int year1, int year2)
On what day of the year does February 29, 2020 fall?
static int ymdToDayOfYear(int year, int month, int day)
In what month does the 120th day of 2011 fall?
static int ydToMonth(int year, int dayOfYear)
Definition bdlt_posixdateimputil.h:836
Implementing a Value-Semantic Date Type
Using the functions supplied in this component, we can easily implement a C++ class that represents abstract (mathematical) date values and performs a few common operations on them. The internal representation could be any of the three supported by this component; in this example, we will choose to represent the date value internally as a "serial date":
class MyDate {
int d_serialDate;
friend MyDate operator+(const MyDate&, int);
friend MyDate operator+(int, const MyDate&);
friend int operator- (const MyDate&, const MyDate&);
friend bool operator==(const MyDate&, const MyDate&);
private:
explicit MyDate(int serialDate);
Next we define the public interface of the class, with function-level documentation conspicuously omitted (note, however, that reference implementations with preconditions asserted will follow):
public:
enum Day {
SUN = 1,
MON = 2,
TUE = 3,
WED = 4,
THU = 5,
FRI = 6,
SAT = 7
};
static bool isValid(int year, int month, int day);
MyDate();
MyDate(const MyDate& original);
~MyDate();
MyDate& operator=(const MyDate& rhs);
MyDate& operator++();
MyDate& operator--();
MyDate& operator+=(int numDays);
MyDate& operator-=(int numDays);
void setYearMonthDay(int year, int month, int day);
bool setYearMonthDayIfValid(int year, int month, int day);
void getYearMonthDay(int *year, int *month, int *day) const;
int year() const;
int month() const;
int day() const;
Day dayOfWeek() const;
bool isLeapYear() const;
bsl::ostream& print(bsl::ostream& stream) const;
};
bool operator==(
const MyDate& lhs,
const MyDate& rhs);
bool operator!=(
const MyDate& lhs,
const MyDate& rhs);
bsl::ostream&
operator<<(bsl::ostream& stream,
const MyDate& rhs);
MyDate
operator+(
const MyDate& lhs,
int rhs);
MyDate
operator+(
int lhs,
const MyDate& rhs);
int operator-(
const MyDate& lhs,
const MyDate& rhs);
bsl::ostream & operator<<(bsl::ostream &stream, const bdlat_AttributeInfo &attributeInfo)
bool operator!=(const FileCleanerConfiguration &lhs, const FileCleanerConfiguration &rhs)
bool operator==(const FileCleanerConfiguration &lhs, const FileCleanerConfiguration &rhs)
TransformIterator< FUNCTOR, ITERATOR > operator--(TransformIterator< FUNCTOR, ITERATOR > &iterator, int)
FunctionOutputIterator< FUNCTION > & operator++(FunctionOutputIterator< FUNCTION > &iterator)
Do nothing and return specified iterator.
Definition bdlb_functionoutputiterator.h:405
TransformIterator< FUNCTOR, ITERATOR > operator+(const TransformIterator< FUNCTOR, ITERATOR > &iterator, typename TransformIterator< FUNCTOR, ITERATOR >::difference_type offset)
TransformIterator< FUNCTOR, ITERATOR > operator-(const TransformIterator< FUNCTOR, ITERATOR > &iterator, typename TransformIterator< FUNCTOR, ITERATOR >::difference_type offset)
We now provide a reference implementation of each of the methods and free (operator) functions associated with the MyDate
class defined above (using bsls_assert to identify preconditions and invariants where appropriate):
inline
MyDate::MyDate(int serialDate)
: d_serialDate(serialDate)
{
}
inline
bool MyDate::isValid(int year, int month, int day)
{
}
inline
MyDate::MyDate()
: d_serialDate(1)
{
}
inline
MyDate::MyDate(const MyDate& original)
: d_serialDate(original.d_serialDate)
{
}
inline
MyDate::~MyDate()
{
}
inline
MyDate& MyDate::operator=(const MyDate& rhs)
{
d_serialDate = rhs.d_serialDate;
return *this;
}
inline
MyDate& MyDate::operator++()
{
++d_serialDate;
return *this;
}
inline
MyDate& MyDate::operator--()
{
--d_serialDate;
return *this;
}
inline
MyDate& MyDate::operator+=(int numDays)
{
d_serialDate += numDays;
return *this;
}
inline
MyDate& MyDate::operator-=(int numDays)
{
d_serialDate -= numDays;
return *this;
}
inline
void MyDate::setYearMonthDay(int year, int month, int day)
{
}
inline
bool MyDate::setYearMonthDayIfValid(int year, int month, int day)
{
month,
day);
d_serialDate = newDate;
return true;
}
return false;
}
inline
void MyDate::getYearMonthDay(int *year, int *month, int *day) const
{
}
inline
int MyDate::year() const
{
}
inline
int MyDate::month() const
{
}
inline
int MyDate::day() const
{
}
inline
MyDate::Day MyDate::dayOfWeek() const
{
d_serialDate));
}
inline
bool MyDate::isLeapYear() const
{
}
inline
bool operator==(
const MyDate& lhs,
const MyDate& rhs)
{
return lhs.d_serialDate == rhs.d_serialDate;
}
inline
bool operator!=(
const MyDate& lhs,
const MyDate& rhs)
{
return !(lhs == rhs);
}
inline
bsl::ostream&
operator<<(bsl::ostream& stream,
const MyDate& rhs)
{
return rhs.print(stream);
}
inline
{
return MyDate(lhs.d_serialDate + rhs);
}
inline
{
return MyDate(lhs + rhs.d_serialDate);
}
inline
int operator-(
const MyDate& lhs,
const MyDate& rhs)
{
return lhs.d_serialDate - rhs.d_serialDate;
}
inline
{
MyDate tmp(object);
++object;
return tmp;
}
inline
{
MyDate tmp(object);
--object;
return tmp;
}
#define BSLS_ASSERT_SAFE(X)
Definition bsls_assert.h:1762
static bool isValidSerial(int serialDay)
Definition bdlt_posixdateimputil.h:760
static void serialToYmd(int *year, int *month, int *day, int serialDay)
static int serialToYear(int serialDay)
static int serialToMonth(int serialDay)
static int ymdToSerial(int year, int month, int day)
static int serialToDay(int serialDay)
static int serialToDayOfWeek(int serialDay)
The following definitions would appropriately appear in the implementation (.cpp
) file:
const char *const monthNames[] = {
0, "JAN", "FEB", "MAR", "APR",
"MAY", "JUN", "JUL", "AUG",
"SEP", "OCT", "NOV", "DEC"
};
bsl::ostream& MyDate::print(bsl::ostream& stream) const
{
if (!stream) {
return stream;
}
const int SIZE = 2 + 3 + 4 + 1;
char buf[SIZE];
int y, m, d;
buf[0] = d / 10 + '0';
buf[1] = d % 10 + '0';
bsl::memcpy(&buf[2], monthNames[m], 3);
buf[5] = y / 1000 + '0';
buf[6] = ((y % 1000) / 100) + '0';
buf[7] = ((y % 100) / 10) + '0';
buf[8] = y % 10 + '0';
buf[9] = 0;
stream << buf;
return stream;
}
The following snippets of code illustrate how to create and use a Mydate
object. First create a default object, d1
:
MyDate d1; assert( 1 == d1.year());
assert( 1 == d1.month());
assert( 1 == d1.day());
Next, set d1
to July 4, 1776:
d1.setYearMonthDay(1776, 7, 4); assert(1776 == d1.year());
assert( 7 == d1.month());
assert( 4 == d1.day());
Then create d2
as a copy of d1
:
MyDate d2(d1); assert(1776 == d2.year());
assert( 7 == d2.month());
assert( 4 == d2.day());
Now, add six days to the value of d2
:
d2 += 6; assert(1776 == d2.year());
assert( 7 == d2.month());
assert( 10 == d2.day());
Next subtract d1
from d2
, storing the difference (in days) in dDays
:
int dDays = d2 - d1; assert(6 == dDays);
Finally, stream the value of d2
to stdout
:
bsl::cout << d2 << bsl::endl;
The streaming operator produces the following output on stdout
: