Click here to Skip to main content
15,890,579 members
Articles / Desktop Programming / MFC
Article

Format Date and Time As Per User's Locale Settings

Rate me:
Please Sign up or sign in to vote.
3.62/5 (16 votes)
19 Aug 2008CPOL2 min read 109.5K   728   24   14
Convert date and time to a string using format settings specified in Control Panel -> Regional Options.

date_and_time_screenshot.PNG

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:

C++
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

Sample Image - regional_options_date.png

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:

C++
::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); // e.g. szLocale = "English_United States.1252"

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.

C++
// Set CRT library locale to user and system
// defaults to get correct date/time formatting
::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);
// e.g. szLocale = "English_United States.1252"

// Win32 way
{
    TCHAR strDate[STR_SZ];
    TCHAR strTime[STR_SZ];

    time_t lt;
    time (&lt);
    tm* timeptr = localtime (&lt); // get current time

    _tcsftime(strDate, STR_SZ, _T("%x"), timeptr); // format date
    _tcsftime(strTime, STR_SZ, _T("%X"), timeptr); // format time
}


// MFC way
{
    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.

C++
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,   // Sunday
                                LOCALE_SDAYNAME1,   
                                LOCALE_SDAYNAME2,
                                LOCALE_SDAYNAME3,
                                LOCALE_SDAYNAME4, 
                                LOCALE_SDAYNAME5, 
                                LOCALE_SDAYNAME6   // Saturday
                            };
                            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,   // Sunday
                                LOCALE_SABBREVDAYNAME1,   
                                LOCALE_SABBREVDAYNAME2,
                                LOCALE_SABBREVDAYNAME3,
                                LOCALE_SABBREVDAYNAME4, 
                                LOCALE_SABBREVDAYNAME5, 
                                LOCALE_SABBREVDAYNAME6   // Saturday
                            };
                            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")); // fallback mechanism
    }

    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; //AM
                            if (time.GetHour() >= 0 && time.GetHour() < 12){
                                am_or_pm = LOCALE_S1159; //AM
                            }else{
                                am_or_pm = LOCALE_S2359; //PM
                            }
                            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")); //fallback mechanism
    }

    return strTime;
}

Further Reading

For more information, see MSDN's topic on GetLocaleInfo. Also read about setlocale on MSDN.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Canada Canada
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionAn easier way to get the current locale active in the CRT Pin
Giles Middleton31-Dec-12 0:42
Giles Middleton31-Dec-12 0:42 
AnswerRe: An easier way to get the current locale active in the CRT Pin
Chris P.17-Jul-18 12:42
Chris P.17-Jul-18 12:42 
GeneralThis is how I format Dates and Times, using MFC the locale set in the control panel. Pin
Richard M Willis9-Sep-09 2:08
Richard M Willis9-Sep-09 2:08 
Generalanother simple solution Pin
Vladimir Alemasov2-May-08 1:06
Vladimir Alemasov2-May-08 1:06 
GeneralRe: another simple solution Pin
Damir Valiulin19-Aug-08 7:05
Damir Valiulin19-Aug-08 7:05 
Generalsimple solution Pin
dis141115-Feb-06 6:52
dis141115-Feb-06 6:52 
GeneralRe: simple solution Pin
Damir Valiulin15-Feb-06 7:50
Damir Valiulin15-Feb-06 7:50 
GeneralRe: simple solution Pin
etking007200410-May-09 23:09
etking007200410-May-09 23:09 
GeneralSQL Problem Pin
Vertyg031-Dec-05 4:23
Vertyg031-Dec-05 4:23 
GeneralRe: SQL Problem Pin
Colin Angus Mackay31-Dec-05 6:39
Colin Angus Mackay31-Dec-05 6:39 
GeneralPossible alternative... Pin
STRX31-Dec-05 2:43
STRX31-Dec-05 2:43 
GeneralRe: Possible alternative... Pin
Damir Valiulin31-Dec-05 8:34
Damir Valiulin31-Dec-05 8:34 
GeneralRe: Possible alternative... Pin
michael_tsai31-Mar-11 19:42
michael_tsai31-Mar-11 19:42 
GeneralRe: Possible alternative... Pin
Damir Valiulin1-Apr-11 3:03
Damir Valiulin1-Apr-11 3:03 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.