Since the inclusion of TTimeZone in Delphi XE’s RTL, I was trying to write a small introductory article into how to get started with the class but never got the time. Now, I got some free time on my hands, so here it goes:
The new class provides support for:
- Converting date/time values representing local time (wall clock) to universal time (UTC) and back.
- Obtaining the abbreviation and display names that can be attached to a date/time string to properly identify it in its time zone.
- Obtaining the UTC offset of a given local time.
- Checking the “type” of a local time (ex. Standard, Daylight, Ambiguous or Invalid).
- Extensible for new implementations (other than the standard one provided by the RTL).
The first thing you might notice is that TTimeZone is declared abstract. The intention was to give the class only the necessary conversion methods and related code while forcing the derived classes to provide the mechanics to retrieve the actual date/time information. Now, since TTimeZone is itself abstract, that means that there is a derived class somewhere that plugs in the necessary plumbing. Indeed there is, and it’s declared and implemented in the implementation section of DateUtils unit. This class provides support for the “local time zone” by querying the current operating system for time zone related details. To access this local time zone you need to use the TTimeZone.Local property.
The rest of the operations are quite simple to understand, but in any case here’s a small list of provided functionality:
- GetUtcOffset returns the UTC offset of a given local date/time.
- ToUniversalTime converts a local date/time to an universal date/time.
- ToLocalTime converts an universal date/time to a local date/time.
- GetDisplayName obtainins a display name for the given local date/time (for example “GTB Standard Time” if the local date/time was in the standard period of the year and “GTB Daylight Time” if the local date/time was in the summer period).
- GetAbbreviation obtainins a “GMT+XX” string also based on DST rules.
- GetLocalTimeType obtains an enumeration that specifies the type of the provided local date/time (ex. Standard, Daylight, Ambiguous or Invalid).
- IsStandardTime checks if the local date/time is in standard period.
- IsInvalidTime checks if the local date/time is invalid.
- IsAmbiguousTime checks if the local date/time is ambiguous.
- IsDaylightTime checks if the local date/time is in daylight period.
- ID returns an ID of the time zone (useful to uniquely identify a time zone).
- DisplayName specifies the display name of the time zone for current date. The value of this property can change based on the period the computer time is in.
- Abbreviation specifies the abbreviation of the time zone for current date. The value of this property can change based on the period the computer time is in.
- UtcOffset specifies the UTC offset of the time zone for current date. The value of this property can change based on the period the computer time is in.
You might notice that some methods also accept an optional “ForceDaylight” parameter. This parameter is only used by the time zone class when the provided local time is ambiguous (usually an hour at the end of the DST period when you need to get your clock an hour back). Since that period may be treated as being daylight or standard, the class allows you to control which one is it.
The other thing to note is that TTimeZone checks for invalid local date/times. Usually when switching between standard and daylight hours, there is an hour that is “missing” (you need to adjust your wall clock to one hour ahead). For example, 03:00 AM is adjusted to 04:00 AM. This means that the hour between 03:00 AM and 04:00 AM is basically … invalid — there is no corresponding universal time for that hour. If a date/time value situated in that period is passed to TTimeZone an exception will be raised. Normally you can use IsInvalidTime to detect this problem ahead of time.
What would be life without examples?
{$APPTYPE CONSOLE} uses SysUtils, DateUtils; begin { Writes the current universal time } WriteLn('Current universal time is:', DateTimeToStr(TTimeZone.Local.ToUniversalTime(Now))); { Write the current date/time along with the abbreviation } WriteLn( Format('%s (%s)', [DateTimeToStr(Now), TTimeZone.Local.GetAbbreviation(Now, true)]) ); { Simply write the type of the current time } case TTimeZone.Local.GetLocalTimeType(Now) of lttStandard: WriteLn('Current time is Standard.'); lttDaylight: WriteLn('Current time is Daylight.'); lttInvalid: WriteLn('This should never happen when the time is coming from the system.'); lttAmbiguous: WriteLn('Current time is ambiguous.'); end; end.
Why should you start using it? Well for a number of reasons:
- It is there. An unified method of working with time zones. Some parts of the RTL started using it already. And it is pretty simple.
- It is cross-platform. Your application need not depend on Windows API anymore for this sort of things.
- For Windows it uses either GetTimeZoneInformation or the newer GetTimeZoneInformationForYear depending on what is available.
- It uses an internal caching mechanic to minimize the number of calls to the underlying OS.
- It is extensible. A well designed application can take advantage of that (more about that in the next post).
- And finally it gives better control over how you treat ambiguity or invalid date/times.
Well, that is all I have to say about TTimeZone for now, hope you found this useful. In the next post I will continue on the same subject and introduce a time zone class that obtains its information from a bundled database rather than OS itself.