mourner / suncalc

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

It doesn't calculate correctly for other timezones relative to my own #116

Open oneuxco opened 6 years ago

oneuxco commented 6 years ago

If I live in GMT-7 (DST), and look up New York coordinates, the times are all messed up.

I made a date variable that is specific for New York (or the location given by user) but the suncalc still screws up the timing...is there any fix for this?

Thanks!

andrewharvey commented 5 years ago

It seems like SunCalc will always calculate sun times in the local time zone. I managed to work around this issue with the following approach.

Where,

Given,

const tzB = tzLookup(B.lat, b.lng)
const currentTimeAtB = DateTime.fromISO(new Date().toISOString(), { zone: tzB })
const suntimesAtB_in_tzA = SunCalc.getTimes(new Date(currentTimeAtB), B.lat, B.lng)

const sunriseAtB_in_tzB = DateTime.fromISO(suntimesAtB_in_tzA.sunrise.toISOString(), { zone: tzB })
prepper25 commented 5 years ago

thanks men is working 👌

marrrrko commented 5 years ago

Also would like a fix for this. AWS machines are always set to UTC.

andrewharvey commented 5 years ago

@SrGrieves Does the workaround I posted work for you? I also blogged about it at https://tech.beyondtracks.com/posts/sun-times/

marrrrko commented 5 years ago

@andrewharvey I'm working on an embedded system. Adding two additional external libraries is something I'd like to avoid. I like that SunCalc is small and self-sufficient.

ottopic commented 5 years ago

@andrewharvey I have tried your script and it seems to works but only if I edit "toJulian" function (with this ) isn't it?

andrewharvey commented 5 years ago

@ottopic I didn't do that, though my times could be a day off without it. I haven't done that detailed testing. If that's the case, good catch.

m3h0w commented 4 years ago

It seems like SunCalc will always calculate sun times in the local time zone. I managed to work around this issue with the following approach.

Where,

Given,

  • Location A is where the user is located (in time zone A)
  • Location B is where you want to calculate the sun times for in time zone B
const tzB = tzLookup(B.lat, b.lng)
const currentTimeAtB = DateTime.fromISO(new Date().toISOString(), { zone: tzB })
const suntimesAtB_in_tzA = SunCalc.getTimes(new Date(currentTimeAtB), B.lat, B.lng)

const sunriseAtB_in_tzB = DateTime.fromISO(suntimesAtB_in_tzA.sunrise.toISOString(), { zone: tzB })

This saved me hours of figuring out how to do the transformation. Such a nice little library but in so many applications you have to operate with UTC times.

Christopher-Hayes commented 3 years ago

For a personal project I ended up creating an NPM package of a modified suncalc to do this. It's called suncalc-tz, if anyone comes across this and is looking for something quick and dirty, might be worth a try. I wouldn't recommend it for production though, I haven't thoroughly tested it.

Similar to the code above, the package basically just pulls in a minified tzlookup to get the timezone and combines that with the Intl standard library:

  // Convert dates to the time at lat/lng
  for (let time in result) {
    const options = {timeZone: tz, year: 'numeric', month: 'numeric', day: 'numeric', hour: 'numeric', minute: 'numeric', second: 'numeric'};
    result[time] = new Date(Date.parse(new Intl.DateTimeFormat('default', options).format(result[time])));
  }
stephanmantler commented 2 years ago

For a personal project I ended up creating an NPM package of a modified suncalc to do this. It's called suncalc-tz, if anyone comes across this and is looking for something quick and dirty, might be worth a try. I wouldn't recommend it for production though, I haven't thoroughly tested it.

Similar to the code above, the package basically just pulls in a minified tzlookup to get the timezone and combines that with the Intl standard library:

  // Convert dates to the time at lat/lng
  for (let time in result) {
    const options = {timeZone: tz, year: 'numeric', month: 'numeric', day: 'numeric', hour: 'numeric', minute: 'numeric', second: 'numeric'};
    result[time] = new Date(Date.parse(new Intl.DateTimeFormat('default', options).format(result[time])));
  }

Found a minor issue - it crashes for high latitudes where some of the values are not available (for example, in summer there is no nautical dusk / dawn or night in Iceland). I think that the fix should be as easy as adding if ( !isNaN(result[time]) ) { ... } around the conversion, so I didn't bother with a pull request.

gwest7 commented 2 years ago

This will probably not work for regions that implement day light saving?

const { sunrise } = getTimes(new Date(), lat, lon);
const tzOffset = sunrise.getTimezoneOffset();
console.log('Corrected time', new Date(sunrise.getTime() - tzOffset * 60000));
aiden-jeffrey commented 2 years ago

If you're only calling getPosition, you can pass in a UTC epoch int in milliseconds for the time, rather than a Date object, as the function only calls valueOf on that parameter.