Initial commit — Seth Calendar & Decimal Time clock site
Pages: /, /simple, /decimal, /seth, /calendar, /astro, /convert, /timegov Features: Seth Calendar (10×36 + holidays), decimal time, moon phases, astronomy (sun/moon), bidirectional time converter, Seth date display, leap day split cell in calendar grid.
This commit is contained in:
+149
@@ -0,0 +1,149 @@
|
||||
// Seth Time Converter
|
||||
// Decimal time: 10 hours/day, 100 minutes/hour, 100 seconds/minute, 100 centiseconds/second
|
||||
// Gregorian: 24 hours/day, 60 minutes/hour, 60 seconds/minute, 100 centiseconds/second
|
||||
//
|
||||
// Conversion: day fraction is the common unit
|
||||
// Gregorian -> fraction: (H*3600 + M*60 + S + cs/100) / 86400
|
||||
// Decimal -> fraction: (H*10000 + M*100 + S + cs/100) / 100000
|
||||
|
||||
const MS_PER_DAY = 86400000;
|
||||
|
||||
function gregToFraction(h, m, s, cs) {
|
||||
return (h * 3600 + m * 60 + s + cs / 100) / 86400;
|
||||
}
|
||||
|
||||
function decToFraction(h, m, s, cs) {
|
||||
return (h * 10000 + m * 100 + s + cs / 100) / 100000;
|
||||
}
|
||||
|
||||
function fractionToGreg(f) {
|
||||
f = ((f % 1) + 1) % 1; // clamp 0..1
|
||||
const totalCs = Math.round(f * 86400 * 100);
|
||||
const cs = totalCs % 100;
|
||||
const totalS = Math.floor(totalCs / 100);
|
||||
const s = totalS % 60;
|
||||
const totalM = Math.floor(totalS / 60);
|
||||
const m = totalM % 60;
|
||||
const h = Math.floor(totalM / 60) % 24;
|
||||
return { h, m, s, cs };
|
||||
}
|
||||
|
||||
function fractionToDec(f) {
|
||||
f = ((f % 1) + 1) % 1;
|
||||
const totalCs = Math.round(f * 100000 * 100);
|
||||
const cs = totalCs % 100;
|
||||
const totalS = Math.floor(totalCs / 100);
|
||||
const s = totalS % 100;
|
||||
const totalM = Math.floor(totalS / 100);
|
||||
const m = totalM % 100;
|
||||
const h = Math.floor(totalM / 100) % 10;
|
||||
return { h, m, s, cs };
|
||||
}
|
||||
|
||||
function pad(n, w) { return String(n).padStart(w, "0"); }
|
||||
|
||||
// DOM refs
|
||||
const gH = document.getElementById("gH");
|
||||
const gM = document.getElementById("gM");
|
||||
const gS = document.getElementById("gS");
|
||||
const gCs = document.getElementById("gCs");
|
||||
const dH = document.getElementById("dH");
|
||||
const dM = document.getElementById("dM");
|
||||
const dS = document.getElementById("dS");
|
||||
const dCs = document.getElementById("dCs");
|
||||
const gregFrac = document.getElementById("gregFrac");
|
||||
const decFrac = document.getElementById("decFrac");
|
||||
|
||||
let updating = false; // prevent feedback loops
|
||||
|
||||
function setGreg(h, m, s, cs) {
|
||||
gH.value = pad(h, 2);
|
||||
gM.value = pad(m, 2);
|
||||
gS.value = pad(s, 2);
|
||||
gCs.value = pad(cs, 2);
|
||||
}
|
||||
|
||||
function setDec(h, m, s, cs) {
|
||||
dH.value = h;
|
||||
dM.value = pad(m, 2);
|
||||
dS.value = pad(s, 2);
|
||||
dCs.value = pad(cs, 2);
|
||||
}
|
||||
|
||||
function updateFromGreg() {
|
||||
if (updating) return;
|
||||
updating = true;
|
||||
const h = parseInt(gH.value) || 0;
|
||||
const m = parseInt(gM.value) || 0;
|
||||
const s = parseInt(gS.value) || 0;
|
||||
const cs = parseInt(gCs.value) || 0;
|
||||
const f = gregToFraction(h, m, s, cs);
|
||||
const dec = fractionToDec(f);
|
||||
setDec(dec.h, dec.m, dec.s, dec.cs);
|
||||
gregFrac.textContent = `day fraction: ${f.toFixed(8)}`;
|
||||
decFrac.textContent = `day fraction: ${f.toFixed(8)}`;
|
||||
updating = false;
|
||||
}
|
||||
|
||||
function updateFromDec() {
|
||||
if (updating) return;
|
||||
updating = true;
|
||||
const h = parseInt(dH.value) || 0;
|
||||
const m = parseInt(dM.value) || 0;
|
||||
const s = parseInt(dS.value) || 0;
|
||||
const cs = parseInt(dCs.value) || 0;
|
||||
const f = decToFraction(h, m, s, cs);
|
||||
const greg = fractionToGreg(f);
|
||||
setGreg(greg.h, greg.m, greg.s, greg.cs);
|
||||
gregFrac.textContent = `day fraction: ${f.toFixed(8)}`;
|
||||
decFrac.textContent = `day fraction: ${f.toFixed(8)}`;
|
||||
updating = false;
|
||||
}
|
||||
|
||||
// Clamp inputs on change
|
||||
function clampInput(el, min, max) {
|
||||
el.addEventListener("change", () => {
|
||||
let v = parseInt(el.value);
|
||||
if (isNaN(v)) v = min;
|
||||
el.value = Math.max(min, Math.min(max, v));
|
||||
});
|
||||
}
|
||||
clampInput(gH, 0, 23); clampInput(gM, 0, 59);
|
||||
clampInput(gS, 0, 59); clampInput(gCs, 0, 99);
|
||||
clampInput(dH, 0, 9); clampInput(dM, 0, 99);
|
||||
clampInput(dS, 0, 99); clampInput(dCs, 0, 99);
|
||||
|
||||
// Tab through fields on Enter or arrow keys within a panel
|
||||
function setupNavigation(inputs) {
|
||||
inputs.forEach((el, i) => {
|
||||
el.addEventListener("keydown", e => {
|
||||
if (e.key === "ArrowRight" || e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
inputs[(i + 1) % inputs.length].focus();
|
||||
inputs[(i + 1) % inputs.length].select();
|
||||
}
|
||||
if (e.key === "ArrowLeft") {
|
||||
e.preventDefault();
|
||||
inputs[(i - 1 + inputs.length) % inputs.length].focus();
|
||||
inputs[(i - 1 + inputs.length) % inputs.length].select();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
setupNavigation([gH, gM, gS, gCs]);
|
||||
setupNavigation([dH, dM, dS, dCs]);
|
||||
|
||||
[gH, gM, gS, gCs].forEach(el => el.addEventListener("input", updateFromGreg));
|
||||
[dH, dM, dS, dCs].forEach(el => el.addEventListener("input", updateFromDec));
|
||||
|
||||
// Now button — fill with current time and convert
|
||||
document.getElementById("nowBtn").addEventListener("click", () => {
|
||||
const now = new Date();
|
||||
setGreg(now.getHours(), now.getMinutes(), now.getSeconds(), Math.floor(now.getMilliseconds() / 10));
|
||||
updateFromGreg();
|
||||
});
|
||||
|
||||
// Init with current time
|
||||
const now = new Date();
|
||||
setGreg(now.getHours(), now.getMinutes(), now.getSeconds(), Math.floor(now.getMilliseconds() / 10));
|
||||
updateFromGreg();
|
||||
Reference in New Issue
Block a user