📄 utctime.cpp
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
//+----------------------------------------------------------------------------
//
//
// File: utctime.cpp
//
// Contents:
//
// implementation file
//
// ITypeMapper classes implemenation
//
//
//-----------------------------------------------------------------------------
#include "headers.h"
#include <math.h>
#include "utctime.h"
const long c_secondsInDay = 24 * 60 * 60 ;
static HRESULT DateToFixedDate(WORD wYear, const SYSTEMTIME * pVariableDate, SYSTEMTIME *pResultDate);
static BOOL IsBetweenDates(const SYSTEMTIME *pDate, const SYSTEMTIME *pStartDate, const SYSTEMTIME * pEndDate);
static BOOL isBefore(const SYSTEMTIME *pDate, const SYSTEMTIME * pmark);
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// function: HRESULT adjustTime(DOUBLE *pdblDate, long lSecondsToAdjust)
//
// parameters:
//
// description:
// takes date and adds the +/- values to it
//
//
// returns:
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
HRESULT adjustTime(DOUBLE *pdblDate, long lSecondsToAdjust)
{
HRESULT hr=S_OK;
double dblValue;
double dSeconds;
long lSeconds;
double dateToAdd = 1;
bool fAvoidVBZero = false;
bool fAvoidVBHistory = false;
dblValue = *pdblDate;
// the timezone holds the diff to UTC in minutes
if (dblValue < 0 && dblValue > -1)
{
dblValue *= -1;
}
if (dblValue < 0)
{
// addjust the bias to compensate for negative representations of dates/times before 1899
fAvoidVBHistory = true;
dblValue *= -1;
dateToAdd = -1;
}
dSeconds = (dblValue - floor(dblValue)) * c_secondsInDay;
lSeconds = (long) (floor(dSeconds + 0.5));
lSeconds += (lSecondsToAdjust);
if (floor(dblValue) == 0)
{
fAvoidVBZero = true;
dblValue += 10;
}
if (lSeconds >= c_secondsInDay)
{
// we need to add a day
lSeconds -= c_secondsInDay;
dblValue = floor(dblValue) + dateToAdd;
}
else if (lSeconds < 0)
{
// we need to substract a day
lSeconds += c_secondsInDay;
dblValue = floor(dblValue) - dateToAdd;
}
else
{
dblValue = floor(dblValue);
}
dblValue += ( ((double) lSeconds) / c_secondsInDay);
if (fAvoidVBHistory)
{
// do the timewarp again.... go back in history
dblValue *= -1;
}
if (fAvoidVBZero)
{
// now this is tricky. We now have the correct time, but because (-0.9999 to 0.9999 is all the same day)
// we might do the wrong calculation here.
if (dblValue - 10 > 0)
{
dblValue -= 10;
}
else
{
// we would now do something like 9.8 - 10 = - 0.2 which is wrong. the result we want is
// - 1.8 (weird, isn't it :)).
dblValue = floor(dblValue) - 10 - (dblValue - floor(dblValue));
}
}
*pdblDate= dblValue;
return hr;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// function: HRESULT VariantTimeToUTCTime(DOUBLE dblVariantTime, DOUBLE *pdblUTCTime)
//
// parameters:
// takes a time in ole format and converts it to UTC using the current timezone information
//
// description:
//
// returns:
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
HRESULT VariantTimeToUTCTime(DOUBLE dblVariantTime, DOUBLE *pdblUTCTime)
{
HRESULT hr=S_OK;
long lSeconds;
TIME_ZONE_INFORMATION timeZone;
*pdblUTCTime = 0;
CHK_BOOL(GetTimeZoneInformation(&timeZone) != TIME_ZONE_ID_INVALID, E_FAIL);
if (timeZone.DaylightDate.wMonth)
{
// this timezone handles daylight saving time
SYSTEMTIME tVariantDate;
SYSTEMTIME tStartDate;
SYSTEMTIME tEndDate;
CHK_BOOL(::VariantTimeToSystemTime(dblVariantTime, &tVariantDate), E_FAIL);
// calculate the start of daylight savings times
CHK(DateToFixedDate(tVariantDate.wYear, &(timeZone.DaylightDate), &tStartDate));
// calculate the end of daylight savings time
CHK(DateToFixedDate(tVariantDate.wYear, &(timeZone.StandardDate), &tEndDate));
// is the date passed in between the start and end date
if (IsBetweenDates(&tVariantDate, &tStartDate, &tEndDate))
{
// adjust for daylight saving time
lSeconds = (timeZone.Bias + timeZone.StandardBias + timeZone.DaylightBias) * 60;
}
else
lSeconds = (timeZone.Bias + timeZone.StandardBias) * 60;
}
else
{
// no information about daylight savings time specified
lSeconds = (timeZone.Bias * 60);
}
*pdblUTCTime = dblVariantTime;
CHK(adjustTime(pdblUTCTime, lSeconds));
Cleanup:
return hr;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// function: HRESULT UTCTimeToVariantTime(DOUBLE dblVariantTime, DOUBLE *pdblUTCTime)
//
// parameters:
// takes a time in UTC format and converts it to local time using the current timezone information
//
// description:
//
// returns:
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
HRESULT UTCTimeToVariantTime(DOUBLE dblUTCTime, DOUBLE *pdblVariantTime)
{
HRESULT hr=S_OK;
long lSeconds;
TIME_ZONE_INFORMATION timeZone;
*pdblVariantTime = 0;
CHK_BOOL(GetTimeZoneInformation(&timeZone) != TIME_ZONE_ID_INVALID, E_FAIL);
if (timeZone.DaylightDate.wMonth)
{
SYSTEMTIME tUTCDate;
SYSTEMTIME tStartDate;
SYSTEMTIME tEndDate;
CHK_BOOL(::VariantTimeToSystemTime(dblUTCTime, &tUTCDate), E_FAIL);
CHK(DateToFixedDate(tUTCDate.wYear, &(timeZone.DaylightDate), &tStartDate));
CHK(DateToFixedDate(tUTCDate.wYear, &(timeZone.StandardDate), &tEndDate));
//tStartDate and tEndDate are in local time, we have to go to UTC time
DOUBLE dblScratch;
// convert the starttime to UTC
lSeconds = (timeZone.Bias + timeZone.StandardBias + timeZone.DaylightBias) * 60;
CHK_BOOL(::SystemTimeToVariantTime(&tStartDate, &dblScratch), E_FAIL);
CHK(adjustTime(&dblScratch, lSeconds));
CHK_BOOL(::VariantTimeToSystemTime(dblScratch, &tStartDate), E_FAIL);
// convert the endtime to UTC
lSeconds = (timeZone.Bias + timeZone.StandardBias + timeZone.DaylightBias) * 60;
CHK_BOOL(::SystemTimeToVariantTime(&tEndDate, &dblScratch), E_FAIL);
CHK(adjustTime(&dblScratch, lSeconds));
CHK_BOOL(::VariantTimeToSystemTime(dblScratch, &tEndDate), E_FAIL);
if (IsBetweenDates(&tUTCDate, &tStartDate, &tEndDate))
{
// adjust for daylight saving time
lSeconds = (timeZone.Bias + timeZone.StandardBias + timeZone.DaylightBias) * 60;
}
else
lSeconds = (timeZone.Bias + timeZone.StandardBias) * 60;
}
else
{
// no information about daylight savings time specified
lSeconds = (timeZone.Bias * 60);
}
lSeconds *= -1;
*pdblVariantTime = dblUTCTime;
CHK(adjustTime(pdblVariantTime, lSeconds));
Cleanup:
return hr;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// function: BOOL IsBetweenDates(const SYSTEMTIME *pDate, const SYSTEMTIME *pStartDate, const SYSTEMTIME * pEndDate)
//
// parameters:
// determines if pDate is in between StartDate and EndDate
//
// description:
//
// returns:
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
static BOOL IsBetweenDates(const SYSTEMTIME *pDate, const SYSTEMTIME *pStartDate, const SYSTEMTIME * pEndDate)
{
if (isBefore(pDate, pStartDate))
return FALSE;
if ( isBefore(pDate, pEndDate))
{
// before the change into standard time;
return TRUE;
}
return FALSE;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// function: BOOL isBefore(SYSTEMTIME * ptime, SYSTEMTIME * pmark)
//
// parameters:
// returns true if ptimes is before pmark
//
// description:
// TRUE - before
// FALSE - otherwise
//
// returns:
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
static BOOL isBefore(const SYSTEMTIME *pDate, const SYSTEMTIME * pmark)
{
#define DCOMP(l,r) { if ((l) < (r)) { return TRUE; } if ((l) > (r)) { return FALSE; } }
// we have a specific date to compare against, we will ignore the year
DCOMP(pDate->wYear, pmark->wYear);
DCOMP(pDate->wMonth, pmark->wMonth);
DCOMP(pDate->wDay, pmark->wDay);
DCOMP(pDate->wHour, pmark->wHour);
DCOMP(pDate->wMinute, pmark->wMinute);
DCOMP(pDate->wSecond, pmark->wSecond);
DCOMP(pDate->wMilliseconds, pmark->wMilliseconds);
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// function: BOOL DateToFixedDate(WORD wYear, SYSTEMTIME * pVariableDate, SYSTEMTIME *pResultDate)
//
// pVariableDate can describe a variable date (like retrieved from TIME_ZONE_INFORMATION structure
// for start and end dates of daylight saving time), or can be a fixed date.
// It calculates a fixed date matching this input in the year wYear.
//
// parameters:
// wYear - the year the rollover date is calculated for
// pVariableDate - describing the variable date
// pResultDate - the date to use as the
// description:
// S_OK - successfull calculation
// E_FAIL - otherwise
//
// returns:
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
HRESULT DateToFixedDate(WORD wYear, const SYSTEMTIME * pVariableDate, SYSTEMTIME *pResultDate)
{
HRESULT hr = S_OK;
SYSTEMTIME scratch;
DOUBLE dblDate;
WORD wWeek;
CHK_BOOL(pVariableDate && pResultDate, E_FAIL);
// make scratch point to the first day of the month in pVariableDate
memcpy(&scratch, pVariableDate, sizeof(SYSTEMTIME));
// set it to the correct year;
scratch.wYear = wYear;
if (pVariableDate->wYear)
{
scratch.wDay = 1;
}
CHK_BOOL(::SystemTimeToVariantTime(&scratch, &dblDate), E_FAIL);
CHK_BOOL(::VariantTimeToSystemTime(dblDate, &scratch), E_FAIL);
if (pVariableDate->wYear)
{
// we are done, scratch contains the date in the new year
memcpy(pResultDate, &scratch, sizeof(SYSTEMTIME));
goto Cleanup;
}
// scratch is now on the first of the month, and wDayOfWeek is set correctly
// now make the dayofweek in scratch match the value in pVariableDay
while (pVariableDate->wDayOfWeek != scratch.wDayOfWeek)
{
scratch.wDay ++;
scratch.wDayOfWeek = (scratch.wDayOfWeek + 1) % 7;
}
wWeek = 1;
CHK_BOOL(::SystemTimeToVariantTime(&scratch, &dblDate), E_FAIL);
// scratch now is set on the first day of the month, which matches pVariableDate->wDayOfWeek
// dblDate is the double representation of scratch
while (TRUE)
{
SYSTEMTIME work;
if (pVariableDate->wDay == wWeek)
{
// we are all set, the correct date is in scratch
memcpy(pResultDate, &scratch, sizeof(SYSTEMTIME));
goto Cleanup;
}
dblDate += 7;
wWeek++;
CHK_BOOL (::VariantTimeToSystemTime(dblDate, &work), E_FAIL);
if (work.wMonth != scratch.wMonth)
{
// Month changed, if we we are looking for the last week (5), we are done
if (pVariableDate->wDay == 5)
{
// the correct result is in scratch
memcpy(pResultDate, &scratch, sizeof(SYSTEMTIME));
goto Cleanup;
}
else
{
// we switch month without finding the week-count, i.e. the week-count could have
// been 4 in the month of feb.
CHK(E_FAIL);
}
}
memcpy(&scratch, &work, sizeof(SYSTEMTIME));
}
Cleanup:
return hr;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// End Of File
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -