6.0.0-git
2018-12-16

[#13947] Calendar breaks on DST start date
Summary Calendar breaks on DST start date
Queue Kronolith
Queue Version 4.2.2
Type Bug
State Assigned
Priority 1. Low
Owners Horde Developers (at)
Requester jsveiga (at) it (dot) eng (dot) br
Created 2015-04-10 (1346 days ago)
Due
Updated 2017-11-23 (388 days ago)
Assigned 2017-11-23 (388 days ago)
Resolved
Milestone
Patch No

History
2017-11-23 17:37:44 Jan Schneider Comment #18
Assigned to Horde DevelopersHorde Developers
State ⇒ Assigned
Reply to this comment
Thanks for the detailed analysis, for providing several solutions, and 
for referring to the (yet unknown to me) moment library!
2017-11-14 10:03:49 lfbm (dot) andamentos (at) gmail (dot) com Comment #17 Reply to this comment
So when you try to create a date at an invalid moment in local time 
with Chrome, for example, it will jump to the next day:

new Date(2017, 9, 15, 0, 0).toString();
"Sun Oct 15 2017 01:00:00 GMT-0200 (-02)"
Sorry, here what I ment was Chrome jumps one hour forward.

2017-11-14 00:24:02 lfbm (dot) andamentos (at) gmail (dot) com Comment #16 Reply to this comment
Although latest Datejs release is 10 years old and probably should not 
be used anymore, this is not a bug with it. Nor it's a bug with Horde.

ECMAScript does not have specs regarding what happens when a local 
time is set to an invalid moment. So each browser deals with it on its 
own way.

In 2017, Brazil shifts to DST at 2017-10-14, precisely at the end of 
23:59. So just before the clock turns to 00:00, it jumps do 
2017-10-15, 01:00. So it creates a gap of missing local time.

Other countries do the same thing at 23:59 (Lebanon, Syria) and 
others, such as US, do it at 02:00, when it jumps to 03:00, and others 
do it at different moments. Every country that has DST will have gaps 
of missing local times. The site www.timeanddate.com is the best 
reference there is.

So when you try to create a date at an invalid moment in local time 
with Chrome, for example, it will jump to the next day:

new Date(2017, 9, 15, 0, 0).toString();
?Sun Oct 15 2017 01:00:00 GMT-0200 (-02)?

Here, I set my computer to Brazil standard time and tried to ask 
Chrome what is the local time in 2017-10-15 (9 is October because the 
counter starts with 0), at midnight (00:00). Since midnight does not 
exist on 2017-10-15, Chrome has jumped to 2017-10-15 01:00 and gave me 
that answer.

If I do the same thing with Firefox, it jumps back to the day before:

new Date(2017, 9, 15, 0, 0).toString();
"Sat Oct 14 2017 23:00:00 GMT-0300"

So in various parts of kronolith.js, when it tries to create moments 
at invalid local times, things get broken depending on the user's 
browser and moment in time. This is also the reason why adding one day 
to 2017-10-14 00:00 will return a result of 2017-10-14 23:00, instead 
of 2017-10-15 00:00, when the user is on Firefox.

This is causing the day 2017-10-14 to appear duplicated in kronolith 
dynamic view, as reported in the original post.

The Horde Calendar Picker will also not let you select the day 
2017-10-15, because, such as Datejs, it always defaults new date 
objects to midnight (00:00) and, as seen above, the moment 2017-10-15 
00:00 simply does not exist in some local times. So when you select 
2017-10-15 it returns 2017-10-14, if your computer is set to any date 
before entering Brazil (and similar countries) DST.

This is why when a JS program is interested only in dates (not time) 
and date calculations, it should *never* trust local time. It should 
always use UTC time, because there are no gaps nor overlaps in UTC.

Another way of working around this issue is to default date creations 
to 12:00, because no DST transitions ever occur in the middle of the 
day, in any time zone. So it?s a safer way of doing date calculations 
when the choice of trusting local times had already been made in the 
first place.

Unfortunately, Datejs is old and abandoned and does not have full UTC support.

Moment.js (https://momentjs.com/) is a much better choice and updated 
library. It also has a nice feature which is the UTC mode. While in 
UTC mode, all display methods will display in UTC time instead of 
local time. Additionally, while in UTC mode, all getters and setters 
will internally use the Date#getUTC and Date#setUTC methods instead of 
the Date#get and Date#set methods 
(https://momentjs.com/docs/#/parsing/utc/). This way you won?t have to 
trust local time when dealing with date calculations.

The best solution here, IMHO, would be to rewrite the parts of 
kronolith the uses Datejs and make it use moment.js, activating the 
UTC mode when necessary.

It should be possible to patch kronolith.js in order to make it create 
dates at 12:00 (midday), when time does not matter, instead of 
midnight. But the first solution (moment.js with UTC mode) seems more 
elegant.

Anyway, this is definitely something that cannot be ignored by horde, 
because different browser deals with invalid local times in different 
manners and it does not seem to change in a near future.

2017-10-04 01:33:07 jsveiga (at) it (dot) eng (dot) br Comment #15 Reply to this comment
It should work, it just sets a time to the datetime so it isn't 
between 23:00 and 00:00, as on the DST switch date this interval does 
not exist.

My server is debian stable, with the debian horde packages.

Joao

[Show Quoted Text - 48 lines]
2017-09-25 18:45:18 leonardo (at) cefetmg (dot) br Comment #14 Reply to this comment
Hi Leonardo,
Hi, Joao
Have you tried the simple workarounds I suggested in my comments?
Yes, I had. However, we plan to move mail infrastructure to Debian 9 
and I don't know if your patch works on the new Horde packages.

We'll try it soon but maybe you already know if it is compatible.
abs,

Joao
Thank you,
Leonardo

[Show Quoted Text - 25 lines]
2017-09-25 16:27:12 jsveiga (at) it (dot) eng (dot) br Comment #13 Reply to this comment
Hi Leonardo,

Have you tried the simple workarounds I suggested in my comments?

abs,

Joao

[Show Quoted Text - 24 lines]
2017-09-25 16:09:44 Jan Schneider Comment #12
Taken from Horde DevelopersHorde Developers
State ⇒ Feedback
Reply to this comment
Comment #6 is still correct.
2017-09-21 16:29:28 leonardo (at) cefetmg (dot) br Comment #11 Reply to this comment
Guys,

I sincerely don't understand why this bug is marked as Low priority. I 
do understand that this is free software and most of you developers - 
who I thank so much - make your valuable contributions in your free 
time or in work breaks.

But I think that setting a higher priority to the bug may put it in 
better condition to be solved, 'cause I imagine a 
developer/contributor would direct his efforts based o bug's priority.

I want to apologize if this sounds rude or non-sense. I really 
appreciate the heroic and many times self sacrificial work Horde guys 
have been doing, both on developing and supporting the community. But 
I would be more thankful if you consider the situation of the entire 
Brazilian Horde community (among others).

I also would be really really happy if I could help more on this, but 
my developer skills are so far from that.

I hope you understand.
Best regards,

Leonardo Lopes
2016-10-14 13:23:41 tiago (dot) delboni (at) almg (dot) gov (dot) br Comment #10
New Attachment: dstbug.jpg Download
Reply to this comment
We're also observing this behaviour in Firefox 45.4.0 with Brazilian 
DST. For 2016 the duplicated day in the calendar is 15th of October.

Any patch to this would be utterly appreciated!

IMHO this should not be a low priority bug. It messes up people 
calendars for an entire month, shifting days in such way that people 
would easily miss an important meet or task.

[Show Quoted Text - 16 lines]
2016-01-25 17:44:53 Jan Schneider Assigned to Horde DevelopersHorde Developers
State ⇒ Assigned
Priority ⇒ 1. Low
 
2015-04-13 17:54:32 jsveiga (at) it (dot) eng (dot) br Comment #9 Reply to this comment
some Android cases, it's 31/12/1969 00:00!).
actually 31/12/1969 23:59:59, or the epoch -1s; as the functions 
return "-1" when that happens
2015-04-13 17:50:56 jsveiga (at) it (dot) eng (dot) br Comment #8 Reply to this comment
It's a bug with the DateJS library then.
(sorry if I'm flooding this with comments; let me know and I'll be quiet)

It is hard to call that a bug, because there is no "right" way to do 
it. Just adding 1 to the month day number would be wrong, because 
"Date" is actually a "DateTime", so the time has to be taken into 
account too.

If you think about it, 00:00 - 00:59 October 18th 2015 does not exist 
in my timezone because at 23:59:59.99 on the 17th you set your clock 
to 01:00:00.00 (and each DST-using timezone has such gap).

So adding 1 day to "Oct 17th 00:00" cannot result in "Oct 18th 00:00" 
as that is an invalid datetime in this timezone. IMO, the "best" 
result would be that the .add or .next methods returned an "invalid 
date", and left it to the calling program to decide what to do. 
Instead, each implementation seems to "decide" what to return (in some 
Android cases, it's 31/12/1969 00:00!).

So when we are just interested in the Date and not Time, it is much 
safer to set the time to something far from 00:00 (some timezones 
switch at 01:00) before doing date math.
2015-04-13 17:09:29 jsveiga (at) it (dot) eng (dot) br Comment #7 Reply to this comment
The problem is that calculations like Date.next().day() don't jump 
to the next day, but add 24 hours. I guess.
Exactly, and it seems that adding a week adds 7*24h. Then as the DST 
switch days do not have 24h, the resulting "day" is not what is 
expected (as it happens when building the week rows). This is why 
setting the hour for something away from 00:00 solves it as in the 
patch I sent: Although it may shift +-1h, it is still within the 
expected day.

It seems that JS date arithmetic for DST is not really consistent 
across browsers around 00:00 (I've found this detailing differences 
even between IE9 and IE10; maybe related to the same issue: 
https://msdn.microsoft.com/en-us/library/ie/jj863688%28v=vs.85%29.aspx).

So would be interesting to make Kronilith "immune" to that, so the 
results are consistent across different browsers/platforms.
2015-04-13 16:22:25 Jan Schneider Comment #6
State ⇒ Feedback
Reply to this comment
It's a bug with the DateJS library then. Setting the timezone on the 
desktop client, I can reproduce this now. The problem is that 
calculations like Date.next().day() don't jump to the next day, but 
add 24 hours. I guess.
2015-04-13 14:16:25 jsveiga (at) it (dot) eng (dot) br Comment #5
New Attachment: calview.patch Download
Reply to this comment
This includes the same workaround for the minical view (patch attached)

--- kronolith.js.orig   2015-04-13 10:56:33.267068678 -0300
+++ kronolith.js        2015-04-13 11:13:42.304493201 -0300
@@ -514,6 +514,7 @@
              var tbody = $('kronolith-month-body'),
                  dates = this.viewDates(date, view),
                  day = dates[0].clone();
+            day.setHours(15);

              $('kronolithCurrent')
                  .update(this.setViewTitle(date, view, data));
@@ -879,6 +880,7 @@
              week = this.viewDates(this.date, 'week'),
              workweek = this.viewDates(this.date, 'workweek'),
              dateString, td, tr, i;
+        day.setHours(15);

          // Remove old calendar rows. Maybe we should only rebuild the minical
          // if necessary.

2015-04-13 14:11:01 jsveiga (at) it (dot) eng (dot) br Comment #4 Reply to this comment
Sorry, I failed to note that the (windows) client timezone should be 
set to a DST timezone (in my case, (UTC-3:00) Brasilia), and I'm using 
Firefox 37.0.1.

The calendar uses javascript functions from the browser, but the 
building of the calendar view is coming from Kronolith (for example 
kronolith.js, updateView for the large calendar view).

So it seems in some js implementations, date arithmetic works 
differently (one can argue if "different" is wrong or more strict), 
but Kronolith should be aware of that and deal with it.

For example, for the large month view, simply doing this:

#############
--- kronolith.js.orig   2015-04-13 10:56:33.267068678 -0300
+++ kronolith.js        2015-04-13 11:03:01.578750995 -0300
@@ -514,6 +514,7 @@
              var tbody = $('kronolith-month-body'),
                  dates = this.viewDates(date, view),
                  day = dates[0].clone();
+            day.setHours(15);

              $('kronolithCurrent')
                  .update(this.setViewTitle(date, view, data));
#################

on the updateView function where it builds the month table, solves the 
issue by moving the time away from 00:00, avoiding that adding 
days/weeks to Date results in an unexpected day due to the DST switch 
point.

Similar workarounds can be done for the other instances where DST is 
causing problems.
2015-04-13 12:48:47 Jan Schneider Comment #3
State ⇒ Not A Bug
Reply to this comment
The calendar is calculated in your browser, not on the server. So this 
is a bug in your browser. Confirmed, because I cannot reproduce this 
either by setting the timezone globally in the PHP configuration, or 
setting it individually in the preferences.
2015-04-10 19:47:35 jsveiga (at) it (dot) eng (dot) br Comment #2 Reply to this comment
Note: Also reproduced on v 4.2.5, at the demo.horde.org
2015-04-10 19:40:41 jsveiga (at) it (dot) eng (dot) br Comment #1
Type ⇒ Bug
State ⇒ Unconfirmed
Priority ⇒ 2. Medium
Summary ⇒ Calendar breaks on DST start date
Queue ⇒ Kronolith
Milestone ⇒
Patch ⇒ No
New Attachment: Desktop.zip Download
Reply to this comment
Calendar views broken on DST start date (see attached).

To reproduce:
1 - set the timezone (php.ini) to one that has DST starting at 00:00 
of a known date. For example, date.timezone = "America/Sao_Paulo".
2 - access the year or month view of the calendar on the month that 
DTS starts (in this case, October) and look at the DST start date

Interestingly, Android is plagued by almost the exact same bug:
https://code.google.com/p/android/issues/detail?id=74754

This is caused by using datetime variables with the time unset (thus 
00:00). There is no 00:00 on the DST start date (the clock "jumps" 
from 23:59 to 01:00), so functions using that date return bad results 
which are not handled properly.

Saved Queries