suncalc icon indicating copy to clipboard operation
suncalc copied to clipboard

Results appear to be inaccurate during summer in northern hemisphere

Open jhsware opened this issue 5 years ago • 6 comments

The tests do not include any variation or edge cases which allows the library to fail silently.

jhsware avatar Jun 10 '20 08:06 jhsware

Example of failing tests (the results aren't even close)

t.test('getTimes returns sun phases in summer in Stockholm', function (t) {
  const date = Date.UTC(2020, 6, 9);
  const sthlm = {lat: 59.33538407920466, lng: 18.03007918439074};
  // https://www.timeanddate.com/sun/sweden/stockholm?month=6&year=2020
  var times = SunCalc.getTimes(date, sthlm.lat, sthlm.lng);

  // Should be aprox GMT 01:34 / 20:00
  t.equal(new Date(times.sunrise).toUTCString().substr(0, 22), 'Thu, 09 Jul 2020 01:34');
  t.equal(new Date(times.sunset).toUTCString().substr(0, 22), 'Thu, 09 Jul 2020 20:00');
  t.end();
});

t.test('getTimes returns sun phases in summer in Kiruna (midnight sun)', function (t) {
  const date = Date.UTC(2020, 7, 1);
  const kiruna = {lat: 67.8537716, lng: 20.1163502};
  // https://www.timeanddate.com/sun/sweden/kiruna?month=7&year=2020
  var times = SunCalc.getTimes(date, kiruna.lat, kiruna.lng);

  // Should be up all day
  t.equal(times.sunrise, undefined);
  t.equal(times.sunset, undefined);
  t.end();
});


t.test('getTimes returns sun phases in summer in Kiruna', function (t) {
  const date = Date.UTC(2020, 7, 17);
  const kiruna = {lat: 67.8537716, lng: 20.1163502};
  // https://www.timeanddate.com/sun/sweden/kiruna?month=7&year=2020
  var times = SunCalc.getTimes(date, kiruna.lat, kiruna.lng);

  // Should be aprox GMT 23:37 (day before) / 21:45
  t.equal(new Date(times.sunrise).toUTCString().substr(0, 22), 'Mon, 16 Aug 2020 23:37');
  t.equal(new Date(times.sunset).toUTCString().substr(0, 22), 'Mon, 17 Aug 2020 21:45');
  t.end();
});

jhsware avatar Jun 10 '20 08:06 jhsware

@jhsware I'm also based in Stockholm and think I'm affected by this issue as well, or at least I'm not confident that SunCalc.getPosition() is accurate after seeing this issue. Did you end up digging in to see where things go wrong?

foolip avatar Jun 22 '20 19:06 foolip

To shed light on the actual behavior I tweaked the tests to pass against the current implementation:

t.test('getTimes returns sun phases in summer in Stockholm', function (t) {
    const date = Date.UTC(2020, 6, 9);
    const sthlm = {lat: 59.33538407920466, lng: 18.03007918439074};
    // https://www.timeanddate.com/sun/sweden/stockholm?month=6&year=2020
    var times = SunCalc.getTimes(date, sthlm.lat, sthlm.lng);

    // Should be approx GMT 01:34 / 20:00
    t.equal(times.sunrise.toUTCString(), 'Thu, 09 Jul 2020 01:48:59 GMT');
    t.equal(times.sunriseEnd.toUTCString(), 'Thu, 09 Jul 2020 01:55:26 GMT');
    t.equal(times.sunsetStart.toUTCString(), 'Thu, 09 Jul 2020 19:53:00 GMT');
    t.equal(times.sunset.toUTCString(), 'Thu, 09 Jul 2020 19:59:26 GMT');

    t.end();
  });

t.test('getTimes returns sun phases in summer in Kiruna (midnight sun)', function (t) {
    const date = Date.UTC(2020, 7, 1);
    const kiruna = {lat: 67.8537716, lng: 20.1163502};
    // https://www.timeanddate.com/sun/sweden/kiruna?month=7&year=2020
    var times = SunCalc.getTimes(date, kiruna.lat, kiruna.lng);

    // Should be up all day
    t.equal(times.sunrise.toUTCString(), 'Sat, 01 Aug 2020 01:00:24 GMT');
    t.equal(times.sunriseEnd.toUTCString(), 'Sat, 01 Aug 2020 01:10:51 GMT');
    t.equal(times.sunsetStart.toUTCString(), 'Sat, 01 Aug 2020 20:23:17 GMT');
    t.equal(times.sunset.toUTCString(), 'Sat, 01 Aug 2020 20:33:45 GMT');

    t.end();
  });

t.test('getTimes returns sun phases in summer in Kiruna', function (t) {
    const date = Date.UTC(2020, 7, 17);
    const kiruna = {lat: 67.8537716, lng: 20.1163502};
    // https://www.timeanddate.com/sun/sweden/kiruna?month=7&year=2020
    var times = SunCalc.getTimes(date, kiruna.lat, kiruna.lng);

    // Should be approx GMT 23:37 (day before) / 21:45
    t.equal(times.sunrise.toUTCString(), 'Mon, 17 Aug 2020 02:11:29 GMT');
    t.equal(times.sunriseEnd.toUTCString(), 'Mon, 17 Aug 2020 02:18:48 GMT');
    t.equal(times.sunsetStart.toUTCString(), 'Mon, 17 Aug 2020 19:11:12 GMT');
    t.equal(times.sunset.toUTCString(), 'Mon, 17 Aug 2020 19:18:31 GMT');

    t.end();
});

Looking at the actual results, it's clear that at least part of the problem is the dates. Date.UTC(2020, 7, 17) is Aug 17, because months are zero-based in that method.

foolip avatar Jun 22 '20 21:06 foolip

Fixing that problem, here's what I end up with:

t.test('getTimes returns sun phases in summer in Stockholm', function (t) {
    const date = new Date('2020-06-09UTC');
    const sthlm = {lat: 59.33538407920466, lng: 18.03007918439074};
    // https://www.timeanddate.com/sun/sweden/stockholm?month=6&year=2020
    var times = SunCalc.getTimes(date, sthlm.lat, sthlm.lng);

    // Should be approx GMT 01:34 / 20:00
    t.equal(times.sunrise.toUTCString(), 'Tue, 09 Jun 2020 01:35:47 GMT');
    t.equal(times.sunriseEnd.toUTCString(), 'Tue, 09 Jun 2020 01:42:29 GMT');
    t.equal(times.sunsetStart.toUTCString(), 'Tue, 09 Jun 2020 19:54:41 GMT');
    t.equal(times.sunset.toUTCString(), 'Tue, 09 Jun 2020 20:01:23 GMT');

    t.end();
  });

t.test('getTimes returns sun phases in summer in Kiruna (midnight sun)', function (t) {
    const date = new Date('2020-07-01UTC');
    const kiruna = {lat: 67.8537716, lng: 20.1163502};
    // https://www.timeanddate.com/sun/sweden/kiruna?month=7&year=2020
    var times = SunCalc.getTimes(date, kiruna.lat, kiruna.lng);

    // Should be up all day
    t.ok(isNaN(times.sunrise.valueOf()));
    t.ok(isNaN(times.sunriseEnd.valueOf()));
    t.ok(isNaN(times.sunsetStart.valueOf()));
    t.ok(isNaN(times.sunset.valueOf()));

    t.end();
  });

t.test('getTimes returns sun phases in summer in Kiruna', function (t) {
    const date = new Date('2020-07-17UTC');
    const kiruna = {lat: 67.8537716, lng: 20.1163502};
    // https://www.timeanddate.com/sun/sweden/kiruna?month=7&year=2020
    var times = SunCalc.getTimes(date, kiruna.lat, kiruna.lng);

    // Should be approx GMT 23:37 (day before) / 21:45
    t.equal(times.sunrise.toUTCString(), 'Thu, 16 Jul 2020 23:17:03 GMT');
    t.equal(times.sunriseEnd.toUTCString(), 'Thu, 16 Jul 2020 23:47:45 GMT');
    t.equal(times.sunsetStart.toUTCString(), 'Fri, 17 Jul 2020 21:45:49 GMT');
    t.equal(times.sunset.toUTCString(), 'Fri, 17 Jul 2020 22:16:31 GMT');

    t.end();
});

The only bit that still doesn't match the expectation from the comments is the first 01:34. @jhsware do you think suncalc is wrong in this instance?

foolip avatar Jun 22 '20 21:06 foolip

@foolip Nice catch about the dates! I am wondering if the fact that Kiruna is att 530m above sea level could account for the difference compared to timeanddate.com?

jhsware avatar Jun 23 '20 08:06 jhsware

I tried following... in my opinion improved a lot for GMT+1

var now = new Date(); now.setHours( now.getHours() -1 )

Maximilian-Jodokus avatar Jul 29 '20 19:07 Maximilian-Jodokus