Lunar phases
This commit is contained in:
parent
161c7987af
commit
195520d66f
11
calendar.js
11
calendar.js
@ -13,6 +13,7 @@ import {
|
||||
getLocalizedWeekdayNames,
|
||||
getLocalizedMonthName,
|
||||
formatDateRange
|
||||
,lunarPhaseSymbol
|
||||
} from './date-utils.js'
|
||||
|
||||
class InfiniteCalendar {
|
||||
@ -330,8 +331,14 @@ class InfiniteCalendar {
|
||||
labelYear = cur.getFullYear()
|
||||
}
|
||||
|
||||
const day = document.createElement('h1')
|
||||
day.textContent = String(cur.getDate())
|
||||
const day = document.createElement('h1')
|
||||
day.textContent = String(cur.getDate())
|
||||
|
||||
// lunar phase symbol (only for main phases)
|
||||
const moon = document.createElement('span')
|
||||
moon.className = 'lunar-phase'
|
||||
moon.textContent = lunarPhaseSymbol(cur) || ''
|
||||
if (moon.textContent) cell.appendChild(moon)
|
||||
|
||||
const date = toLocalString(cur)
|
||||
cell.dataset.date = date
|
||||
|
@ -22,6 +22,12 @@
|
||||
transition: background-color .15s ease;
|
||||
font-size: 1em;
|
||||
}
|
||||
.cell .lunar-phase {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: .25em;
|
||||
left: 50%;
|
||||
}
|
||||
.cell.today h1 {
|
||||
border-radius: 2em;
|
||||
background: var(--today);
|
||||
|
@ -121,6 +121,35 @@ function formatDateRange(startDate, endDate) {
|
||||
return `${startISO}/${endISO}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute lunar phase symbol for the four main phases on a given date.
|
||||
* Returns one of: 🌑 (new), 🌓 (first quarter), 🌕 (full), 🌗 (last quarter), or '' otherwise.
|
||||
* Uses an approximate algorithm with a fixed epoch.
|
||||
*/
|
||||
function lunarPhaseSymbol(date) {
|
||||
// Reference new moon: 2000-01-06 18:14 UTC (J2000 era), often used in approximations
|
||||
const ref = Date.UTC(2000, 0, 6, 18, 14, 0)
|
||||
const synodic = 29.530588853 // days
|
||||
// Use UTC noon of given date to reduce timezone edge effects
|
||||
const dUTC = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), 12, 0, 0)
|
||||
const daysSince = (dUTC - ref) / DAY_MS
|
||||
const phase = ((daysSince / synodic) % 1 + 1) % 1
|
||||
const phases = [
|
||||
{ t: 0.0, s: '🌑' }, // New Moon
|
||||
{ t: 0.25, s: '🌓' }, // First Quarter
|
||||
{ t: 0.5, s: '🌕' }, // Full Moon
|
||||
{ t: 0.75, s: '🌗' } // Last Quarter
|
||||
]
|
||||
// threshold in days from exact phase to still count for this date
|
||||
const thresholdDays = 0.5 // ±12 hours
|
||||
for (const p of phases) {
|
||||
let delta = Math.abs(phase - p.t)
|
||||
if (delta > 0.5) delta = 1 - delta
|
||||
if (delta * synodic <= thresholdDays) return p.s
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
// Export all functions and constants
|
||||
export {
|
||||
monthAbbr,
|
||||
@ -136,4 +165,5 @@ export {
|
||||
getLocalizedWeekdayNames,
|
||||
getLocalizedMonthName,
|
||||
formatDateRange
|
||||
,lunarPhaseSymbol
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user