Local Time Conversions Across DST Shift - Bug or My Design Flaw?


it does use the location timezone:

return (new Date()).parse(dateOrTimeOrString + (!(dateOrTimeOrString =~ /(\s[A-Z]{3}((\+|\-)[0-9]{2}\:[0-9]{2}|\s[0-9]{4})?$)/)? ' ' + formatLocalTime(now(), 'z') : ''))

formatLocalTime(…) uses location.timeZone this is around line 7522:


what we need is a function which based on current location checks if it uses DST for that location and if it does then calculate DST change between current and target date and adjust the time accordingly.


Hi Bangali. Yes. I think so. If I designate the post-DST date/time as EST vs EDT, it converts as expected. Thanks again for looking into this!


Absolutely agree. Looking forward to the day we all simply operate on UTC.

My current track of thinking on this is that it’s more of a (my) perception problem than a logic problem. I agree that trying to fix this in the core may well break other things. If you look at the results below, when I feed the datetime function “Nov 05, 2018 12:00 AM EDT”, the system is smart enough to know that my use of EDT with 05-Nov-18 in my region is really an invalid nomenclature since EST will have started the day before. It feeds back to me a corrected date & time of 04-Nov-2018 11:00 PM EST. Similarly, if I omit the time zone, the system just assumes I’m referring to my current time zone of EDT and responds similarly.

So if I want the system to process a specific, future date / time, I need to be more specific about my own assumptions and use a valid time zone, appropriate to the period.

I could be completely off base here but wanted to plant an alternate thread of thought that may lead back to addressing this as a Piston design question vs bug fix. Thanks!


its not when a tz is specified … its when a tz is not specified that theres a bug.


replacing this line in localToUtcTime:

return (new Date()).parse(dateOrTimeOrString + (!(dateOrTimeOrString =~ /(\s[A-Z]{3}((\+|\-)[0-9]{2}\:[0-9]{2}|\s[0-9]{4})?$)/)? ' ' + formatLocalTime(now(), 'z') : ''))

with this code should fix it. needs a bit more testing off course but only affects strings passed in to datetime function without timezone:

 def newDate = (new Date()).parse(dateOrTimeOrString + (!(dateOrTimeOrString =~ /(\s[A-Z]{3}((\+|\-)[0-9]{2}\:[0-9]{2}|\s[0-9]{4})?$)/)? ' ' + formatLocalTime(now(), 'z') : ''))
 if (!(dateOrTimeOrString =~ /(\s[A-Z]{3}((\+|\-)[0-9]{2}\:[0-9]{2}|\s[0-9]{4})?$)/))	{
    def currentOffset = (new Date(now())).format("Z", location.timeZone)
    def newOffset = (new Date(newDate)).format("Z", location.timeZone)
    if (currentOffset != newOffset)
       newDate = newDate + ((currentOffset.toInteger() - newOffset.toInteger()) * 36000L)
 return newDate


Thanks Bangali!

I’m unfamiliar with the release management processes here. I am assuming your fix has not been applied to the core system. Is that correct? Merely rerunning the same piston yields unchanged results for me.



Correct, we’re just spitballing solutions and publishing the changes privately to test for now.

I like the direction of this, it avoids parsing the date twice like my original solution and has the same reliability.

There do not seem to be any actual issues on the daylight savings boundaries (Nov 4, 2018 2:00am and Mar 10, 2019 2:00 am) with these approaches; just that people scheduling tasks on those days would need to realize that 1:30 AM will happen twice in November. A date of Mar 10, 2019 2:30 AM (technically an invalid time) automatically maps to 3:30 AM.

I have been unable to find documentation on that Date.parse method, all I can find is the static parse method which requires a format. The class method supports a variety of date formats but unlike the static method it doesn’t seem to support a time zone argument. I think our approach is the best way to handle this.

My next iteration uses the data available from the time zone:

if (!(dateOrTimeOrString =~ /(\s[A-Z]{3}((\+|\-)[0-9]{2}\:[0-9]{2}|\s[0-9]{4})?$)/)) {
    def newDate = (new Date()).parse(dateOrTimeOrString + ' ' + formatLocalTime(now(), 'Z'))
    def currentOffset = location.timeZone.getOffset(now())
    def newOffset = location.timeZone.getOffset(newDate)
    return newDate + (currentOffset == newOffset ? 0 : currentOffset - newOffset)
return (new Date()).parse(dateOrTimeOrString)

Yet another concern in this function is the abbreviations, many time zone abbreviations have more or fewer than 3 characters. If we attempt to create a date for Alaska (e.g. formatDateTime(datetime('Jan 3, 2018 4:00 pm AKST'), 'h:mm a')) even if the regex is fixed to allow it (new Date()).parse(dateOrTimeOrString) fails to parse it. I suppose there is nothing we can do about that other than encouraging people to use the offset rather than the abbreviation if their time zone is affected.


on mobile … to your last point …I am ok with the parse failing so long as it throws an error that’s user visible in the piston.


Unfortunately it doesn’t currently and it won’t with these changes since the function has a few levels of try/catch to handle alternate formats. We can’t easily distinguish here between expected exceptions (different format than supported by parse) and unexpected exceptions (known format but bad time zone). Java just pumps out an InvalidArgumentException for both.


sigh … thought that might be. at least it’s better than what we have now.


also dont need the ternary … which means we dont need to save the offsets to variables … this works:

if (!(dateOrTimeOrString =~ /(\s[A-Z]{3}((\+|\-)[0-9]{2}\:[0-9]{2}|\s[0-9]{4})?$)/)) {
    def newDate = (new Date()).parse(dateOrTimeOrString + ' ' + formatLocalTime(now(), 'Z'))
    return newDate + (location.timeZone.getOffset(now()) - location.timeZone.getOffset(newDate))
    return (new Date()).parse(dateOrTimeOrString)


Perfect I think we have a final patch there



need a PR or you got this from here?


I’ll take it if you make it! Otherwise I’ll commit this tomorrow.


sent you PR … thanks for taking of it.


This was just released in v0.3.108.20180906


Basic test looks good! With no changes (never entered Edit mode) I ran a test with the earlier Piston and the results look great. Thanks a ton for your expertise and attention on this!


…and I assume this is expected behavior with use of he explicit time zone.

Since 12:00 AM 05-Nov-18 at my location is actually EST (vs the specified EDT), WebCoRE is properly knocking off an hour (“fall back”).


you are welcome.


that is correct.