Using JavaScript to Submit UTC Dates To The ColdFusion Server
Yesterday, I blogged about changes to the [undocumented] Date::getTime() method in ColdFusion 10. It seems that in the lasest release of ColdFusion (10), the date/time objects are starting to become more timezone-aware. But, being that my production server is still running ColdFusion 9, this was not so comforting. As a follow-up experiment, I wanted to make sure that I could still post UTC milliseconds to the ColdFusion server (from JavaScript) and then successfully store those values in the database as Coordinated Universal Time (UTC) dates.
The JavaScript Date() object is pretty powerful. It allows us to work, on the client, with both local times and global times. We can even convert complex Date/Time strings into JavaScript Date() instances. These Date() instances can then supply us with the number of milliseconds that have passed since January 1, 1970 UTC - a value that represents a universal date/time.
In this experiment, I am going to let the user enter in their local date/time. This local date/time value will then be convert into UTC milliseconds and submitted to the ColdFusion server. The ColdFusion server will then convert this UTC milliseconds back to a server-local date/time value (EST in my case), before inserting it into the database as a UTC date/time value.
<!--- Param the form values. --->
<cfparam name="form.submitted" type="boolean" default="false" />
<cfparam name="form.utc" type="string" default="" />
<!--- ----------------------------------------------------- --->
<!--- ----------------------------------------------------- --->
<!--- Check to see if the form has been submitted. --->
<cfif (
form.submitted &&
isNumeric( form.utc )
)>
<!--- Create a local date from the UTC value. --->
<cfset localDate = createObject( "java", "java.util.Date" ).init(
javaCast( "long", form.utc )
) />
<!---
Convert the local date to a UTC date (my server / computer
currently runs in EST).
--->
<cfset utcDate = dateConvert( "local2utc", localDate ) />
<!--- Store the UTC entry. --->
<cfquery name="insertUtc">
INSERT INTO utc_testing
(
utc,
createdAt
) VALUES (
<cfqueryparam value="#utcDate#" cfsqltype="cf_sql_timestamp" />,
<cfqueryparam value="#now()#" cfsqltype="cf_sql_timestamp" />
);
</cfquery>
</cfif>
<!--- ----------------------------------------------------- --->
<!--- ----------------------------------------------------- --->
<!--- Query for all the dates. --->
<cfquery name="utcEntries">
SELECT
id,
utc,
createdAt
FROM
utc_testing
ORDER BY
createdAt DESC
</cfquery>
<!--- ----------------------------------------------------- --->
<!--- ----------------------------------------------------- --->
<!--- Reset the output buffer. --->
<cfcontent type="text/html; charset=utf-8" />
<cfoutput>
<!doctype html>
<html>
<head>
<title>Submitting UTC Times To The ColdFusion Server</title>
</head>
<body>
<h1>
Submitting UTC Times To The ColdFusion Server
</h1>
<!--- For refererence. --->
<p>
Now: #now()#<br />
UTC: #dateConvert( "local2utc", now() )#
</p>
<form method="post" action="#cgi.script_name#">
<!--- Flags the form submission. --->
<input type="hidden" name="submitted" value="true" />
<!---
The UTC milliseconds - what's *really* being
submitted back to the server as the date/time.
--->
<input type="hidden" name="utc" value="" />
<p>
Enter your local date/time:
<input type="text" name="date" size="17" />
at
<input type="text" name="time" size="22" />
</p>
<p>
<input type="submit" value="Submit Time" />
<!--- In case we mess up the form values. --->
<a href="#cgi.script_name#">Reset</a>
</p>
</form>
<!--- Dump out the current entries. --->
<cfdump
var="#utcEntries#"
label="UTC Entries"
/>
<script type="text/javascript" src="./jquery-1.8.2.min.js"></script>
<script type="text/javascript">
// Get and cache the DOM references we'll need.
var dom = {
form: $( "form" ),
utc: $( "input[ name = 'utc' ]"),
date: $( "input[ name = 'date' ]"),
time: $( "input[ name = 'time' ]")
};
// Set the default values for the date and time fields.
dom.date.val(
(new Date()).toDateString()
);
dom.time.val(
(new Date()).toTimeString()
);
// List for the form submission. When the user submits
// the form, we want to convert their local date/time
// value into UTC millisecodns so we can send back the
// universal time value.
dom.form.submit(
function( event ) {
// Try to parse a valid date from the user input.
// This will return the number of milliseconds
// from 1970, UTC.
var localUtc = Date.parse(
dom.date.val() + " " +
dom.time.val()
);
// Make sure the value is numeric. This means the
// date could be parsed.
if ( isNaN( localUtc ) ) {
// Oop, invliad date.
alert( "Could not parse your date." );
// Stop the form from submitting.
return( event.preventDefault() );
}
// If we made it this far, the date could be
// parsed - store it in the form (and then
// let the form submission happen).
dom.utc.val( localUtc );
}
);
</script>
</body>
</html>
</cfoutput>
As you can see, we are using the UTC milliseconds as a way to transport time from the client to the server without having to know the client's current timezone. We simply let the client report its universal time and let the server worry about storing that in the database.
This approach wouldn't work if we need to initiate tasks on the server, relative to the client's local time (ex. sending out an email or SMS text message at a specific client time). However, for client-initiated requests, passing UTC milliseconds to the server seems to enable us to respond to the client without having to know their local timezone.
Want to use code from this post? Check out the license.
Reader Comments
Thanks for this. will it helpful for multiple servers?
@John,
This will play nicely with multiple servers because it doesn't matter where the server is located. As long as the server stores the data with UTC date, everyone will be on the same page.
(I know that this is an old post, but I just stumbled on it).
a better way to convert the epoch time to CFML might be to use DateAdd(), like so:
the above works properly on Railo, but ColdFusion chokes on large numbers for "utc" if they are outside the valid range of a 32bit integer (at least when they are parsed from a hardcoded value in the source), so in ColdFusion you can do it like so: