solar-time
Version:
This library provides functions to calculate local solar time, also known as local apparent time, based on a given location and date.
89 lines (88 loc) • 3.93 kB
JavaScript
function y(t) {
const n = t.getUTCFullYear(), e = t.getUTCMonth(), a = t.getUTCDate(), s = Date.UTC(n, 0, 0), o = Date.UTC(n, e, a) - s;
return Math.floor(o / (1e3 * 60 * 60 * 24));
}
function l(t) {
if (t.endsWith("Z"))
return 0;
const n = t.match(/([+-])(\d{2}):(\d{2})$/);
if (!n)
return 0;
const e = n[1] === "+" ? 1 : -1, a = parseInt(n[2], 10), s = parseInt(n[3], 10);
return e * (a + s / 60);
}
function E(t) {
const n = new Date(t), e = l(t), a = n.getTime() + e * 60 * 60 * 1e3, s = new Date(a), c = new Date(Date.UTC(s.getUTCFullYear(), s.getUTCMonth(), s.getUTCDate(), 0, 0, 0, 0));
return e !== 0 ? I(c, e) : c.toISOString();
}
function f(t, n, e) {
const a = new Date(t), s = new Date(a.getTime() + n * 60 * 1e3);
return e !== 0 ? I(s, e) : s.toISOString();
}
function I(t, n) {
const e = new Date(t.getTime() + n * 60 * 60 * 1e3), a = e.getUTCFullYear(), s = String(e.getUTCMonth() + 1).padStart(2, "0"), c = String(e.getUTCDate()).padStart(2, "0"), o = String(e.getUTCHours()).padStart(2, "0"), r = String(e.getUTCMinutes()).padStart(2, "0"), i = String(e.getUTCSeconds()).padStart(2, "0"), M = n >= 0 ? "+" : "-", u = Math.abs(n), h = String(Math.floor(u)).padStart(2, "0"), d = String(Math.round(u % 1 * 60)).padStart(2, "0");
return `${a}-${s}-${c}T${o}:${r}:${i}${M}${h}:${d}`;
}
function L(t) {
const n = new Date(t), e = l(t);
let s = n.getUTCHours() * 60 + n.getUTCMinutes() + n.getUTCSeconds() / 60 + e * 60;
return s < 0 && (s += 1440), s >= 1440 && (s -= 1440), s;
}
function R(t) {
if (typeof t != "number" || Number.isNaN(t))
throw new Error("Longitude must be a valid number");
if (t < -180 || t > 180)
throw new Error("Longitude must be between -180 and 180 degrees");
}
function z(t) {
if (typeof t != "string")
throw new Error("DateTime must be an ISO 8601 string");
const n = new Date(t);
if (Number.isNaN(n.getTime()))
throw new Error("Invalid ISO 8601 datetime string");
}
const Y = (t, n) => {
z(t), R(n);
const e = new Date(t), a = l(t), s = 15 * a, c = y(e), o = 2 * Math.PI * (c - 1) / 365, r = 1440 / (2 * Math.PI) * (75e-7 + 1868e-6 * Math.cos(o) - 0.032077 * Math.sin(o) - 0.014615 * Math.cos(2 * o) - 0.040849 * Math.sin(2 * o)), M = (6918e-6 - 0.399912 * Math.cos(o) + 0.070257 * Math.sin(o) - 6758e-6 * Math.cos(2 * o) + 907e-6 * Math.sin(2 * o) - 2697e-6 * Math.cos(3 * o) + 148e-5 * Math.sin(3 * o)) * (180 / Math.PI), u = 360 / 365 * (c - 1), h = 4 * (n - s) + r;
return {
LST: f(t, h, a),
// Preserve timezone from input
TC: h,
EoT: r,
B: u,
LSTM: s,
declination: M
};
};
function A(t) {
if (typeof t != "number" || Number.isNaN(t))
throw new Error("Latitude must be a valid number");
if (t < -90 || t > 90)
throw new Error("Latitude must be between -90 and 90 degrees");
}
const x = (t, n, e) => {
A(n);
const a = Y(t, e), { declination: s, EoT: c } = a, o = l(t), r = n * (Math.PI / 180), i = s * (Math.PI / 180), M = 90.833 * (Math.PI / 180), u = (Math.cos(M) - Math.sin(r) * Math.sin(i)) / (Math.cos(r) * Math.cos(i)), h = E(t);
let d = null, S = null;
if (Math.abs(u) <= 1) {
const b = Math.acos(u) * (180 / Math.PI), $ = 720 - 4 * (e + b) - c, v = 720 - 4 * (e - b) - c;
d = f(h, $, o), S = f(h, v, o);
}
const p = 720 - 4 * e - c, D = f(h, p, o);
let g = (L(t) + a.TC) / 60;
g < 0 && (g += 24), g >= 24 && (g -= 24);
const w = 15 * (g - 12) * (Math.PI / 180), C = Math.sin(r) * Math.sin(i) + Math.cos(r) * Math.cos(i) * Math.cos(w), T = Math.asin(C), U = T * (180 / Math.PI), O = 90 - U, N = -Math.sin(w) * Math.cos(i) / Math.cos(T), P = (Math.sin(i) - Math.sin(r) * C) / (Math.cos(r) * Math.cos(T));
let m = Math.atan2(N, P) * (180 / Math.PI);
return m < 0 && (m += 360), {
sunrise: d,
sunset: S,
solarNoon: D,
azimuth: m,
elevation: U,
zenith: O
};
};
export {
Y as getSolarTime,
x as getSunPosition
};