Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at CFUNITED 2010 (Landsdown, VA) with:

Converting To GMT And From GMT In ColdFusion For Use With HTTP Time Stamps

By Ben Nadel on
Tags: ColdFusion

Yesterday, I was working with some HTTP time stamps, which I learned are all stored in GMT (Greenwich Mean Time) format. All time throughout the world is calculated as an offset to UTC (Universal Time Coordinated) which is based on the GMT time zone. By storing HTTP time stamps in GMT format, it allows clients in all different locales to accurately convert GMT time stamps to local times without having to know the originating time zone of the issuing service.

ColdFusion has a couple of methods that allow us to easily work with GMT / HTTP time stamps. For starters, there is the GetTimeZoneInfo() method which gives us our local time offset relative to the UTC time. Outputting GetTimeZoneInfo() on my machine gives us:

 
 
 
 
 
 
GetTimeZoneInfo() Returns Information About The Current Time Relative To UTC / GMT Time. 
 
 
 

The UTCTotalOffset gives us the number of seconds that the machine's time zone is offset from GMT / UTC. The UTCHourOffset and the UTCMinuteOffset are simply different representations of this value. Taking the seconds offset, we can easily convert our local times to GMT time using ColdFusion's DateAdd() function:

  • <!--- Get current time. --->
  • <cfset dtNow = Now() />
  •  
  • <!--- Convert to GMT using UTC Offset. --->
  • <cfset dtGMT = DateAdd(
  • "s",
  • GetTimeZoneInfo().UTCTotalOffset,
  • dtNow
  • ) />
  •  
  • <!--- Output both dates. --->
  • Now: #dtNow#<br />
  • GMT: #dtGMT#<br />

As you can see, using DateAdd(), we are simply adding the UTCTotalOffset to our current date. Running the above code, we get the following output:

Now: {ts '2009-05-26 09:17:55'}
GMT: {ts '2009-05-26 13:17:55'}

As you can see, the GMT time is 4 hours later than the local date (just as we saw in the UTCHourOffset value returned from GetTimeZoneInfo()). If we wanted to convert from a GMT time back to a local time, all we would have to do is go back the other way, again using DateAdd():

  • <!--- Convert from GMT back to Now(). --->
  • <cfset dtNow = DateAdd(
  • "s",
  • -GetTimeZoneInfo().UTCTotalOffset,
  • dtGMT
  • ) />
  •  
  • <!--- Output both dates. --->
  • Now: #dtNow#<br />
  • GMT: #dtGMT#<br />

Here, rather than adding the UTCTotalOffset to get to GMT, we are subtracting the UTCTotalOffset to get back from GMT to our local time. This gives us the following output:

Now: {ts '2009-05-26 09:21:04'}
GMT: {ts '2009-05-26 13:21:04'}

Once we have our time in GMT format, we have to take it a step further to format the date/time stamp as an HTTP date/time stamp. We can do this manually using ColdFusion's DateFormat() and TimeFormat() methods:

  • <!--- Convert GMT time stampe to HTTP time stamp. --->
  • <cfset strGMT = (
  • DateFormat( dtGMT, "ddd, dd mmm yyyy" ) &
  • " " &
  • TimeFormat( dtGMT, "HH:mm:ss" ) &
  • " GMT"
  • ) />
  •  
  • <!--- Output value. --->
  • HTTP: #strGMT#

Running this code gives us the following output:

HTTP: Tue, 26 May 2009 13:25:54 GMT

Converting local dates to GMT format and then HTTP format is not too complicated, but it is an involved process. Luckily, if our ultimate goal is to create an HTTP time stamp, ColdFusion actually has a function that encapsulates all of the above work into a single method: GetHTTPTimeString(). GetHTTPTimeString() takes a standard ColdFusion date/time object and converts it UTC / GMT time and then formats it as HTTP time/stamp in one step:

  • <!--- Output HTTP Time stamp. --->
  • HTTP: #GetHTTPTimeString( Now() )#

As you can see, all we pass in is the current time stamp. Running the above code gives us the following HTTP time stamp output:

HTTP: Tue, 26 May 2009 13:29:39 GMT

Pretty easy right! GetHTTPTimeString() takes all the hassle out of making HTTP time stamps.

As a final note, if you want to parse an HTTP time stamp back into a regular ColdFusion time stamp, all you need to do is use ParseDateTime():

  • <!--- Convert HTTP time stampe back to GMT time. --->
  • <cfset dtGMT = ParseDateTime(
  • GetHTTPTimeString( Now() )
  • ) />
  •  
  • <!--- Output parsed GMT time. --->
  • ParseDateTime(): #dtGMT#

Running the above code, we get the following output:

ParseDateTime(): {ts '2009-05-26 13:46:09'}

Keep in mind, of course, that this parsed date/time is still in GMT format. To get back to your local time, you have to subtract the UTCTotalOffset.



Looking For A New Job?

100% of job board revenue is donated to Kiva. Loans that change livesFind out more »

Reader Comments

ben you're going to fall into tz hell if the server's tz has DST & you get datetimes that are on the cusp of DST--these datestimes will *never* exist on the server.

Reply to this Comment

assuming your server's tz has DST what does this give you?

<cfscript>
t=createDateTime(2006,4,2,2,1,0);
writeoutput("#t#");
</cfscript>

if i remember rightly, cf7 (on a server w/DST) would just barf.

Reply to this Comment

@Paul,

Ahh, I see what you're saying. I assume that DateAdd() and GetHTTPTimeString() both handle this gracefully as they won't create invalid dates.

ParseDateTime() on the other hand, might error out if you try to parse an invalid date - not sure what happens off-hand.

Reply to this Comment

@Ben,

I know little about ColdFusion (my specialty is ASP.NET), but I'm wondering if GetTimeZoneInfo returns the info for the web server (the documentation says that it returns the time info on the machine where it's called, so I assume this is called on the server, right?). If so, it would not really help much if your goal is to convert time to the local time on the client. Or did I miss the point?

Reply to this Comment

@Alek,

I started looking into this when dealing with HTTP headers. Some of them require HTTP time stamps using GMT time. It is the Client's responsibility to convert the HTTP time stamp into client-local time. I suppose that is the point of the universal time format.

@John,

Oh nice! I didn't know about those functions. Looks like those will be useful when GetHTTPTimeString() is not enough. Thanks!

Reply to this Comment

@Ben,

I'm not sure what you meant by "Client's responsibility". Did you mean user or JavaScript code? I think it's a programmer's responsibility to convert time to local time. After all, the programmer is responsible for writing both server and client-side code. And it's not a simple problem to solve.

I came up with an approach to make time localization seamless for both the programmer and the user. It's based on using JavaScript's getTimezoneOffset function and session cookie, and it's an ASP.NET-specific solution, but it can be ported to other frameworks (I guess). But having invested a lot of time in this area, I'm wondering if there is a better approach.

So what you're saying is that your method does not actually deal with determining and converting timestamps to local lime on the client (per browser), right?

Reply to this Comment

@Alek,

It depends on what the time is being used for. If you are writing HTTP headers, such as the "Expires" header, the time is always sent in GMT format. The client code is not responsible for using this - the browser itself is responsible for understanding GMT and how that relates to the local machine's time.

If, however you are outputting local time on the web page, then yeah, you'd have to write something for it.

Reply to this Comment

ben

on cf8 & above it will automagically rollover to DST (ie 3:01 AM) even if your intention is that it's UTC or whatever non-DST tz. if you let cf mess w/your datetimes (dateAdd, etc.) it will chomp them into its own tz.

and maybe because i've been dealing with tz & offsets all day & my head is messed up but don't you want to subtract offsets to get to UTC? your date math is bassackwards?

Reply to this Comment

I'm still struggling with this as well but for different reasons.

A DJ scheduled for Monday 10p.m. GMT actually falls on Tuesday in many parts of the world so I've had serious issues with things falling on the wrong week (previous sunday) or day depending on where you live.

With MSSQL you can actually do this little bit which is nice.

declare @deltaGMT int

exec master.dbo.xp_regread 'HKEY_LOCAL_MACHINE',
'SYSTEM\CurrentControlSet\Control\TimeZoneInformation',
'ActiveTimeBias', @DeltaGMT OUT

SELECT

DATEPART(hour,getdate()) as LOCALH,
DATEPART(hour, dateadd(minute, @deltaGMT, getdate() )) as GMTH

FROM tblBlah

<cfif tblBlah.LOCALH GT tblBlah.GMTH>
<cfset moveTime = tblBlah.LOCALH - tblBlah.GMTH>
<cfset OffSet = DateAdd("h", moveTime, StartTime)>
<cfelse>
<cfset moveTime = tblBlah.GMTH - tblBlah.LOCALH>
<cfset OffSet = DateAdd("h", -moveTime, StartTime)>
</cfif>

Local Time: #TimeFormat(OffSet, 'h:MMtt')#

but I still don't think this is correct some of the time as well.

c.

Reply to this Comment

@PaulH,

As you go West, the time get's later, which is why I think you need to add the UTCTotalOffset time, not subtract.

@Casey,

I am very new to this stuff; but, from what I can tell, GMT is simply a sort of "universal" way to store date/time stamps. You store it this way so that any consuming service can convert it to local time. However, depending on how you make it consumable, I suppose, depends on how you convert the time.

I don't think I did (or even can) answer you question, as I am still learning; but, it seems that storing time in GMT is not magical in any way - its simply storing it in a way that needs to be converted again later on based on locale.

Reply to this Comment

@ben look around the room, red fellow w/horns & a pitchfork? man you've fallen into tz hell for sure.

the offset should handle the direction. here in bangkok we're UTC+7 (ICT), so to get back to UTC i'd subtract 7. for tz America/Dawson_Creek (UTC-7) i'd still subtract but since the UTC offset is negative....you do the math.

as far as your definition of GMT goes, rather than berate you or smack you in the back of the head (which would take a 12 hour+ plane ride & you seem to have bigger arms than me anyway ;-) i'll simply point you to the wiki on GMT: http://en.wikipedia.org/wiki/Greenwich_Mean_Time as well as point out that locale hs nothing to do w/tz.

Reply to this Comment

Yes, Paul is correct regarding tz hell. It is so annoying (to put it lightly), and hopefully CF implements a better solution.

Reply to this Comment

@PaulH,

Wiki pages are really long :) Is there something that I am not understanding? From what I read previously, it seems that GMT is used simply to allow a standardized time storage format that could easily be translated into different timezones.

As for TimeZone and locale, sorry if I was using them inappropriately. I understand that a locale is different than a timezone.

Reply to this Comment

I would nod to Paul for all his research in this: http://www.sustainablegis.com/blog/cfg11n/index.cfm?mode=entry&entry=77223B6A-20ED-7DEE-2AB7FBB1F37ABD77

Let me see if I can explain this properly.

The issue can occur when all of the following are true:
1. The operating system is configured to automatically adjust the clock for Daylight Saving Time.
2. ColdFusion did not start the Java Virtual Machine with the '-Duser.timezone=GMT' JVM argument (Server Settings > Java and JVM page of ColdFusion Administrator).
3. DST is starting or ending.

When DST starts, the operating system clock leaps forward an hour. The hour that is skipped never exists in ColdFusion. ColdFusion's date/time functions cannot correct for this.

When DST ends, the operating system clock jumps backward an hour. The hour that is repeated exists twice in ColdFusion. ColdFusion's date/time functions cannot correct for this.

Hopefully I'm close w/ that!

Reply to this Comment

@kinky ben
"GMT is used simply to allow a standardized time storage format" uh, no (but nice try sweeping away 200+ years of history & culture). it was originally used to help maritime brits (you know "iron men, wooden ships") calculate longitude. greenwich (the "G" in GMT) was zero degrees longitude by convention (and by convention i mean all those royal navy man-of-wars). on board ships, one clock was kept to GMT, one to local or sun time ("sun" because they reset it to noon everyday when the sun was at it's highest). every hour of difference between the two clocks equaled +/-15 degrees of longitude (the earth takes 24 hours to rotate 360 degrees).

developing a reliable/accurate clock for use on ships was like the 18th century's manhattan project. somebody wrote, IMHO, a very good book about this: http://en.wikipedia.org/wiki/Longitude_(book) which somebody else turned into a TV series: http://en.wikipedia.org/wiki/Longitude_(TV_serial) sadly which i've never seen :-(

to get even more nit picky GMT's not really used anymore expect as a synonym for UTC (they're not exactly equivalent).

and here ends the brief history lesson.

Reply to this Comment

@Aaron,

It seems like the worst that can happen is that twice a year, for a bit, the time is slightly off. I can certainly live with that. Maybe this is naive, but that hardly feels like a problem unless you're creating some time-critical application like a financial transaction ledger or something - certainly more advanced than anything that I am building.

@PaulH,

That stuff sounds very interesting; I like a good history lesson. But, aside from sweeping away 200 years of history and culture :) is it really used for more than a standardized time format?

Also, if you wouldn't mind, what is the difference between GMT and UTC?

Reply to this Comment

@PaulH & @Ben

Sorry, I couldn't resist bit more on time history in the USA.

We have a monument here behind a bank for Charles Dowd, a Saratoga Springs resident who got us all in sync!

In 1869 Dowd devised a system of "time belts" for the U.S. but the railroads resisted.
He spent more than a decade streamlining his system,and trying to convince the railroads, and the government, of its value.

Finally, the railroads caved, and in November 1883, America's clocks switched to what was called, "Standard Railway Time". Eventually the name was shortened to Standard Time, and its derivative, Daylight Savings Time, came along later.

http://alloveralbany.com/archive/2008/08/07/its-10-oclock-do-you-know-what-time-it-is

Reply to this Comment

@kinky ben

not "getting" tz & DST is a bad habit & *will* come back to bite your ankles off eventually. kind of like not var scoping your variables in a CFC. DST is also a moving target controlled by knucklehead politicians, it can come & go on a whim (or major sporting event, ask the ozzies). compound that w/apps having to serve multiple tz & the potential for killing somebody or dropping a bunch of money down a blackhole is more than zero.

no, GMT's not a "standardized time format". first off it's not a format. secondly, it's original intention was as a navigation aide (though british railways also used it to standardize their schedules for a while).

UTC (it actually stands for coordinated universal time, after a compromise with francophiles) is excitingly based on an atomic time scale, "international atomic time". atomic means precise, predictable. it apparently also made girls hot in the 50s-60s but that's another story.

GMT is the "mean" (the "M" in GMT) of astronomical observations of the noon sun at greenwich w/an annual deviation i think of 15-16 minutes (seasonal change, the earth's rotation speed isn't uniform, etc). there's also actually another GMT system (astronomical) where zero hour starts at noon (civil time starts zero at midnight), so just using "GMT" could mean a time +/- 12 hours. GMT isn't so precise (these days, it was the bee's knees back in the day) & can be confusing.

UTC replaced GMT in the 1970s. there's a 1 second difference betwen GMT & UTC (though i forgot whether this occurs naturally or is some kind of rule). in theory, java epoch offsets are based on UTC.

ps: if you know of any dan brown novel fans, might want to clue them in that longitude is a recent & completely artificial construct.

Reply to this Comment

@PaulH,

You're making me nervous about ever using it :) It seems much more complicated that I had even begun to imagine.

Reply to this Comment

@Casey,

Since SQL 2005 there is GETUTCDATE() or, if you need total precision SYSUTCDATETIME(). These guys handle the offset calc for you.

When getting date/time data back out to the server's local time, use SYSDATETIMEOFFSET()

http://msdn.microsoft.com/en-us/library/ms178635.aspx

@Et al,

Hot off the presses:
http://www.sustainablegis.com/blog/cfg11n/index.cfm?mode=cat&catid=162AEDB4-20ED-7DEE-2A6CF1B79AC2E03A

Try the demo. Sick!

As for the client side, ship UTC values and convert to local time in javascript. You have to poll a summer date and a winter date to get good-enough accuracy:

http://codingforums.com/showthread.php?t=139888

Remember that for some places, you have to deal with minutes of offset not just hours in some parts of the world. For example, Nepal is UTC+5:45, Chatham Islands (to New Zealand) is UTC+12:45. (I know, aren't you sick of all those dang Chatham Is. folks calling to bitch that their time values are off?!)

Here is a fun article to get the full scope of the tz hell landscape:

http://www.worldtimezone.com/faq.html

Finally, CF specific stuff:

http://www.sustainablegis.com/blog/cfg11n/index.cfm?mode=entry&entry=764FA362-20ED-7DEE-2A73C18894D4D203

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.