Web Connection
wwjsonserializer WriteDate() to UTC - ugh
Gravatar is a globally recognized avatar based on your email address. wwjsonserializer WriteDate() to UTC - ugh
  Bob Roenigk
  Rick
  Oct 8, 2014 @ 05:38pm
Rick,

The wwJsonSerializer :: WriteData function attempts to convert all datetime values into a UTC date by adding the timezone value (of wherever the server might be located). This is a problem. In my case, all the dates within my SQL table are already stored in UTC. Thus, the resulting JSON is returning every datetime misstated by some number of hours.

My need is for the JsonSerializer to return the data as it is stored in the table.

~bob

Gravatar is a globally recognized avatar based on your email address. Re: wwjsonserializer WriteDate() to UTC - ugh
  Rick Strahl
  Bob Roenigk
  Oct 8, 2014 @ 09:39pm

Since FoxPro has no sense of UTC dates it assumes all dates are local dates. There's no way for wwJsonSerializer to tell the difference to really work around this generically so I think the only solution to this is to convert the dates before conversion.

You can use the FromUtcTime() function (in wwapi) to perform that for you. If it's in an object list you'll have to loop through and do it to each one before serialization.

+++ Rick ---



Rick,

The wwJsonSerializer :: WriteData function attempts to convert all datetime values into a UTC date by adding the timezone value (of wherever the server might be located). This is a problem. In my case, all the dates within my SQL table are already stored in UTC. Thus, the resulting JSON is returning every datetime misstated by some number of hours.

My need is for the JsonSerializer to return the data as it is stored in the table.

~bob




Rick Strahl
West Wind Technologies

Making waves on the Web
from Maui

Gravatar is a globally recognized avatar based on your email address. Re: wwjsonserializer WriteDate() to UTC - ugh
  Rick Strahl
  Bob Roenigk
  Oct 8, 2014 @ 09:58pm
Ok, so I gave this a little more thought and I suppose it's worthy of adding a flag to the serializer.

So we now have:

*** If .t. doesn't convert dates to UTC first
*** Assumes that any date is already UTC formatted

AssumeUtcDates = .F.

in the class header and:

************************************************************************
* wwJsonSerializer :: WriteDate
****************************************
*** Function: Turns a date into an ISO formatted date value string
*** Assume: Input dates are assumed to be local dates
*** If you have UTC dates to start you'll need to convert
*** them to local dates first
*** Pass:
*** Return:
************************************************************************

FUNCTION WriteDate(lvValue)

IF VARTYPE(lvValue) = "D"
lvValue = DTOT(lvValue)
ENDIF

*** turn into UTC date
IF !THIS.AssumeUtcDates
lvValue = lvValue + (GetTimeZone() * 60)
ENDIF

IF EMPTY(lvValue)
lvValue = {^1970-1-1 :}
ENDIF

#IF wwVFPVersion > 8
this.cOutput = this.cOutput + '"' + TTOC(lvValue,3) + 'Z"'
RETURN
#ENDIF

*** ISO Format
this.cOutput = this.cOutput + ;
'"' + TRANSFORM(YEAR(ldDateVal)) + "-" + ;
PADL(TRANSFORM(MONTH(ldDateVal)),2,"0") + "-" +;
PADL(TRANSFORM(Day(ldDateVal)),2,"0") + "T" +;
PADL(TRANSFORM(Hour(ldDateVal)),2,"0") + ":" +;
PADL(TRANSFORM(Minute(ldDateVal)),2,"0") + ":" +;
PADL(TRANSFORM(Sec(ldDateVal)),2,"0") + 'Z"'

ENDFUNC
* wwJsonSerializer :: WriteDate

where the code skips generation if this flag is set to .T.

This will be in a future release so you can make this change now to get you around the problem you described here and it'll just be there in the next version.

+++ Rick ---


Rick,

The wwJsonSerializer :: WriteData function attempts to convert all datetime values into a UTC date by adding the timezone value (of wherever the server might be located). This is a problem. In my case, all the dates within my SQL table are already stored in UTC. Thus, the resulting JSON is returning every datetime misstated by some number of hours.

My need is for the JsonSerializer to return the data as it is stored in the table.

~bob




Rick Strahl
West Wind Technologies

Making waves on the Web
from Maui

Gravatar is a globally recognized avatar based on your email address. Re: wwjsonserializer WriteDate() to UTC - ugh
  Bob Roenigk
  Rick Strahl
  Oct 9, 2014 @ 08:34am
Thank you, Rick That is a good solution. I am collecting data from around the world and had the good sense to normalize all dates to UTC before they hit the SQL database.

Where should the "AssumeUtcDates = .F." declaration be placed so your next update does not overwrite it?

Also, did you see my "wwjsonserializer bug" posted on 10/8 within your ParseObjectJson()?

~bob



Ok, so I gave this a little more thought and I suppose it's worthy of adding a flag to the serializer.

So we now have:

*** If .t. doesn't convert dates to UTC first
*** Assumes that any date is already UTC formatted

AssumeUtcDates = .F.

in the class header and:

************************************************************************
* wwJsonSerializer :: WriteDate
****************************************
*** Function: Turns a date into an ISO formatted date value string
*** Assume: Input dates are assumed to be local dates
*** If you have UTC dates to start you'll need to convert
*** them to local dates first
*** Pass:
*** Return:
************************************************************************

FUNCTION WriteDate(lvValue)

IF VARTYPE(lvValue) = "D"
lvValue = DTOT(lvValue)
ENDIF

*** turn into UTC date
IF !THIS.AssumeUtcDates
lvValue = lvValue + (GetTimeZone() * 60)
ENDIF

IF EMPTY(lvValue)
lvValue = {^1970-1-1 :}
ENDIF

#IF wwVFPVersion > 8
this.cOutput = this.cOutput + '"' + TTOC(lvValue,3) + 'Z"'
RETURN
#ENDIF

*** ISO Format
this.cOutput = this.cOutput + ;
'"' + TRANSFORM(YEAR(ldDateVal)) + "-" + ;
PADL(TRANSFORM(MONTH(ldDateVal)),2,"0") + "-" +;
PADL(TRANSFORM(Day(ldDateVal)),2,"0") + "T" +;
PADL(TRANSFORM(Hour(ldDateVal)),2,"0") + ":" +;
PADL(TRANSFORM(Minute(ldDateVal)),2,"0") + ":" +;
PADL(TRANSFORM(Sec(ldDateVal)),2,"0") + 'Z"'

ENDFUNC
* wwJsonSerializer :: WriteDate

where the code skips generation if this flag is set to .T.

This will be in a future release so you can make this change now to get you around the problem you described here and it'll just be there in the next version.

+++ Rick ---


Rick,

The wwJsonSerializer :: WriteData function attempts to convert all datetime values into a UTC date by adding the timezone value (of wherever the server might be located). This is a problem. In my case, all the dates within my SQL table are already stored in UTC. Thus, the resulting JSON is returning every datetime misstated by some number of hours.

My need is for the JsonSerializer to return the data as it is stored in the table.

~bob




Gravatar is a globally recognized avatar based on your email address. Re: wwjsonserializer WriteDate() to UTC - ugh
  n/a
  Rick Strahl
  Nov 23, 2014 @ 11:26pm
YEA !!!!! Thank you for this!

Can you also update the wwAPI functions: GetUtcTime and FromUtcTime to account for relative changes in the timezone? Like passing a date from 100 days ago while DST was in affect. I included some code below that uses the Windows API.

Thanks!
Bo


Ok, so I gave this a little more thought and I suppose it's worthy of adding a flag to the serializer.

So we now have:

*** If .t. doesn't convert dates to UTC first
*** Assumes that any date is already UTC formatted

AssumeUtcDates = .F.

...

This will be in a future release so you can make this change now to get you around the problem you described here and it'll just be there in the next version.

+++ Rick ---


** Today
?LocalToUtcTime(DATETIME())
** 150 days ago
?LocalToUtcTime(DATETIME()-(150*24*60*60))


************************************************************************
FUNCTION LocalToUtcTime(ltLocalTime)
****************************************
*** Function: Returns a UTC DateTime from a Local DateTime
*** Accounts for daylight saving time
************************************************************************

LOCAL lqUTCTime, lqLocalTime, ltUtcTime

DECLARE Long TzSpecificLocalTimeToSystemTime IN WIN32API ;
String lpTimeZone, ;
String lpLocalTime, ;
String @lpUniversalTime

lqLocalTime = VFPToSystemTime(ltLocalTime)
lqUTCTime = VFPToSystemTime()
ltUtcTime = {// ::}

IF TzSpecificLocalTimeToSystemTime(NULL,lqLocalTime,@lqUTCTime) = 1
ltUtcTime = SystemTimeToVFP(lqUTCTime)
ENDIF

RETURN ltUtcTime
ENDFUNC


************************************************************************
FUNCTION UtcToLocalTime(ltUTCTime)
****************************************
*** Function: Returns a Local DateTime from a UTC DateTime
*** Accounts for daylight saving time
************************************************************************

LOCAL lqUTCTime, lqLocalTime, ltLocalTime

DECLARE Long SystemTimeToTzSpecificLocalTime IN WIN32API ;
String lpTimeZone, ;
String lpUniversalTime, ;
String @lpLocalTime

lqUTCTime = VFPToSystemTime(ltUTCTime)
lqLocalTime = VFPToSystemTime()
ltLocalTime = {// ::}

IF SystemTimeToTzSpecificLocalTime(NULL,lqUTCTime,@lqLocalTime) = 1
ltLocalTime = SystemTimeToVFP(lqLocalTime)
ENDIF

RETURN ltLocalTime
ENDFUNC


************************************************************************
FUNCTION SYSTEMTIMEToVFP(lqSYSTEMTIME)
****************************************
*** Function: Returns a VFP DateTime from a SYSTEMTIME API structure (as a binary string)
************************************************************************

LOCAL ltVFPTime

ltVFPTime = DATETIME( ;
CTOBIN(SUBSTR(lqSYSTEMTIME, 1,2),"2rs"), ;
CTOBIN(SUBSTR(lqSYSTEMTIME, 3,2),"2rs"), ;
CTOBIN(SUBSTR(lqSYSTEMTIME, 7,2),"2rs"), ;
CTOBIN(SUBSTR(lqSYSTEMTIME, 9,2),"2rs"), ;
CTOBIN(SUBSTR(lqSYSTEMTIME,11,2),"2rs"), ;
CTOBIN(SUBSTR(lqSYSTEMTIME,13,2),"2rs"))

RETURN ltVFPTime
ENDFUNC


************************************************************************
FUNCTION VFPToSYSTEMTIME(ltVFPTime)
****************************************
*** Function: Returns a SYSTEMTIME API structure (as a binary string) from a VFP DateTime
************************************************************************

LOCAL lqSYSTEMTIME

lqSYSTEMTIME = 0h0000+0h0000+0h0000+0h0000+0h0000+0h0000+0h0000+0h0000

IF NOT EMPTY(ltVFPTime)
lqSYSTEMTIME = STUFF(lqSYSTEMTIME, 1, 2, BINTOC(YEAR(ltVFPTime),"2rs"))
lqSYSTEMTIME = STUFF(lqSYSTEMTIME, 3, 2, BINTOC(MONTH(ltVFPTime),"2rs"))
lqSYSTEMTIME = STUFF(lqSYSTEMTIME, 5, 2, BINTOC(DOW(ltVFPTime),"2rs"))
lqSYSTEMTIME = STUFF(lqSYSTEMTIME, 7, 2, BINTOC(DAY(ltVFPTime),"2rs"))
lqSYSTEMTIME = STUFF(lqSYSTEMTIME, 9, 2, BINTOC(HOUR(ltVFPTime),"2rs"))
lqSYSTEMTIME = STUFF(lqSYSTEMTIME, 11, 2, BINTOC(MINUTE(ltVFPTime),"2rs"))
lqSYSTEMTIME = STUFF(lqSYSTEMTIME, 13, 2, BINTOC(SEC(ltVFPTime),"2rs"))
lqSYSTEMTIME = STUFF(lqSYSTEMTIME, 15, 2, BINTOC(0000,"2rs"))
ENDIF

RETURN lqSYSTEMTIME
ENDFUNC


© 1996-2024