13.3 Data Formatting
Programmers working with SourcePro DB have several classes available for datatype normalization. (See Chapter 7, “The Data Model.”) These classes provide data structures to hold complex types, yet allow them to be treated like simple types. These classes include:
*RWDateTime, RWDate, RWTime (deprecated) and RWDecimalPortable from the Essential Tools Module
*RWDBDateTime (deprecated) from the DB Interface Module
Classes RWTime and RWDBDateTime are deprecated. Use RWDateTime instead. Please see Section 7.4.3, “RWDBDateTime.”
The data that all these classes hold are locale sensitive; in other words, legal input and output of these types look different depending on the locale in effect. The issue here is input and output of the data from the point of view of the user of an application. SourcePro DB automatically formats the data when sending to and retrieving from a database. The application programmer must be concerned only with the raw form of the data as it comes into the program or goes out to a program user.
These classes all have associated techniques to allow input and output from various locales. The following sections outline the techniques used for RWDateTime, RWDate, RWTime, and RWDecimalPortable. For more information on these classes, see the Essential Tools Module User’s Guide and the SourcePro C++ API Reference Guide.
13.3.1 RWDateTime
As mentioned in Section 7.4.2, “RWDateTime,” class RWDateTime from the Essential Tools Module is the primary date and time class of SourcePro DB. It is used to hold a date and a time, primarily in association with moving data in and out of DATETIME (or similar) columns in databases.
Compatibility with SourcePro DB is provided to instances that are initialized, either to RWDateTime::null or to a valid datetime. Once an instance of RWDateTime is initialized, it can be used like any other datatype. SourcePro DB performs any formatting or binding automatically in a locale neutral manner. The programmer does not have to address localization issues with regard to getting this datatype in and out of the database.
However, getting a valid RWDateTime value from users in various locales can be tricky. The next example demonstrates how to take a string representing a localized date time value and convert it into an RWDateTime instance. Following the conversion, the RWDateTime is streamed to standard output to demonstrate output in various locales.
Example 16 – Converting a localized date/time value into an RWDateTime instance
 
void
demoI18NDateTimes()
{
RWCString germanDateString("04.05.98");
RWCString germanTimeString("16:26:42");
 
RWLocaleSnapshot germany("de");
//If we're already in a german locale
//the next line will work as an alternate
//RWLocale& germany = *RWLocale::global();
 
struct tm aTimeStruct;
germany.stringToDate(germanDateString, &aTimeStruct);
germany.stringToTime(germanTimeString, &aTimeStruct);
 
RWDateTime aDateTime(&aTimeStruct);
 
cout << "in France: "
<< aDateTime.asString('\0', RWZone::local(),
RWLocaleSnapshot("fr"))
<< endl;
cout << "in the US: "
<< aDateTime.asString('\0', RWZone::local(),
RWLocaleSnapshot("en_US"))
<< endl;
}
The output for this example should look like this:
 
in France: 04.05.98 16:26:42.000
in the US: 05/04/98 16:26:42.000
13.3.2 RWDBDateTime
Class RWDBDateTime used to be the primary class for representing and manipulating dates and time in the DB Interface Module; however, RWDBDateTime has been deprecated. New code should be written using class RWDateTime.
The DB Interface Module does not include an operator<< for ostreams and instances of RWDBDateTime. The following example shows how to make an operator<< that outputs an RWDBDateTime correctly with whatever locale was imbued in the ostream.
 
ostream&
operator<<(ostream& o, const RWDBDateTime& dt)
{
const RWLocale& localeOfTheStream = RWLocale::of(o);
return o << dt.asString('\0', RWZone::local(), localeOfTheStream);
}
13.3.3 RWDate
RWDate is a class from the Essential Tools Module that can also be used as a database datatype. The DB Interface Module can handle any formatting issues without intervention when getting this type in and out of a database, as it can for RWDateTime. Of course, as in Section 13.3.1, the trick is getting localized information from a user to initialize an instance of RWDate. The Essential Tools Module gives several options.
First, there is a constructor that takes a reference to an istream and a reference to an RWLocale. This allows direct initialization of the RWDate instance from the istream.
A second technique is shown in the example below. A string initialized with a representation of a localized date is created and then converted to an RWDate with the assistance of an RWLocale reference.
 
void
demoI18NDate()
{
RWDate aDate("26 Mai 1998", RWLocaleSnapshot("de"));
 
cout << "in the US: "
<< aDate.asString("%d %b %Y", RWLocaleSnapshot("en_US"))
<< endl;
cout << "in France: "
<< aDate.asString("%d %b %Y", RWLocaleSnapshot("fr"))
<< endl;
}
The output for this example should be:
 
in the US: 26 May 1998
in France: 26 mai 1998
13.3.4 RWDecimalPortable
RWDecimalPortable is a class from Essential Tools Module that is used to hold monetary values. For this job, it is more accurate than float or double. The types float and double introduce errors in monetary values because binary is not entirely accurate for fractional base 10 values. RWDecimalPortable uses the less efficient but perfectly accurate ASCII method.
When an RWDecimalPortable instance is given to the DB Interface Module to be inserted into a table, the module handles any formatting required by the server automatically. However, you do need to get localized values into instances of RWDecimalPortable.
Unfortunately, there is no constructor for RWDecimalPortable that takes an RWLocale as a parameter. When an RWDecimalPortable is initialized from a string, it always assumes that the string is formatted in the style required by the current global locale.
 
void
demoI18NDecimal()
{
// this example is supposed to be French
// these two lines make sure that's true
RWLocaleSnapshot france("fr");
const RWLocale* oldLocale = RWLocale::global(&france);
 
RWCString frenchDecimalString("123.456,78");
RWDecimalPortable x(frenchDecimalString);
 
cout << "in France: " << x << endl;
RWLocaleSnapshot USLocale("en_us");
USLocale.imbue(cout);
cout << "in the US: " << x << endl;
 
//restore previous locale
RWLocale::global(oldLocale);
}
The output for this example should be:
 
in France: 123456,78
in the US: 123456.78
By itself, class RWDecimalPortable cannot produce output with embedded thousand separators. When used in conjunction with the Currency Module, it has that capability.
13.3.5 Formatting Raw Numbers
The class RWLocale from the Essential Tools Module provides an easy way to convert between strings that represent numbers into the basic long or double data types for any locale. The number strings can even contain the national equivalents of the thousand and decimal point separators.
Example 17 – Formatting raw numbers
void
insertFrenchNumber(RWDBDatabase& aDB)
{
RWLocaleSnapshot france("fr");
//If we're already in a french locale - use this line instead
//RWLocale& france= *RWLocale::global();
 
RWCString aNumberAsAString("12.345,67");
double aNumber(0.0);
france.stringToNum(aNumberAsAString, &aNumber);
 
RWDBInserter ins = aDB.table("t3").inserter();
 
ins << aNumber;
 
cout << ins.asString(true) << endl;
}
The output from this example should be an SQL insert statement like this:
 
INSERT INTO t3 VALUES ( 1.2345670000000e+04 )