【问题标题】:How to find out the next time when the clock will be adjusted for Daylight Saving?如何知道下一次时钟何时调整为夏令时?
【发布时间】:2014-05-01 04:08:18
【问题描述】:

我很好奇,是否有任何方法可以找出下一次夏令时调整的 UTC 日期/时间?

类似于 Windows 报告的内容(见圆圈):

【问题讨论】:

标签: c++ c windows winapi dst


【解决方案1】:

此信息在 Windows 中由 EnumDynamicTimeZoneInformation 函数提供。

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

【讨论】:

  • 感谢您的回复。它实际上使我了解了我在下面的代码中使用的GetDynamicTimeZoneInformation API 或GetTimeZoneInformation
【解决方案2】:

有一个数据库有代码和数据:http://www.iana.org/time-zones

【讨论】:

    【解决方案3】:

    我认为没有特定的 API。我只会使用<ctime> (C++) 或<time.h> (C) 中的localtime(可能还有timemktime)进行二分搜索。

    一种基本方法是一次提前三个月扫描,直到返回的数据结构中的tm_isdst 标志被翻转。然后您可以在最后两个日期之间开始二进制搜索,以准确确定它何时翻转。

    请参阅http://www.cplusplus.com/reference/ctime/tm/ 以获取参考资料。

    【讨论】:

    • 对于完全不使用 DST 的国家,我想合理的限制是在当地时间一年后停止。
    • @SirDarius 是的,一年就可以了。
    • 我认为 C r/t 答案不适合 Winapi 问题,是吗?
    • @david.pfx 我没有注意到 winapi 标签,并假设需要更通用的解决方案(仅以 Windows 对话框为例)。我也不知道您在回答中引用的 API 调用。
    【解决方案4】:

    感谢您的所有回复。而且,是的,我确实在询问 Windows 的 WinAPI。

    我做了更多的研究,并想出了以下方法来满足我的需求。它使用 C++ 和 MFC 的 COleDateTime 来简化日期/时间计算。除此之外,它只是 C++ 和 WinAPI。请检查我是否正确理解了DYNAMIC_TIME_ZONE_INFORMATION 的文档。代码如下:

    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;
    }
    

    附言。并划掉我对UTC 时间的要求。据我所知,在这种情况下更容易处理当地时间。

    【讨论】:

    • 很晚了,但由于这个答案对你有用(Q+A 风格),它应该被标记为答案。你怎么看?
    猜你喜欢
    • 2023-03-18
    • 2018-12-12
    • 1970-01-01
    • 1970-01-01
    • 2019-11-20
    • 2011-11-05
    • 2014-10-27
    • 2023-03-05
    • 2016-03-06
    相关资源
    最近更新 更多