Skip to content

Instantly share code, notes, and snippets.

@ruiokada
Last active March 27, 2024 16:49
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ruiokada/b28076d4911820ddcbbc to your computer and use it in GitHub Desktop.
Save ruiokada/b28076d4911820ddcbbc to your computer and use it in GitHub Desktop.
Calculate sunrise/sunset times from latitude/longitude coordinates using Javascript.
/**
* Calculates today's sunrise and sunset hours in local time (or in the given tz) for the given latitude, longitude.
* The tz parameter is mainly for the possible circumstance that your system timezone does not match the location
* you are currently at.
*
* Computations are based on the formulas found in:
* https://en.wikipedia.org/wiki/Julian_day#Converting_Julian_or_Gregorian_calendar_date_to_Julian_Day_Number
* https://en.wikipedia.org/wiki/Sunrise_equation#Complete_calculation_on_Earth
*
* @method suntimes
* @param {Float} lat Latitude of location (South is negative)
* @param {Float} lng Longitude of location (West is negative)
* @param {Integer} tz Timezone hour offset. e.g. Pacific/Los Angeles Standard Time is -8 (Optional, defaults to system timezone)
* @return {Array} Returns an array of length 2 with the sunrise and sunset times as floats on 24-hour time.
* e.g. 6.5 is 6:30am, 23.2 is 11:12pm, 0.3 is 12:18am
* Returns an array with [null, -1] if the sun never rises, and [-1, null] if the sun never sets.
*
* ### LICENSING ###
*
* This code contains information derived from Wikipedia content licensed under
* the Creative Commons Attribution-ShareAlike License. As such, this code
* is distributed under the GPLv3 license:
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
function suntimes(lat, lng, tz) {
var d = new Date();
var radians = Math.PI / 180.0;
var degrees = 180.0 / Math.PI;
var a = Math.floor((14 - (d.getMonth() + 1.0)) / 12)
var y = d.getFullYear() + 4800 - a;
var m = (d.getMonth() + 1) + 12 * a - 3;
var j_day = d.getDate() + Math.floor((153 * m + 2)/5) + 365 * y + Math.floor(y/4) - Math.floor(y/100) + Math.floor(y/400) - 32045;
var n_star = j_day - 2451545.0009 - lng / 360.0;
var n = Math.floor(n_star + 0.5);
var solar_noon = 2451545.0009 - lng / 360.0 + n;
var M = 356.0470 + 0.9856002585 * n;
var C = 1.9148 * Math.sin( M * radians ) + 0.02 * Math.sin( 2 * M * radians ) + 0.0003 * Math.sin( 3 * M * radians );
var L = ( M + 102.9372 + C + 180 ) % 360;
var j_transit = solar_noon + 0.0053 * Math.sin( M * radians) - 0.0069 * Math.sin( 2 * L * radians );
var D = Math.asin( Math.sin( L * radians ) * Math.sin( 23.45 * radians ) ) * degrees;
var cos_omega = ( Math.sin(-0.83 * radians) - Math.sin( lat * radians ) * Math.sin( D * radians ) ) / ( Math.cos( lat * radians ) * Math.cos( D * radians ) );
// sun never rises
if( cos_omega > 1)
return [null, -1];
// sun never sets
if( cos_omega < -1 )
return [-1, null];
// get Julian dates of sunrise/sunset
var omega = Math.acos( cos_omega ) * degrees;
var j_set = j_transit + omega / 360.0;
var j_rise = j_transit - omega / 360.0;
/*
* get sunrise and sunset times in UTC
* Check section "Finding Julian date given Julian day number and time of
* day" on wikipedia for where the extra "+ 12" comes from.
*/
var utc_time_set = 24 * (j_set - j_day) + 12;
var utc_time_rise = 24 * (j_rise - j_day) + 12;
var tz_offset = tz === undefined ? -1 * d.getTimezoneOffset() / 60 : tz;
var local_rise = (utc_time_rise + tz_offset) % 24;
var local_set = (utc_time_set + tz_offset) % 24;
return [local_rise, local_set];
}
@PHDPeter
Copy link

PHDPeter commented Apr 6, 2021

Sorry if this is a silly question but how do I convert the output into a readable time?

@ruiokada
Copy link
Author

ruiokada commented Apr 6, 2021

@PHDPeter Good question, I can answer it only partially. I forgot to specify that the output gives the decimal "hour of the day" of the sunrise and sunset times (i.e. a decimal number between 0 and 24). For example, 6.5 is 6:30am, 23.2 is 11:12pm, and 0.3 is 12:18am. Getting the hour is the floor of this decimal number and the minute is ((decimal hour) - floor(decimal hour)) * 60. So something like this:

let times = suntimes(input_lat, input_lng);
let sunrise_hour = Math.floor(times[0]);
let sunrise_minute = (times[0] - Math.floor(times[0])) * 60;

Then you can do what you need with that information.

@justingolden21
Copy link

justingolden21 commented Apr 26, 2023

I saw your docs on tz:

* @param {Integer} tz Timezone hour offset. e.g. Pacific/Los Angeles is -8 (Optional, defaults to system timezone)

And I'm in LA and when I give it -8 as a parameter it gives a different result. Seems to be because (new Date()).getTimezoneOffset()/60 is 7 and not -8. I'm guessing the 1hr difference is daylight savings and the negative vs positive is because we're UTC-8 but we're 8 (now 7) hours behind)

@ruiokada
Copy link
Author

I saw your docs on tz:

* @param {Integer} tz Timezone hour offset. e.g. Pacific/Los Angeles is -8 (Optional, defaults to system timezone)

And I'm in LA and when I give it -8 as a parameter it gives a different result. Seems to be because (new Date()).getTimezoneOffset()/60 is 7 and not -8. I'm guessing the 1hr difference is daylight savings and the negative vs positive is because we're UTC-8 but we're 8 (now 7) hours behind)

Thanks for mentioning this. I have specified that -8 is PST in the docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment