CreateTimeSpan() is a really easy way to create timeouts for both your Application and your user Sessions. It takes days, hours, minutes, and seconds and returns the numeric representation of that time span. I am sure most of you, if not all of you, are using this in your CFApplication tags or Application.cfc ColdFusion components.
CreateTimeSpan() is really cool, but it is a method call. A tiny one nonetheless, but a method call that has with it, processing overhead. Is this overhead needed? Not really. Unless you are in the habit of changing your timeouts, most likely all your CreateTimeSpan() calls return a constant value. Well, if they are returning a constant value, why bother using the method call. Readability? Sure. Maintainability? Of course.
But what if you were NEVER going to change the time span value. Could you just put in the value that CreateTimeSpan() would have returned. The answer is quite tricky in its subtlety. You can AND you can't. To demonstrate, I have set up a very simple HTML page:
<html> <head> <title>Session Timeout Test</title> </head> <body> <cfoutput> <p> ID: #SESSION.CFID#<br /> Token: #SESSION.CFTOKEN# </p> </cfoutput> </body> </html>
All this does is output the SESSION information. Then, I set up an Application.cfc that defines the Application and Session management in several different ways. Depending on which variable I pass in the URL (a,b,c,d), the SESSION timeout is set using a different methodology:
<cfcomponent> <cfset THIS.Name = "SessionTimeoutTest" /> <cfset THIS.ApplicationTimeout = CreateTimeSpan( 0, 1, 0, 0 ) /> <cfset THIS.SessionManagement = true /> <!--- Check to see which session timeout we are going to utilize. ---> <cfif StructKeyExists( URL, "a" )> <!--- Here, we are going to try to use the result of the CreateTimeSpan() without calling the method. CreateTimeSpan() for 5 minutes results in the number: 0.00347222222222. Use this instead of the method call. ---> <cfset THIS.SessionTimeout = 0.00347222222222 /> <cfelseif StructKeyExists( URL, "b" )> <!--- Try to set an intermediary variable that stores the result of the CreateTimeSpan() method, and then applies that to the application setup. ---> <cfset dtTimeSpan = CreateTimeSpan( 0, <!--- Days. ---> 0, <!--- Hours. ---> 5, <!--- Minutes. ---> 0 <!--- Seconds. ---> ) /> <!--- Set the sessiontimeout. ---> <cfset THIS.SessionTimeout = dtTimeSpan /> <cfelseif StructKeyExists( URL, "c" )> <!--- Try to set an intermediary variable that stores the value we built in "a" and then applies that to the application setup. ---> <cfset dtTimeSpan = 0.00347222222222 /> <!--- Set the sessiontimeout. ---> <cfset THIS.SessionTimeout = dtTimeSpan /> <cfelseif StructKeyExists( URL, "d" )> <!--- Try to set an intermediary variable that stores the value we built in "a". However, this time, store the value as a Java Double. and then applies that to the application setup. ---> <cfset dtTimeSpan = JavaCast( "double", 0.00347222222222 )/> <!--- Set the sessiontimeout. ---> <cfset THIS.SessionTimeout = dtTimeSpan /> <cfelse> <!--- For our default, we are just going to use the CreateTimeSpan() directly. ---> <cfset THIS.SessionTimeout = CreateTimeSpan( 0, <!--- Days. ---> 0, <!--- Hours. ---> 5, <!--- Minutes. ---> 0 <!--- Seconds. ---> ) /> </cfif> <!--- Set page settings. ---> <cfsetting showdebugoutput="false" /> </cfcomponent>
If I call "index.cfm" with no URL variable, we are setting the SESSION timeout using a direct CreateTimeSpan() call. This works just fine and outputs the SESSION information.
If I call "index.cfm?a", we are setting the SESSION timeout using the known result of our 5-minute CreateTimeSpan() call. CreateTimeSpan( 0, 0, 5, 0 ) returns the value 0.00347222222222. To test this for yourself see what (5 / 60 / 24) comes out to. When I try this methodology, it returns the ColdFusion error:
Element CFID is undefined in SESSION.
Clearly, the session is not working. If I call "index.cfm?b", we are setting an intermediary variable to a CreateTimeSpan() call and then setting the SESSION timeout equal to that variable. My theory here was that maybe indirect settings just would not work. This worked just fine, so it had nothing to do with where the CreateTimeSpan() was applied.
If I call "index.cfm?c", I set an intermediary variable to our known time span value, just to see if maybe directly setting the known value was breaking. This returns the same ColdFusion error as above.
I realized at this point, that it had nothing to do with directly or intermediary setting. It must have been something to do with the value returned by CreateTimeSpan() that was some how getting lost in translation to the known value. I decided that it was time to see what class of data we were even dealing with:
#CreateTimeSpan( 0, 0, 5, 0 ).GetClass().GetName()#
Ahhhhh! We are NOT dealing with just any old number. We are dealing with a Java Double. So, now, when I call "index.cfm?d", I set the intermediary value to the JavaCast-Double value of the known time span value. This works like a charm. This could be done without an intermediary variable as well.
That is very subtle. Very subtle indeed. I think the ColdFusion error is not helpful in this case. It's some sort of casting issue. When I put the known value of the time span directly in the code, ColdFusion sees this as String value. Then, it has to cast that to a numeric value for use with the SessionTimeout value. When casting from string, it must NOT cast to a double by default and then the value is not coming through.
This seems like a bug to me. But, then again, ColdFusion is typeless... how can I hold it accountable for not knowing which type of number to cast to.
Want to use code from this post? Check out the license.