3.5 RWDateTime
An
RWDateTime may be constructed in several ways. Unlike
RWDate and the deprecated
RWTime described in
Section 3.6 and
Section 3.7, the
RWDateTime default constructor does not automatically set the time and date to the current time and date. This change provides an efficient way to construct large arrays of
RWDateTime. The current time and date can still be set by using a constructor. Simply include the enum
RWDateTime::setCurrentTime in
RWDateTime’s constructor argument list. See the
SourcePro C++ API Reference Guide for a complete description of
RWDateTime.
For example, to construct an
RWDateTime with today’s date and current time, use the following:
RWDateTime dt(RWDateTime::setCurrentTime); // today, current time
An
RWDateTime can also be constructed from a given date and/or a given time. Or you can use an
RWDateTime constructor that takes a string and the enum
RWDateTime::SetType, which indicates whether you are setting a date, a time, or both. By default, the constructor expects to set both the date and time. For example, to construct an
RWDateTime for a given date, where time is of no concern, use:
RWDateTime dt(“June 2, 1952”, RWDateTime::setDate,
RWLocale::global()); // sets date to 6/2/52, 00:00:00
Similarly, to construct an
RWDateTime for a given time:
RWDateTime dt(“10:00 pm”, RWDateTime::setTime, RWLocale::global());
// sets date and time to today, 22:00:00
Finally, to construct an
RWDateTime for a given date and time, you can use the following techniques. Note that when you initialize both the day and time, the date precedes the time and the two values are separated by a semicolon.
RWDateTime dt(“June 4, 1968; 10:51 am”,
RWDateTime::setBoth,
RWLocale::global()); // 6/4/68, 10:51:00
or
RWDateTime dt(4,6,1968,10,51,0); // 6/4/68, 10:51:00
3.5.1 About Two or Four Digit Years
The default format for an
RWDateTime on many systems and in many locales contains only a two-digit year. Two-digit year specifiers assume the century 1900, so you should create programs that use four-digit year specifiers. You can enforce the use of a four-digit year by defining the build macro
RW_CENTURY_REQD in RCB when building the libraries.
Be aware, however, that defining this macro
requires a four-digit year to be provided when constructing an
RWDateTime. For example, the following code may fail:
std::stringstream s;
RWDateTime dt = RWDateTime::now();
s << dt << std::endl; // 1
RWDateTime dt2;
s >> dt2; // Exception!
The solution is to first convert the
RWDateTime to a string using
RWDateTime::asString() with a format string suitable for your locale that includes four-digit years. For example, replace line
//1 above with:
s << dt.asString("%m/%d/%Y %H:%M:%S") << std::endl; // 1
3.5.2 Member Functions
Class
RWDateTime has member functions to compare, store, add, and subtract
RWDateTimes. An
RWDateTime may return specific time units (days, hours, seconds), or fill a
struct tm for any time zone. A complete list of member functions appears in the
SourcePro C++ API Reference Guide.
For example, here is a code fragment that outputs the hour in local and UTC (GMT) zones, and then the complete local time and date.
RWDateTime dt(RWDateTime::setCurrentTime);
cout << dt.dayOfMonth() << endl; // Current day of month
cout << dt.hour() << endl; // Local hour
cout << dt.hourGMT() << endl; // UTC (GMT) hour
dt.writeDate(cout); // Local date
dt.writeTime(cout); // Local time
cout << dt << endl; // Local time and date
3.5.3 RWDateTime Sentinels
RWDateTime has the ability to hold sentinel values that are not necessarily dates, but are convenient to use as comparison values within applications. For example, a “future” sentinel provides a value that represents a date larger than the largest
RWDateTime.
RWDateTime currently provides four internal, pre-defined sentinels and can accommodate up to 128 user-defined sentinels. The
RWDateTime sentinels are described in the following sections.
Table 3 summarizes the responses of
RWDateTime operations when applied to various sentinels. The sections that follow
Table 3 provide more information about the sentinels.
Table 3 – RWDateTime sentinels
Operator/Function | Valid | Null | Invalid | Past/Future | User Sentinel |
Extract Part | OK | RWTHROW | RWTHROW | RWTHROW | RWTHROW |
Arithmetic | OK | Propagate to Invalid | Propagate | Propagate Past/Future | Propagate to Invalid |
Relational | OK | OK | OK | OK | OK |
asString()/Output | OK | “NULL” | “#INVALID#” | “#INVALID#” | “#>num<#” |
Read String | OK | N/A | OK | N/A | N/A |
Create | OK | OK | OK | OK | OK |
Attempting to calculate the time differential between two RWDateTime objects (utilizing the – operator) results in the throwing of an RWInternalErr if either of the RWDateTime objects are sentinels.
3.5.3.1 The “Invalid” Sentinel and Other Invalid Instances
The Essential Tools Module allows for
RWDateTime instances with an “invalid” state that can be compared to other
RWDateTime instances and can be persisted. The default
RWDateTime constructor constructs an invalid instance, and other construction techniques are available, including the
RWDateTime::invalidSentinel constant. You can pass this constant to the
RWDateTime(rwint64) constructor to create instances of the minimum and maximum
RWDateTime as needed, or you can use it to compare directly with the millisecond count returned by
RWDateTime::milliSeconds().
No other operations work, other than persisting the invalid object and using it in comparisons of equality and inequality. Arithmetic manipulation of an invalid
RWDateTime results in an invalid
RWDateTime. Attempts to use the part extraction functions (
day(),
month(),
extract(), etc.) result in an
RWInternalErr being thrown by
RWTHROW. The
asString() family of functions always return the string
"#INVALID#" when called with an invalid
RWDateTime.
The following examples show several ways to construct an
RWDateTime with an invalid state:
// construct dt1 with invalid state:
RWDateTime dt1;
// construct dt2 with invalid state:
RWDateTime dt2(RWDateTime::invalid);
// construct dt3 with invalid state:
RWDateTime dt3(RWDateTime::invalidSentinel);
// construct dt4 with invalid state:
RWDateTime dt4(RWDate(29,02,1997));
3.5.3.2 The “Null” Sentinel
Sometimes it is convenient to have an
RWDateTime that is valid yet holds no specific value. The Essential Tools Module includes the “null” state sentinel for this purpose. You can construct a null
RWDateTime or use the constant
RWDateTime::nullSentinel in construction by passing the constant
RWDateTime::nullSentinel to the
RWDateTime(rwint64) constructor. You can also use the constant in direct comparisons with the millisecond count returned by
RWDateTime::milliSeconds().
Comparison operations for equality and inequality do work, with an
RWDateTime in a “null” state always being less than a valid
RWDateTime. Arithmetic manipulation of a null
RWDateTime results in an invalid
RWDateTime. Attempts to use any part extraction functions (
day(),
month(),
extract(), etc.) result in an
RWInternalErr being thrown by
RWTHROW. The
asString() family of functions always return the string
NULL when called for a null
RWDateTime.
The following examples show how to construct an
RWDateTime with a null state, and demonstrate what happens when it is used in an arithmetic operation:
// construct dt1 with null state:
RWDateTime dt1(RWDateTime::null);
// construct dt2 with null state
RWDateTime dt2(RWDateTime::nullSentinel);
// attempted arithmetic manipulation:
assert(dt2.isValid());
dt2.incrementMillisecond(23); // dt2 set to invalid state
assert(dt2.isValid()); // invalid
3.5.3.3 The Past and Future Sentinels
When sorting and searching for
RWDateTime objects it is useful to have sentinels that represent “smaller than the smallest”
RWDateTime and “larger than the largest”
RWDateTime. The “past” and “future” sentinels are special kinds of invalid sentinels.
RWDateTime includes two global static constants to determine the minimum and maximum valid
RWDateTime objects:
RWDateTime::minDateTime and
RWDateTime::maxDateTime. You can pass these constants to the
RWDateTime(rwint64) constructor to create instances of the minimum and maximum
RWDateTime as needed, or you can use them to compare directly with the millisecond count returned by
RWDateTime::milliSeconds().
The past and future sentinels can be used in any relational operation, and every valid
RWDateTime falls strictly between the past and future sentinels. Arithmetic manipulation of past or future sentinels does not change their values, nor cause any error. Attempts to use any part extraction functions (
day(), month(), extract(), etc.) result in an
RWInternalErr being thrown by
RWTHROW. The
asString() family of functions always return the string
"#INVALID#" for past and future sentinels.
Here are some examples of constructing and using past and future sentinels:
// construct a past sentinel
RWDateTime past(RWDateTime::pastSentinel);
// construct a future sentinel
RWDateTime future(RWDateTime::futureSentinel);
// construct an RWDateTime holding the largest valid time
RWDateTime maxDT(RWDateTime::maxDateTime);
// construct an RWDateTime holding the smallest valid time
RWDateTime minDT(RWDateTime::minDateTime);
bool a = past < minDT; // a == true
bool b = future > maxDT; // b == true
bool c = minDT.isValid(); // c == true
bool d = maxDT.isValid(); // d == true
bool e = !past.isValid(); // e == true
bool f = !future.isValid(); // f == true
3.5.3.4 User-Defined Sentinels
RWDateTime also provides for the use of 128 user-defined sentinels numbered 0 through 127. Each of these sentinels behaves like the null sentinel for part extraction functions, arithmetic manipulation, and relational operations. The
asString() family of functions return
"#>num<#" where
num is the user sentinel number. The static method
RWDateTime::userSentinel(int) is available for constructing user sentinels. These can be used to define different kind of dates, such as “refused”, for cases where a user has declined to enter data in a date field.
Here is an example of constructing and using user-defined sentinels:
RWDateTime sent0 = RWDateTime::userSentinel(0);
RWDateTime sent1 = RWDateTime::userSentinel(1);
std::cout << sent1.asString() << std::endl; // outputs #>1<#