Lunar phases
This commit is contained in:
parent
161c7987af
commit
195520d66f
11
calendar.js
11
calendar.js
@ -13,6 +13,7 @@ import {
|
|||||||
getLocalizedWeekdayNames,
|
getLocalizedWeekdayNames,
|
||||||
getLocalizedMonthName,
|
getLocalizedMonthName,
|
||||||
formatDateRange
|
formatDateRange
|
||||||
|
,lunarPhaseSymbol
|
||||||
} from './date-utils.js'
|
} from './date-utils.js'
|
||||||
|
|
||||||
class InfiniteCalendar {
|
class InfiniteCalendar {
|
||||||
@ -330,8 +331,14 @@ class InfiniteCalendar {
|
|||||||
labelYear = cur.getFullYear()
|
labelYear = cur.getFullYear()
|
||||||
}
|
}
|
||||||
|
|
||||||
const day = document.createElement('h1')
|
const day = document.createElement('h1')
|
||||||
day.textContent = String(cur.getDate())
|
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)
|
const date = toLocalString(cur)
|
||||||
cell.dataset.date = date
|
cell.dataset.date = date
|
||||||
|
@ -22,6 +22,12 @@
|
|||||||
transition: background-color .15s ease;
|
transition: background-color .15s ease;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
}
|
}
|
||||||
|
.cell .lunar-phase {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
top: .25em;
|
||||||
|
left: 50%;
|
||||||
|
}
|
||||||
.cell.today h1 {
|
.cell.today h1 {
|
||||||
border-radius: 2em;
|
border-radius: 2em;
|
||||||
background: var(--today);
|
background: var(--today);
|
||||||
|
@ -121,6 +121,35 @@ function formatDateRange(startDate, endDate) {
|
|||||||
return `${startISO}/${endISO}`
|
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 all functions and constants
|
||||||
export {
|
export {
|
||||||
monthAbbr,
|
monthAbbr,
|
||||||
@ -136,4 +165,5 @@ export {
|
|||||||
getLocalizedWeekdayNames,
|
getLocalizedWeekdayNames,
|
||||||
getLocalizedMonthName,
|
getLocalizedMonthName,
|
||||||
formatDateRange
|
formatDateRange
|
||||||
|
,lunarPhaseSymbol
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user