Rogue Wave banner
Previous fileTop of DocumentContentsIndex pageNext file
Essential Tools Module User's Guide
Rogue Wave web site:  Home Page  |  Main Documentation Page

7.5 RWLocale and RWZone

The Essential Tools Module solves these problems with the classes RWLocale and RWZone. If you have used RWDateTime, you have already used RWLocale, perhaps unknowingly. Every time you convert a date or time to or from a string, a default argument carries along an RWLocale reference. Unless you change it, this is a reference to a global instance of a class derived from RWLocale at program startup to provide the effect of a C locale.

To use RWLocale explicitly, you can construct your own instance and pass it in place of the default. Similarly, when you manipulate times, you can substitute your own instance for the default RWZone reference.

You can also install your own instance of RWLocale or RWZone as the global default. Many streams even allow you to install your RWLocale instance in the stream so that dates and times transferred on and off that stream are formatted or parsed accordingly, without any special arguments. This is called imbuing the stream, a process described in more detail in the next section.

In the following sections, let us look at some examples of how to localize various data using RWLocale and RWZone. Let us begin by constructing a date, today's date:

We can display it the usual way using ordinary C-locale conventions:

But what if you're outside your home locale? Or perhaps you have set your environment variable LANG to fr, because you want French formatting. (Despite the existing standard for locale names, many vendors provide variant naming schemes. Check your vendor's documentation for details.) To display the date in your preferred format, construct an RWLocaleSnapshot object:

This has the effect of creating an RWLocaleShapshot object which is constructed based on the information contained in the environment variables LC_* and LANG.

7.5.1 RWLocaleSnapshot and RWAnsiLocale

Classes RWLocaleSnapshot and RWAnsiLocale are two implementations of the interface defined by RWLocale. RWLocaleSnapshot extracts the information it needs from the global environment during construction with the help of such Standard C Library functions as strftime() and localeconv(). RWAnsiLocale wraps the C++ Standard Library std::locale, and can take advantage of the locales that are shipped with that implementation.

In discussion of the following examples, RWLocaleSnapshot and RWAnsiLocale are interchangeable.

The most straightforward way to use RWLocaleSnapshot is to pass it directly to the RWDateTime member function asString(), as follows. The first argument of the function asString() is a character, which may be any of the format options supported by the Standard C Library function strftime().

There is, however, a more convenient way. You can install here as the global default locale so the insertion operator will use it:

There are three ways to use an RWAnsiLocale object:

7.5.2 Dates

Suppose you are American and want to format a date in German, but don't want German to be the default. Construct a German locale:

You can format the same date for both local and German readers as follows:

See the definition of x in the Reference Guide entry for RWLocale.

Would you like to read in a German date string? Again, the straightforward way is to call everything explicitly:


On your operating system, the name of the German locale may be different.

Sometimes, however, you would prefer to use the extraction operator >>. Since the operator must expect a German-formatted date, and know how to parse it, you pass this information along by imbuing a stream with the German locale.

The following code snippet imbues the stream cin with the German locale, reads in and converts a date string from German, and displays it in the local format:

Imbuing is useful when many values must be inserted or extracted according to a particular locale, or when there is no way to pass a locale argument to the point where it will be needed. By using the static member function RWLocale::of(ios&), your code can discover the locale imbued in a stream. If the stream has not yet been imbued, of() returns the current global locale. Note that you can restore a stream to its unimbued condition with the static member function RWLocale::unimbue(ios&), but this is not the same as imbuing it with the current global locale.

The interface defined by RWLocale handles more than dates. It can also convert times, numbers, and monetary values to and from strings. Each has its complications. Time conversions are complicated by the need to identify the time zone of the person who entered the time string, or the person who will read it. The mishmash of Daylight Saving Time jurisdictions can magnify the difficulty. Numbers are somewhat messy to format because their insertion and extraction operators (<< and >>) are already defined by <iostream.h>. For money, the main problem is that there is no standard internal representation for monetary values. Fortunately, none of these problems are overwhelming with the Essential Tools Module.

7.5.3 Time

Let us first consider the time zone problem. We can easily see that there is no simple relationship between time zones and locales. All of Switzerland shares a single time zone, including Daylight Saving Time (DST) rules, but it has four official languages: French, German, Italian, and Romansch. On the other hand, Hawaii and New York share a common language, but occupy time zones five hours apart — sometimes six hours apart, because Hawaii does not observe DST. Furthermore, time zone formulas have little to do with cultural formatting preferences. For these reasons, the Essential Tools Module uses a separate time zone class RWZone, rather than letting RWLocale incorporate time zone responsibilities.

In the Essential Tools Module, the class RWZone encapsulates knowledge about time zones. It is an abstract base class, with a public implementation in the class RWZoneSimple. RWZone provides two instances by default, one that encapsulates the rules returned by the operating system, and a second representing the rules for Universal Coordinated Time (UTC). RWZone also allows your application to specify a local zone which, by default, represents the operating system rules. Whenever you convert an absolute time to or from a string, as in the class RWDateTime, an instance of RWZone is involved. By default, the local time is assumed, but you can pass a reference to any RWZone instance.If your primary need is to represent times in the current zone, you will likely not need to use the RWZone class directly. If, however, your application deals with multiple time zones or a zone that is not the system default, you can explicitly instantiate an RWZone instance to represent a different time zone. The easiest way to do so is through the RWZoneSimple class.

Here are some examples. Imagine you had scheduled a trip from New York to Paris. You were to leave New York on December 20, 2008, at 11:00 p.m., and return on March 30, 2009, leaving Paris at 5:00 a.m., Paris time. What will the clocks show at your destination when you arrive?

First, construct the time zones and the departure times:

The flight is about seven hours long each way, so:

Now, display the arrival times and dates according to their respective local conventions, French in Paris and American English in New York:

The code works even though your flight crosses several time zones and arrives on a different day than it departed; even though, on the day of the return trip in the following year, France has already begun observing DST, but the U.S. has not. None of these details are visible in the example code above — they are handled silently and invisibly by RWDateTime and RWZone.

All this is easy for places that follow the Essential Tools Module built-in DST rules for North America, Western Europe, and "no DST". But what about places that follow other rules, such as Argentina, where spring begins in September and summer ends in March? RWZoneSimple is table-driven; if the rule is simple enough, you can construct your own table of type RWDaylightRule, and specify it as you construct an RWZoneSimple. For example, imagine that DST begins at 2 a.m. on the last Sunday in September, and ends the first Sunday in March. Two rules are required for the southern hemisphere because DST starts later in a calendar year (September) than it ends (March), so the additional rule sets its initial starting point.

This example creates two static instances of RWDaylightRule, one indicating the transition to DST in September, and the second the transition to STD in March and then the transition back to DST in September. (The year 1970 is for example purposes only and is not relevant to any specific DST rules.)

For details on an RWDaylightRule instance, see the documentation for RWZoneSimple.

Then construct an RWZone object:

Now you can use ciudadSud just like you used paris or newYork above.

But what about places where the DST rules are too complicated to describe with a simple table, such as Great Britain? There, DST begins on the morning after the third Saturday in April, unless that is Easter, in which case it begins the week prior! For such jurisdictions, you might best use standard time, properly labeled. If that just won't do, you can derive from RWZone and implement its interface for Britain alone. This strategy is much easier than trying to generalize a case to handle all possibilities including Britain, and it's smaller and faster besides.

As you can see, Daylight Saving Time rules are volatile, often reflecting geographical and political changes. In some cases, the hard-coded table-driven struct RWDaylightRule does not accurately reflect the locale installed on your machine. For these cases, RWZone::os() can create a new RWZone containing the daylight rule discovered from the underlying operating system. The onus of correctness for this DST rule is on the operating system itself.

In cases where you want more explicit control of the DST rule for the intended RWZoneSimple, you can build a DST rule with arbitrary begin and end times, and provide it as a parameter to RWZoneSimple.

The last time problem we will discuss here is that there is no standard way to discover what DST rules are in force for any particular place. In this the Standard C Library is no help; you must get the information you need from the local environment your application is running on, perhaps by asking the user.

One example of this problem is that the local wall clock time RWZone instance is constructed to use North American DST rules, if DST is observed at all. If the user is not in North America, the default local time zone probably performs DST conversions wrong, and you must replace it. If you are a user in Paris, for example, you could solve this problem as follows:

If you look closely into <rw/locale.h>, you will find that RWDateTime is never mentioned. Instead, RWLocale uses the Standard C Library type struct tm. RWDateTime provides conversions to this type, and you may prefer using it directly rather than using RWDateTime::asString(). For example, suppose you must write out a time string containing only hours and minutes, such as 12:33. The standard formats defined for strftime() and implemented by RWLocale do not include that option, but you can work around it. Here's one way:

Without using various manipulators, this code might produce a string like 9:5. Here is another option:

This produces 09:05.

In each of the previous examples, now is disassembled into component parts twice, once to extract the hour and once to extract the minute. This is an expensive operation. If you expect to work often with the components of a time or date, you may be better off disassembling the time only once:

7.5.4 Numbers

RWLocale also provides you with an interface for conversions between strings and numbers, both integers and floating point values. RWLocaleSnapshot and RWAnsiLocale implement this interface. RWLocaleSnapshot provides the full range of capabilities defined by the Standard C Library type struct lconv, while RWAnsiLocale provides this functionality through the numerics facets as defined by the C++ Standard. The capabilities include using appropriate digit group separators, decimal "point", and currency notation. When converting from strings, both RWLocaleSnapshot and RWAnsiLocale allow and check the same digit group separators.

Unfortunately, stream operations of this class are clumsier than we might like, since the standard iostream library provides definitions for number insertion and extraction operators which cannot be overridden. Instead, we can use RWCString functions directly:

Since the French use periods for digit group separators, and commas to separate the integer from the fraction part of a number, this code might display as:

You will notice that numbers with digit group separators are easier to read.

If you are using RWDecimalPortable to parse decimals, it is recommended that you first explicitly define a locale, rather than allowing RWLocale to provide a default locale. RWDecimalPortable's parsing behavior is locale-dependent, so may produce unexpected results. For example, the C locale does not use a comma as a thousands separator, and parsing would therefore recognize such a comma instead as a separator between groups of digits.


It is recommended that you explicitly define a locale when using RWDecimalPortable, as the default locale may produce unexpected results.

To ensure predictable and expected behavior, the program should explicitly specify the locale before using the RWDecimalPortable constructor, and input strings should be formatted according the numeric string facets for that locale.

7.5.5 Currency

Currency conversions are trickier than number conversions, mainly because there is no standard way to represent monetary values in a computer. We have adopted the convention that such values represent an integral number of the smallest unit of currency in use. For example, to represent a balance of $10.00 in the United States, you could say:

This representation has the advantages of wide range, exactness, and portability. Wide range means you can exactly represent values from $0.00 up to and beyond $10,000,000,000,000.00 larger than any likely budget. Exactness means that, representing monetary values without fractional parts, you can perform arithmetic on them and compare the results for equality:

This would not be possible if the values were naively represented, as in price = 9.99;.

Portability means simply that double is a standard type, unlike common 64-bit integer or BCD representations. Of course, you can perform financial calculations on such other representations, but because you can always convert between them and double, they are all supported. In the future, RWLocale may directly support some other common representations as well.

Consider the following examples of currency conversions:

In a United States locale, the code would display as:



Previous fileTop of DocumentContentsNo linkNext file

Copyright © Rogue Wave Software, Inc. All Rights Reserved.

The Rogue Wave name and logo, and SourcePro, are registered trademarks of Rogue Wave Software. All other trademarks are the property of their respective owners.
Provide feedback to Rogue Wave about its documentation.