tracker issue : CF-3777192

select a category, or use search below
(searches all categories and all time range)
Title:

dateConvert() casts datetme as UTCOleDateTime which causes issues with date operations

| View in Tracker

Status/Resolution/Reason: Closed/Won't Fix/Workaround

Reporter/Name(from Bugbase): Dan Switzer / Dan Switzer (Dan Switzer)

Created: 06/17/2014

Components: Language

Versions: 10.0

Failure Type: Data Corruption

Found In Build/Fixed In Build: Final /

Priority/Frequency: Major / All users will encounter

Locale/System: English / Win 2003 Server

Vote Count: 2

Problem Description:

When calling dateConvert("local2utc", now()) the internal Java object is an UTCOleDateTime, where as all other datetime variables appear to be normal OleDateTime (which is what CF9 used to do.) The problem is when you do many math operations on a UTCOleDateTime object, the value is cast back to an OleDateTime object, which causes the results to be off by whatever the current server's TimeZone offset is.

Steps to Reproduce:

First, in order to see the behavior, you must set your server to run in an TimeZone other than UTC. My development server is set to Eastern w/DLS, so I'm currently in EDT, which means the offset is -04:00 hours.

I've added a test case which shows the issue. CF9 behaves as I would expect it should. 

Run the follow code:

dtNow = now();
dt = dateConvert("local2utc", dtNow);

// use regular addition--which is needed when the value comes from a createTimeSpan() output
test1 = createODBCDateTime(dt + 0);
// the dateAdd() works fine
test2 = dateAdd("d", 0, dt);
// this converts the original time to a string, which seems to work okay
test3 = createODBCDateTime(dt.toString() + 0);

writeDump(label="Original - The original date/time value", var=[
		dt.getClass().getName()
	, dt
]);

writeDump(label="Test 1 - Adding date/time to number", var=[
		test1.getClass().getName()
	, test1
]);

writeDump(label="Test 2 - Using dateAdd()", var=[
		test2.getClass().getName()
	, test2
]);

writeDump(label="Test 3 - Convert date/time to a String before math", var=[
		test3.getClass().getName()
	, test3
]);

// convert the UTC into an OLE time and add 5 minutes
dtOleDateTime = dateAdd("n", 5, createODBCDateTime(dt.toString()));

writeDump(label="Date Diff Issue", var=[
		"dt variable info:"
	, dt.getClass().getName()
	, dt
	, "dtOleDateTime variable info:"
	, dtOleDateTime.getClass().getName()
	, dtOleDateTime
	, "Difference of dt & dtOleDateTime, should be 5 minutes:"
	, dateDiff("n", dt, dtOleDateTime)
]);


Actual Result:

Original - The original date/time value - array
1 	coldfusion.runtime.UTCOleDateTime
2 	{ts '2014-06-17 14:48:10'}
Test 1 - Adding date/time to number - array
1 	coldfusion.runtime.OleDateTime
2 	{ts '2014-06-17 10:48:10'}
Test 2 - Using dateAdd() - array
1 	coldfusion.runtime.UTCOleDateTime
2 	{ts '2014-06-17 14:48:10'}
Test 3 - Convert date/time to a String before math - array
1 	coldfusion.runtime.OleDateTime
2 	{ts '2014-06-17 14:48:10'}
Date Diff Issue - array
1 	dt variable info:
2 	coldfusion.runtime.UTCOleDateTime
3 	{ts '2014-06-17 14:48:10'}
4 	dtOleDateTime variable info:
5 	coldfusion.runtime.OleDateTime
6 	{ts '2014-06-17 14:53:10'}
7 	Difference of dt & dtOleDateTime, should be 5 minutes:
8 	244 

Expected Result:

Original - The original date/time value - array
1 	coldfusion.runtime.OleDateTime
2 	{ts '2014-06-17 14:48:04'}
Test 1 - Adding date/time to number - array
1 	coldfusion.runtime.OleDateTime
2 	{ts '2014-06-17 14:48:04'}
Test 2 - Using dateAdd() - array
1 	coldfusion.runtime.OleDateTime
2 	{ts '2014-06-17 14:48:04'}
Test 3 - Convert date/time to a String before math - array
1 	coldfusion.runtime.OleDateTime
2 	{ts '2014-06-17 14:48:04'}
Date Diff Issue - array
1 	dt variable info:
2 	coldfusion.runtime.OleDateTime
3 	{ts '2014-06-17 14:48:04'}
4 	dtOleDateTime variable info:
5 	coldfusion.runtime.OleDateTime
6 	{ts '2014-06-17 14:53:04'}
7 	Difference of dt & dtOleDateTime, should be 5 minutes:
8 	5

Any Workarounds:

The only work around appears to be to cast the UTCOleDateTime objects to a string, which will treat them as OleDateTime
objects.

----------------------------- Additional Watson Details -----------------------------

Watson Bug ID:	3777192

External Customer Info:
External Company:  
External Customer Name: Dan G. Switzer, II
External Customer Email:  
External Test Config: My Hardware and Environment details:



Server Product 	ColdFusion

Version 	10,0,13,287689

Tomcat Version 	7.0.23.0

Edition 	Standard  

Serial Number 	1187-5008-6294-9378-3114-7592  

Operating System 	Windows 2003  

OS Version 	5.2  

Update Level 	/D:/ColdFusion10/cfusion/lib/updates/chf10000013.jar  

Adobe Driver Version 	4.1 (Build 0001)   



Java Version 	1.7.0_60

Attachments:

  1. June 17, 2014 00:00:00: 1_cf10_utc_date_add_issue.cfm

Comments:

In case it's not clear why I think this is a buggy behavior, it's because it's very unexpected. ColdFusion is supposed to be typeless and mask the complexity of this stuff, but this behavior is really unexpected. For example, the dateDiff() issue is really important. We store all our datetimes in UTC in the database. Datetime values from a query resultset are stored as OleDateTime objects. This means if you wanted to get the age of a record, you would first need to convert the result of now() to UTC. This gets you an UTCOleDateTime object. When you do a difference between the values, your results are going to be off by whatever the current TZ offset for your server. This also causes issues with all of our UDFs that rely on createTimeSpan() to create an "age" attribute, which we use to determine a date/time stamp to use to filter data. We use this a lot for purging records from cache, database, etc.
Comment by External U.
11874 | June 17, 2014 09:32:43 AM GMT
Bloody hell that's a lot of code to demonstrate that performing numeric arithmetic on a converted date results in "unexpected" results. You just need this: <cfscript> nowInLocalTimeZone = now(); nowInLocalTimeZoneAfterArithmetic = nowInLocalTimeZone+0; nowInUtc = dateConvert("local2utc", nowInLocalTimeZone); nowInUtcAfterArithmetic = nowInUtc + 0; writeDump([ {nowInLocalTimeZone=nowInLocalTimeZone}, {nowInLocalTimeZoneAfterArithmetic=nowInLocalTimeZoneAfterArithmetic}, {nowInUtc=nowInUtc}, {nowInUtcAfterArithmetic=nowInUtcAfterArithmetic} ]); </cfscript> Note how the numeric versions are both the same, despite being based on "different" dates. The issue is that when performing arithmetic in a date/time in UTC, CF10 (and 11) converts it back to the current TZ first. Which is not how CF9 worked, so this is, I agree, a bug. There is no reason why CF should suddenly become TZ aware here, when it was not before. -- Adam
Comment by External U.
11875 | June 17, 2014 10:21:54 AM GMT
Please correct this as it is broken.
Vote by External U.
11878 | June 19, 2014 10:47:01 AM GMT
Yeah, for all my waffling in my comment, I didn't formally say "yeah, this should be fixed". So +1. -- Adam
Vote by External U.
11879 | June 20, 2014 06:43:57 AM GMT
Arithmetic operations on ColdFusion date objects use the timestamp since epoch which is the same for any TimeZone. That is why the TimeZone information is lost after arithmetic operations. To preseve the TimeZone information, please use ColdFusion date functions like dateAdd etc.
Comment by Himavanth R.
11876 | November 19, 2014 02:16:21 AM GMT
You seemed to have missed the bit where this is a regression Himavanth. What you say is all well and good, but it ignores the fact that it's a change in behaviour, and will break people's code. You have this mantra of "backwards compat is king". You need to apply it both when it means you can avoid work (which is when you usually trot it out), but also when it means you *do* have to do some work.
Comment by External U.
11877 | December 05, 2014 01:49:41 AM GMT