mourner / suncalc

A tiny JavaScript library for calculating sun/moon positions and phases.
BSD 2-Clause "Simplified" License
3.07k stars 412 forks source link

getTimes off by a day #107

Open ianvonseggern1 opened 6 years ago

ianvonseggern1 commented 6 years ago

I was doing some investigation of sunsets and I noticed something strange:

SunCalc.getTimes(new Date(2017, 6, 11), 40.7128, -74.0060).sunset Mon Jul 10 2017 20:29:46 GMT-0400 (EDT)

SunCalc.getTimes(new Date(2017, 6, 11), 40.7128, -74.0060).sunsetStart Mon Jul 10 2017 20:26:30 GMT-0400 (EDT)

I was expecting it to return the sunset times for July 11th.

A little more digging and I found

SunCalc.getTimes(new Date(2017, 4, 3, 0, 0), 40.7128, -74.0060).sunsetStart Tue May 02 2017 19:51:05 GMT-0400 (EDT)

SunCalc.getTimes(new Date(2017, 3, 3, 0, 0), 40.7128, -74.0060).sunsetStart Sun Apr 02 2017 19:19:46 GMT-0400 (EDT)

SunCalc.getTimes(new Date(2017, 2, 3, 0, 0), 40.7128, -74.0060).sunsetStart Fri Mar 03 2017 17:47:52 GMT-0500 (EST)

SunCalc.getTimes(new Date(2017, 3, 3, 1, 0), 40.7128, -74.0060).sunsetStart Mon Apr 03 2017 19:20:48 GMT-0400 (EDT)

It looks like during daylight savings time it interperpates midnight to be the prior day. A little more specifically I checked:

SunCalc.getTimes(new Date(2017, 2, 11, 0, 0), 40.7128, -74.0060).sunsetStart Sat Mar 11 2017 17:56:41 GMT-0500 (EST)

SunCalc.getTimes(new Date(2017, 2, 13, 0, 0), 40.7128, -74.0060).sunsetStart Sun Mar 12 2017 18:57:46 GMT-0400 (EDT)

SunCalc.getTimes(new Date(2017, 10, 5, 0, 0), 40.7128, -74.0060).sunsetStart Sat Nov 04 2017 17:47:23 GMT-0400 (EDT)

SunCalc.getTimes(new Date(2017, 10, 7, 0, 0), 40.7128, -74.0060).sunsetStart Tue Nov 07 2017 16:44:06 GMT-0500 (EST)

I figured adding an hour would fix it and it did, but strangely the cut off seems to be 58 minutes:

SunCalc.getTimes(new Date(2017, 3, 3, 0, 57), 40.7128, -74.0060).sunsetStart Sun Apr 02 2017 19:19:46 GMT-0400 (EDT)

SunCalc.getTimes(new Date(2017, 3, 3, 0, 58), 40.7128, -74.0060).sunsetStart Mon Apr 03 2017 19:20:48 GMT-0400 (EDT)

SunCalc.getTimes(new Date(2017, 7, 3, 0, 57), 40.7128, -74.0060).sunsetStart Wed Aug 02 2017 20:08:51 GMT-0400 (EDT)

SunCalc.getTimes(new Date(2017, 7, 3, 0, 58), 40.7128, -74.0060).sunsetStart Thu Aug 03 2017 20:07:45 GMT-0400 (EDT)

Anyway, as a decent workaround I can just use noon on the day I'm curious about, but I think in general people probably assume Date without hours or minutes would work.

alexburner commented 6 years ago

I too am seeing this issue, repro'ed in JS Bin: https://jsbin.com/wotegujuho/edit?js,console

I noticed it yesterday when Daylight Savings Time ended in Seattle. However, it seems like my DST bug might be different? @ianvonseggern1 noticed that passing in a time with the date fixes the issue, confirmed in the JS Bin: https://jsbin.com/nexiteyato/edit?js,console

But with my issue, even passing time along with the date didn't fix the problem. Here it is with an input date of Nov 5th 2017, 2:30pm, but an output of 3:30pm (and DST still in effect): daylight-dst

I'm currently unable to diagnose my own code, I'll report back when I've had a chance to debug deeper.

If anyone is interested to tinker in the meantime, the page in question is here: https://daylight.website/ and you can use the keyboard arrow keys to change days (and alt + arrow keys to change the hour)

Tronald commented 6 years ago

My repo CoordinateSharp uses certain formulas from SunCalc. I was running into the same issue, and determined the cause was over precision from the toJulian() function being called from the toDays() function in getTimes().

the current function is as follows:

function toJulian(date) { return date.valueOf() / dayMs - 0.5 + J1970; }

I replaced the function with a less precise version and it fixed the issue.

function toJulian(date) 
{ 
            var month = date.getMonth() + 1;
            var day = date.getDate();
            var year = date.getFullYear();

            var gregorian = (year < 1583) ? false : true;

            if ((month === 1) || (month === 2))
            {
                year = year - 1;
                month = month + 12;
            }

            var a = Math.trunc(year / 100);
            var b = 0;

            if (gregorian)
                b = 2 - a + Math.trunc(a / 4);
            else
                b = 0.0;

            var jd = Math.trunc(365.25 * (year + 4716))
                       + Math.trunc(30.6001 * (month + 1))
                       + day + b - 1524.5;

            return jd +.5;
}

My library was able to use a replacement, but SunCalc may rely on the precision in other functions, so I would recommend making a new function for less precise julian and calling it as required. Hope this helps!

ottopic commented 5 years ago

Hello @Tronald, with your script I have same problems:

USA "date":"2019-02-11","lat":"40.0171676694347","lng":"-85.4997813813306"

[WARN]  WARN: solarNoon: Sun Feb 10 2019 18:57:36 GMT+0100 (CET)
[WARN]  WARN: nadir: Sun Feb 10 2019 06:57:36 GMT+0100 (CET)
[WARN]  WARN: sunrise: Sun Feb 10 2019 13:42:44 GMT+0100 (CET)
[WARN]  WARN: sunset: Mon Feb 11 2019 00:12:28 GMT+0100 (CET)
[WARN]  WARN: sunriseEnd: Sun Feb 10 2019 13:45:40 GMT+0100 (CET)
[WARN]  WARN: sunsetStart: Mon Feb 11 2019 00:09:32 GMT+0100 (CET)
[WARN]  WARN: dawn: Sun Feb 10 2019 13:14:39 GMT+0100 (CET)
[WARN]  WARN: dusk: Mon Feb 11 2019 00:40:33 GMT+0100 (CET)
[WARN]  WARN: nauticalDawn: Sun Feb 10 2019 12:42:41 GMT+0100 (CET)
[WARN]  WARN: nauticalDusk: Mon Feb 11 2019 01:12:30 GMT+0100 (CET)
[WARN]  WARN: nightEnd: Sun Feb 10 2019 12:11:09 GMT+0100 (CET)
[WARN]  WARN: night: Mon Feb 11 2019 01:44:03 GMT+0100 (CET)
[WARN]  WARN: goldenHourEnd: Sun Feb 10 2019 14:21:03 GMT+0100 (CET)
[WARN]  WARN: goldenHour: Sun Feb 10 2019 23:34:09 GMT+0100 (CET)

EUROPE "date":"2019-02-11","lat":"46.2880792530864","lng":"11.4771642400853"

[WARN]  WARN: solarNoon: Mon Feb 11 2019 12:29:42 GMT+0100 (CET)
[WARN]  WARN: nadir: Mon Feb 11 2019 00:29:42 GMT+0100 (CET)
[WARN]  WARN: sunrise: Mon Feb 11 2019 07:25:41 GMT+0100 (CET)
[WARN]  WARN: sunset: Mon Feb 11 2019 17:33:44 GMT+0100 (CET)
[WARN]  WARN: sunriseEnd: Mon Feb 11 2019 07:28:58 GMT+0100 (CET)
[WARN]  WARN: sunsetStart: Mon Feb 11 2019 17:30:27 GMT+0100 (CET)
[WARN]  WARN: dawn: Mon Feb 11 2019 06:54:24 GMT+0100 (CET)
[WARN]  WARN: dusk: Mon Feb 11 2019 18:05:01 GMT+0100 (CET)
[WARN]  WARN: nauticalDawn: Mon Feb 11 2019 06:18:59 GMT+0100 (CET)
[WARN]  WARN: nauticalDusk: Mon Feb 11 2019 18:40:25 GMT+0100 (CET)
[WARN]  WARN: nightEnd: Mon Feb 11 2019 05:44:07 GMT+0100 (CET)
[WARN]  WARN: night: Mon Feb 11 2019 19:15:18 GMT+0100 (CET)
[WARN]  WARN: goldenHourEnd: Mon Feb 11 2019 08:08:55 GMT+0100 (CET)
[WARN]  WARN: goldenHour: Mon Feb 11 2019 16:50:29 GMT+0100 (CET)

It's seems return wrong timezone in USA location

Tronald commented 5 years ago

@ottopic I was able to solve the issue in CoordinateSharp using the method described, but the library works a lot differently so I can’t verify the fix for suncalc.

With that said I’ve converted most of my calculations to Meeus and don’t have much suncalc logic left in CoordinateSharp.

If you are working in .NET I would highly recommend switching to CoordinateSharp, but if you need JavaScript you may be able to at least review how it does solar calculations and find a fix. Best of luck!