This function is beatiful from Rick Strahl
Representing dates and times across timezones can be a challenge especially if you don’t lay out a plan up front on how to store dates consistently. The sneaky thing with date time management in larger applications and especially applications that live on the Web or are shared across locations, is that problems don’t usually show up until much later in the lifetime of the application. For FoxPro in particular it’s not natural to store dates in anything but local machine format as the language doesn’t support direct UTC formats so it’s very common to see FoxPro applications use local dates which is usually a bad idea.
Here’s why and how we can address these issues
*** Code exists also in wwAPI of any West Wind Tools!
*** SET PROCEDURE TO wwAPI Additive
#define Testing .t.
SET PROCEDURE TO TimeZone additive
#IF Testing
? "Timezone: " + TRANSFORM(GetTimeZone()) + " minutes"
ltTime = DATETIME()
? "Current Time: ", ltTime
ltUtc = GetUtcTime(ltTime)
? "UTC Time: ", ltUtc
ltTime = FromUtcTime(ltUtc)
? "Back to local: ", ltTime
#ENDIF
************************************************************************
* GetUtcTime
****************************************
*** Function: Returns UTC time from local time
*** Assume:
*** Pass:
*** Return:
************************************************************************
FUNCTION GetUtcTime(ltTime)
IF EMPTY(ltTime)
ltTime = DATETIME()
ENDIF
*** Adjust the timezone offset
RETURN ltTime + (GetTimeZone() * 60)
ENDFUNC
* GetUtcTime
************************************************************************
* FromUtcTime
****************************************
*** Function: Returns local time from UTC Time
*** Assume:
*** Pass:
*** Return:
************************************************************************
FUNCTION FromUtcTime(ltTime)
RETURN ltTime - (GetTimeZone() * 60)
ENDFUNC
* FromUtcTime
************************************************************************
FUNCTION GetTimeZone
*********************************
*** Function: Returns the TimeZone offset from GMT including
*** daylight savings. Result is returned in minutes.
************************************************************************
PUBLIC __TimeZone
*** Cache the timezone so this is fast
IF VARTYPE(__TimeZone) = "N"
RETURN __TimeZone
ENDIF
DECLARE integer GetTimeZoneInformation IN Win32API ;
STRING @ TimeZoneStruct
lcTZ = SPACE(256)
lnDayLightSavings = GetTimeZoneInformation(@lcTZ)
lnOffset = CharToBin(SUBSTR(lcTZ,1,4),.T.)
*** Subtract an hour if daylight savings is active
IF lnDaylightSavings = 2
lnOffset = lnOffset - 60
ENDIF
__TimeZone = lnOffset
RETURN lnOffSet
************************************************************************
FUNCTION CharToBin(lcBinString,llSigned)
****************************************
*** Function: Binary Numeric conversion routine.
*** Converts DWORD or Unsigned Integer string
*** to Fox numeric integer value.
*** Pass: lcBinString - String that contains the binary data
*** llSigned - if .T. uses signed conversion
*** otherwise value is unsigned (DWORD)
*** Return: Fox number
************************************************************************
LOCAL m.i, lnWord
lnWord = 0
FOR m.i = 1 TO LEN(lcBinString)
lnWord = lnWord + (ASC(SUBSTR(lcBinString, m.i, 1)) * (2 ^ (8 * (m.i - 1))))
ENDFOR
IF llSigned AND lnWord > 0x80000000
lnWord = lnWord - 1 - 0xFFFFFFFF
ENDIF
RETURN lnWord
Representing dates and times across timezones can be a challenge especially if you don’t lay out a plan up front on how to store dates consistently. The sneaky thing with date time management in larger applications and especially applications that live on the Web or are shared across locations, is that problems don’t usually show up until much later in the lifetime of the application. For FoxPro in particular it’s not natural to store dates in anything but local machine format as the language doesn’t support direct UTC formats so it’s very common to see FoxPro applications use local dates which is usually a bad idea.
Here’s why and how we can address these issues
*** Code exists also in wwAPI of any West Wind Tools!
*** SET PROCEDURE TO wwAPI Additive
#define Testing .t.
SET PROCEDURE TO TimeZone additive
#IF Testing
? "Timezone: " + TRANSFORM(GetTimeZone()) + " minutes"
ltTime = DATETIME()
? "Current Time: ", ltTime
ltUtc = GetUtcTime(ltTime)
? "UTC Time: ", ltUtc
ltTime = FromUtcTime(ltUtc)
? "Back to local: ", ltTime
#ENDIF
************************************************************************
* GetUtcTime
****************************************
*** Function: Returns UTC time from local time
*** Assume:
*** Pass:
*** Return:
************************************************************************
FUNCTION GetUtcTime(ltTime)
IF EMPTY(ltTime)
ltTime = DATETIME()
ENDIF
*** Adjust the timezone offset
RETURN ltTime + (GetTimeZone() * 60)
ENDFUNC
* GetUtcTime
************************************************************************
* FromUtcTime
****************************************
*** Function: Returns local time from UTC Time
*** Assume:
*** Pass:
*** Return:
************************************************************************
FUNCTION FromUtcTime(ltTime)
RETURN ltTime - (GetTimeZone() * 60)
ENDFUNC
* FromUtcTime
************************************************************************
FUNCTION GetTimeZone
*********************************
*** Function: Returns the TimeZone offset from GMT including
*** daylight savings. Result is returned in minutes.
************************************************************************
PUBLIC __TimeZone
*** Cache the timezone so this is fast
IF VARTYPE(__TimeZone) = "N"
RETURN __TimeZone
ENDIF
DECLARE integer GetTimeZoneInformation IN Win32API ;
STRING @ TimeZoneStruct
lcTZ = SPACE(256)
lnDayLightSavings = GetTimeZoneInformation(@lcTZ)
lnOffset = CharToBin(SUBSTR(lcTZ,1,4),.T.)
*** Subtract an hour if daylight savings is active
IF lnDaylightSavings = 2
lnOffset = lnOffset - 60
ENDIF
__TimeZone = lnOffset
RETURN lnOffSet
************************************************************************
FUNCTION CharToBin(lcBinString,llSigned)
****************************************
*** Function: Binary Numeric conversion routine.
*** Converts DWORD or Unsigned Integer string
*** to Fox numeric integer value.
*** Pass: lcBinString - String that contains the binary data
*** llSigned - if .T. uses signed conversion
*** otherwise value is unsigned (DWORD)
*** Return: Fox number
************************************************************************
LOCAL m.i, lnWord
lnWord = 0
FOR m.i = 1 TO LEN(lcBinString)
lnWord = lnWord + (ASC(SUBSTR(lcBinString, m.i, 1)) * (2 ^ (8 * (m.i - 1))))
ENDFOR
IF llSigned AND lnWord > 0x80000000
lnWord = lnWord - 1 - 0xFFFFFFFF
ENDIF
RETURN lnWord