Hidden treasures of Sysutils, Part 1

by Mark G. Wiseman

As I am sure you know, the Visual Component Library (VCL) in C++Builder contains a wealth of visual and non-visual components and classes. You might not know that there are a lot of very useful stand-alone functions and global variables in the VCL.

Many of these functions and variables are hidden in the Sysutils namespace. I say hidden, because although most of these functions and variables are actually documented in the online help, they are not easy to find. Naturally, the header that prototypes these variables and functions is SYSUTILS.HPP. This header is automatically included by VCL.H so it is not necessary to specifically include it in a default VCL application.

The SYSUTILS unit contains functions and variables you can use to work with time, dates, numbers, currency, files, file names, and strings. There is also a small hodgepodge of miscellaneous system functions and variables.

In this article I will explain some of the variables and functions you can use when working with times and dates. In future articles I will explain other hidden treasures found in the SYSUTILS unit.

Times and dates

The functions and variables you can use to work with times and dates are based on the TDateTime class. As you might expect, TDateTime is a C++ class that implements dates and times. It was designed to interface with the Delphi TDateTime data type, which is nothing more than a double. Several of the functions in Sysutils mirror methods in the C++Builder TDateTime class.

Many of the time and date variables are set by the GetFormatSettings() function in Sysutils. GetFormatSettings() uses the locale information provided by Windows and is called when the VCL is initialized at application startup. The examples you will see in this article are specific to my system, which uses the United States locale settings.

GetFormatSettings() is also called by the TApplication object any time your program receives a WM_WININICHANGE message. The good news is that your programs should automatically update the display formats of times and dates if the user changes time or date settings. The bad news is that you must be careful when using the time and date variables. It is important to understand that these variables can change while your program is running and you should write your code to account for possible changes.

In the sample program that accompanies this article, I actually assign values to some of the variables to demonstrate how the variables are used by some of the time and date functions. In general, this is not a good practice since the system may change their values at any time.

Dealing with times

One set of variables and functions deals specifically with times. I will discuss those variables and functions in the following sections.

Time variables

Two time variables contain constant values and are self-explanatory. Here are their declarations:

int SecsPerDay = 86400;

int MSecsPerDay = 8640000;

Other variables contain values that are set at runtime using the GetFormatSettings() function. Those variables include:

char TimeSeparator;
AnsiString TimeAMString;
AnsiString TimePMString;
AnsiString ShortTimeFormat;
AnsiString LongTimeFormat;

On my system the TimeSeparator variable contains the ‘:’ character. The VCL uses this value for formatting output of time strings, and for parsing input strings into times. Look at the “FormatDateTime” topic in the C++Builder online help for an explanation of time and date formatting options.

Time functions

In addition to the time variables explained in the previous section, Sysutils also contains several time functions. Those functions include:

TDateTime EncodeTime(Word Hour, 
  Word Min, Word Sec, Word MSec);void DecodeTime(
  TDateTime Time, Word &Hour, Word &Min,
  Word &Sec, Word &MSec);
TDateTime Time(void);
AnsiString TimeToStr(TDateTime Time);
TDateTime StrToTime(const AnsiString S);

The EncodeTime() function takes four parameters representing hours, minutes, seconds, and milliseconds, and returns a TDateTime. Here is an example:

TDateTime t = EncodeTime(21, 10, 30, 0);

When EncodeTime() returns, the TDateTime variable will contain the time as specified by the parameters passed to the function.

The DecodeTime() function performs the reverse:

Word hours, minutes, 
  seconds, milliseconds;
DecodeTime(t, hours, 
  minutes, seconds,
milliseconds);

The Time() function returns the current system time and TimeToStr() converts a TDateTime to an AnsiString using the format specified in the LongTimeFormat variable. For example:

TDateTime t = Time();
String s = TimeToStr(t);

The StrToTime() function takes an AnsiString and returns a TDateTime. Note that the character used to separate the time portions in the String (hours, minutes, and seconds) must match TimeSeparator for this function to work. Here is an example:

TDateTime t = StrToTime("8:27");

Working with dates

Now that I have covered the time variables and functions found in Sysutils, I will explain the date variables and functions.

Date variables

The VCL sets the following variables using the GetFormatSettings() function:

char DateSeparator;
AnsiString ShortDateFormat;
AnsiString LongDateFormat;
AnsiString ShortMonthNames[12];
AnsiString LongMonthNames[12];
AnsiString ShortDayNames[7];
AnsiString LongDayNames[7];

ShortMonthNames contains abbreviated names for the months of the year ( “Jan”, “Feb”, “Mar”, and so on). As you can see from the declaration, ShortMonthNames is an array. To extract the short month name for the month of February, for example, you could use code like this:

String Feb = ShortMonthNames[1];

LongMonthNames contains the full name of the months of the year (“January”, “February”, “March”, etc.). The variables for day names work similarly.

There are two more date variables that are set by the VCL, but they are not connected with the Windows locale information.

Word TwoDigitYearCenturyWindow;
Word MonthDays[2][12];

MonthDays is very useful for the quick lookup of the number of days in a month. Here is a simple function that uses MonthDays:

int DaysInMonth(int year, int month)
{
  int leap = IsLeapYear(year) ? 1 : 0;
  return(MonthDays[leap][month - 1]);
}

Borland included the woDigitYearCenturyWindow variable so users with legacy code could address Y2K issues related to 2-digit years. If you’d like more information on this variable, look in the online help under the topic “Y2K Issues.”

Date functions

The Sysutils namespace declares the following date functions:

TDateTime EncodeDate(
  Word Year, Word Month, Word Day);
void DecodeDate(TDateTime Date, 
  Word &Year, Word &Month, Word &Day);
bool IsLeapYear(Word Year);
int DayOfWeek(TDateTime Date);
TDateTime Date(void);
AnsiString DateToStr(TDateTime Date);
TDateTime IncMonth(const TDateTime Date, 
  int NumberOfMonths);
TDateTime StrToDate(const AnsiString S);

The EncodeDate() function takes year, month, and day parameters and returns a TDateTime object. The DecodeDate() function performs the reverse by taking a TDateTime and returning the year, month, and day in variables passed to the function by reference.

IsLeapYear() returns true if a year is a leap year and false otherwise. IsLeapYear() does work correctly for year 2000.

DayOfWeek() takes a TDateTime as input and returns an int representing the day of the week. This is a one-based value, so to use it with the Sysutils array variables (such as LongDayNames or LongMonthNames) you will have to subtract one. Here is an example:

TDateTime id(2000, 7, 4);
int dw = DayOfWeek(id);
String dayName = LongDayNames[dw - 1];

The Date() function returns the current system date. DateToStr() takes a TDateTime and returns an AnsiString representing the date using the format specified in ShortDateFormat.

IncMonth() is a very useful function. You can use it to add or subtract months from a date. It takes a TDateTime value argument and an int argument that represents the number of months to increment or decrement. Positive numbers add months and negative numbers subtract months. Here are some examples:

TDateTime d = TDateTime(2000, 7, 31);
TDateTime nextmonth = IncMonth(d, 1);
TDateTime lastmonth = IncMonth(d, -1);
TDateTime nextyear = IncMonth(d, 12);
TDateTime lastyear = IncMonth(d, -12);

There is one potential “gotcha” with IncMonth(). In the previous example, d is set to 7/31/2000. When d is decremented by one month, the result, lastmonth, is 6/30/2000. This is because June has only 30 days. When you think about it, though, this is the correct behavior.

StrToDate() takes an AnsiString and parses it into a date. This function is not as useful as it may sound, because it is very particular about the format of the string. The separator character in the date string must match the separator contained in the DateSeparator variable or an exception is thrown. StrToDate() will accept dates of the form “8/27” and will assume the missing year is the current year. StrToDate() will also accept two-digit years, but it may not translate as you would expect unless you have set the TwoDigitYearCenturyWindow variable correctly.

Date/time functions

There are also functions in Sysutils that work on both the date and time portions of a TDateTime object. Those functions are:

TDateTime Now(void);
AnsiString DateTimeToStr(
  TDateTime DateTime);
AnsiString FormatDateTime(
  const AnsiString Format, 
  TDateTime DateTime);
void DateTimeToString(
  AnsiString &Result, 
  const AnsiString Format, 
  TDateTime DateTime);
TDateTime StrToDateTime(
  const AnsiString S);
void DateTimeToSystemTime(
  TDateTime DateTime, 
  SYSTEMTIME &SystemTime);
TDateTime SystemTimeToDateTime(
  const SYSTEMTIME &SystemTime);

The Now() function returns a TDateTime that contains the current system time and date. DateTimeToStr() takes a TDateTime and returns an AnsiString with the date formatted using ShortDateFormat and the time formatted using LongTimeFormat.

FormatDateTime() is a very useful function. FormatDateTime() will return an AnsiString containing the date, the time, or the date and time in nearly any format you want. For example:

TDateTime n = Now();
String s = FormatDateTime(
  "hh:nn 'on' mmmm, d, yyyy);
// Example: s = 10:27 on March 15, 2000

Calling FormatDateTime(LongTimeFormat, t) is the same as calling TimeToStr(t), and FormatDateTime(ShortDateFormat, d) is the same as DateToStr(d). In fact, TimeToStr(), DateToStr(), and FormatDateTime() all call the DateTimeToString() function internally.

DateTimeToString() is nearly identical to FormatDateTime() in function. Instead of returning an AnsiString, though, it takes an additional argument, a reference to an AnsiString that receives the formatted output.

StrToDateTime() parses an AnsiString and returns a TDateTime. Like StrToDate() and StrToTime(), you may find it of limited use.

DateTimeToSystemTime() and SystemTimeToDateTime() can be used to translate TDateTime values into the SYSTEMTIME structure used by Windows and vice versa.

Conclusion

As I mentioned, you can find most of these functions and variables in the C++Builder online help. But unless you know their exact names, it is difficult to know what to look for. A great source for learning about all the variables and functions in the Sysutils namespace is the source itself. You can look at the header file, SYSUTILS.HPP, but you will find much more information in the Pascal source, SYSUTILS.PAS, which is very well documented.

I am sure you will find some of the time and date variables and functions found in Sysutils to be useful. In Part 2 of this series I will examine number and currency routines.