A developer asked me how he could get absolutely accurate time information for an application that he is working on. He cannot rely on local server time as he has no control over the machine, and can't verify that it is accurate (and can't change the time if not). There is no NTP (network time protocol) tag in ColdFusion, but fortunately one is not needed, because the NIST time servers also respond to plain text daytime protocol requests.
Here is a quick UDF I threw together to solve the problem. Call GetNISTTime() and it'll return a structure containing the raw data returned from the time server, as well as individual fields broken out for ease of use:
<!---
Name: GetNISTTime()
Author: Ben Forta, 12/6/2005
Description: Obtains current time data from NIST
Internet Time Service servers.
DST: US daylight savings time flag.
HEALTHY: TRUE if time server is healthy, FALSE if not.
JULIAN: Last 5 digits of Julian date/time value.
LEAPMONTH: TRUE is second will be added to or subtracted
from the current month.
MSADV: Number of milliseconds advanced by server to
compensate for network latency.
NOW: Current date/time.
RAW: Raw data from time server.
SUCCESS: TRUE if worked, FALSE if not, check
this flag first.
Note: For a list of NIST time servers see:
http://tf.nist.gov/timefreq/service/time-servers.html
Servers should be addressed via IP address rather than
host name. The server used here is time.nist.gov
(192.43.244.18), but any of the listed servers will work.
To use an alternate server, just specify the IP
address in timeServer variable.
--->
<cffunction name="GetNISTTime" returntype="struct" output="false">
<cfset var timeServer="192.43.244.18">
<cfset var result=StructNew()>
<!--- Try/catch block --->
<cftry>
<!--- Try get time data --->
<cfhttp url="http://#timeServer#:13/" />
<!--- Save raw data --->
<cfset result.raw = CFHTTP.FileContent>
<!--- Extract Julian date --->
<cfset result.julian=ListGetAt(result.raw, 1, " ")>
<!--- Extract current date and time --->
<cfset result.now=ParseDateTime(ListGetAt(result.raw, 2, " ")
& " "
& ListGetAt(result.raw, 3, " "))>
<!--- Extract daylight savings time flag --->
<cfset result.dst=IIf(ListGetAt(result.raw, 4, " ") IS 0,
FALSE, TRUE)>
<!--- Extract leap month flag --->
<cfset result.leapmonth=IIf(ListGetAt(result.raw, 5, " ") IS 0,
FALSE, TRUE)>
<!--- Extract health flag --->
<cfset result.healthy=IIf(ListGetAt(result.raw, 6, " ") IS 0,
FALSE, TRUE)>
<!--- Extract advance milliseconds --->
<cfset result.msadv=ListGetAt(result.raw, 7, " ")>
<!--- Success --->
<cfset result.success=TRUE>
<!--- Catch any errors --->
<cfcatch type="any">
<cfset result.success=FALSE>
</cfcatch>
</cftry>
<cfreturn result>
</cffunction>
To test this code you can just use:
<cfset x=GetNISTTime()>
<cfdump var="#x#">
http://alpha.uhasselt.be/Research/Algebra/Members/...
And then use code like this:
<cfscript>
ntpServer = "time.nist.gov";
ntpConn = CreateObject("java", "be.ac.luc.vdbergh.ntp.NtpConnection");
ntpConn.init(CreateObject("java", "java.net.InetAddress").getByName(ntpServer));
time = ntpConn.getTime();
</cfscript>
<cfoutput>#time#</cfoutput>
It's actually much trickier than it seems , mostly because of daylight savings. DS is chosen by governments and can change any year. It doesn't have to be a full hour, and it doesn't even have to be there at all.
Here in Oz, we have DS in victoria (melbourne), but not in Tasmania (the island). My point is, there is absolutely no way to calculate it, so you have to get the info somewhere. you could use a timeserver, but :
1.what if it goes down?
2.different business objects in our system can belong to different timezones, so we can't just save a time-offset once and for all in the session.
So what we did (not me but a guy here) was instanciate the java.util.TimeZone class that's shipped with JRun and contains all that info (updated everytime they update jrun I think). add a whole lot of small udfs to calculate offsets to the recipe, and that's a wrap. We did have a few load problems, apparently because we're still running on CF5 :-( which doesn't handle java as well as MX, but now it seems to be fine.
Any comment on this solution would be more than welcome. don't hesitate to tell me if I'm plain wrong about something because I'm a junior developer, and I didnt work on that project so I may be mistaken on some points.
http://www.sustainablegis.com/projects/tz/testTZCF...
And read his cfg11n site, it has really good stuff on i18n/g11n/l10n and CF:
http://cfg11n.blogspot.com/
What is the purpose of this:
<cfset result.leapmonth=IIf(ListGetAt(result.raw, 5, " ") IS 0,FALSE, TRUE)>
Wouldn't
<cfset result.leapmonth=ListGetAt(result.raw, 5, " ") NEQ 0>
Do the same without the added overhead of Iif()?
<!--- Extract current date and time --->
<br >
<cfset result.now=LSParseDateTime(ListGetAt(result.raw, 2, " ")
& " "
& ListGetAt(result.raw, 3, " "))>
I was about to post pretty much the exact same message as you.. that is until I saw your message :-) And I'm still curious to hear the answer of course :-)
Btw: looking at the timestamps of these postings, it looks like your server could use some time synchronization :-D
In any event, I prefer not to use IIF for two reasons:
1. <cfif> tags are more readable
2. All three of the parts of an IIF need to be valid at run-time or it will throw an error, while <cfif> doesn't. For instance, this will bomb out: <cfset foo=iif(isDefined("variables.bar"),variables.bar,de("Not defined"))> if variables.bar is not defined, while this will not:
<cfif isDefined("variables.bar")>
<cfset foo=variables.bar>
<cfelse>
<cfset foo="Not defined">
</cfif>
...
<select name="selectBox">
<cfloop query="qQuery">
<option
value="#nID#
#iif( nID eq url.nParam, de( 'selected="selected"' ), de( '' ))#
>#sName#</option>
</cfloop>
</select>
...