Show Seth/Gregorian astronomy columns and dual-date lunar event list
This commit is contained in:
+38
-13
@@ -35,16 +35,28 @@
|
|||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
margin-top: 0.2rem;
|
margin-top: 0.2rem;
|
||||||
}
|
}
|
||||||
|
.astro-head {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 130px 1fr 1fr;
|
||||||
|
gap: 0.35rem 0.9rem;
|
||||||
|
font-size: 0.72rem;
|
||||||
|
color: #808892;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
margin-top: 0.35rem;
|
||||||
|
}
|
||||||
.astro-grid {
|
.astro-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(2, 1fr);
|
grid-template-columns: 130px 1fr 1fr;
|
||||||
gap: 0.4rem 1rem;
|
gap: 0.35rem 0.9rem;
|
||||||
font-size: 0.92rem;
|
font-size: 0.9rem;
|
||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
margin: 1.2rem 0;
|
margin: 0.45rem 0 1.1rem;
|
||||||
|
}
|
||||||
|
.astro-label {
|
||||||
|
color: var(--text);
|
||||||
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
.astro-grid p { margin: 0; }
|
|
||||||
.astro-grid strong { color: var(--text); }
|
|
||||||
.date-nav {
|
.date-nav {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -92,10 +104,19 @@
|
|||||||
border-bottom: 1px solid #2a2a2a;
|
border-bottom: 1px solid #2a2a2a;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
.event-list li:last-child { border-bottom: none; }
|
.event-list li:last-child { border-bottom: none; }
|
||||||
.event-name { color: var(--text); }
|
.event-name { color: var(--text); }
|
||||||
|
.event-dual {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: right;
|
||||||
|
gap: 0.04rem;
|
||||||
|
font-size: 0.77rem;
|
||||||
|
}
|
||||||
|
.event-dual span:first-child { color: var(--text); }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -117,19 +138,23 @@
|
|||||||
<div class="moon-pct" id="moonPct">—</div>
|
<div class="moon-pct" id="moonPct">—</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="astro-head">
|
||||||
|
<div></div><div>Seth</div><div>Gregorian</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="section-head">Sun</div>
|
<div class="section-head">Sun</div>
|
||||||
<div class="astro-grid">
|
<div class="astro-grid">
|
||||||
<p><strong>Sunrise:</strong> <span id="sunrise">—</span></p>
|
<div class="astro-label">Sunrise</div><div id="sunriseSeth">—</div><div id="sunriseGreg">—</div>
|
||||||
<p><strong>Sunset:</strong> <span id="sunset">—</span></p>
|
<div class="astro-label">Sunset</div><div id="sunsetSeth">—</div><div id="sunsetGreg">—</div>
|
||||||
<p><strong>Day length:</strong> <span id="dayLen">—</span></p>
|
<div class="astro-label">Day length</div><div id="dayLenSeth">—</div><div id="dayLenGreg">—</div>
|
||||||
<p><strong>Solar noon:</strong> <span id="solarNoon">—</span></p>
|
<div class="astro-label">Solar noon</div><div id="solarNoonSeth">—</div><div id="solarNoonGreg">—</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="section-head">Moon</div>
|
<div class="section-head">Moon</div>
|
||||||
<div class="astro-grid">
|
<div class="astro-grid">
|
||||||
<p><strong>Age:</strong> <span id="moonAge">—</span></p>
|
<div class="astro-label">Age</div><div id="moonAgeSeth">—</div><div id="moonAgeGreg">—</div>
|
||||||
<p><strong>Next new moon:</strong> <span id="nextNew">—</span></p>
|
<div class="astro-label">Next new moon</div><div id="nextNewSeth">—</div><div id="nextNewGreg">—</div>
|
||||||
<p><strong>Next full moon:</strong> <span id="nextFull">—</span></p>
|
<div class="astro-label">Next full moon</div><div id="nextFullSeth">—</div><div id="nextFullGreg">—</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul class="event-list" id="yearEvents" style="margin-top:1rem;"></ul>
|
<ul class="event-list" id="yearEvents" style="margin-top:1rem;"></ul>
|
||||||
|
|||||||
@@ -143,6 +143,54 @@ function dayLengthStr(riseJD, setJD) {
|
|||||||
return `${h}h ${String(m).padStart(2,"0")}m`;
|
return `${h}h ${String(m).padStart(2,"0")}m`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function jdToDecimalTime(jd) {
|
||||||
|
const frac = ((jd + 0.5) % 1 + 1) % 1;
|
||||||
|
const dec = frac * 10;
|
||||||
|
const h = Math.floor(dec);
|
||||||
|
const m = Math.floor((dec - h) * 100);
|
||||||
|
return `${h}:${String(m).padStart(2, "0")}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dayLengthDecimalStr(riseJD, setJD) {
|
||||||
|
const decHours = (setJD - riseJD) * 10;
|
||||||
|
const h = Math.floor(decHours);
|
||||||
|
const m = Math.floor((decHours - h) * 100);
|
||||||
|
return `${h}:${String(m).padStart(2, "0")}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Seth date helpers ───────────────────────────────────────────────────────
|
||||||
|
function isLeapYear(year) {
|
||||||
|
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDayOfYearUTC(date) {
|
||||||
|
const y = date.getUTCFullYear();
|
||||||
|
const m = date.getUTCMonth() + 1;
|
||||||
|
const d = date.getUTCDate();
|
||||||
|
const cum = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
|
||||||
|
let doy = cum[m - 1] + d;
|
||||||
|
if (isLeapYear(y) && m > 2) doy++;
|
||||||
|
return { year: y, doy };
|
||||||
|
}
|
||||||
|
|
||||||
|
function toSethDate(year, dayOfYear) {
|
||||||
|
const leap = isLeapYear(year);
|
||||||
|
if (leap && dayOfYear === 60) return { type: "leapday", year };
|
||||||
|
const idx = (leap && dayOfYear > 60 ? dayOfYear - 1 : dayOfYear) - 1;
|
||||||
|
if (idx < 360) {
|
||||||
|
const month = Math.floor(idx / 36);
|
||||||
|
const dayInM = idx % 36;
|
||||||
|
return { type: "month", year, month, day: dayInM };
|
||||||
|
}
|
||||||
|
return { type: "holiday", year, holiday: idx - 360 };
|
||||||
|
}
|
||||||
|
|
||||||
|
function sethLog(sd) {
|
||||||
|
if (sd.type === "leapday") return `Leap Day ${sd.year}`;
|
||||||
|
if (sd.type === "holiday") return `H${sd.holiday}.${sd.year}`;
|
||||||
|
return `${String(sd.day).padStart(2, "0")}.${sd.month}.${sd.year}`;
|
||||||
|
}
|
||||||
|
|
||||||
// ── Solstice / Equinox (Meeus Table 27.a + correction) ───────────────────────
|
// ── Solstice / Equinox (Meeus Table 27.a + correction) ───────────────────────
|
||||||
// Returns approximate JD for each event in a given year
|
// Returns approximate JD for each event in a given year
|
||||||
// season: 0=March equinox, 1=June solstice, 2=September equinox, 3=December solstice
|
// season: 0=March equinox, 1=June solstice, 2=September equinox, 3=December solstice
|
||||||
@@ -246,15 +294,27 @@ function render() {
|
|||||||
const jd = jdFromDate(date);
|
const jd = jdFromDate(date);
|
||||||
const nextNewJD = nextMoonPhaseJD(jd, 0);
|
const nextNewJD = nextMoonPhaseJD(jd, 0);
|
||||||
const nextFullJD = nextMoonPhaseJD(jd, 2);
|
const nextFullJD = nextMoonPhaseJD(jd, 2);
|
||||||
document.getElementById("nextNew").textContent = dateFromJD(nextNewJD).toLocaleDateString("en-US", { month: "short", day: "numeric", timeZone: "UTC" });
|
const nextNewDate = dateFromJD(nextNewJD);
|
||||||
document.getElementById("nextFull").textContent = dateFromJD(nextFullJD).toLocaleDateString("en-US", { month: "short", day: "numeric", timeZone: "UTC" });
|
const nextFullDate = dateFromJD(nextFullJD);
|
||||||
|
const nextNewSeth = toSethDate(getDayOfYearUTC(nextNewDate).year, getDayOfYearUTC(nextNewDate).doy);
|
||||||
|
const nextFullSeth = toSethDate(getDayOfYearUTC(nextFullDate).year, getDayOfYearUTC(nextFullDate).doy);
|
||||||
|
document.getElementById("nextNewSeth").textContent = sethLog(nextNewSeth);
|
||||||
|
document.getElementById("nextNewGreg").textContent = nextNewDate.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric", timeZone: "UTC" });
|
||||||
|
document.getElementById("nextFullSeth").textContent = sethLog(nextFullSeth);
|
||||||
|
document.getElementById("nextFullGreg").textContent = nextFullDate.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric", timeZone: "UTC" });
|
||||||
|
|
||||||
// Sun
|
// Sun
|
||||||
const sun = sunriseSunset(date, LAT, LON);
|
const sun = sunriseSunset(date, LAT, LON);
|
||||||
document.getElementById("sunrise").textContent = sun.rise || (sun.alwaysUp ? "Midnight sun" : "Below horizon");
|
document.getElementById("sunriseSeth").textContent = sun.riseJD ? jdToDecimalTime(sun.riseJD) : "—";
|
||||||
document.getElementById("sunset").textContent = sun.set || (sun.alwaysUp ? "Midnight sun" : "Below horizon");
|
document.getElementById("sunriseGreg").textContent = sun.rise || (sun.alwaysUp ? "Midnight sun" : "Below horizon");
|
||||||
document.getElementById("solarNoon").textContent = sun.noon;
|
document.getElementById("sunsetSeth").textContent = sun.setJD ? jdToDecimalTime(sun.setJD) : "—";
|
||||||
document.getElementById("dayLen").textContent = sun.rise && sun.set ? dayLengthStr(sun.riseJD, sun.setJD) : "—";
|
document.getElementById("sunsetGreg").textContent = sun.set || (sun.alwaysUp ? "Midnight sun" : "Below horizon");
|
||||||
|
document.getElementById("solarNoonSeth").textContent = sun.noonJD ? jdToDecimalTime(sun.noonJD) : "—";
|
||||||
|
document.getElementById("solarNoonGreg").textContent = sun.noon;
|
||||||
|
document.getElementById("dayLenSeth").textContent = sun.rise && sun.set ? dayLengthDecimalStr(sun.riseJD, sun.setJD) : "—";
|
||||||
|
document.getElementById("dayLenGreg").textContent = sun.rise && sun.set ? dayLengthStr(sun.riseJD, sun.setJD) : "—";
|
||||||
|
document.getElementById("moonAgeSeth").textContent = `${(age * (10 / 29.53058867)).toFixed(2)} days`;
|
||||||
|
document.getElementById("moonAgeGreg").textContent = `${age.toFixed(1)} days`;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -290,9 +350,12 @@ function render() {
|
|||||||
ul.innerHTML = "";
|
ul.innerHTML = "";
|
||||||
allEvents.forEach(ev => {
|
allEvents.forEach(ev => {
|
||||||
const li = document.createElement("li");
|
const li = document.createElement("li");
|
||||||
const ds = ev.date.toLocaleDateString("en-US", { month: "short", day: "numeric", timeZone: "UTC" });
|
const ds = ev.date.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric", timeZone: "UTC" });
|
||||||
|
const sy = getDayOfYearUTC(ev.date);
|
||||||
|
const sseth = toSethDate(sy.year, sy.doy);
|
||||||
|
const sds = sethLog(sseth);
|
||||||
const isToday = dateToParam(ev.date) === dateToParam(date);
|
const isToday = dateToParam(ev.date) === dateToParam(date);
|
||||||
li.innerHTML = `<span class="event-name"${isToday ? ' style="color:var(--accent)"' : ""}>${ev.name}</span><span>${ds}</span>`;
|
li.innerHTML = `<span class="event-name"${isToday ? ' style="color:var(--accent)"' : ""}>${ev.name}</span><span class="event-dual"><span>${sds}</span><span>${ds}</span></span>`;
|
||||||
ul.appendChild(li);
|
ul.appendChild(li);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user