opening-hours / opening_hours.js

Library to parse and process the opening_hours tag from OpenStreetMap data
https://openingh.ypid.de/evaluation_tool/
222 stars 119 forks source link

Sunrise/sunset times not working properly #377

Open noc7c9 opened 3 years ago

noc7c9 commented 3 years ago

First off, thanks for the great work on the library :)

I'm encountering strange results for the sunrise/sunset times. I'm using the version on npm and running on Node v12.

See the example, hopefully is self-explanatory.

const OpeningHours = require('opening_hours');

const oh = 'Mo-Su sunrise-sunset';
const date = '2021-04-18T00:00:00.000Z';

console.log('No coordinates provided');
helper(oh, {}, date, 2);
// 2021-02-06T06:00:00.000Z open
// 2021-02-06T18:00:00.000Z close

console.log('Singapore');
helper(oh, { lon: '103.821', lat: '1.340' }, date, 2);
// No output

console.log('London');
helper(oh, { lon: '-0.093', lat: '51.505' }, date, 2);
// 2021-02-06T07:33:00.000Z open
// 2021-02-06T16:59:00.000Z close

console.log('Seattle');
helper(oh, { lon: '-122.322', lat: '47.606' }, date, 2);
// No output

console.log('Melbourne');
helper(oh, { lon: '144.951', lat: '-37.818' }, date, 2);
// No output

console.log('Rio de Janeiro');
helper(oh, { lon: '-43.220', lat: '-22.918' }, date, 2);
// 2021-02-06T08:37:00.000Z open
// 2021-02-06T21:39:00.000Z close

function helper(ohValue, location, date, limit) {
  const oh = new OpeningHours(ohValue, location);
  const it = oh.getIterator(new Date(date));
  for (let i = 0; i < limit && it.advance(); i += 1) {
    console.log(it.getDate(), it.getStateString());
  }
}
noc7c9 commented 3 years ago

I've noticed that the timezone the script is running in seems to matter.

Here's a bunch of examples: (changed a bit to make it easier to read)

$ TZ=UTC node tmp.js
No coordinates provided
   2021-04-18T06:00:00.000Z open
   2021-04-18T18:00:00.000Z close
Singapore
London
   2021-04-18T05:01:00.000Z open
   2021-04-18T19:02:00.000Z close
Seattle
Melbourne
Rio de Janeiro
   2021-04-18T09:08:00.000Z open
   2021-04-18T20:38:00.000Z close

$ TZ=Europe/Paris node tmp.js
No coordinates provided
   2021-04-18T04:00:00.000Z open
   2021-04-18T16:00:00.000Z close
Singapore
   2021-04-18T11:09:00.000Z close
   2021-04-18T23:00:00.000Z open
London
   2021-04-18T05:01:00.000Z open
   2021-04-18T19:02:00.000Z close
Seattle
Melbourne
Rio de Janeiro
   2021-04-18T09:08:00.000Z open
   2021-04-18T20:38:00.000Z close

$ TZ=Australia/Melbourne node tmp.js
No coordinates provided
   2021-04-18T08:00:00.000Z close
   2021-04-18T20:00:00.000Z open
Singapore
   2021-04-18T11:09:00.000Z close
   2021-04-18T23:00:00.000Z open
London
Seattle
   2021-09-24T14:00:00.000Z open
   2021-09-25T02:04:00.000Z close
Melbourne
   2021-04-18T07:51:00.000Z close
   2021-04-18T20:51:00.000Z open
Rio de Janeiro

$ TZ=America/New_York node tmp.js
No coordinates provided
   2021-04-18T10:00:00.000Z open
   2021-04-18T22:00:00.000Z close
Singapore
London
   2021-04-18T05:01:00.000Z open
   2021-04-18T19:02:00.000Z close
Seattle
   2021-04-18T03:02:00.000Z close
   2021-04-18T13:17:00.000Z open
Melbourne
Rio de Janeiro
   2021-04-18T09:08:00.000Z open
   2021-04-18T20:38:00.000Z close
ypid commented 3 years ago

I can reproduce this. Needs to be investigated why the timezone environment has that effect. Note that the library works with local time. That Z denotes UTC which is probably not the desired way to output the timestamp. The dates would normally be shown in local time where the user it. So there is probably something wrong where the library mixes up UTC and local time. What is interesting is that the tests don’t care about the TZ.

happyfloat commented 3 years ago

I run into the same problem with the getOpenIntervals function. It's different on the server and client.

happyfloat commented 3 years ago

I did find a solution in my case. AWS Lambda nodejs timezone will always be UTC according to their documentation.

  1. The client will send a request with his local_time (2021-03-16T19:30:00.000Z) and timezone (Europe/Berlin).
  2. My server will transform it to a date in UTC format by doing this: dayjs(dayjs(localTime).tz(timezone).format("YYYY-MM-DDTHH:mm:ss")).utc(); // 2021-03-16T20:30:00.000Z
  3. Calculate everything you need with the opening_hours library
  4. Transform the result back to your local_time by doing this: dayjs(utc).tz(timezone, true); (notice the second argument of tz; basically the inverse of step 2)
  5. Send back to client
noc7c9 commented 3 years ago

@happyfloat The timezone isn't really the issue here. The issue is that sunrise/sunset hours based intervals seemingly aren't calculated correctly.

Notice how in the first example Singapore (which is on the equator and so has consistent sunrise/sunset hours) has no output. You'd expect an open and close time for that location throughout the year.

The fact that the nodejs timezone is affecting the output is a surprise and could possibly help fix this issue.

happyfloat commented 3 years ago

@noc7c9 I noticed the same problems for the intervals. They are different in each node timezone, also for normal opening hours without sunrise/sunset. So I thought it is the same issue... but I also tested your Singapore case now and it's empty too.. sunrise/sunset is working for me in Munich at least.

noc7c9 commented 3 years ago

Timezone affecting normal opening hours without sunrise/sunset sounds like a different (though possibly related) issue to me.

You might want to open a separate issue for that, sounds like a serious bug.