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 Hal Helms' Real World OO (Feb. 2009) with: Steve Good and Joakim Marner and Charlie Griefer

Converting ISO Date/Time To ColdFusion Date/Time

By Ben Nadel on
Tags: ColdFusion

I am working with some fun XML RPC (remote procedure call) stuff for a client when I had to convert the XML RPC standard ISO date/time stamp to a ColdFusion date/time stamp. I tried looking at CFLib.org, as I know nothing about ISO standards, but it seems the ones they have all expect the date to have dashes (ex. 1998-07-17T14:08:55). The problem is, the XML RPC date/time stamp does not have dashes:

19980717T14:08:55

It was easy to update the UDF to use a regular expression with optional dashes:

  • <cffunction
  • name="ISOToDateTime"
  • access="public"
  • returntype="string"
  • output="false"
  • hint="Converts an ISO 8601 date/time stamp with optional dashes to a ColdFusion date/time stamp.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="Date"
  • type="string"
  • required="true"
  • hint="ISO 8601 date/time stamp."
  • />
  •  
  • <!---
  • When returning the converted date/time stamp,
  • allow for optional dashes.
  • --->
  • <cfreturn ARGUMENTS.Date.ReplaceFirst(
  • "^.*?(\d{4})-?(\d{2})-?(\d{2})T([\d:]+).*$",
  • "$1-$2-$3 $4"
  • ) />
  • </cffunction>

Notice that the "-?" provide for optional dashes. Then, this can be called as such:

  • <!---
  • Output the standard ColdFusion date/time stamp
  • for this XML RPC standards compliant ISO 8601
  • date/time stamp.
  • --->
  • #ISOToDateTime( "19980717T14:08:55" )#

The above code will give us the proper ColdFusion date/time formatting:

1998-07-17 14:08:55

XML RPC is some fun stuff to work with. I know there are some libraries out there already, but I hope to release some sort of utility of mine own.




Reader Comments

Ben, this returns a STRING that happens to look like a date. There's a slight semantical difference in that.

I recommend using parseDateTime() and use a returntype of date in your UDF.

--
Adam

Reply to this Comment

@Adam,

As far as I know, ColdFusion stores all simple values as strings and casts them on the fly when needed, which is why "3" is a date and so is "00A". But, I am interested in this ParseDateTime() method. I tried using it:

#ParseDateTime( "19980717T14:08:55" )#

And it is throwing the error:

"19980717T14:08:55" is not a valid date format.

I also tried:

#ParseDateTime( "19980717T14:08:55", "pop" )#

... but got a similar error. How does this function work?? Thanks.

Reply to this Comment

@Ben
I think you would use ParseDateTime() / LSParseDateTime() on the replaced string ie:

<cfreturn ParseDateTime(
ARGUMENTS.Date.ReplaceFirst(
"^.*?(\d{4})-?(\d{2})-?(\d{2})T([\d:]+).*$",
"$1-$2-$3 $4"
)) />

Does that work?

Reply to this Comment

i believe cf datetimes internally are offsets (decimal days since 31-dec-1899) though i have no idea if they are raw strings & converted on the fly.

parseDateTime or lsParseDateTime won't work as that string isn't like the default locale en_US formatting or any other locale i can recall. but then again neither is the datetime string returned by your function.

Reply to this Comment

Ben, sorry, but you're mistaken. CF actually does it the other way around: it stores variables as specific data types, but will attempt to cast the data to a string when called for (like when going <cfoutput>#now()#<cfoutput>.

Run this sample code. Apologies if the output here is munged, but your blog won't let me submit the post with tags in it, which kinda sux on a CF-oriented blog! It also won't let me preview my post to see if using HTML-entities will work instead of angle-brackets.

<cfoutput>
<cfset i = int(0)>
int(0)<br />
#i.getClass()#<br />
Cast to String: #i#<br /><br />

<cfset s = "foo">
"foo"<br />
#s.getClass()#<br />
Cast to String: #s#<br /><br />

<cfset ts = now()>
now()<br />
#ts.getClass()#<br />
Cast to String: #ts#<br /><br />

<cfset d = createOdbcDate(now())>
createOdbcDate(now())<br />
#d.getClass()#<br />
Cast to String: #d#<br /><br />

<cfset q = querynew("col")>
<cfset queryAddRow(q)>
<cfset querySetCell(q, "col", 1)>
q<br />
#q.getClass()#<br />
Won't automatically cast to a string<br /><br />

q["col"]<br />
#q["col"].getClass()#<br />
Won't automatically cast to a string<br /><br />

q.col<br />
#q.col.getClass()#<br />
Cast to String: #q.col#<br /><br />

q.col[1]<br />
#q.col[1].getClass()#<br />
Cast to String: #q.col[1]#<br /><br />

<cfset x = xmlNew()>
<cfset x.xmlRoot = xmlElemNew(x, "foo")>
xmlNew()<br />
#x.getClass()#<br />
Cast to String: #x# (need to view-source to see it)<br /><br />

xmlRoot<br />
#x.xmlRoot.getClass()#<br />
Cast to String: #x.xmlRoot# (need to view-source to see it)<br /><br />

</cfoutput>

--
Adam

Reply to this Comment

Oh, and sorry: I didn't mean do the parseDateTime() on the INPUT string, I meant as a last step within your UDF, before returning the value. The yyyy-mm-dd HH:mm:ss format will reliably convert to a proper CF date/time object with parseDateTime().

--
Adam

Reply to this Comment

@Adam,

My apologies for the mistake. I had assumed that ColdFusion will convert string to particular data types when necessary. For instance, when I create the string:

<cfset dtToday = "07/04/2007" />

I had assumed this was stored as a string and then converted to a date when date-manipulation was run on it? But from what it seems you are saying, it is actually creating a date and then converting it back to a string when I use it a string?

Similarly,

<cfset intValue = "34" />

... is creating a number and then being cast back to a string when it is needed as a string??

This seems very wasteful. This makes ColdFusion have to guess that the data type before you ever have to use it. I think it would be more efficient to keep it as a string and then only cast it when necessary.

And, also keep in mind that I was specifically talking about Simple values... not sure why you threw in XML and Query objects :) I don't even know how ColdFusion would store them as a string and cast to complex types as necessary.

However, dumping out the Java class name for 34 and "34" both return String as their class. So, while date might be different, it looks like all numbers and strings are stored as strings. Although, I know that CreateTimeSpan() returns a Double (if memory serves correctly).

Honestly, I think we are splitting hairs here :)

Also, what tags where you trying to submit? As far as I know, my comments only block the A-tag?

Reply to this Comment

Hi Ben.
I'm not sure which tag it was: the error msg didn't say, it just said "you can't post that". Once I changed the left-angle-brackets in the code to < entities, it posted fine. It certainly didn't mind the angle-brackets in the CFOUTPUT tags at the top, which I did not think to "escape". Anyhow, using HTML entities works fine, so I'll do that.

Now, back to CF data types: you're not quite getting it.

When you do this:

<cfset dtToday = "07/04/2007" /> (*)

you are NOT creating a date. You're creating a string. Things wrapped in quotation marks get created as strings. Irrespective of what they might look like to a human.

Just because it looks like a date to you, doesn't mean it's a date. In the same way CF will automatically cast a date object to a string if it thinks that's best (like when you output it), CF will also cast a string to be a date if it seems fit. So if you were to use that string in a function that expects a date, you will not get an error: CF will work out what to do.

Well it kind of will, most of the time. And dates are the best example of when it can go wrong in CF to use the wrong date type for the job at hand. You see 4/7/2007 as July 4. I see it as April 7 (at least we can agree on the year). A computer will need to take a bit of a stab as to which way around it interprets that, educating its guess by looking @ the locale settings on the OS or applications involved. And computers will often cock it up, and get the dd and mm parts around the wrong way. You will see many posts on the Adobe forums in which people have this issue. It's usually when there are mismatched locales, or - perish the thought - the person is outside USA (who I think are the only ones with mm/dd/yy as their chosen short-format? Not sure).

So this is why one should shy away from using a string when the situation actually calls for a date: using the best tool for the job.

To create a date in CF, the easiest way is to do this:

<cfset dtToday = createDate(2007, 7, 4)>

This will create a date object which will never be handled ambiguously in date-oriented operations.

So - back to the original post - this is why I recommended parsing your string to a date, in a function which claims to be doing date conversions. In your case, as the string format is the unambiguous ISO format, one will never have issues using a stirng insetad of a date, as CF will always correctly cast it as a date. However "best practice" would suggest you should MAKE IT a date, if that's what it is. That's all.

More on the casting thing.

If one does this:

<cfset i = 42>

CF will create a string, not an integer. Why? Because CF is typeless (well: kinda. Sometimes. Except for when it isn't), and in the normal run of events - and if possible - it will create everything as strings to be as type-neutral as possible. This makes sense as usually CF is used for outputting strings of HTML.

This is why - to demonstrate my point about CF types - I used the int(0). int() returns a number (read the docs to confirm that). It is interesting - given the function name and purpose - it actually returns a double-precision float, rather than an actual integer. This is a bit crap on the part of CF, but so be it.

As for the queries and the XML in my example, I just wanted to offer examples of when the auto-casting DOESN'T occur (query); when it DOES occur, with other complex objects (XML); and does, but in possibly an unexpected or "unstable" way (query columns: two different results, depending on syntax).

I agree re the hair splitting, in all regards other than the date issue. Because it *is* a real issue, and it causes people grief. So when a date is called for... always *use one*.

Cheers.

--
Adam

(*) No need to close your CF tags btw. CFML is SGML, not XML.

Reply to this Comment

It didn't like your CFSET tag that I reproduced that time (and forgot to escape the opening angle bracket).

--
Adam

Reply to this Comment

@Adam,

I see what you mean, and I think it makes a lot of sense. Actually, I do a lot of interfacing with the underlying Java APIs and I can tell you that it will throw data-type errors at seemingly random times. However, if you actually understand that not all simple values are actually strings, this starts to make a lot of sense.

I try to use JavaCast() for all numeric Java arguments, but I generally let the string arguments pass through as-is, assuming that ColdFusion will convert them to Java strings as needed. However, like I said before, ColdFusion is really just guessing at the conversion you want, and it certainly doesn't always work.

So, I guess what I am saying is, thanks for pointing out my misunderstanding about the data types and I agree, actually creating a date/time stamp as the return for this function would be the proper thing to do.

Sweeet!

Also, I will look into the CFSET tag restriction. I don't think it should be doing that (at least it was not intentional). What kind of computer are you on? Maybe the browser doesn't escape it the same way?

Reply to this Comment

Hi Ben.
I'm using WinXP Pro and Firefox 2.0.0.3.

Another consideration on this "escaping" issue... when I am returned to the data-entry form having had my post rejected, any escaping I had manually done (eg: using &lt; entities instead of left-angle-bracket characters) has been "de-escaped"; eg the HTML entities are gone, and the actual characters are posted into the message area. I would expect my original, unmodified text to be in place.

This is not a problem at all, but given we're talking about it, perhaps worth mentioning.

--
Adam

Reply to this Comment

@Adam,

This is a headache dealing with the form inputs in general. If you look at the source code, you will see that your actual escaped text is in there. However, the textarea interprets it as the actual character. In order for me to re-display what you typed, I would have to actually escape the "&" before the "lt;". Same thing for quotes (well, really any escaped character I guess).

I guess then it becomes a decision: do I return exactly what you wrote (and have the textarea possibly change it)? Or, do I modify what you wrote so that the data will be different but the display will be the same... see what I mean.

I still need to look into it though, there is no reason it should block cfset. For instance, when I do:

<cfset adam = true />

I do not get blocked. Which is why I was hoping you were on a Mac or something (that I have not tested so much on).

Reply to this Comment

Hi Ben.
As a standard practice, you should be using htmlEditFormat() when putting a value in a form control, maybe?

--
Adam

Reply to this Comment

@Adam,

That could be.... and to be totally honest, I only recently found out that that ColdFusion method even existed :) But certainly that is a good idea. Is that something you do? I don't know what the best practice is here; I always just do something like this:

<input value="#FORM.value#" />

However, before the form is rendered, I usually do loop over the form fields to clean them up for output. This would be the perfect place to run this function.

Thanks for the tip.

Reply to this Comment

I rarely write code for UI type stuff, but when I do: yes, I htmlEditFormat() form values, if they originate from human input. It's a security issue to NOT do that.

--
Adam

Reply to this Comment

Ben,

First, let me say that your function is an excellent little parser that demonstrates the power of regex to find a pattern and reformat it. Given your post talks about two issues: XML-RPC and ISO-8601, I'd like to address them separately.

XML-RPC

The datetime data type in the XML-RPC specification is woefully inadequate and violates W3C Recommendation XML Schema, Part 2: Datatypes Second Edition (the standard for XML datatypes). The type of lexical truncation done to the datetime type is not allowed in Section D of the XML Schema. As such, the datatype is malformed.

The XML-RPC spec sidesteps the issue of internationalization completely:

"What timezone should be assumed for the dateTime.iso8601 type? UTC? localtime?

"Don't assume a timezone. It should be specified by the server in its documentation what assumptions it makes about timezones."

So when this malformed date-time value is passed to the data consumer, exactly what assumption should be made? I don't know the set up the sender has -- they have a database server in Chicago, have a web server hosted in San Jose, and my system is located in Toronto. Which timezone do I use?

The whole point of a standard is to ensure everyone follows the same protocol to avoid programming because everyone's "Doing their own thing." XML-RPC reintroduces date-time ambiguity simply because the authors' refuse to conform to the W3C Recommendation.

Personally, I won't touch XML-RPC because of issues like this. It may very well be implemented by thousands of machines world-wide, but 50,000 servers telling me the world is flat doesn't make them right.

CF uses SOAP to natively expose remote methods, and with good reason. SOAP pretty much conforms to the XML Schema. If push comes to shove, I'll still rely on good old WDDX to transfer data from one machines to another.

ISO-8601

There's good reason the W3C adopted this as its standard for date time data. It's unambiguous, non-ethnocentric, and lexically formats Gregorian dates and time-of-day data from largest to smallest unit of time, and handles time zones.

As your example function deals with parsing an ISO-8601 dateTime value into something ColdFusion can consume natively, my example to confines itself to the same task. However, it takes time zone data into account when parsing and returns the native data type -- that of the ColdFusion Datetime value.

Here's the function. It's been tested in CFMX 6.1 through CF8 and is currently used in production projects.

<pre>
<cffunction name="ConvertIsoToDateTime" access="public" returntype="date" output="yes">
<cfargument name="sDate" required="yes" type="string" hint="Properly formed ISO-8601 dateTime String">
<cfargument name="sConvert" required="no" type="string" default="local" hint="utc|local">
<cfset var sWork = "">
<cfset var sDatePart = "">
<cfset var sTimePart = "">
<cfset var sOffset = "">
<cfset var nPos = 0>
<cfset var dtDateTime = CreateDateTime(1900,1,1,0,0,0)>
<!--- Trim the inbound string; set it to uppercase in preparation for conversion --->
<cfset sWork = UCase(Trim(Arguments.sDate))>
<!--- if the rightmost character of the sting is "Z" (for Zulu meaning UTC), remove it and change the offset to positive-zero) --->
<cfif Right(sWork,1) IS "Z">
<cfset sWork = Replace(sWork,"Z"," +0000", "ONE")>
</cfif>
<!--- extract the "T" and split out the date --->
<cfif sWork CONTAINS "T">
<cfset sWork = Replace(sWork, "T", " ", "ONE")>
</cfif>
<cfset sDatePart = ListFirst(sWork, " ")>
<cfset sTimePart = ListGetAt(sWork, 2, " ")>
<!--- figure out where the offset begins in the time part --->
<cfset nPos = REFind("[\+\-]",sTimePart)>
<cfif nPos GT 0>
<!--- split out the offset from the time part --->
<cfset sOffset = Right(sTimePart,Len(sTimePart)-nPos+1)>
<cfset sTimePart = Replace(sTimePart,sOffset,"","ONE")>
</cfif>
<!--- convert the parts into the formats that are needed for conversion to POP datetime format --->
<cfset sDatePart = DateFormat(sDatePart,"ddd, dd mmm yyyy")>
<cfset sTimePart = TimeFormat(sTimePart,"HH:mm:ss")>
<cfset sOffset = Replace(sOffset,":","","ALL")>
<!--- parse the date, time and offset parts as a POP datetime formatted string --->
<cfset dtDateTime = ParseDateTime("#sDatePart# #sTimePart# #sOffset#","pop")>
<cfdump var="#dtDateTime#">
<!--- convert the date time to local time if required --->
<cfif Arguments.sConvert IS "local">
<cfset dtDateTime = DateConvert("utc2local",dtDateTime)>
</cfif>
<!--- return the date-time object, which allows the calling process to handle it however it likes --->
<cfreturn dtDateTime>
</cffunction>
</pre>

I didn't include all the error handling this function does through try-catch. The example assumes a valid date-time string is going in.

If you put in the value: "2007-11-20T12:00:00-05:00" (Noon Eastern Standard Time) into the function, you'll get back (if set to "utc") a date/time value of {ts '2007-06-14 17:00:00'}. If set to "local", it returns {ts '2007-11-20 12:00:00'}.

You may ask why this is important? Let's assume that I receive data from a server located in St. John's Newfoundland. I am in the Eastern Standard Time Zone. Newfoundland Standard Time is 3 hours and 30 minutes behind UTC. They've sent me the data at 12:00 noon their time.

Passing the string "2007-11-20T12:00:00-03:30" returns a result of {ts '2007-11-20 10:30:00'}. Newfoundland isn't the only jurisdiction in the world on a partial time zone value. There are others.

If I ran that value through your parser, I'd receive the value of "2007-11-20 12:00:00" and that is not the time it was sent either in my timezone nor in Co-ordinated Universal Time. It was sent at 2007-11-20 15:30:00 UTC, which is 2007-11-20 10:30:00 EST.

I hope you find this of interest, especially when using it to parse DateTime values from XML from disparate sources (other than XML-RPC).

Reply to this Comment

@A. Codemonkey,

I find this stuff very fascinating. Unfortunately, I know almost nothing about internationalization. That's that this really is, right? Making an application make sense no matter what time zone you are in. My work has always just used the date on the ColdFusion server and that's it. To date, I haven't built any applications that depend on date/time stamps in any significant way.

This is, however, something that I think is very important and it is really something that I should be looking into.

Thanks for your insight into XML-RPC and into SOAP. I happen to think SOAP is a bit wordy when you are dealing with it manually, but so is XML-RPC. But, all things being equal, I would rather go with something that upholds a standard.

Your function is very cool. Again, I don't know much about actual ISO date time stamps, so looking at this is very helpful to see what is going on (or rather, what should be going on). Thanks!

Reply to this Comment

Date-time values are part of internationalization and ISO-8601:2000 was developed to create a singular standard format for dates and times.

The problem with XML-RPC, in part, is that its date-time format doesn't take into account differences in time-zone nor specify that all dates and times must be expressed in UTC. It simply says, "Make no assumptions about timezone," which a developer can't help but make.

Here's the link, btw, to the W3C Recommendation I cited (XML Schema Part 2: Datatypes Second Edition) Section D deals with the date-time datatype, it's format and what's acceptable or not.

http://www.w3.org/TR/xmlschema-2/

As for wordy, XML is always that, no matter what's being sent. :) What I truly love about it is for data exchanges, I don't have to worry about how your data model is structured. I only have to worry about mine. And most datatypes are somewhat universal.

Oh, and to throw in my two cents to the discussion about how some datatypes are are stored natively in CF. Date-times are stored in memory as a double-precision floating point number representing an offset from Dec 30, 1899 at 12:00 midnight. :)

The integer portion of the number is the number of days the current date is off of Dec. 30, 1899. The fractional (decimal) portion represents the fraction of days. Today (Nov. 22/07, 3:45 PM EST roughly) is 39408.6566550926. Negative offsets are used to handle date-times before 1899-12-30 00:00:00. :)

If you do the following, you'll see CF has no problems with figuring out the date:

<cfset nRightNow = 39408.6566550926>
<cfoutput>#DateFormat(nRightNow, "yyyy-mm-dd")# #TimeFormat(nRightNow, "h:mm:ss tt")#</cfoutput>

---

As for preferences for standards-compliant anything, I'm all for that. Following a standard makes my life way easier when I write code.

Reply to this Comment

@CodeMonkey,

Yeah, I happen to love the fact that ColdFusion stores dates as numbers (or at least that it easily can play with them). I find treating them as numbers to be a much faster, and often times a much less wordy, more clear means to manipulate them. Just have to be careful to use IsNumericDate() more often than IsDate().

Reply to this Comment

Great thread. Even though this post is old. It is still relevant because Google API's dates depend on the ISO 8601 format.

@Ben, @A. Codemonkey
There are plenty of functions graciously shared by their authors for translating ISO 8601 dates into a Coldfusion date object. But I found that none of them consider a special case for this format.
From Wikipedia:
Midnight is a special case and can be referred to as both "00:00" and "24:00". The notation "00:00" is used at the beginning of a calendar day and is the more frequently used. At the end of a day use "24:00". Note that "2007-04-05T24:00" is the same instant as "2007-04-06T00:00".

These are the resources I found relevant.
Peter Fritag: http://www.petefreitag.com/item/247.cfm
Ray Camden :http://www.coldfusionjedi.com/index.cfm/2006/4/20/GoogleCalendarcfc-Version-1
Cflib: http://www.cflib.org/index.cfm?event=page.udfbyid&udfid=1144

After looking at all of them I found that the code shared by A. Codemonkey is the one that consider most of the cases so I altered it to consider the midnight special case after I found that an atom feed from Google contained a date that made all of these functions fail.

Google date:"2009-01-08T24:17:51Z"

Final code that worked for me.. Thank you A.Codemonkey and all the other mentioned before for sharing your work with the community.

<cffunction name="ConvertIsoToDateTime" access="public" returntype="date" output="yes">
<cfargument name="sDate" required="yes" type="string" hint="Properly formed ISO-8601 dateTime String">
<cfargument name="sConvert" required="no" type="string" default="local" hint="utc|local">
<cfset var sWork = "">
<cfset var sDatePart = "">
<cfset var sTimePart = "">
<cfset var sOffset = "">
<cfset var nPos = 0>
<cfset var dtDateTime = CreateDateTime(1900,1,1,0,0,0)>
<!--- Trim the inbound string; set it to uppercase in preparation for conversion --->
<cfset sWork = UCase(Trim(Arguments.sDate))>
<!--- if the rightmost character of the sting is "Z" (for Zulu meaning UTC), remove it and change the offset to positive-zero) --->
<cfif Right(sWork,1) IS "Z">
<cfset sWork = Replace(sWork,"Z"," +0000", "ONE")>
</cfif>
<!--- extract the "T" and split out the date --->
<cfif sWork CONTAINS "T">
<cfset sWork = Replace(sWork, "T", " ", "ONE")>
</cfif>
<cfset sDatePart = ListFirst(sWork, " ")>
<cfset sTimePart = ListGetAt(sWork, 2, " ")>
<!--- figure out where the offset begins in the time part --->
<cfset nPos = REFind("[\+\-]",sTimePart)>
<cfif nPos GT 0>
<!--- split out the offset from the time part --->
<cfset sOffset = Right(sTimePart,Len(sTimePart)-nPos+1)>
<cfset sTimePart = Replace(sTimePart,sOffset,"","ONE")>
</cfif>
<cftry>

<!--- convert the parts into the formats that are needed for conversion to POP datetime format --->
<cfset sDatePart = DateFormat(sDatePart,"ddd, dd mmm yyyy")>
<!--- Considers the special rule created when midnight is represented as 24:xx--->
<cfif ListFirst(sTimePart,":") EQ 24>
<cfset sTimePart = ListSetAt(sTimePart,1,"00",":")>
<cfset sDatePart = DateFormat(DateAdd("d",1,sDatePart),"ddd, dd mmm yyyy")>
</cfif>
<cfset sTimePart = TimeFormat(sTimePart,"HH:mm:ss")>
<cfset sOffset = Replace(sOffset,":","","ALL")>
<!--- parse the date, time and offset parts as a POP datetime formatted string --->
<cfset dtDateTime = ParseDateTime("#sDatePart# #sTimePart# #sOffset#","pop")>
<cfdump var="#dtDateTime#">
<!--- convert the date time to local time if required --->
<cfif Arguments.sConvert IS "local">
<cfset dtDateTime = DateConvert("utc2local",dtDateTime)>
</cfif>
<!--- return the date-time object, which allows the calling process to handle it however it likes --->
<cfcatch type="any">
<cfabort showerror="sDatePart =#sDatePart# sTimePart=#sTimePart#">
</cfcatch>
</cftry>
<cfreturn dtDateTime>
</cffunction>

Reply to this Comment

@EstebanD,

Thank you very much for posting your updates to the ISO time conversions. I haven't run into this problem... yet! But, when I do, I'll know where to turn.

Reply to this Comment

This came in super handy for me as well. I used Esteban's version of the function and it looks like it'll work wonders in my case. It turns out Amazon's ordering API sends ISO dates back in it's notification xml.

I saw what to me looked like a crazy date and knew it was prob time to hit the CF talk list, who directed me over here!

Thanks for the post Ben :)

Reply to this Comment

Great function! I read an inferior blog post telling me to just Replace the T and Z with spaces which obviously won't output the correct time.
Using this for a Twitter RSS Feed display. The XML date from the feed is in T & Z format.

Reply to this Comment

This post helped a lot. I'm parsing NOOA ATOM and CAP Watches, Warnings, and Advisories feeds. Their times (for cap:effective and cap:expires) are in this format. Finding out what the format was called was the hard part. Once I found that, out your post came up 1st on Google. Thanks!

Reply to this Comment

Hi

I changed return statement to following:

<cfreturn ParseDateTime(REReplace(arguments.Date, "^.*?(\d{4})-?(\d{2})-?(\d{2})T([\d:]+).*$", "\1-\2-\3 \4", "ONE"))>

replaceFirst() is method of java.lang.String and didn't work with BlueDragon.NET which I unfortunately have to work with.

Tero Pikala

Reply to this Comment

@Tero,

Ah, very nice. Yeah, replaceAll() and reReplace() do *mostly* the same thing. ReplaceAll() is just a bit more robust in what is supports, regex-wise. For these purposes though, reReplace() will rock just the same :)

Reply to this Comment

Hi,
I've simplified this a little:

  •  
  • <cffunction name="ConvertISOToDateTime" access="private" returntype="date">
  • <cfargument name="ISODateString" required="yes" type="string" hint="Properly formed ISO-8601 dateTime String">
  • <cfscript>
  • // time formats have 2 ways of showing themselves: 1994-11-05T13:15:30Z UTC format OR 1994-11-05T08:15:30-05:00
  • local.initial_date = parseDateTime(REReplace(ISODateString, "(\d{4})-?(\d{2})-?(\d{2})T([\d:]+).*", "\1-\2-\3 \4"));
  • // If not in UTC format then we need to
  • if (right(arguments.ISODateString, 1) neq "Z") {
  • local.timeModifier = "";
  • //Now we determing if we are adding or deleting the the time modifier.
  • if (ISODateString contains '+' and listlen(listrest(ISODateString,"+"),":") eq 2){
  • local.timeModifier = listrest(ISODateString,"+");
  • local.multiplier = 1; // Add
  • } else if (listlen(listrest(ISODateString,"-"),":") eq 2) {
  • local.timeModifier = listrest(ISODateString,"+");
  • local.multiplier = -1; // Delete
  • }
  • if (len(local.timeModifier)){
  • local.initial_date = dateAdd("h", val(listfirst(local.timeModifier))*local.multiplier,local.initial_date);
  • local.initial_date = dateAdd("m", val(listlast(local.timeModifier))*local.multiplier,local.initial_date);
  • }
  • }
  • return local.initial_date;
  • </cfscript>
  • </cffunction>

Reply to this Comment

Will do. Just for clarity, further testing has shown this to be slightly wrong and am providing the new code. (If you want delete the other one). One reason to put this here is that it is referenced on stackoverflow.

  • <cffunction name="ConvertISOToDateTime" access="private" returntype="date">
  • <cfargument name="ISODateString" required="yes" type="string" hint="Properly formed ISO-8601 dateTime String">
  • <cfscript>
  • // time formats have 2 ways of showing themselves: 1994-11-05T13:15:30Z UTC format OR 1994-11-05T08:15:30-05:00
  • local.initial_date = parseDateTime(REReplace(ISODateString, "(\d{4})-?(\d{2})-?(\d{2})T([\d:]+).*", "\1-\2-\3 \4"));
  • // If not in UTC format then we need to
  • if (right(arguments.ISODateString, 1) neq "Z") {
  • local.timeModifier = "";
  • //Now we determine if we are adding or deleting the the time modifier.
  • if (ISODateString contains '+' and listlen(listrest(ISODateString,"+"),":") eq 2){
  • local.timeModifier = listrest(ISODateString,"+");
  • local.multiplier = 1; // Add
  • } else if (listlen(listlast(ISODateString,"-"),":") eq 2) {
  • local.timeModifier = listlast(ISODateString,"-");
  • local.multiplier = -1; // Delete
  • }
  • if (len(local.timeModifier)){
  • local.initial_date = dateAdd("h", val(listfirst(local.timeModifier,":"))*local.multiplier,local.initial_date);
  • local.initial_date = dateAdd("m", val(listlast(local.timeModifier,":"))*local.multiplier,local.initial_date);
  • }
  • }
  • return local.initial_date;
  • </cfscript>
  • </cffunction>

Reply to this Comment

Usually when it comes to finding a better function my google searches are usually Ben Nadel _______________

This is great to find a post from 2007, updated in late 2012

Thanks Ben, and Adam

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.