
Introduction
Some time ago, in one of my projects, I needed to print the current date and time in the page footer. Since the program was in MFC, I just went ahead and quickly added three lines:
CTime time = CTime::GetCurrentTime();
CString strDate = time.Format("%x");
CString strTime = time.Format ("%X");
Well, I noticed right away that the output looked like this:
12/30/05
19:10:23
Not quite the same as I set in my Windows' Regional Settings:
2005-12-30
19:10:23 PM

There must be an easy way to match the user's settings, I thought. After digging into MSDN, I found that the API function that I should use is GetLocaleInfo
. It can be queried to get all kinds of information, including the format string used by the user for the date and time. So then, I wrote two functions to parse the format strings for date and time and build the output string. These functions are shown at the end (Listing 2).
Update 2008-08-19
The great thing about CodeProject is that it is a two-way street: everybody can comment on your code and suggest better ways. Well, thanks to Vladimir Alemasov for pointing out setlocale
. This is what I was missing in my simple three-liner above. If I set the CRT's locale as per user settings, I'd get exactly the same format as he/she set in the Control Panel. Here's the way to set CRT's locale, suggested by Chris Grimes in his article:
::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SENGLANGUAGE, szBuf, STR_SZ);
_tcscpy(szLocale, szBuf);
::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SENGCOUNTRY, szBuf, STR_SZ);
if (_tcsclen(szBuf) != 0){
_tcscat(szLocale, _T("_"));
_tcscat(szLocale, szBuf);
}
::GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTANSICODEPAGE, szBuf, STR_SZ);
if (_tcsclen(szBuf) != 0){
_tcscat(szLocale, _T("."));
_tcscat(szLocale, szBuf);
}
_tsetlocale(LC_ALL, szLocale);
Note, that I use LOCALE_SYSTEM_DEFAULT
for the call to get the default code page. There's not much about it on MSDN, but I found this by trial and error.
Also note that if you are not doing anything else locale specific in the rest of your program, you should reset the locale back to default after you are done with formatting the date and time. Otherwise, you might get some unexpected surprises when outputting or parsing numbers.
Listing 1
This code sets the CRT library's locale to match the user settings specified in the Regional Options. Then, it uses simple formatting functions to create a string with the current date and time. Both Win32 and MFC formatting ways are shown. Make sure to reset the locale back to default after you are done. This code will work for Unicode and non-Unicode programs.
::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SENGLANGUAGE, szBuf, STR_SZ);
_tcscpy(szLocale, szBuf);
::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SENGCOUNTRY, szBuf, STR_SZ);
if (_tcsclen(szBuf) != 0){
_tcscat(szLocale, _T("_"));
_tcscat(szLocale, szBuf);
}
::GetLocaleInfo(LOCALE_SYSTEM_DEFAULT,
LOCALE_IDEFAULTANSICODEPAGE, szBuf, STR_SZ);
if (_tcsclen(szBuf) != 0){
_tcscat(szLocale, _T("."));
_tcscat(szLocale, szBuf);
}
_tsetlocale(LC_ALL, szLocale);
{
TCHAR strDate[STR_SZ];
TCHAR strTime[STR_SZ];
time_t lt;
time (<);
tm* timeptr = localtime (<);
_tcsftime(strDate, STR_SZ, _T("%x"), timeptr);
_tcsftime(strTime, STR_SZ, _T("%X"), timeptr);
}
{
CTime time = CTime::GetCurrentTime();
CString strDate = time.Format("%x");
CString strTime = time.Format ("%X");
}
Listing 2
This code shows two functions, one for formatting date and the other for formatting time. Each function retrieves the appropriate formatting string from locale settings, then it parses this string to build the output string for date or time. This code does not set the CRT library locale, so you don't need to worry about switching it back. It uses MFC, and will work for Unicode and non-Unicode programs.
CString get_date_in_user_format (CTime& time)
{
CString strTmpFormat;
CString strDate;
WCHAR* szData = NULL;
int num_chars =
GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SSHORTDATE, szData, 0);
if (num_chars != 0)
{
szData = new WCHAR[num_chars+1];
szData[num_chars] = '\0';
GetLocaleInfoW(LOCALE_USER_DEFAULT,
LOCALE_SSHORTDATE, szData, num_chars);
CString strTmp (szData);
int ind = 0;
int len = strTmp.GetLength();
while (ind < len)
{
switch (strTmp[ind])
{
case 'y':
{
int year_type = 0;
while (ind < len && strTmp[ind] == 'y'){
ind++;
year_type++;
}
ind--;
switch (year_type){
case 4: strTmpFormat.Format(_T("%d"), time.GetYear());
strDate += strTmpFormat; break;
case 2: strTmpFormat.Format(_T("%02d"), time.GetYear() % 100);
strDate += strTmpFormat; break;
case 1: strTmpFormat.Format(_T("%d"), time.GetYear() % 10);
strDate += strTmpFormat; break;
}
break;
}
case 'M':
{
int month_type = 0;
while (ind < len && strTmp[ind] == 'M'){
ind++;
month_type++;
}
ind--;
switch (month_type){
case 4:
{
WCHAR szMonth[500]={0};
if (0<GetLocaleInfoW(LOCALE_USER_DEFAULT,
LOCALE_SMONTHNAME1+time.GetMonth()-1, szMonth, 499)){
strDate += szMonth;
}
break;
}
case 3:
{
WCHAR szMonth[500]={0};
if (0<GetLocaleInfoW(LOCALE_USER_DEFAULT,
LOCALE_SABBREVMONTHNAME1+time.GetMonth()-1,
szMonth, 499)){
strDate += szMonth;
}
break;
}
case 2: strTmpFormat.Format(_T("02d"), time.GetMonth());
strDate += strTmpFormat; break;
case 1: strTmpFormat.Format(_T("%d"), time.GetMonth());
strDate += strTmpFormat; break;
}
break;
}
case 'd':
{
int day_type = 0;
while (ind < len && strTmp[ind] == 'd'){
ind++;
day_type++;
}
ind--;
switch (day_type){
case 4:
{
UINT DayOfWeekFull[] = {
LOCALE_SDAYNAME7,
LOCALE_SDAYNAME1,
LOCALE_SDAYNAME2,
LOCALE_SDAYNAME3,
LOCALE_SDAYNAME4,
LOCALE_SDAYNAME5,
LOCALE_SDAYNAME6
};
WCHAR szDayOfWeek[500]={0};
if (0<GetLocaleInfoW(LOCALE_USER_DEFAULT,
DayOfWeekFull[time.GetDayOfWeek()-1],
szDayOfWeek, 499)){
strDate += szDayOfWeek;
}
break;
}
case 3:
{
UINT DayOfWeekAbbr[] = {
LOCALE_SABBREVDAYNAME7,
LOCALE_SABBREVDAYNAME1,
LOCALE_SABBREVDAYNAME2,
LOCALE_SABBREVDAYNAME3,
LOCALE_SABBREVDAYNAME4,
LOCALE_SABBREVDAYNAME5,
LOCALE_SABBREVDAYNAME6
};
WCHAR szDayOfWeek[500]={0};
if (0<GetLocaleInfoW(LOCALE_USER_DEFAULT,
DayOfWeekAbbr[time.GetDayOfWeek()-1],
szDayOfWeek, 499)){
strDate += szDayOfWeek;
}
break;
}
case 2: strTmpFormat.Format(_T("%02d"), time.GetDay());
strDate += strTmpFormat; break;
case 1: strTmpFormat.Format(_T("%d"), time.GetDay());
strDate += strTmpFormat; break;
}
break;
}
default:
strDate += CString(strTmp[ind]);
break;
}
ind++;
}
delete szData;
}
if (strDate.IsEmpty()){
strDate = time.Format(_T("%x"));
}
return strDate;
}
CString get_time_in_user_format (CTime& time)
{
CString strTmpFormat;
CString strTime;
WCHAR* szData = NULL;
int num_chars = GetLocaleInfoW(LOCALE_USER_DEFAULT,
LOCALE_STIMEFORMAT, szData, 0);
if (num_chars != 0)
{
szData = new WCHAR[num_chars+1];
szData[num_chars] = '\0';
GetLocaleInfoW(LOCALE_USER_DEFAULT,
LOCALE_STIMEFORMAT, szData, num_chars);
CString strTmp (szData);
int ind = 0;
int len = strTmp.GetLength();
while (ind < len)
{
switch (strTmp[ind])
{
case 't':
{
int time_marker_type = 0;
while (ind < len && strTmp[ind] == 't'){
ind++;
time_marker_type++;
}
ind--;
switch (time_marker_type){
case 2:
case 1:
{
WCHAR szTimemarker[500]={0};
LCTYPE am_or_pm = LOCALE_S1159;
if (time.GetHour() >= 0 && time.GetHour() < 12){
am_or_pm = LOCALE_S1159;
}else{
am_or_pm = LOCALE_S2359;
}
if (0<GetLocaleInfoW(LOCALE_USER_DEFAULT,
am_or_pm, szTimemarker, 499)){
if (time_marker_type == 1){
strTime += CString(szTimemarker, 1);
}else{
strTime += szTimemarker;
}
}
break;
}
}
break;
}
case 's':
{
int seconds_type = 0;
while (ind < len && strTmp[ind] == 's'){
ind++;
seconds_type++;
}
ind--;
switch (seconds_type){
case 2: strTmpFormat.Format(_T("%02d"), time.GetSecond());
strTime += strTmpFormat; break;
case 1: strTmpFormat.Format(_T("%d"), time.GetSecond());
strTime += strTmpFormat; break;
}
break;
}
case 'm':
{
int minute_type = 0;
while (ind < len && strTmp[ind] == 'm'){
ind++;
minute_type++;
}
ind--;
switch (minute_type){
case 2: strTmpFormat.Format(_T("%02d"), time.GetMinute());
strTime += strTmpFormat; break;
case 1: strTmpFormat.Format(_T("%d"), time.GetMinute());
strTime += strTmpFormat; break;
}
break;
}
case 'H':
{
int hour_type = 0;
while (ind < len && strTmp[ind] == 'H'){
ind++;
hour_type++;
}
ind--;
switch (hour_type){
case 2: strTmpFormat.Format(_T("02d"), time.GetHour());
strTime += strTmpFormat; break;
case 1: strTmpFormat.Format(_T("%d"), time.GetHour());
strTime += strTmpFormat; break;
}
break;
}
case 'h':
{
int hour_12_format = time.GetHour() % 12;
if (hour_12_format==0){
hour_12_format = 12;
}
int hour_type = 0;
while (ind < len && strTmp[ind] == 'h'){
ind++;
hour_type++;
}
ind--;
switch (hour_type){
case 2: strTmpFormat.Format(_T("02d"), hour_12_format);
strTime += strTmpFormat; break;
case 1: strTmpFormat.Format(_T("%d"), hour_12_format);
strTime += strTmpFormat; break;
}
break;
}
default:
strTime += CString(strTmp[ind]);
break;
}
ind++;
}
delete szData;
}
if (strTime.IsEmpty()){
strTime = time.Format(_T("%X"));
}
return strTime;
}
Further Reading
For more information, see MSDN's topic on GetLocaleInfo
. Also read about setlocale
on MSDN.