1

I have a simple script

<cfscript>
public string function convertTimeZone( 
    string  toTimeZone  = "America/New_York"
,   date    thisDate  = now()
){
    var azTimeZone = createObject("java", "java.util.TimeZone").getTimeZone(javaCast("string","US/Arizona"));
    var azCalendar = createObject("java", "java.util.GregorianCalendar" ).init(azTimeZone);

    azCalendar.set(
        javaCast( "int", year( arguments.thisDate ) ),
        javaCast( "int", (month( arguments.thisDate ) - 1) ),
        javaCast( "int", day( arguments.thisDate ) ),
        javaCast( "int", hour( arguments.thisDate ) ),
        javaCast( "int", minute( arguments.thisDate ) ),
        javaCast( "int", second( arguments.thisDate ) )
    );
    var newCal = azCalendar.clone();
    newCal.setTimeInMillis(azCalendar.getTimeInMillis());
    newCal.setTimeZone(javaCast("string",arguments.toTimeZone));


    writeOutput('AZ Cal');
    writeDump(azCalendar);
    writeDump(azCalendar.Timezone);
    writeDump(azCalendar.Time);
    writeDump(azCalendar.getTime());
    writeOutput('New Cal');
    writeDump(newCal);
    writeDump(newCal.Timezone);
    writeDump(newCal.Time);
    writeDump(newCal.getTime());

}

convertTimeZone();
</cfscript>

If you look at the output you will see that the new calendar contains the correct timezone and time but if I use getTime() it is not returning the correct value. I am sure that I have done something either in setting the time in the new calendar or maybe even the clone but I can't quite figure it out.

If I add writeDump(newCal.getTimezone()); to the list of dumps I do get "America/New_York"

enter image description here

Lance
  • 3,193
  • 2
  • 32
  • 49
  • 1
    I think you're experiencing what's explained [in this answer](https://stackoverflow.com/a/13472173/985709) and the [link shared in it](https://stackoverflow.com/a/7695910/985709). – Tony Junkes Oct 08 '17 at 02:57
  • 1
    Have you checked out the timezone.cfc library? https://github.com/rip747/TimeZone-CFC It has a lot of functions that will enable you to identify DST & offset based on 92 different US/CA timezones. – James Moberg Oct 09 '17 at 15:52

2 Answers2

1

You can "trick" ColdFusion into using timezone adjusted date objects (since java.util.Date cannot be timezone aware).

<cfscript>

    srcDate = createDateTime(2017, 10, 07, 16, 33, 37);
    srcTZ   = "US/Arizona";

    dstTZ   = "America/New_York";

    TimeZone = createObject("java", "java.util.TimeZone");
    srcTZinfo = TimeZone.getTimeZone(srcTZ);
    dstTZinfo = TimeZone.getTimeZone(dstTZ);

    srcOffset = (srcTZinfo.getRawOffset() / 1000);
    dstOffset = (dstTZinfo.getRawOffset() / 1000);

    srcDSToffset = (srcTZinfo.getDSTSavings() / 1000);
    dstDSToffset = (dstTZinfo.getDSTSavings() / 1000);

    src2utc = dateAdd("s", -(srcOffset + srcDSToffset), srcDate);
    utc2dst = dateAdd("s",  (dstOffset + dstDSToffset), src2utc);

</cfscript>

<cfdump label="#srcTZ#" var="#srcDate#">
<cfdump label="#dstTZ#" var="#utc2dst#">

The snippet converts the source date object srcDate into a UTC date using the specified timezone srcTZ. The UTC date is then adjusted to the desired timezone dstTZ and the final date object put into utc2dst.

Alex
  • 7,743
  • 1
  • 18
  • 38
  • Thanks Alex, I was trying to avoid the whole offset calc. I am now more interested on why I can get the correct time INTO the `newCal` object but can't seem to get it out of the object – Lance Oct 08 '17 at 19:36
  • Because the time is returned as offset to UTC i.e. not aware of a timezone. That's simply a problem with Java's old date/time classes, which was tackled in Java 8 (`java.Time`). You have to add the offset of the desired timezone. You can use ColdFusion's [`dateConvert("local2Utc", now())`](https://helpx.adobe.com/coldfusion/cfml-reference/coldfusion-functions/functions-c-d/DateConvert.html) to get the UTC right away. – Alex Oct 08 '17 at 20:44
0

Actually it doesn't contain the correct time. The presentation in the dump just makes it seem that way. WriteDump() must apply extra magic when displaying Calendar objects.

The date value does not change due to how newCal is created. Instead of cloning azCalendar, create a new instance and apply the azCalendar time with set(...). Then the date value will change. However I'm not sure it works as expected for arbitrary time zones, only the jvm default

Supposedly Java 8+ has improved date options, which are worth investigating. For earlier versions I have used the SimpleDateFormat trick. Create two formatters. One to parse the input using the original time zone. Another to format the output, using the desired time zone. Then convert the final string back into a date object with parseDateTime().

thisDate = now();
fromTimeZone = "America/Phoenix";
toTimeZone = "America/New_York";

dateMask = "yyyy-MM-dd HH:mm:ss";
timeZone = createObject("java", "java.util.TimeZone");

fromFormat = createObject("java", "java.text.SimpleDateFormat").init(dateMask);
fromFormat.setTimeZone(timeZone.getTimeZone(fromTimeZone));
// parse input as date string
dateString = dateTimeFormat(thisDate, "yyyy-MM-dd HH:nn:ss");
fromDate = fromFormat.parse(dateString); 

// format output as date string
toFormat = createObject("java", "java.text.SimpleDateFormat").init(dateMask);
toFormat.setTimeZone(timeZone.getTimeZone(toTimeZone));

// convert back into date
toDate = parseDateTime(toFormat.format(fromDate));
writeDump(toDate);
Ageax
  • 1
  • 1