Weather Station / ECOWITT / DNT
0

Configure Feed

Select the types of activity you want to include in your feed.

at stats2.0 6.0 kB View raw
1import SunCalc from "suncalc"; 2 3/** 4 * Represents the result of astronomical calculations. 5 * @property {Date | null} sunrise - The time of sunrise. 6 * @property {Date | null} sunset - The time of sunset. 7 * @property {Date | null} moonrise - The time of moonrise. 8 * @property {Date | null} moonset - The time of moonset. 9 * @property {number} phase - The moon phase, from 0.0 (new moon) to 1.0 (new moon). 10 * @property {string} phaseName - The name of the moon phase. 11 * @property {number} illumination - The fraction of the moon's illuminated limb. 12 * @property {Date | null} civilDawn - The time when the sun is 6 degrees below the horizon in the morning. 13 * @property {Date | null} civilDusk - The time when the sun is 6 degrees below the horizon in the evening. 14 * @property {Date | null} nauticalDawn - The time when the sun is 12 degrees below the horizon in the morning. 15 * @property {Date | null} nauticalDusk - The time when the sun is 12 degrees below the horizon in the evening. 16 * @property {Date | null} astronomicalDawn - The time when the sun is 18 degrees below the horizon in the morning. 17 * @property {Date | null} astronomicalDusk - The time when the sun is 18 degrees below the horizon in the evening. 18 */ 19export type AstroResult = { 20 sunrise: Date | null; 21 sunset: Date | null; 22 moonrise: Date | null; 23 moonset: Date | null; 24 phase: number; // 0..1 25 phaseName: string; 26 illumination: number; // 0..1 fraction lit 27 // Twilight times 28 civilDawn: Date | null; // Sun -6° below horizon -> start of civil twilight 29 civilDusk: Date | null; // Sun -6° below horizon -> end of civil twilight 30 nauticalDawn: Date | null; // Sun -12° -> start of nautical twilight 31 nauticalDusk: Date | null; // Sun -12° -> end of nautical twilight 32 astronomicalDawn: Date | null;// Sun -18° -> start of astronomical twilight (night ends) 33 astronomicalDusk: Date | null;// Sun -18° -> end of astronomical twilight (night begins) 34}; 35 36/** 37 * Gets the name of the moon phase for a given phase value. 38 * @param {number} phase - The moon phase, from 0.0 (new moon) to 1.0 (new moon). 39 * @param {string} [locale="en"] - The locale to use for the phase name (e.g., "en" or "de"). 40 * @returns {string} The name of the moon phase. 41 */ 42export function moonPhaseName(phase: number, locale: string = "en"): string { 43 // Phase: 0=new, 0.25=first quarter, 0.5=full, 0.75=last quarter 44 const namesEn = [ 45 "New Moon", 46 "Waxing Crescent", 47 "First Quarter", 48 "Waxing Gibbous", 49 "Full Moon", 50 "Waning Gibbous", 51 "Last Quarter", 52 "Waning Crescent" 53 ]; 54 const namesDe = [ 55 "Neumond", 56 "Zunehmende Sichel", 57 "Erstes Viertel", 58 "Zunehmender Mond", 59 "Vollmond", 60 "Abnehmender Mond", 61 "Letztes Viertel", 62 "Abnehmende Sichel" 63 ]; 64 const idx = Math.round(((phase % 1 + 1) % 1) * 7) as 0|1|2|3|4|5|6|7; 65 return (locale?.startsWith("de") ? namesDe : namesEn)[idx]; 66} 67 68/** 69 * Computes astronomical data for a given latitude, longitude, and date. 70 * @param {number} lat - The latitude. 71 * @param {number} lon - The longitude. 72 * @param {Date} [date=new Date()] - The date for the calculation. 73 * @param {string} [locale="en"] - The locale for the moon phase name. 74 * @returns {AstroResult} An object containing the astronomical data. 75 */ 76export function computeAstro(lat: number, lon: number, date: Date = new Date(), locale: string = "en"): AstroResult { 77 const times = SunCalc.getTimes(date, lat, lon); 78 const mt = SunCalc.getMoonTimes(date, lat, lon, true /* UTC to avoid host tz issues */); 79 const ill = SunCalc.getMoonIllumination(date); 80 return { 81 sunrise: times.sunrise ?? null, 82 sunset: times.sunset ?? null, 83 moonrise: mt.rise ?? null, 84 moonset: mt.set ?? null, 85 phase: ill.phase, 86 illumination: ill.fraction, 87 phaseName: moonPhaseName(ill.phase, locale), 88 // Twilight mappings according to suncalc docs 89 civilDawn: (times as any).dawn ?? null, 90 civilDusk: (times as any).dusk ?? null, 91 nauticalDawn: (times as any).nauticalDawn ?? null, 92 nauticalDusk: (times as any).nauticalDusk ?? null, 93 astronomicalDawn: (times as any).nightEnd ?? null, 94 astronomicalDusk: (times as any).night ?? null, 95 }; 96} 97 98/** 99 * Formats a date object into a time string (HH:mm). 100 * @param {Date | null} d - The date to format. 101 * @param {string} [tz] - The time zone to use. 102 * @param {string} [locale="en"] - The locale to use for formatting. 103 * @returns {string} The formatted time string, or "—" if the date is null. 104 */ 105export function formatTime(d: Date | null, tz?: string, locale: string = "en"): string { 106 if (!d) return "—"; 107 try { 108 return new Intl.DateTimeFormat(locale || "en", { 109 hour: "2-digit", 110 minute: "2-digit", 111 hour12: false, 112 timeZone: tz || undefined 113 }).format(d); 114 } catch { 115 const hh = String(d.getHours()).padStart(2, "0"); 116 const mm = String(d.getMinutes()).padStart(2, "0"); 117 return `${hh}:${mm}`; 118 } 119} 120 121/** 122 * Calculates the percentage of the day that has passed for a given date and time zone. 123 * @param {Date} d - The date object. 124 * @param {string} [tz] - The time zone to use. 125 * @returns {number} The percentage of the day passed, from 0.0 to 1.0. 126 */ 127export function timeOfDayPercent(d: Date, tz?: string): number { 128 // returns 0..1 position within day for the given date in tz 129 try { 130 const parts = new Intl.DateTimeFormat("en-GB", { 131 timeZone: tz || undefined, 132 hourCycle: "h23", 133 hour: "2-digit", 134 minute: "2-digit", 135 second: "2-digit" 136 }).formatToParts(d); 137 const h = Number(parts.find(p => p.type === "hour")?.value || 0); 138 const m = Number(parts.find(p => p.type === "minute")?.value || 0); 139 const s = Number(parts.find(p => p.type === "second")?.value || 0); 140 return (h * 3600 + m * 60 + s) / 86400; 141 } catch { 142 const h = d.getHours(); 143 const m = d.getMinutes(); 144 const s = d.getSeconds(); 145 return (h * 3600 + m * 60 + s) / 86400; 146 } 147}