2

I'm curious, if there's any way to find out the UTC date/time when the next Daylight Saving adjustment will take place?

Something akin to what Windows reports (see circled):

enter image description here

c00000fd
  • 20,994
  • 29
  • 177
  • 400
  • possible duplicate of [Get Daylight Saving Transition Dates For Time Zones in C](http://stackoverflow.com/questions/678445/get-daylight-saving-transition-dates-for-time-zones-in-c) – Retired Ninja Mar 24 '14 at 00:12
  • A pedantic note: the second after Nov 2, 2014 1:59:59 daylight time is Nov 2, 2014 1:00:00 standard time. Nov 2, 2014 2:00 AM daylight time does not exist. See [Procedure](http://en.wikipedia.org/wiki/Daylight_saving_time#Procedure) – chux - Reinstate Monica Mar 24 '14 at 05:38

4 Answers4

3

This information is provided in Windows by the EnumDynamicTimeZoneInformation function.

See http://msdn.microsoft.com/en-us/library/windows/desktop/hh706893%28v=vs.85%29.aspx

david.pfx
  • 10,520
  • 3
  • 30
  • 63
  • I appreciate your reply. It actually clued me in to the `GetDynamicTimeZoneInformation` API, or `GetTimeZoneInformation` that I used in my code below. – c00000fd Mar 24 '14 at 06:32
2

There is a database that has code and data: http://www.iana.org/time-zones

Marichyasana
  • 2,966
  • 1
  • 19
  • 20
1

I don't think there's a specific API for this. I would just do a binary search, using localtime (and maybe time and mktime) from <ctime> (C++) or <time.h> (C).

A basic approach is to scan ahead three months at a time until the tm_isdst flag in the returned data structure is flipped. Then you can start binary searching between the last two two dates to figure out exactly when it flips.

See http://www.cplusplus.com/reference/ctime/tm/ for reference material.

paddy
  • 60,864
  • 6
  • 61
  • 103
  • 1
    In the case of countries that do not use DST at all, I guess a sane limit would be to stop one year after the local time. – SirDarius Mar 24 '14 at 00:21
  • 1
    @SirDarius Yeah, a year would probably be fine. – paddy Mar 24 '14 at 00:23
  • I don't think a C r/t answer is a good match for a Winapi question, is it? – david.pfx Mar 24 '14 at 00:59
  • @david.pfx I didn't notice the winapi tag, and had assumed a more generic solution was required (citing a Windows dialog as an example only). I also wasn't aware of the API call that you have referenced in your answer. – paddy Mar 24 '14 at 01:04
1

I appreciate all your replies. And, yes, indeed I was asking about a WinAPI for Windows.

I did more research and came up with the following method that does what I wanted. It uses C++ and MFC's COleDateTime for easier date/time calculations. Other than that it's just C++ and WinAPIs. Please check if I understood the documentation for the DYNAMIC_TIME_ZONE_INFORMATION correctly. Here's the code:

int GetNextDaylightSavingAdjustmentTime(SYSTEMTIME* pOutDtNextDST_Local, int* pnOutAdjustmentMin)
{
    //Get next time when DST adjustment will take place
    //'pOutDtNextDST_Local' = if not NULL, receives the (local) time when next DST adjustment will take place
    //'pnOutAdjustmentMin' = if not NULL, receives the amount of adjustment in minutes
    //RETURN:
    //      = 1 if got the time, or
    //      = 0 if DST is not used
    //      = -1 if error (check GetLastError() for info)
    int nOSError = NO_ERROR;

    //Load API dynamically (in case of Windows XP)
    BOOL (WINAPI *pfnGetDynamicTimeZoneInformation)(PDYNAMIC_TIME_ZONE_INFORMATION);
    (FARPROC&)pfnGetDynamicTimeZoneInformation =
        ::GetProcAddress(::GetModuleHandle(L"Kernel32.dll"), "GetDynamicTimeZoneInformation");

    DWORD tzID;
    SYSTEMTIME StandardDate;
    SYSTEMTIME DaylightDate;
    int nBiasDaylight;

    //Use newer API if possible
    if(pfnGetDynamicTimeZoneInformation)
    {
        DYNAMIC_TIME_ZONE_INFORMATION dtzi = {0};
        tzID = pfnGetDynamicTimeZoneInformation(&dtzi);

        StandardDate = dtzi.StandardDate;
        DaylightDate = dtzi.DaylightDate;

        nBiasDaylight = dtzi.DaylightBias;
    }
    else
    {
        //Older API
        TIME_ZONE_INFORMATION tzi = {0};
        tzID = GetTimeZoneInformation(&tzi);

        StandardDate = tzi.StandardDate;
        DaylightDate = tzi.DaylightDate;

        nBiasDaylight = tzi.DaylightBias;
    }

    int nRes = -1;
    int nAdjMins = 0;
    SYSTEMTIME stDstChange;
    memset(&stDstChange, 0, sizeof(stDstChange));

    SYSTEMTIME stDst;
    if(tzID == TIME_ZONE_ID_STANDARD ||
        tzID == TIME_ZONE_ID_DAYLIGHT)
    {
        stDst = tzID != TIME_ZONE_ID_DAYLIGHT ? DaylightDate : StandardDate;

        if(stDst.wMonth >= 1 &&
            stDst.wMonth <= 12 &&
            stDst.wDay >= 1 &&
            stDst.wDayOfWeek >= 0 &&
            stDst.wDayOfWeek <= 6)
        {
            //Get adjustment bias
            nAdjMins = tzID != TIME_ZONE_ID_DAYLIGHT ? -nBiasDaylight : nBiasDaylight;

            if(stDst.wYear == 0)
            {
                //Relative date

                SYSTEMTIME stLocal;
                ::GetLocalTime(&stLocal);

                //Begin from the 1st day of the month &
                //make sure that the date is in the future
                COleDateTime dt;
                for(int nYear = stLocal.wYear;; nYear++)
                {
                    dt.SetDateTime(nYear, stDst.wMonth, 1, stDst.wHour, stDst.wMinute, stDst.wSecond);
                    if(dt > COleDateTime::GetCurrentTime())
                        break;
                }

                int nRequiredWeek = stDst.wDay >= 1 && stDst.wDay <= 5 ? stDst.wDay : 5;

                for(int nCntDOW = 1;;)
                {
                    //0=Sunday, 1=Monday; 2=Tuesday; 3=Wednesday; 4=Thursday; 5=Friday; 6=Saturday
                    int dow = dt.GetDayOfWeek() - 1;
                    ASSERT(dow >= 0 && dow <= 6);

                    if(dow == stDst.wDayOfWeek)
                    {
                        if(nCntDOW >= nRequiredWeek)
                        {
                            //Stop
                            break;
                        }
                        else
                        {
                            nCntDOW++;
                        }
                    }

                    //Go to next day
                    dt += COleDateTimeSpan(1, 0, 0, 0);
                }

                //Convert back to system time
                if(dt.GetAsSystemTime(stDstChange))
                {
                    //Success
                    nRes = 1;
                }
                else
                {
                    //Failed
                    nOSError = ERROR_INVALID_FUNCTION;
                    ASSERT(NULL);
                }
            }
            else
            {
                //Absolute date
                stDstChange = stDst;

                nRes = 1;
            }
        }
        else
        {
            //Failed
            nOSError = ERROR_INVALID_PARAMETER;
            ASSERT(NULL);
        }
    }
    else
    {
        //DST is not used
        if(tzID == TIME_ZONE_ID_UNKNOWN)
        {
            nRes = 0;
        }
        else
        {
            //Error
            nOSError = ERROR_INVALID_DATA;
            ASSERT(NULL);
        }
    }


    if(pOutDtNextDST_Local)
        *pOutDtNextDST_Local = stDstChange;
    if(pnOutAdjustmentMin)
        *pnOutAdjustmentMin = nAdjMins;

    ::SetLastError(nOSError);
    return nRes;
}

PS. And scratch my request for the UTC time. As I learned, it is easier to deal with local time in this situation.

c00000fd
  • 20,994
  • 29
  • 177
  • 400