Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Firefox mangles date axis (possibly only during BST?) #1196

Closed
NickTulett opened this issue Apr 12, 2013 · 15 comments
Closed

Firefox mangles date axis (possibly only during BST?) #1196

NickTulett opened this issue Apr 12, 2013 · 15 comments
Labels
bug Something isn’t working
Milestone

Comments

@NickTulett
Copy link

Example from an old stackoverflow question:
http://jsfiddle.net/nrabinowitz/JTrnC/

In firefox the date axis is filled with "01 AM" markers instead of the dates visible in Chrome and IE.

Stepping through, it looks like Firefox is translating dates with no time as occuring at 1AM, leading d3_time_scaleFormat() to select the format that returns getUTCHours() rather than the day and date.

Would it be better to use getHours() as the user is probably more interested in local times (I know I'm opening a can of worms here)?

BST is UTC+1, by the way, what we laugingly refer to in the UK as British "Summer" Time.

@mbostock
Copy link
Member

I am not able to reproduce this problem in Firefox, either in the current version (20) or in ancient 3.6. It would be helpful if you could isolate where the problem is occurring. For example, is x.ticks returning the wrong values (i.e., not in midnight local time)? Or is it a problem with how the dates are formatted? Since I cannot reproduce the problem, I cannot investigate further.

Also, you are using an ancient version of D3. You want something like:

<script charset="utf-8" src="http://d3js.org/d3.v3.min.js"></script>

And you are parsing dates using the Date constructor, rather than d3.time.format, and this behavior varies from browser to browser and should not be relied on.

@NickTulett
Copy link
Author

On further investigation getUTCHours() seems to have inconsistent behaviour
You can reproduce in the firefox console

Current (BST)

(new Date()).getUTCHours()
22
(new Date()).getHours()
23

Historical (GMT/UTC)

new Date("22/11/2011").getUTCHours()
23
new Date("22/11/2011").getHours()
0

Recent (BST)

new Date("12/04/2013").getUTCHours()
0
new Date("12/04/2013").getHours()
0

I changed the fiddle when I was running it to pick up the latest D3

This may not be your problem after all but is there a way I can override the use of getUTCHours() ?

@mbostock
Copy link
Member

The nature of British Summer Time (or daylight saving time) is that it is only active part of the year. Therefore it is entirely expected that, depending on the date of the year, the offset between getHours and getUTCHours will change.

For example, I am in Pacific Daylight Time, PDT, so my offset from UTC is -7 hours (-420 minutes):

> new Date
Fri Apr 12 2013 16:04:58 GMT-0700 (PDT)
> (new Date).getTimezoneOffset()
420

However, on November 3, we’ll switch back to Pacific Standard Time, PST, changing the offset:

> new Date(2013, 10, 3, 1)
Sun Nov 03 2013 01:00:00 GMT-0800 (PST)
> (new Date(2013, 10, 3, 1)).getTimezoneOffset()
480

@NickTulett
Copy link
Author

OK but I'm only specifying a date in the data (and assuming zero time).
The variations in UTC hours cause d3 to select a time format for the x-axis, rather than a date format:

function d3_time_scaleFormat(formats) {
return function(date) {
var i = formats.length - 1, f = formats[i];
while (!f1) f = formats[--i];//this stops at [4] from below because the time is non-zero
return f0;
};
}

var d3_time_scaleUTCFormats = [ [ d3.time.format.utc("%Y"), function(d) {
return true;
} ], [ d3.time.format.utc("%B"), function(d) {
return d.getUTCMonth();
} ], [ d3.time.format.utc("%b %d"), function(d) {
return d.getUTCDate() != 1;
} ], [ d3.time.format.utc("%a %d"), function(d) {
return d.getUTCDay() && d.getUTCDate() != 1;
} ], [ d3.time.format.utc("%I %p"), function(d) {
return d.getUTCHours();
} ], [ d3.time.format.utc("%I:%M"), function(d) {
return d.getUTCMinutes();
} ], [ d3.time.format.utc(":%S"), function(d) {
return d.getUTCSeconds();
} ], [ d3.time.format.utc(".%L"), function(d) {
return d.getUTCMilliseconds();
} ] ];

@mbostock
Copy link
Member

Yes, but since your date is specified in local time, it will be either in BST or GMT depending on the date.

The code that you’ve pasted in applies to d3.time.scale.utc, the UTC time scale, not d3.time.scale, the local time scale.

@NickTulett
Copy link
Author

Sorry, I pasted the wrong bit in. It IS selecting the time format in the local scale.

The point is this only happens in Firefox. What can I do to get around it?

@mbostock
Copy link
Member

Again, I don't understand the problem, and I can't reproduce it. What you’ve shown me with getHours vs. getUTCHours is entirely expected. I can only offer clarification on information you provide, as the example JsFiddle does not exhibit the described behavior in any browser I can test on. I would, however, recommend you fix your code to use d3.time.format to parse dates, rather than relying on the Date constructor.

@NickTulett
Copy link
Author

Sorry it's late and I've got myself tied up in knots - ignore getUTCHours.

With the latest d3 and firefox in the UK today and data including dates such as "09/22/11", firefox returns 1 for .getHours() in d3_time_scaleLocalFormats, causing d3_time_scaleFormat to display the x axis as "01 AM" rather than "25 Sep".

The fiddle isn't mine so I'm not entirely able to follow what it's doing but if I do (new Date("09/22/11")).getHours() in the firebug console I get 0 as I'd expect, It's only once the data is inside d3_time_scaleLocalFormats that it returns 1 on Firefox for some reason.

@jasondavies
Copy link
Contributor

I would avoid using new Date("mm/dd/yy"), since it returns different results in different browsers. Firefox interprets this as the year 19yy, whereas Chrome interprets it as the year 20yy. You should use D3’s date parsing instead.

(There may still be a bug in D3 relating to years prior to 1970.)

@jasondavies
Copy link
Contributor

Possible fix for the <1970 dates (which have negative timestamps):

diff --git a/src/time/day.js b/src/time/day.js
index cd004a8..4d3f5c5 100644
--- a/src/time/day.js
+++ b/src/time/day.js
@@ -5,6 +5,7 @@ import "year";
 d3.time.day = d3_time_interval(function(date) {
   var day = new d3_time(1970, 0);
   day.setFullYear(date.getFullYear(), date.getMonth(), date.getDate());
+  day.setHours(0, 0, 0, 0);
   return day;
 }, function(date, offset) {
   date.setDate(date.getDate() + offset);

@NickTulett
Copy link
Author

I've changed all the dates to use 2011 and replaced getDate() with

function getDate(d) {
return d3.time.format("%m/%d/%Y").parse(d.jsonDate);
//return new Date(d.jsonDate);
}

And still get "01 AM" all along the x axis

Is that not what you meant by date parsing?

@NickTulett
Copy link
Author

Is the 1 AM business something to do with interpolation for the tick marks?

jasondavies added a commit that referenced this issue Apr 13, 2013
d3.time.day should always return midnights.  However, Firefox doesn't
return midnight for the following:

  var d = new Date(1970, 0);
  d.setFullYear(2011, 8, 22);
  // Thu Sep 22 2011 01:00:00 GMT+0100 (BST)

Chrome (and Node.js) both return:

  // Thu Sep 22 2011 00:00:00 GMT+0100 (BST)

This is because `new Date(1970, 0)` has different daylight saving
behaviour in the two browsers.

Chrome: Thu Jan 01 1970 00:00:00 GMT+0000 (GMT)
Firefox: Thu Jan 01 1970 01:00:00 GMT+0100 (BST)

In fact, Firefox is more correct!  The UK was on permanent BST from
February 1968 until October 1971.  This seemingly minor difference in
interpretation results in non-midnight times being returned for all day
intervals and other intervals that use d3.time.day internally.

The fix forces midnight to always be returned.

Fixes #1196.
jasondavies added a commit that referenced this issue Apr 13, 2013
d3.time.day should always return midnights.  However, Firefox doesn't
return midnight for the following:

  var d = new Date(1970, 0);
  d.setFullYear(2011, 8, 22);
  // Thu Sep 22 2011 01:00:00 GMT+0100 (BST)

Chrome and Node.js return:

  // Thu Sep 22 2011 00:00:00 GMT+0100 (BST)

This is because `new Date(1970, 0)` has different daylight saving
behaviour in the two browsers.

Chrome: Thu Jan 01 1970 00:00:00 GMT+0000 (GMT)
Firefox: Thu Jan 01 1970 01:00:00 GMT+0100 (BST)

In fact, Firefox is more correct!  The UK was on permanent BST from
February 1968 until October 1971.  This seemingly minor difference in
interpretation results in non-midnight times being returned for all day
intervals and other intervals that use d3.time.day internally.

The fix forces midnight to always be returned.

Fixes #1196.
@jasondavies
Copy link
Contributor

This really is a bug relating to the UK being on permanent BST during 1970. Firefox was returning 1AM (BST) for the epoch, whereas Chrome was returning midnight (GMT).

@NickTulett
Copy link
Author

That patch is exactly what I want - works in Firefox (Win &, Linux and Android JB) and doesn't break on IE9 or Chrome. Good work!

@mbostock mbostock reopened this May 28, 2013
mbostock added a commit that referenced this issue May 28, 2013
While note as complete a solution as #1197, this fixes the immediate problem of
d3.time.day not returning midnights when on British Summer Time (BST), due to
the UK being on permanent BST during 1970.
@mbostock
Copy link
Member

Fix merged into #1285 for 3.2.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn’t working
Development

No branches or pull requests

3 participants