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:
2026-03-08 22:32:38 +00:00
commit a6b3f039d8
48 changed files with 7324 additions and 0 deletions
+4
View File
@@ -0,0 +1,4 @@
__pycache__/
*.pyc
*.pyo
.env
+91
View File
@@ -0,0 +1,91 @@
const zoneSelect = document.getElementById("zoneSelect");
const zoneLabel = document.getElementById("zoneLabel");
const offsetLabel = document.getElementById("offsetLabel");
const utcLabel = document.getElementById("utcLabel");
const dateLine = document.getElementById("dateLine");
const digital = document.getElementById("digital");
const zones = [
"America/New_York",
"America/Chicago",
"America/Denver",
"America/Los_Angeles",
"America/Anchorage",
"Pacific/Honolulu",
"UTC"
];
let serverOffsetMs = 0;
let selectedZone = Intl.DateTimeFormat().resolvedOptions().timeZone || "America/New_York";
if (!zones.includes(selectedZone)) {
selectedZone = "America/New_York";
}
for (const zone of zones) {
const option = document.createElement("option");
option.value = zone;
option.textContent = zone;
if (zone === selectedZone) {
option.selected = true;
}
zoneSelect.appendChild(option);
}
zoneSelect.addEventListener("change", () => {
selectedZone = zoneSelect.value;
zoneLabel.textContent = selectedZone;
});
async function syncWithServer() {
const start = Date.now();
try {
const response = await fetch(`/api/time?ts=${start}`, { cache: "no-store" });
const end = Date.now();
const data = await response.json();
const rtt = end - start;
const estimatedServerAtEnd = data.epoch_ms + rtt / 2;
serverOffsetMs = estimatedServerAtEnd - end;
offsetLabel.textContent = `Synchronized (${serverOffsetMs >= 0 ? "+" : ""}${serverOffsetMs.toFixed(0)} ms, RTT ${rtt} ms)`;
} catch (_error) {
offsetLabel.textContent = "Sync failed";
}
}
function formatParts(date, zone) {
const formatter = new Intl.DateTimeFormat("en-US", {
timeZone: zone,
hour12: false,
year: "numeric",
month: "long",
day: "2-digit",
weekday: "long",
hour: "2-digit",
minute: "2-digit",
second: "2-digit"
});
return formatter.formatToParts(date).reduce((accumulator, part) => {
if (part.type !== "literal") {
accumulator[part.type] = part.value;
}
return accumulator;
}, {});
}
function render() {
const now = new Date(Date.now() + serverOffsetMs);
const parts = formatParts(now, selectedZone);
const centiseconds = String(Math.floor(now.getMilliseconds() / 10)).padStart(2, "0");
dateLine.textContent = `${parts.weekday}, ${parts.month} ${parts.day}, ${parts.year}`;
digital.textContent = `${parts.hour}:${parts.minute}:${parts.second}.${centiseconds}`;
zoneLabel.textContent = selectedZone;
utcLabel.textContent = now.toISOString().replace("T", " ").replace("Z", " UTC");
requestAnimationFrame(render);
}
syncWithServer();
setInterval(syncWithServer, 20000);
render();
+139
View File
@@ -0,0 +1,139 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>SethPC Astronomy</title>
<link rel="icon" type="image/png" href="https://storage.googleapis.com/sethfreiberg.com/sethflix/favicon.png">
<link rel="stylesheet" href="/style.css">
<style>
.astro-wrap {
width: min(540px, 100%);
background: var(--panel);
border: 1px solid var(--border);
border-radius: 12px;
padding: clamp(1rem, 2.5vw, 2rem);
box-shadow: 0 10px 28px rgb(0 0 0 / 42%);
}
.moon-display {
text-align: center;
margin: 1.5rem 0 1rem;
}
.moon-glyph {
font-size: 5rem;
line-height: 1;
display: block;
}
.moon-name {
font-size: 1.1rem;
font-weight: 700;
margin-top: 0.5rem;
color: var(--text);
}
.moon-pct {
font-size: 0.85rem;
color: var(--muted);
margin-top: 0.2rem;
}
.astro-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 0.4rem 1rem;
font-size: 0.92rem;
color: var(--muted);
margin: 1.2rem 0;
}
.astro-grid p { margin: 0; }
.astro-grid strong { color: var(--text); }
.date-nav {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.5rem;
margin-bottom: 1.2rem;
}
.date-nav h2 {
margin: 0;
font-size: clamp(1rem, 2.5vw, 1.3rem);
text-align: center;
flex: 1;
}
.nav-btn {
background: #2a2a2a;
border: 1px solid var(--border);
border-radius: 8px;
color: var(--text);
padding: 0.4rem 0.8rem;
cursor: pointer;
font-size: 1rem;
line-height: 1;
transition: border-color 0.15s;
}
.nav-btn:hover { border-color: var(--accent); color: var(--accent); }
.section-head {
font-size: 0.75rem;
color: var(--accent);
font-weight: 600;
letter-spacing: 0.06em;
text-transform: uppercase;
margin: 1.2rem 0 0.4rem;
border-bottom: 1px solid var(--border);
padding-bottom: 0.2rem;
}
.event-list {
list-style: none;
padding: 0;
margin: 0;
font-size: 0.88rem;
color: var(--muted);
}
.event-list li {
padding: 0.2rem 0;
border-bottom: 1px solid #2a2a2a;
display: flex;
justify-content: space-between;
gap: 0.5rem;
}
.event-list li:last-child { border-bottom: none; }
.event-name { color: var(--text); }
</style>
</head>
<body>
<main class="astro-wrap">
<div class="brand-row">
<img class="logo-img" src="https://storage.googleapis.com/sethfreiberg.com/sethflix/favicon.png" alt="SethPC logo">
<span class="brand-text">SethPC Astronomy</span>
</div>
<div class="date-nav">
<button class="nav-btn" id="prevDay">&#8249;</button>
<h2 id="dateTitle"></h2>
<button class="nav-btn" id="nextDay">&#8250;</button>
</div>
<div class="moon-display">
<span class="moon-glyph" id="moonGlyph">🌑</span>
<div class="moon-name" id="moonName"></div>
<div class="moon-pct" id="moonPct"></div>
</div>
<div class="section-head">Sun</div>
<div class="astro-grid">
<p><strong>Sunrise:</strong> <span id="sunrise"></span></p>
<p><strong>Sunset:</strong> <span id="sunset"></span></p>
<p><strong>Day length:</strong> <span id="dayLen"></span></p>
<p><strong>Solar noon:</strong> <span id="solarNoon"></span></p>
</div>
<div class="section-head">Moon</div>
<div class="astro-grid">
<p><strong>Age:</strong> <span id="moonAge"></span></p>
<p><strong>Next new moon:</strong> <span id="nextNew"></span></p>
<p><strong>Next full moon:</strong> <span id="nextFull"></span></p>
</div>
<ul class="event-list" id="yearEvents" style="margin-top:1rem;"></ul>
</main>
<script src="/astro.js"></script>
</body>
</html>
+309
View File
@@ -0,0 +1,309 @@
// Astronomical calculations for SethPC Astronomy page
// Moon phase: Jean Meeus "Astronomical Algorithms" simplified
// Sun rise/set: NOAA solar calculator algorithm
// Solstice/equinox: Meeus Table 27.a
// ── Helpers ──────────────────────────────────────────────────────────────────
const DEG = Math.PI / 180;
const RAD = 180 / Math.PI;
function frac(x) { return x - Math.floor(x); }
function mod360(x) { return ((x % 360) + 360) % 360; }
// Julian Day Number from calendar date (noon UT)
function julianDay(year, month, day) {
if (month <= 2) { year--; month += 12; }
const A = Math.floor(year / 100);
const B = 2 - A + Math.floor(A / 4);
return Math.floor(365.25 * (year + 4716)) + Math.floor(30.6001 * (month + 1)) + day + B - 1524.5;
}
function jdFromDate(date) {
return julianDay(date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate()) + date.getUTCHours() / 24 + date.getUTCMinutes() / 1440;
}
function dateFromJD(jd) {
const z = Math.floor(jd + 0.5);
const f = jd + 0.5 - z;
let A = z;
if (z >= 2299161) {
const alpha = Math.floor((z - 1867216.25) / 36524.25);
A = z + 1 + alpha - Math.floor(alpha / 4);
}
const B = A + 1524;
const C = Math.floor((B - 122.1) / 365.25);
const D = Math.floor(365.25 * C);
const E = Math.floor((B - D) / 30.6001);
const day = B - D - Math.floor(30.6001 * E) + f;
const month = E < 14 ? E - 1 : E - 13;
const year = month > 2 ? C - 4716 : C - 4715;
return new Date(Date.UTC(year, month - 1, Math.floor(day)));
}
// ── Moon phase ────────────────────────────────────────────────────────────────
// Returns moon age in days (0 = new moon, ~14.77 = full moon, ~29.53 = back to new)
// and illumination fraction (01)
function moonPhase(date) {
// Reference new moon: Jan 6, 2000 18:14 UTC (JD 2451549.756)
const KNOWN_NEW_MOON_JD = 2451549.756;
const SYNODIC_MONTH = 29.53058867;
const jd = jdFromDate(date) + 0.5; // use noon of that day
const daysSinceNew = jd - KNOWN_NEW_MOON_JD;
const cycles = daysSinceNew / SYNODIC_MONTH;
const age = frac(cycles) * SYNODIC_MONTH; // 0..29.53
// Illumination: cos curve, 0 at new, 1 at full
const illumination = (1 - Math.cos(2 * Math.PI * age / SYNODIC_MONTH)) / 2;
return { age, illumination, cycles };
}
function moonPhaseName(age) {
const s = age / 29.53058867;
if (s < 0.025 || s >= 0.975) return "New Moon";
if (s < 0.25) return "Waxing Crescent";
if (s < 0.275) return "First Quarter";
if (s < 0.475) return "Waxing Gibbous";
if (s < 0.525) return "Full Moon";
if (s < 0.725) return "Waning Gibbous";
if (s < 0.75) return "Last Quarter";
return "Waning Crescent";
}
function moonPhaseGlyph(age) {
const s = age / 29.53058867;
if (s < 0.025 || s >= 0.975) return "🌑";
if (s < 0.25) return "🌒";
if (s < 0.275) return "🌓";
if (s < 0.475) return "🌔";
if (s < 0.525) return "🌕";
if (s < 0.725) return "🌖";
if (s < 0.75) return "🌗";
return "🌘";
}
// Find JD of next phase after given JD: phase 0=new,1=first,2=full,3=last
function nextMoonPhaseJD(jd, phase) {
const SYNODIC_MONTH = 29.53058867;
const KNOWN_NEW_MOON_JD = 2451549.756;
const daysSince = jd - KNOWN_NEW_MOON_JD;
const cycles = daysSince / SYNODIC_MONTH;
const phaseFrac = phase / 4;
let n = Math.floor(cycles - phaseFrac) + phaseFrac;
let targetJD = KNOWN_NEW_MOON_JD + n * SYNODIC_MONTH;
if (targetJD <= jd) targetJD += SYNODIC_MONTH;
return targetJD;
}
// ── Sun rise/set (NOAA algorithm) ────────────────────────────────────────────
function sunriseSunset(date, lat, lon) {
const jd = julianDay(date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate());
const n = jd - 2451545.0 + 0.0008;
const Js = n - lon / 360;
const M = mod360(357.5291 + 0.98560028 * Js);
const C = 1.9148 * Math.sin(M * DEG) + 0.0200 * Math.sin(2 * M * DEG) + 0.0003 * Math.sin(3 * M * DEG);
const lam = mod360(M + C + 180 + 102.9372);
const Jt = 2451545.0 + Js + 0.0053 * Math.sin(M * DEG) - 0.0069 * Math.sin(2 * lam * DEG);
const sinDec = Math.sin(lam * DEG) * Math.sin(23.4397 * DEG);
const cosHa = (Math.sin(-0.833 * DEG) - Math.sin(lat * DEG) * sinDec) / (Math.cos(lat * DEG) * Math.cos(Math.asin(sinDec)));
if (cosHa < -1) return { rise: null, set: null, noon: jdToTime(Jt), alwaysUp: true };
if (cosHa > 1) return { rise: null, set: null, noon: jdToTime(Jt), alwaysDown: true };
const Ha = Math.acos(cosHa) * RAD / 360;
const Jrise = Jt - Ha;
const Jset = Jt + Ha;
return {
rise: jdToTime(Jrise),
set: jdToTime(Jset),
noon: jdToTime(Jt),
riseJD: Jrise,
setJD: Jset,
noonJD: Jt,
};
}
// Convert fractional JD to UTC HH:MM string
function jdToTime(jd) {
const totalMin = Math.round(((jd + 0.5) % 1) * 1440);
const h = Math.floor(totalMin / 60) % 24;
const m = totalMin % 60;
return `${String(h).padStart(2,"0")}:${String(m).padStart(2,"0")} UTC`;
}
function dayLengthStr(riseJD, setJD) {
const mins = Math.round((setJD - riseJD) * 1440);
const h = Math.floor(mins / 60);
const m = mins % 60;
return `${h}h ${String(m).padStart(2,"0")}m`;
}
// ── Solstice / Equinox (Meeus Table 27.a + correction) ───────────────────────
// Returns approximate JD for each event in a given year
// season: 0=March equinox, 1=June solstice, 2=September equinox, 3=December solstice
function seasonJD(year, season) {
const Y = (year - 2000) / 1000;
const JDE0s = [
// March equinox
2451623.80984 + 365242.37404*Y + 0.05169*Y*Y - 0.00411*Y*Y*Y - 0.00057*Y*Y*Y*Y,
// June solstice
2451716.56767 + 365241.62603*Y + 0.00325*Y*Y + 0.00888*Y*Y*Y - 0.00030*Y*Y*Y*Y,
// September equinox
2451810.05917 + 365242.01767*Y - 0.11575*Y*Y + 0.00337*Y*Y*Y + 0.00078*Y*Y*Y*Y,
// December solstice
2451900.05952 + 365242.74049*Y - 0.06223*Y*Y - 0.00823*Y*Y*Y + 0.00032*Y*Y*Y*Y,
];
return JDE0s[season];
}
function seasonEvents(year) {
const names = ["March Equinox", "June Solstice", "September Equinox", "December Solstice"];
const emojis = ["🌱", "☀️", "🍂", "❄️"];
return names.map((name, i) => {
const jd = seasonJD(year, i);
const d = dateFromJD(jd);
return { name, emoji: emojis[i], date: d, month: d.getUTCMonth() + 1, day: d.getUTCDate() };
});
}
// ── Moonrise / Moonset (simplified — horizon crossing estimate) ───────────────
// Uses a simple approximation: moon moves ~13.18°/day, rises ~50min later each day.
// Good enough for a display page; full computation needs perturbation terms.
function moonriseApprox(date, lat, lon) {
// Approximate: moon rises ~50 min later each day than previous
// Anchor: use sun rise/set as rough guide, offset by moon's daily retardation
// For a simple display we just note this is approximate
return null; // placeholder — show "~" note instead
}
// ── DOM ───────────────────────────────────────────────────────────────────────
// Parse ?date=YYYY-MM-DD from URL, default to today
function parseDate() {
const params = new URLSearchParams(window.location.search);
const ds = params.get("date");
if (ds) {
const [y, m, d] = ds.split("-").map(Number);
if (y && m && d) return new Date(Date.UTC(y, m - 1, d));
}
const now = new Date();
return new Date(Date.UTC(now.getFullYear(), now.getMonth(), now.getDate()));
}
function dateToParam(date) {
const y = date.getUTCFullYear();
const m = String(date.getUTCMonth() + 1).padStart(2, "0");
const d = String(date.getUTCDate()).padStart(2, "0");
return `${y}-${m}-${d}`;
}
function stepDate(date, days) {
const d = new Date(date);
d.setUTCDate(d.getUTCDate() + days);
return d;
}
function formatDate(date) {
return date.toLocaleDateString("en-US", { weekday: "long", year: "numeric", month: "long", day: "numeric", timeZone: "UTC" });
}
// Default lat/lon: roughly central US (no geolocation, just reasonable default)
// Will use user's timezone offset to pick a reasonable longitude
function guessLon() {
const offsetHours = -new Date().getTimezoneOffset() / 60;
return offsetHours * 15; // rough: UTC offset * 15° per hour
}
const LAT = 40.0; // roughly mid-US latitude
const LON = guessLon();
let currentDate = parseDate();
function render() {
const date = currentDate;
// Update URL without reload
const newUrl = `${window.location.pathname}?date=${dateToParam(date)}`;
history.replaceState(null, "", newUrl);
document.getElementById("dateTitle").textContent = formatDate(date);
// Moon
const { age, illumination } = moonPhase(date);
document.getElementById("moonGlyph").textContent = moonPhaseGlyph(age);
document.getElementById("moonName").textContent = moonPhaseName(age);
document.getElementById("moonPct").textContent =
`${Math.round(illumination * 100)}% illuminated · ${age.toFixed(1)} days old`;
document.getElementById("moonAge").textContent = `${age.toFixed(1)} days`;
// Next new & full
const jd = jdFromDate(date);
const nextNewJD = nextMoonPhaseJD(jd, 0);
const nextFullJD = nextMoonPhaseJD(jd, 2);
document.getElementById("nextNew").textContent = dateFromJD(nextNewJD).toLocaleDateString("en-US", { month: "short", day: "numeric", timeZone: "UTC" });
document.getElementById("nextFull").textContent = dateFromJD(nextFullJD).toLocaleDateString("en-US", { month: "short", day: "numeric", timeZone: "UTC" });
// Sun
const sun = sunriseSunset(date, LAT, LON);
document.getElementById("sunrise").textContent = sun.rise || (sun.alwaysUp ? "Midnight sun" : "Below horizon");
document.getElementById("sunset").textContent = sun.set || (sun.alwaysUp ? "Midnight sun" : "Below horizon");
document.getElementById("solarNoon").textContent = sun.noon;
document.getElementById("dayLen").textContent = sun.rise && sun.set ? dayLengthStr(sun.riseJD, sun.setJD) : "—";
// Year events
const year = date.getUTCFullYear();
const events = seasonEvents(year);
const SYNODIC = 29.53058867;
const KNOWN_NEW_MOON_JD = 2451549.756;
// Add major moon phases for the year
const moonEvents = [];
let testJD = julianDay(year, 1, 1);
const endJD = julianDay(year, 12, 31);
for (let phase = 0; phase < 4; phase++) {
let pJD = nextMoonPhaseJD(testJD - SYNODIC, phase);
while (pJD <= endJD) {
const d = dateFromJD(pJD);
if (d.getUTCFullYear() === year) {
const phaseNames = ["🌑 New Moon", "🌓 First Quarter", "🌕 Full Moon", "🌗 Last Quarter"];
moonEvents.push({ name: phaseNames[phase], date: d, jd: pJD });
}
pJD += SYNODIC;
}
}
moonEvents.sort((a, b) => a.jd - b.jd);
const allEvents = [
...events.map(e => ({ name: `${e.emoji} ${e.name}`, date: e.date, jd: jdFromDate(e.date) })),
...moonEvents,
].sort((a, b) => a.jd - b.jd);
const ul = document.getElementById("yearEvents");
ul.innerHTML = "";
allEvents.forEach(ev => {
const li = document.createElement("li");
const ds = ev.date.toLocaleDateString("en-US", { month: "short", day: "numeric", timeZone: "UTC" });
const isToday = dateToParam(ev.date) === dateToParam(date);
li.innerHTML = `<span class="event-name"${isToday ? ' style="color:var(--accent)"' : ""}>${ev.name}</span><span>${ds}</span>`;
ul.appendChild(li);
});
}
document.getElementById("prevDay").addEventListener("click", () => {
currentDate = stepDate(currentDate, -1);
render();
});
document.getElementById("nextDay").addEventListener("click", () => {
currentDate = stepDate(currentDate, +1);
render();
});
render();
+290
View File
@@ -0,0 +1,290 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>SethPC Calendar</title>
<link rel="icon" type="image/png" href="https://storage.googleapis.com/sethfreiberg.com/sethflix/favicon.png">
<link rel="stylesheet" href="/style.css">
<style>
.cal-wrap {
width: min(860px, 100%);
background: var(--panel);
border: 1px solid var(--border);
border-radius: 12px;
padding: clamp(1rem, 2.5vw, 2rem);
box-shadow: 0 10px 28px rgb(0 0 0 / 42%);
}
/* Nav row */
.cal-nav {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 1.2rem;
gap: 0.5rem;
}
.cal-nav h2 {
margin: 0;
font-size: clamp(1rem, 2.5vw, 1.4rem);
text-align: center;
flex: 1;
}
.cal-nav .sub {
font-size: 0.78rem;
color: var(--muted);
font-weight: 400;
display: block;
}
.nav-btn {
background: #2a2a2a;
border: 1px solid var(--border);
border-radius: 8px;
color: var(--text);
padding: 0.4rem 0.8rem;
cursor: pointer;
font-size: 1rem;
line-height: 1;
transition: border-color 0.15s;
}
.nav-btn:hover { border-color: var(--accent); color: var(--accent); }
/* Month tabs */
.month-tabs {
display: flex;
flex-wrap: wrap;
gap: 0.3rem;
margin-bottom: 1rem;
}
.month-tab {
background: #2a2a2a;
border: 1px solid var(--border);
border-radius: 6px;
color: var(--muted);
padding: 0.25rem 0.6rem;
cursor: pointer;
font-size: 0.82rem;
transition: all 0.15s;
}
.month-tab:hover { border-color: var(--accent); color: var(--accent); }
.month-tab.active {
background: var(--accent);
border-color: var(--accent);
color: #fff;
}
.month-tab.holiday-tab {
font-style: italic;
}
/* Grid */
.cal-grid {
display: grid;
grid-template-columns: repeat(6, 1fr);
gap: 4px;
margin-bottom: 1rem;
}
.cal-grid.holiday-grid {
grid-template-columns: repeat(3, 1fr);
}
.col-header {
text-align: center;
font-size: 0.72rem;
color: var(--accent);
font-weight: 600;
letter-spacing: 0.05em;
padding: 0.2rem 0;
text-transform: uppercase;
}
.cal-cell {
background: #2a2a2a;
border: 1px solid var(--border);
border-radius: 6px;
padding: 0.35rem 0.4rem;
min-height: 56px;
cursor: default;
transition: border-color 0.15s;
position: relative;
}
.cal-cell:hover { border-color: #555; }
.cal-cell.today {
border-color: var(--accent);
background: #2e1f15;
}
.cal-cell.holiday-cell {
grid-column: span 1;
background: #1e1e2e;
border-color: #444;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 70px;
}
.seth-day {
font-size: 1.1rem;
font-weight: 700;
color: var(--text);
line-height: 1;
}
.greg-date {
font-size: 0.7rem;
color: var(--muted);
margin-top: 0.25rem;
line-height: 1.2;
}
.holiday-name {
font-size: 0.75rem;
color: var(--muted);
margin-top: 0.2rem;
}
.moon-link {
position: absolute;
top: 3px;
right: 4px;
opacity: 0.5;
text-decoration: none;
transition: opacity 0.15s;
display: block;
line-height: 0;
}
.moon-link:hover { opacity: 1; }
.moon-link canvas { display: block; border-radius: 50%; }
.cal-cell.seth-weekend {
border-color: #1a2a4a;
background: #1c2130;
}
.cal-cell.greg-weekend {
border-color: #1a3a35;
background: #1c2a28;
}
.cal-cell.seth-weekend.greg-weekend {
border-color: #2a3a4a;
background: #1c2530;
}
.cal-cell.today.seth-weekend,
.cal-cell.today.greg-weekend {
border-color: var(--accent);
background: #2e1f15;
}
.cal-cell.has-holiday {
border-color: #5a4a2a;
}
/* The outer wrapper for the split cell: transparent, no own border/bg */
.cal-cell.has-leap-split {
padding: 0;
gap: 4px;
display: flex;
flex-direction: column;
background: transparent;
border-color: transparent;
box-shadow: none;
}
.cal-cell.has-leap-split:hover {
border-color: transparent;
}
/* Each half is its own independent mini-cell — same color as Seth weekend */
.leap-top,
.leap-bottom {
position: relative;
padding: 0.25rem 0.4rem;
flex: 1;
border: 1px solid #1a2a4a;
border-radius: 6px;
background: #1c2130;
min-height: 0;
overflow: hidden;
cursor: default;
transition: border-color 0.15s;
}
.leap-top:hover,
.leap-bottom:hover {
border-color: #555;
}
/* Today highlights on each half independently */
.leap-top.leap-top-today {
border-color: var(--accent);
background: #2e1f15;
}
.leap-bottom.leap-today {
border-color: var(--accent);
background: #1a2e40;
}
.hol-label {
font-size: 0.62rem;
color: #c8a060;
margin-top: 0.2rem;
line-height: 1.2;
white-space: normal;
overflow-wrap: break-word;
}
.hol-label a {
color: inherit;
text-decoration: underline;
text-underline-offset: 2px;
}
.hol-label a:hover {
color: #e0b878;
}
/* Legend */
.legend {
display: flex;
gap: 1rem;
font-size: 0.78rem;
color: var(--muted);
align-items: center;
flex-wrap: wrap;
}
.legend-dot {
width: 8px; height: 8px;
background: var(--accent);
border-radius: 50%;
display: inline-block;
margin-right: 4px;
}
.legend-box {
width: 12px; height: 12px;
background: #1e1e2e;
border: 1px solid #444;
border-radius: 3px;
display: inline-block;
margin-right: 4px;
}
</style>
</head>
<body>
<main class="cal-wrap">
<div class="brand-row">
<img class="logo-img" src="https://storage.googleapis.com/sethfreiberg.com/sethflix/favicon.png" alt="SethPC logo">
<span class="brand-text">SethPC Calendar</span>
</div>
<div class="cal-nav">
<button class="nav-btn" id="prevYear">&#8810;</button>
<button class="nav-btn" id="prevMonth">&#8249;</button>
<h2 id="navTitle">-<span class="sub" id="navSub"></span></h2>
<button class="nav-btn" id="nextMonth">&#8250;</button>
<button class="nav-btn" id="nextYear">&#8811;</button>
</div>
<div class="month-tabs" id="monthTabs"></div>
<div id="calGrid"></div>
<div class="legend">
<span><span class="legend-dot"></span><a id="todayLink" href="#" style="color:inherit;text-decoration:underline;text-underline-offset:2px;">Today</a></span>
<span><span class="legend-box"></span>Holiday days</span>
<span style="display:inline-flex;align-items:center;gap:4px;">
<span style="width:12px;height:12px;border-radius:3px;background:#1c2130;border:1px solid #1a2a4a;display:inline-block;"></span>Seth weekend (D4D5)
</span>
<span style="display:inline-flex;align-items:center;gap:4px;">
<span style="width:12px;height:12px;border-radius:3px;background:#1c2a28;border:1px solid #1a3a35;display:inline-block;"></span>Gregorian weekend
</span>
<span style="display:inline-flex;align-items:center;gap:4px;">
<canvas id="legendMoon" width="12" height="12" style="border-radius:50%;vertical-align:middle;"></canvas>Moon phase (click for astronomy)
</span>
</div>
</main>
<script src="/calendar.js"></script>
</body>
</html>
+648
View File
@@ -0,0 +1,648 @@
// Seth Calendar — calendar view
//
// 10 months × 36 days (6 weeks × 6 days), then 5 or 6 holiday days.
// Jan 1 = Month 1 Day 1. Same year numbers as Gregorian.
// ── Moon phase rendering ──────────────────────────────────────────────────────
const KNOWN_NEW_MOON_JD = 2451549.756; // Jan 6, 2000 18:14 UTC
const SYNODIC_MONTH = 29.53058867;
function jdFromCalDate(year, month, day) {
if (month <= 2) { year--; month += 12; }
const A = Math.floor(year / 100);
const B = 2 - A + Math.floor(A / 4);
return Math.floor(365.25 * (year + 4716)) + Math.floor(30.6001 * (month + 1)) + day + B - 1524.5;
}
// Returns { age (029.53), illumination (01), waxing (bool) }
function moonPhaseForDate(jsDate) {
const jd = jdFromCalDate(jsDate.getFullYear(), jsDate.getMonth() + 1, jsDate.getDate()) + 0.5;
const daysSince = jd - KNOWN_NEW_MOON_JD;
const cycles = daysSince / SYNODIC_MONTH;
const age = (cycles - Math.floor(cycles)) * SYNODIC_MONTH;
const illumination = (1 - Math.cos(2 * Math.PI * age / SYNODIC_MONTH)) / 2;
const waxing = age < SYNODIC_MONTH / 2;
return { age, illumination, waxing };
}
// Draw a moon phase into a canvas element (size x size pixels)
function drawMoon(canvas, illumination, waxing) {
const size = canvas.width;
const r = size / 2 - 0.5;
const cx = size / 2;
const cy = size / 2;
const ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, size, size);
// Dark side color
const dark = "#1a1a2e";
// Light side color
const light = "#e8dfc0";
// Draw full dark circle first
ctx.beginPath();
ctx.arc(cx, cy, r, 0, 2 * Math.PI);
ctx.fillStyle = dark;
ctx.fill();
// The terminator: lit fraction maps to an ellipse x-scale
// illumination 0 = new (all dark), 0.5 = quarter (semi-circle), 1 = full (all light)
// Ellipse x-radius: at 0.5 it's 0, at 0 or 1 it's r
// Phase angle: 0=new, π/2=first quarter, π=full, 3π/2=last quarter
const phaseAngle = (age => 2 * Math.PI * age / SYNODIC_MONTH)(
waxing ? Math.acos(1 - 2 * illumination) * SYNODIC_MONTH / (2 * Math.PI)
: (SYNODIC_MONTH / 2) + Math.acos(2 * illumination - 1) * SYNODIC_MONTH / (2 * Math.PI)
);
// Simpler direct approach: draw lit half-circle + terminator ellipse
// Lit side: right if waxing, left if waning
// x-scale of terminator ellipse: cos(phase_angle) where angle 0=new, π=full
const angle = 2 * Math.PI * (waxing
? illumination < 0.5 ? illumination : illumination
: illumination);
// Direct: ellipse terminator x-width = (1 - 2*illumination)*r for waxing,
// (2*illumination - 1)*r for waning
const termX = Math.abs(1 - 2 * illumination) * r;
const litOnRight = waxing;
// Draw lit semicircle on the appropriate side
ctx.save();
ctx.beginPath();
ctx.arc(cx, cy, r, -Math.PI / 2, Math.PI / 2, !litOnRight);
ctx.fillStyle = light;
ctx.fill();
ctx.restore();
// Draw terminator ellipse to carve/extend into the lit side
ctx.save();
ctx.beginPath();
// Clip to the dark half
ctx.arc(cx, cy, r + 1, -Math.PI / 2, Math.PI / 2, litOnRight);
ctx.lineTo(cx, cy - r - 1);
ctx.closePath();
ctx.clip();
// Fill terminator ellipse (same as lit color if < half, dark if > half)
ctx.beginPath();
ctx.ellipse(cx, cy, termX, r, 0, 0, 2 * Math.PI);
ctx.fillStyle = illumination < 0.5 ? dark : light;
ctx.fill();
ctx.restore();
// Thin border
ctx.beginPath();
ctx.arc(cx, cy, r, 0, 2 * Math.PI);
ctx.strokeStyle = "rgba(255,255,255,0.15)";
ctx.lineWidth = 0.5;
ctx.stroke();
}
function makeMoonCanvas(jsDate, size) {
const { illumination, waxing } = moonPhaseForDate(jsDate);
const canvas = document.createElement("canvas");
canvas.width = size;
canvas.height = size;
canvas.style.width = size + "px";
canvas.style.height = size + "px";
drawMoon(canvas, illumination, waxing);
return canvas;
}
const GREG_MONTHS = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
const GREG_WEEKDAYS = ["Su","M","T","W","Tr","F","S"];
const WEEK_DAY_LABELS = ["D0","D1","D2","D3","D4","D5"];
// --- Holidays ---
// Fixed: { month, day, name, emoji }
// Floating: computed per year via buildFloatingHolidays(year)
const FIXED_HOLIDAYS = [
{ month: 1, day: 1, name: "New Year's Day", emoji: "🎆", url: "https://en.wikipedia.org/wiki/New_Year%27s_Day" },
{ month: 2, day: 13, name: "1 1 1 Day", emoji: "1️⃣" },
{ month: 3, day: 28, name: "2 2 2 Day", emoji: "2️⃣" },
{ month: 5, day: 10, name: "3 3 3 Day", emoji: "3️⃣" },
{ month: 6, day: 22, name: "4 4 4 Day", emoji: "4️⃣" },
{ month: 8, day: 4, name: "5 5 5 Day", emoji: "5️⃣" },
{ month: 4, day: 20, name: "420", emoji: "🌿", url: "https://en.wikipedia.org/wiki/420_(cannabis_culture)" },
{ month: 6, day: 6, name: "4 2 0 Day", emoji: "🌿" },
{ month: 7, day: 10, name: "710", emoji: "🍯", url: "https://weedmaps.com/learn/cannabis-and-its-evolution/everything-you-need-to-know-about-710" },
{ month: 9, day: 16, name: "7 1 0 Day", emoji: "🍯" },
{ month: 1, day: 2, name: "Science Fiction Day", emoji: "🚀", url: "https://en.wikipedia.org/wiki/Isaac_Asimov" },
{ month: 1, day: 3, name: "Festival of Sleep Day", emoji: "😴" },
{ month: 1, day: 16, name: "Nothing Day", emoji: "🕳️", url: "https://en.wikipedia.org/wiki/National_Nothing_Day" },
{ month: 1, day: 20, name: "Penguin Awareness Day", emoji: "🐧", url: "https://en.wikipedia.org/wiki/Penguin" },
{ month: 1, day: 21, name: "Squirrel Appreciation Day", emoji: "🐿️", url: "https://en.wikipedia.org/wiki/Squirrel" },
{ month: 1, day: 25, name: "Opposite Day", emoji: "🙃", url: "https://en.wikipedia.org/wiki/Opposite_Day" },
{ month: 1, day: 27, name: "Chocolate Cake Day", emoji: "🎂", url: "https://en.wikipedia.org/wiki/Chocolate_cake" },
{ month: 1, day: 27, name: "e-Day", emoji: "📐", url: "https://en.wikipedia.org/wiki/E_(mathematical_constant)" },
{ month: 1, day: 31, name: "Backwards Day", emoji: "🔄" },
{ month: 2, day: 2, name: "Groundhog Day", emoji: "🦔", url: "https://en.wikipedia.org/wiki/Groundhog_Day" },
{ month: 2, day: 6, name: "Work Naked Day", emoji: "😳" },
{ month: 2, day: 7, name: "Eat Ice Cream for Breakfast Day", emoji: "🍦" },
{ month: 2, day: 12, name: "Darwin Day", emoji: "🐒", url: "https://en.wikipedia.org/wiki/Darwin_Day" },
{ month: 2, day: 14, name: "Valentine's Day", emoji: "❤️", url: "https://en.wikipedia.org/wiki/Valentine%27s_Day" },
{ month: 2, day: 17, name: "Random Act of Kindness Day", emoji: "🤝", url: "https://en.wikipedia.org/wiki/Random_act_of_kindness" },
{ month: 2, day: 22, name: "Be Humble Day", emoji: "🙇" },
{ month: 2, day: 28, name: "Public Sleeping Day", emoji: "💤" },
{ month: 3, day: 4, name: "March Forth and Do Something Day", emoji: "🚶" },
{ month: 3, day: 9, name: "Napping Day", emoji: "😪", url: "https://en.wikipedia.org/wiki/Napping" },
{ month: 3, day: 10, name: "Mario Day", emoji: "🍄", url: "https://en.wikipedia.org/wiki/Mario_Day" },
{ month: 3, day: 14, name: "Pi Day", emoji: "🥧", url: "https://en.wikipedia.org/wiki/Pi_Day" },
{ month: 3, day: 15, name: "Everything You Think is Wrong Day", emoji: "🤔" },
{ month: 3, day: 17, name: "St. Patrick's Day", emoji: "🍀", url: "https://en.wikipedia.org/wiki/Saint_Patrick%27s_Day" },
{ month: 3, day: 22, name: "International Goof Off Day", emoji: "🤪" },
{ month: 3, day: 23, name: "Puppy Day", emoji: "🐶", url: "https://en.wikipedia.org/wiki/Dog" },
{ month: 3, day: 26, name: "Make Up Your Own Holiday Day", emoji: "📅" },
{ month: 4, day: 1, name: "April Fools' Day", emoji: "🃏", url: "https://en.wikipedia.org/wiki/April_Fools%27_Day" },
{ month: 4, day: 5, name: "First Contact Day", emoji: "👽", url: "https://en.wikipedia.org/wiki/Star_Trek:_First_Contact" },
{ month: 4, day: 12, name: "Grilled Cheese Day", emoji: "🧀", url: "https://en.wikipedia.org/wiki/Grilled_cheese" },
{ month: 4, day: 12, name: "Yuri's Night", emoji: "🛸", url: "https://en.wikipedia.org/wiki/Yuri%27s_Night" },
{ month: 4, day: 16, name: "Wear Pajamas to Work Day", emoji: "🛌" },
{ month: 4, day: 22, name: "Jelly Bean Day", emoji: "🫘", url: "https://en.wikipedia.org/wiki/Jelly_bean" },
{ month: 4, day: 23, name: "Impossible Astronaut Day", emoji: "🕐", url: "https://en.wikipedia.org/wiki/The_Impossible_Astronaut" },
{ month: 5, day: 1, name: "No Pants Day", emoji: "👖", url: "https://en.wikipedia.org/wiki/No_Pants_Day" },
{ month: 5, day: 4, name: "Star Wars Day", emoji: "⚔️", url: "https://en.wikipedia.org/wiki/Star_Wars_Day" },
{ month: 5, day: 5, name: "Cinco de Mayo", emoji: "🌮", url: "https://en.wikipedia.org/wiki/Cinco_de_Mayo" },
{ month: 5, day: 9, name: "Lost Sock Memorial Day", emoji: "🧦" },
{ month: 5, day: 11, name: "Eat What You Want Day", emoji: "🍕" },
{ month: 5, day: 21, name: "Talk Like Yoda Day", emoji: "🟢", url: "https://en.wikipedia.org/wiki/Yoda" },
{ month: 5, day: 25, name: "Towel Day", emoji: "🏖️", url: "https://en.wikipedia.org/wiki/Towel_Day" },
{ month: 5, day: 29, name: "Put a Pillow on Your Fridge Day", emoji: "🛋️" },
{ month: 6, day: 3, name: "Repeat Day", emoji: "🔁", url: "https://en.wikipedia.org/wiki/Repetition" },
{ month: 6, day: 3, name: "Repeat Day", emoji: "🔁", url: "https://en.wikipedia.org/wiki/Repetition" },
{ month: 6, day: 4, name: "Hug Your Cat Day", emoji: "🐱", url: "https://en.wikipedia.org/wiki/Cat" },
{ month: 6, day: 18, name: "International Panic Day", emoji: "😱" },
{ month: 6, day: 19, name: "Juneteenth", emoji: "✊", url: "https://en.wikipedia.org/wiki/Juneteenth" },
{ month: 6, day: 22, name: "Onion Ring Day", emoji: "🧅", url: "https://en.wikipedia.org/wiki/Onion_ring" },
{ month: 6, day: 26, name: "Seth's Birthday!", emoji: "🎂" },
{ month: 6, day: 28, name: "Tau Day", emoji: "📐", url: "https://en.wikipedia.org/wiki/Turn_(angle)" },
{ month: 7, day: 1, name: "International Joke Day", emoji: "😂", url: "https://en.wikipedia.org/wiki/Joke" },
{ month: 7, day: 2, name: "World UFO Day", emoji: "🛸", url: "https://en.wikipedia.org/wiki/World_UFO_Day" },
{ month: 7, day: 4, name: "Independence Day", emoji: "🎇", url: "https://en.wikipedia.org/wiki/Independence_Day_(United_States)" },
{ month: 7, day: 4, name: "Sidewalk Egg Frying Day", emoji: "🍳", url: "https://en.wikipedia.org/wiki/Frying_an_egg_on_the_sidewalk" },
{ month: 7, day: 11, name: "Cheer Up the Lonely Day", emoji: "🤗" },
{ month: 7, day: 13, name: "Embrace Your Geekness Day", emoji: "🤓" },
{ month: 7, day: 14, name: "Pandemonium Day", emoji: "🌀", url: "https://en.wikipedia.org/wiki/Pandemonium" },
{ month: 7, day: 17, name: "World Emoji Day", emoji: "😄", url: "https://en.wikipedia.org/wiki/World_Emoji_Day" },
{ month: 7, day: 19, name: "Stick Out Your Tongue Day", emoji: "👅" },
{ month: 7, day: 22, name: "Pi Approximation Day", emoji: "≈", url: "https://en.wikipedia.org/wiki/Pi_Approximation_Day" },
{ month: 7, day: 27, name: "Take Your Pants for a Walk Day", emoji: "🚶" },
{ month: 8, day: 2, name: "Ice Cream Sandwich Day", emoji: "🍨", url: "https://en.wikipedia.org/wiki/Ice_cream_sandwich" },
{ month: 8, day: 10, name: "Lazy Day", emoji: "🛋️", url: "https://en.wikipedia.org/wiki/Laziness" },
{ month: 8, day: 12, name: "Middle Child Day", emoji: "😐", url: "https://en.wikipedia.org/wiki/Middle_child_syndrome" },
{ month: 8, day: 13, name: "Left-Handers Day", emoji: "🤚", url: "https://en.wikipedia.org/wiki/Left-Handers_Day" },
{ month: 8, day: 15, name: "Relaxation Day", emoji: "🧘" },
{ month: 8, day: 16, name: "Tell a Joke Day", emoji: "🤣" },
{ month: 8, day: 24, name: "Pluto Demoted Day", emoji: "🔭", url: "https://en.wikipedia.org/wiki/IAU_definition_of_planet" },
{ month: 8, day: 24, name: "National Waffle Day", emoji: "🧇", url: "https://en.wikipedia.org/wiki/Waffle" },
{ month: 8, day: 30, name: "Frankenstein Day", emoji: "⚡", url: "https://en.wikipedia.org/wiki/Frankenstein" },
{ month: 9, day: 5, name: "Be Late for Something Day", emoji: "⏰" },
{ month: 9, day: 6, name: "Fight Procrastination Day", emoji: "✅", url: "https://en.wikipedia.org/wiki/Procrastination" },
{ month: 9, day: 13, name: "Blame Someone Else Day", emoji: "👉" },
{ month: 9, day: 13, name: "Fortune Cookie Day", emoji: "🥠", url: "https://en.wikipedia.org/wiki/Fortune_cookie" },
{ month: 9, day: 19, name: "Talk Like a Pirate Day", emoji: "🏴‍☠️", url: "https://en.wikipedia.org/wiki/International_Talk_Like_a_Pirate_Day" },
{ month: 9, day: 22, name: "Elephant Appreciation Day", emoji: "🐘", url: "https://en.wikipedia.org/wiki/Elephant" },
{ month: 9, day: 28, name: "Ask a Stupid Question Day", emoji: "❓" },
{ month: 10, day: 4, name: "National Taco Day", emoji: "🌮", url: "https://en.wikipedia.org/wiki/Taco" },
{ month: 10, day: 12, name: "Moment of Frustration Day", emoji: "😤" },
{ month: 10, day: 14, name: "Dessert Day", emoji: "🍮", url: "https://en.wikipedia.org/wiki/Dessert" },
{ month: 10, day: 21, name: "Back to the Future Day", emoji: "⏱️", url: "https://en.wikipedia.org/wiki/Back_to_the_Future_Day" },
{ month: 10, day: 23, name: "Mole Day", emoji: "🦡", url: "https://en.wikipedia.org/wiki/Mole_Day" },
{ month: 10, day: 31, name: "Halloween", emoji: "🎃", url: "https://en.wikipedia.org/wiki/Halloween" },
{ month: 11, day: 8, name: "Cook Something Bold Day", emoji: "🔥" },
{ month: 11, day: 11, name: "Veterans Day", emoji: "🎖️", url: "https://en.wikipedia.org/wiki/Veterans_Day" },
{ month: 11, day: 13, name: "World Kindness Day", emoji: "💛", url: "https://en.wikipedia.org/wiki/World_Kindness_Day" },
{ month: 11, day: 17, name: "Take a Hike Day", emoji: "🥾" },
{ month: 11, day: 19, name: "Have a Bad Day Day", emoji: "😞" },
{ month: 12, day: 12, name: "Gingerbread House Day", emoji: "🏠", url: "https://en.wikipedia.org/wiki/Gingerbread_house" },
{ month: 12, day: 21, name: "Crossword Puzzle Day", emoji: "📝", url: "https://en.wikipedia.org/wiki/Crossword" },
{ month: 12, day: 25, name: "Christmas", emoji: "🎄", url: "https://en.wikipedia.org/wiki/Christmas" },
{ month: 12, day: 30, name: "Bacon Day", emoji: "🥓", url: "https://en.wikipedia.org/wiki/Bacon" },
{ month: 12, day: 31, name: "New Year's Eve", emoji: "🥂", url: "https://en.wikipedia.org/wiki/New_Year%27s_Eve" },
];
function nthWeekday(year, month, n, weekday) {
// n: 1=first, -1=last. weekday: 0=Mon..6=Sun
const base = new Date(year, month - 1, 1);
if (n > 0) {
const diff = (weekday - base.getDay() + 7) % 7;
base.setDate(1 + diff + (n - 1) * 7);
} else {
const lastDay = new Date(year, month, 0).getDate();
base.setDate(lastDay);
const diff = (base.getDay() - weekday + 7) % 7;
base.setDate(lastDay - diff);
}
return base;
}
function easterDate(year) {
// Anonymous Gregorian algorithm
const a = year % 19, b = Math.floor(year / 100), c = year % 100;
const d = Math.floor(b / 4), e = b % 4, f = Math.floor((b + 8) / 25);
const g = Math.floor((b - f + 1) / 3), h = (19*a + b - d - g + 15) % 30;
const i = Math.floor(c / 4), k = c % 4;
const l = (32 + 2*e + 2*i - h - k) % 7;
const m = Math.floor((a + 11*h + 22*l) / 451);
const month = Math.floor((h + l - 7*m + 114) / 31);
const day = ((h + l - 7*m + 114) % 31) + 1;
return new Date(year, month - 1, day);
}
// Approximate JD of solstice/equinox (Meeus Table 27.a)
// season: 0=March equinox, 1=June solstice, 2=September equinox, 3=December solstice
function seasonJD(year, season) {
const Y = (year - 2000) / 1000;
const jde = [
2451623.80984 + 365242.37404*Y + 0.05169*Y*Y - 0.00411*Y*Y*Y - 0.00057*Y*Y*Y*Y,
2451716.56767 + 365241.62603*Y + 0.00325*Y*Y + 0.00888*Y*Y*Y - 0.00030*Y*Y*Y*Y,
2451810.05917 + 365242.01767*Y - 0.11575*Y*Y + 0.00337*Y*Y*Y + 0.00078*Y*Y*Y*Y,
2451900.05952 + 365242.74049*Y - 0.06223*Y*Y - 0.00823*Y*Y*Y + 0.00032*Y*Y*Y*Y,
][season];
// Convert JD to JS Date (JD 2440587.5 = Unix epoch)
const ms = (jde - 2440587.5) * 86400000;
return new Date(ms);
}
function buildFloatingHolidays(year) {
const results = [];
const add = (date, name, emoji, url) => {
results.push({ month: date.getMonth() + 1, day: date.getDate(), name, emoji, url });
};
add(nthWeekday(year, 1, 3, 1), "MLK Day", "✊", "https://en.wikipedia.org/wiki/Martin_Luther_King_Jr._Day");
add(nthWeekday(year, 2, 3, 1), "Presidents Day", "🏛️", "https://en.wikipedia.org/wiki/Presidents%27_Day");
add(nthWeekday(year, 5, -1, 1), "Memorial Day", "🎖️", "https://en.wikipedia.org/wiki/Memorial_Day");
add(nthWeekday(year, 9, 1, 1), "Labor Day", "🔨", "https://en.wikipedia.org/wiki/Labor_Day");
add(nthWeekday(year, 10, 2, 1), "Columbus Day", "⛵", "https://en.wikipedia.org/wiki/Columbus_Day");
add(nthWeekday(year, 11, 4, 4), "Thanksgiving", "🦃", "https://en.wikipedia.org/wiki/Thanksgiving_(United_States)");
add(easterDate(year), "Easter", "🐣", "https://en.wikipedia.org/wiki/Easter");
const shrove = new Date(easterDate(year));
shrove.setDate(shrove.getDate() - 47);
add(shrove, "Pancake Day", "🥞", "https://en.wikipedia.org/wiki/Shrove_Tuesday");
// Solstices & equinoxes
add(seasonJD(year, 0), "March Equinox", "🌱", "https://en.wikipedia.org/wiki/March_equinox");
add(seasonJD(year, 1), "June Solstice", "☀️", "https://en.wikipedia.org/wiki/June_solstice");
add(seasonJD(year, 2), "September Equinox", "🍂", "https://en.wikipedia.org/wiki/September_equinox");
add(seasonJD(year, 3), "December Solstice", "❄️", "https://en.wikipedia.org/wiki/December_solstice");
return results;
}
// Build a DOY-keyed map of holidays for a given year
function buildHolidayMap(year) {
const map = {};
const cum = [0,31,59,90,120,151,181,212,243,273,304,334];
const leap = isLeapYear(year);
const addHol = ({ month, day, name, emoji, url }) => {
if (month === 2 && day === 29 && !leap) return;
let doy = cum[month - 1] + day;
if (leap && month > 2) doy++;
if (!map[doy]) map[doy] = [];
map[doy].push({ name, emoji, url });
};
FIXED_HOLIDAYS.forEach(addHol);
buildFloatingHolidays(year).forEach(addHol);
return map;
}
// --- Seth calendar helpers ---
function isLeapYear(y) {
return (y % 4 === 0 && y % 100 !== 0) || y % 400 === 0;
}
// Returns { year, doy } for a given JS Date (local time)
function getDayOfYear(date) {
const y = date.getFullYear();
const m = date.getMonth() + 1;
const d = date.getDate();
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 };
}
// Returns a JS Date for day-of-year `doy` in `year`
function dateFromDoy(year, doy) {
return new Date(year, 0, doy); // Jan 0 + doy = doy-th day
}
// Gregorian month/day string for a doy
function gregLabel(year, doy) {
const d = dateFromDoy(year, doy);
return `${GREG_MONTHS[d.getMonth()]} ${d.getDate()}`;
}
// Holiday names (0-indexed: n = 0..4 or 0..5)
function holidayName(n, leap) {
const lastIdx = leap ? 5 : 4;
const names = [
leap ? "Holiday 0 — Boxing Day" : "Holiday 0",
"Holiday 1",
"Holiday 2",
"Holiday 3",
leap ? "Holiday 4" : "Holiday 4 — New Year's Eve",
"Holiday 5 — New Year's Eve", // leap only
];
return names[n] || `Holiday ${n}`;
}
// --- State ---
const today = new Date();
const todayDoy = getDayOfYear(today);
let viewYear = todayDoy.year;
let holidayMap = buildHolidayMap(viewYear);
let viewMonth = (() => {
// Start on today's Seth month (or holiday block)
const doy = todayDoy.doy;
return doy <= 360 ? Math.floor((doy - 1) / 36) : 10; // 10 = holidays
})();
// --- DOM ---
const navTitle = document.getElementById("navTitle");
const navSub = document.getElementById("navSub");
const calGrid = document.getElementById("calGrid");
const monthTabs = document.getElementById("monthTabs");
document.getElementById("prevYear").addEventListener("click", () => { viewYear--; holidayMap = buildHolidayMap(viewYear); render(); });
document.getElementById("nextYear").addEventListener("click", () => { viewYear++; holidayMap = buildHolidayMap(viewYear); render(); });
document.getElementById("prevMonth").addEventListener("click", () => { stepMonth(-1); render(); });
document.getElementById("nextMonth").addEventListener("click", () => { stepMonth(+1); render(); });
function stepMonth(dir) {
viewMonth += dir;
if (viewMonth < 0) { viewMonth = 10; viewYear--; holidayMap = buildHolidayMap(viewYear); }
if (viewMonth > 10) { viewMonth = 0; viewYear++; holidayMap = buildHolidayMap(viewYear); }
}
// --- Render ---
function render() {
renderTabs();
if (viewMonth <= 9) {
renderMonth(viewYear, viewMonth);
} else {
renderHolidays(viewYear);
}
}
function renderTabs() {
monthTabs.innerHTML = "";
for (let m = 0; m <= 9; m++) {
const tab = document.createElement("button");
tab.className = "month-tab" + (m === viewMonth ? " active" : "");
tab.textContent = `Month ${m}`;
tab.addEventListener("click", () => { viewMonth = m; render(); });
monthTabs.appendChild(tab);
}
const htab = document.createElement("button");
htab.className = "month-tab holiday-tab" + (viewMonth === 10 ? " active" : "");
htab.textContent = "Holidays";
htab.addEventListener("click", () => { viewMonth = 10; render(); });
monthTabs.appendChild(htab);
}
function renderMonth(year, month) {
const leap = isLeapYear(year);
const monthStart = month * 36 + 1; // doy of Month N Day 0 (N=0..9)
// Approx gregorian range for subtitle
const gStart = gregLabel(year, monthStart);
const gEnd = gregLabel(year, monthStart + 35);
navTitle.childNodes[0].textContent = `${year} · Month ${month}`;
navSub.textContent = `${gStart} ${gEnd}`;
// Build grid
const grid = document.createElement("div");
grid.className = "cal-grid";
// Column headers: D0 D1 D2 D3 D4 D5
for (const lbl of WEEK_DAY_LABELS) {
const h = document.createElement("div");
h.className = "col-header";
h.textContent = lbl;
grid.appendChild(h);
}
// In a leap year, M1 W3 D4 is split: top=Feb28(DOY59), bottom=LeapDay(DOY60)
// All cells from dayInMonth>=23 (W3 D5 onward) get doy+1 to skip over leap day
const isLeapM1 = leap && month === 1;
for (let week = 0; week <= 5; week++) {
for (let wd = 0; wd <= 5; wd++) {
const dayInMonth = week * 6 + wd; // 0..35
// In leap M1, dayInMonth 22 = D4 W3 = Feb28/LeapDay split cell
// dayInMonth 23+ shift by +1 to account for leap day
const doy = monthStart + dayInMonth + (isLeapM1 && dayInMonth >= 23 ? 1 : 0);
const isLeapSplit = isLeapM1 && dayInMonth === 22; // the split cell
const cell = document.createElement("div");
cell.className = "cal-cell";
let topHalf = null; // only set for split cells; used by holiday marker logic below
// Weekend coloring
if (wd === 4 || wd === 5) cell.classList.add("seth-weekend");
// For the split cell, gregDate is Feb 28 (the top half)
const gregDate = dateFromDoy(year, doy);
const gregWd = GREG_WEEKDAYS[gregDate.getDay()];
const gregDay = gregDate.getDay(); // 0=Sun, 6=Sat
if (gregDay === 0 || gregDay === 6) cell.classList.add("greg-weekend");
// Highlight today (today could be Feb 28 or Feb 29 in this cell)
const leapDoyTop = doy; // Feb 28
const leapDoyBot = doy + 1; // Feb 29 (leap day)
const isToday = year === todayDoy.year &&
(todayDoy.doy === doy || (isLeapSplit && todayDoy.doy === leapDoyBot));
if (isToday) cell.classList.add("today");
const astroParam = `${gregDate.getFullYear()}-${String(gregDate.getMonth()+1).padStart(2,"0")}-${String(gregDate.getDate()).padStart(2,"0")}`;
if (isLeapSplit) {
// Split cell: transparent outer wrapper containing two independent mini-cells
cell.classList.add("has-leap-split");
// Remove border/bg classes — the outer cell is invisible; halves handle their own styling
cell.classList.remove("seth-weekend", "greg-weekend", "today");
// ── Top half: normal D4 (Feb 28), Seth weekend ──
topHalf = document.createElement("div");
topHalf.className = "leap-top";
const isTopToday = year === todayDoy.year && todayDoy.doy === doy;
if (isTopToday) topHalf.classList.add("leap-top-today");
const topMoonLink = document.createElement("a");
topMoonLink.className = "moon-link";
topMoonLink.href = `/astro?date=${astroParam}`;
topMoonLink.title = `Astronomy for ${gregLabel(year, doy)}`;
topMoonLink.appendChild(makeMoonCanvas(gregDate, 14));
topHalf.appendChild(topMoonLink);
const topNum = document.createElement("div");
topNum.className = "seth-day";
topNum.textContent = dayInMonth;
const topGreg = document.createElement("div");
topGreg.className = "greg-date";
topGreg.textContent = `${gregWd} ${gregLabel(year, doy)}`;
const topWd = document.createElement("div");
topWd.className = "greg-date";
topWd.style.color = "#555";
topWd.textContent = `W${week} D${wd}`;
topHalf.appendChild(topNum);
topHalf.appendChild(topGreg);
topHalf.appendChild(topWd);
cell.appendChild(topHalf);
// ── Bottom half: Leap Day (Feb 29), its own independent mini-cell ──
const botHalf = document.createElement("div");
botHalf.className = "leap-bottom";
const isLeapToday = year === todayDoy.year && todayDoy.doy === 60;
if (isLeapToday) botHalf.classList.add("leap-today");
const leapDate = new Date(year, 1, 29); // Feb 29
const leapAstroParam = `${year}-02-29`;
const botMoonLink = document.createElement("a");
botMoonLink.className = "moon-link";
botMoonLink.href = `/astro?date=${leapAstroParam}`;
botMoonLink.title = "Astronomy for Feb 29";
botMoonLink.appendChild(makeMoonCanvas(leapDate, 14));
botHalf.appendChild(botMoonLink);
const botNum = document.createElement("div");
botNum.className = "seth-day";
botNum.textContent = "Leap Day";
const botGreg = document.createElement("div");
botGreg.className = "greg-date";
const leapGregWd = GREG_WEEKDAYS[leapDate.getDay()];
botGreg.textContent = `${leapGregWd} Feb 29`;
botHalf.appendChild(botNum);
botHalf.appendChild(botGreg);
cell.appendChild(botHalf);
} else {
// Normal cell
const moonLink = document.createElement("a");
moonLink.className = "moon-link";
moonLink.href = `/astro?date=${astroParam}`;
moonLink.title = `Astronomy for ${gregLabel(year, doy)}`;
moonLink.appendChild(makeMoonCanvas(gregDate, 14));
cell.appendChild(moonLink);
const dayNum = document.createElement("div");
dayNum.className = "seth-day";
dayNum.textContent = dayInMonth;
const greg = document.createElement("div");
greg.className = "greg-date";
greg.textContent = `${gregWd} ${gregLabel(year, doy)}`;
const wdLabel = document.createElement("div");
wdLabel.className = "greg-date";
wdLabel.style.color = "#555";
wdLabel.textContent = `W${week} D${wd}`;
cell.appendChild(dayNum);
cell.appendChild(greg);
cell.appendChild(wdLabel);
}
// Holiday markers — for split cells, attach to topHalf (Feb 28); otherwise to cell
const holTarget = isLeapSplit ? topHalf : cell;
const hols = holidayMap[doy];
if (hols) {
holTarget.classList.add("has-holiday");
hols.forEach(({ name, emoji, url }) => {
const hl = document.createElement("div");
hl.className = "hol-label";
if (url) {
const a = document.createElement("a");
a.href = url;
a.target = "_blank";
a.rel = "noopener noreferrer";
a.textContent = `${emoji} ${name}`;
hl.appendChild(a);
} else {
hl.textContent = `${emoji} ${name}`;
}
holTarget.appendChild(hl);
});
}
grid.appendChild(cell);
}
}
calGrid.innerHTML = "";
calGrid.appendChild(grid);
}
function renderHolidays(year) {
const leap = isLeapYear(year);
const count = leap ? 6 : 5;
const doyBase = 361; // H0 = doy 361
navTitle.childNodes[0].textContent = `${year} · Holidays`;
navSub.textContent = `${gregLabel(year, doyBase)} ${gregLabel(year, doyBase + count - 1)}`;
const grid = document.createElement("div");
grid.className = "cal-grid holiday-grid";
for (let h = 0; h < count; h++) {
const doy = doyBase + h;
const isToday = year === todayDoy.year && doy === todayDoy.doy;
const cell = document.createElement("div");
cell.className = "cal-cell holiday-cell";
if (isToday) cell.classList.add("today");
const gregDate = dateFromDoy(year, doy);
const gregWd = GREG_WEEKDAYS[gregDate.getDay()];
const astroParam = `${gregDate.getFullYear()}-${String(gregDate.getMonth()+1).padStart(2,"0")}-${String(gregDate.getDate()).padStart(2,"0")}`;
const moonLink = document.createElement("a");
moonLink.className = "moon-link";
moonLink.href = `/astro?date=${astroParam}`;
moonLink.title = `Astronomy for ${gregLabel(year, doy)}`;
moonLink.appendChild(makeMoonCanvas(gregDate, 14));
cell.appendChild(moonLink);
const hNum = document.createElement("div");
hNum.className = "seth-day";
hNum.textContent = `H${h}`;
const greg = document.createElement("div");
greg.className = "greg-date";
greg.textContent = `${gregWd} ${gregLabel(year, doy)}`;
const name = document.createElement("div");
name.className = "holiday-name";
name.textContent = holidayName(h, leap);
cell.appendChild(hNum);
cell.appendChild(greg);
cell.appendChild(name);
grid.appendChild(cell);
}
calGrid.innerHTML = "";
calGrid.appendChild(grid);
}
render();
// "Today" legend link — jumps back to today's month/year
document.getElementById("todayLink").addEventListener("click", e => {
e.preventDefault();
viewYear = todayDoy.year;
viewMonth = todayDoy.doy <= 360 ? Math.floor((todayDoy.doy - 1) / 36) : 10;
holidayMap = buildHolidayMap(viewYear);
render();
});
// Draw sample moon in legend (waxing gibbous ~0.7 illumination)
const legendMoon = document.getElementById("legendMoon");
if (legendMoon) drawMoon(legendMoon, 0.7, true);
+14
View File
@@ -0,0 +1,14 @@
[Unit]
Description=Clock site web service
After=network.target
[Service]
Type=simple
WorkingDirectory=/opt/clock-site
ExecStart=/usr/bin/python3 /opt/clock-site/server.py
Restart=always
RestartSec=2
User=root
[Install]
WantedBy=multi-user.target
+155
View File
@@ -0,0 +1,155 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>SethPC Time Converter</title>
<link rel="icon" type="image/png" href="https://storage.googleapis.com/sethfreiberg.com/sethflix/favicon.png">
<link rel="stylesheet" href="/style.css">
<style>
.convert-wrap {
width: min(520px, 100%);
background: var(--panel);
border: 1px solid var(--border);
border-radius: 12px;
padding: clamp(1rem, 2.5vw, 2rem);
box-shadow: 0 10px 28px rgb(0 0 0 / 42%);
}
.converter {
display: flex;
flex-direction: column;
gap: 1.5rem;
margin-top: 1.5rem;
}
.conv-panel {
background: #222;
border: 1px solid var(--border);
border-radius: 8px;
padding: 1rem 1.1rem;
}
.conv-label {
font-size: 0.72rem;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--accent);
margin-bottom: 0.6rem;
}
.time-inputs {
display: flex;
align-items: center;
gap: 0.3rem;
font-family: "JetBrains Mono", monospace;
font-size: 1.6rem;
}
.time-inputs input {
background: transparent;
border: none;
border-bottom: 2px solid var(--border);
color: var(--text);
font-family: inherit;
font-size: inherit;
text-align: center;
outline: none;
transition: border-color 0.15s;
-moz-appearance: textfield;
}
.time-inputs input::-webkit-outer-spin-button,
.time-inputs input::-webkit-inner-spin-button { -webkit-appearance: none; }
.time-inputs input:focus { border-color: var(--accent); }
.time-inputs input.w2 { width: 2.2ch; }
.time-inputs input.w3 { width: 2.6ch; }
.time-inputs .sep { color: var(--muted); user-select: none; }
.conv-sub {
font-size: 0.78rem;
color: var(--muted);
margin-top: 0.5rem;
font-family: "JetBrains Mono", monospace;
min-height: 1.2em;
}
.arrow {
text-align: center;
font-size: 1.2rem;
color: var(--muted);
}
.now-btn {
display: block;
margin: 0 auto;
background: #2a2a2a;
border: 1px solid var(--border);
border-radius: 6px;
color: var(--muted);
padding: 0.3rem 1rem;
cursor: pointer;
font-size: 0.82rem;
transition: all 0.15s;
}
.now-btn:hover { border-color: var(--accent); color: var(--accent); }
.ratio-note {
font-size: 0.75rem;
color: var(--muted);
text-align: center;
margin-top: 1rem;
line-height: 1.5;
}
</style>
</head>
<body>
<main class="convert-wrap">
<div class="brand-row">
<img class="logo-img" src="https://storage.googleapis.com/sethfreiberg.com/sethflix/favicon.png" alt="SethPC logo">
<span class="brand-text">Time Converter</span>
</div>
<p class="kicker">Decimal &harr; Gregorian &nbsp;·&nbsp; <a href="/seth" style="color:var(--accent);text-decoration:none;font-size:0.85rem;">Seth Date &rarr;</a></p>
<button class="now-btn" id="nowBtn">Use current time</button>
<div class="converter">
<div class="conv-panel" id="gregPanel">
<div class="conv-label">Gregorian (24h)</div>
<div class="time-inputs">
<input id="gH" class="w2" type="number" min="0" max="23" placeholder="HH">
<span class="sep">:</span>
<input id="gM" class="w2" type="number" min="0" max="59" placeholder="MM">
<span class="sep">:</span>
<input id="gS" class="w2" type="number" min="0" max="59" placeholder="SS">
<span class="sep">.</span>
<input id="gCs" class="w2" type="number" min="0" max="99" placeholder="cs">
</div>
<div class="conv-sub" id="gregFrac"></div>
</div>
<div class="arrow"></div>
<div class="conv-panel" id="decPanel">
<div class="conv-label">Decimal (Seth)</div>
<div class="time-inputs">
<input id="dH" class="w2" type="number" min="0" max="9" placeholder="H">
<span class="sep">:</span>
<input id="dM" class="w2" type="number" min="0" max="99" placeholder="MM">
<span class="sep">:</span>
<input id="dS" class="w2" type="number" min="0" max="99" placeholder="SS">
<span class="sep">.</span>
<input id="dCs" class="w3" type="number" min="0" max="99" placeholder="cs">
</div>
<div class="conv-sub" id="decFrac"></div>
</div>
</div>
<p class="ratio-note">
1 decimal hour = 2h 24m Gregorian &nbsp;·&nbsp;
1 decimal second = 0.864 SI seconds
</p>
</main>
<script src="/convert.js"></script>
</body>
</html>
+149
View File
@@ -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();
+142
View File
@@ -0,0 +1,142 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>SethPC Decimal Time</title>
<link rel="icon" type="image/png" href="https://storage.googleapis.com/sethfreiberg.com/sethflix/favicon.png">
<link rel="stylesheet" href="/style.css">
<style>
.decimal-note {
margin: 0 0 1rem;
font-size: 0.85rem;
color: var(--muted);
line-height: 1.5;
}
.decimal-note a {
color: var(--accent);
text-decoration: none;
}
.decimal-note a:hover {
text-decoration: underline;
}
.meta-grid .label-col {
color: var(--text);
}
.ref-table {
width: 100%;
border-collapse: collapse;
font-size: 0.82rem;
margin: 0.4rem 0 0.9rem;
}
.ref-table th {
text-align: left;
color: var(--accent);
border-bottom: 1px solid var(--border);
padding: 0.2rem 0.5rem 0.2rem 0;
font-weight: 600;
}
.ref-table td {
padding: 0.15rem 0.5rem 0.15rem 0;
color: var(--muted);
border-bottom: 1px solid #2a2a2a;
}
.ref-table td:first-child {
color: var(--text);
width: 2rem;
}
</style>
</head>
<body>
<main class="wrap">
<div class="brand-row">
<img class="logo-img" src="https://storage.googleapis.com/sethfreiberg.com/sethflix/favicon.png" alt="SethPC logo">
<span class="brand-text">SethPC Decimal Time</span>
</div>
<p class="kicker">French Republican / Decimal Time &amp; Date</p>
<h1 id="dateLine">Loading date...</h1>
<p id="dateTrans" class="decimal-note" style="margin:0 0 0.6rem; font-size:0.9rem;"></p>
<p id="digital" class="digital">-:--.--</p>
<div class="controls">
<label class="zone-picker" for="zoneSelect">Display Time Zone</label>
<select id="zoneSelect"></select>
</div>
<div class="decimal-note">
<p>
<strong>Decimal time</strong> divides the day into 10 hours, each hour into 100 minutes,
and each minute into 100 seconds. The time shown counts from local midnight in the selected zone.
</p>
<p>
The date uses the <strong>French Republican Calendar</strong>, introduced in 1793.
The year begins at the autumnal equinox (~22 September). Each year has 12 months of exactly
30 days, plus 5 complementary days at year end (6 in a leap year). Years are numbered in
Roman numerals from An&nbsp;I (1792).
</p>
<p><strong>Reading the date:</strong> dates are written as
<em>Weekday, Day Month An Year</em> &mdash; e.g. <em>Sextidi, 6 Ventôse An CCXXXIII</em>
means the 6th day of the month, in the 6th day of the décade (10-day week), in the month of Ventôse, year 233.
</p>
<p><strong>Months</strong> (each 30 days, grouped by season):</p>
<table class="ref-table">
<thead><tr><th>#</th><th>French</th><th>English</th><th>Approx. Gregorian</th></tr></thead>
<tbody>
<tr><td>1</td><td>Vendémiaire</td><td>Vintage</td><td>Sep 22 Oct 21</td></tr>
<tr><td>2</td><td>Brumaire</td><td>Mist</td><td>Oct 22 Nov 20</td></tr>
<tr><td>3</td><td>Frimaire</td><td>Frost</td><td>Nov 21 Dec 20</td></tr>
<tr><td>4</td><td>Nivôse</td><td>Snowy</td><td>Dec 21 Jan 19</td></tr>
<tr><td>5</td><td>Pluviôse</td><td>Rainy</td><td>Jan 20 Feb 18</td></tr>
<tr><td>6</td><td>Ventôse</td><td>Windy</td><td>Feb 19 Mar 20</td></tr>
<tr><td>7</td><td>Germinal</td><td>Budding</td><td>Mar 21 Apr 19</td></tr>
<tr><td>8</td><td>Floréal</td><td>Flowery</td><td>Apr 20 May 19</td></tr>
<tr><td>9</td><td>Prairial</td><td>Meadow</td><td>May 20 Jun 18</td></tr>
<tr><td>10</td><td>Messidor</td><td>Harvest</td><td>Jun 19 Jul 18</td></tr>
<tr><td>11</td><td>Thermidor</td><td>Heat</td><td>Jul 19 Aug 17</td></tr>
<tr><td>12</td><td>Fructidor</td><td>Fruit</td><td>Aug 18 Sep 16</td></tr>
</tbody>
</table>
<p><strong>Weekdays</strong> (the 10-day décade):</p>
<table class="ref-table">
<thead><tr><th>Day</th><th>French</th><th>English</th></tr></thead>
<tbody>
<tr><td>1</td><td>Primidi</td><td>First day</td></tr>
<tr><td>2</td><td>Duodi</td><td>Second day</td></tr>
<tr><td>3</td><td>Tridi</td><td>Third day</td></tr>
<tr><td>4</td><td>Quartidi</td><td>Fourth day</td></tr>
<tr><td>5</td><td>Quintidi</td><td>Fifth day</td></tr>
<tr><td>6</td><td>Sextidi</td><td>Sixth day</td></tr>
<tr><td>7</td><td>Septidi</td><td>Seventh day</td></tr>
<tr><td>8</td><td>Octidi</td><td>Eighth day</td></tr>
<tr><td>9</td><td>Nonidi</td><td>Ninth day</td></tr>
<tr><td>10</td><td>Décadi</td><td>Tenth day (rest day)</td></tr>
</tbody>
</table>
<p><strong>Complementary days</strong> (jours complémentaires, after Fructidor):</p>
<table class="ref-table">
<thead><tr><th>Day</th><th>French</th><th>English</th></tr></thead>
<tbody>
<tr><td>1</td><td>Jour de la Vertu</td><td>Day of Virtue</td></tr>
<tr><td>2</td><td>Jour du Génie</td><td>Day of Genius</td></tr>
<tr><td>3</td><td>Jour du Travail</td><td>Day of Labour</td></tr>
<tr><td>4</td><td>Jour de l'Opinion</td><td>Day of Opinion</td></tr>
<tr><td>5</td><td>Jour des Récompenses</td><td>Day of Rewards</td></tr>
<tr><td>6*</td><td>Jour de la Révolution</td><td>Day of the Revolution</td></tr>
</tbody>
</table>
<p style="font-size:0.8rem;">* Leap years only.</p>
</div>
<div class="meta-grid">
<p><strong>Decimal Time:</strong> <span id="decimalUtc">-</span></p>
<p><strong>Day of Year:</strong> <span id="dayOfYear">-</span></p>
<p><strong>Republican Year:</strong> <span id="repYear">-</span></p>
<p><strong>Time Zone:</strong> <span id="zoneLabel">-</span></p>
<p><strong>Status:</strong> <span id="offsetLabel">calibrating...</span></p>
<p><strong>Gregorian UTC:</strong> <span id="utcLabel">-</span></p>
<p><strong>Source:</strong> Hosted by SethPC</p>
</div>
</main>
<script src="/decimal.js"></script>
</body>
</html>
+280
View File
@@ -0,0 +1,280 @@
// Decimal time & French Republican Calendar
//
// Decimal time: 1 day = 10 decimal hours
// 1 decimal hour = 100 decimal minutes
// 1 decimal minute = 100 decimal seconds
//
// French Republican Calendar epoch: 22 September 1792 (Gregorian) = 1 Vendémiaire An I
// Each year has 12 months of 30 days + 5 (or 6 in sextile years) complementary days.
// Sextile (leap) years: the Republican leap rule approximates the Gregorian one —
// years 3, 7, 11, 15, 20 of each 20-year cycle were designated sextile in the original
// Romme proposal. For simplicity we use the widely accepted rule:
// A Republican year is sextile if the *following* Gregorian year is a Gregorian leap year.
const REPUBLICAN_EPOCH_JD = 2375840; // Julian Day Number of 1 Vendémiaire An I (22 Sep 1792)
const MONTH_NAMES = [
"Vendémiaire", "Brumaire", "Frimaire",
"Nivôse", "Pluviôse", "Ventôse",
"Germinal", "Floréal", "Prairial",
"Messidor", "Thermidor", "Fructidor"
];
const MONTH_NAMES_EN = [
"Vintage", "Mist", "Frost",
"Snowy", "Rainy", "Windy",
"Budding", "Flowery", "Meadow",
"Harvest", "Heat", "Fruit"
];
const COMPLEMENTARY_NAMES = [
"Jour de la Vertu", // 1
"Jour du Génie", // 2
"Jour du Travail", // 3
"Jour de l'Opinion", // 4
"Jour des Récompenses", // 5
"Jour de la Révolution", // 6 (sextile only)
];
const COMPLEMENTARY_NAMES_EN = [
"Day of Virtue",
"Day of Genius",
"Day of Labour",
"Day of Opinion",
"Day of Rewards",
"Day of the Revolution",
];
const WEEKDAY_NAMES = [
"Primidi", "Duodi", "Tridi", "Quartidi", "Quintidi",
"Sextidi", "Septidi", "Octidi", "Nonidi", "Décadi"
];
const WEEKDAY_NAMES_EN = [
"First day", "Second day", "Third day", "Fourth day", "Fifth day",
"Sixth day", "Seventh day", "Eighth day", "Ninth day", "Tenth day"
];
// --- Julian Day Number helpers ---
function gregorianToJD(year, month, day) {
// Algorithm from Jean Meeus, "Astronomical Algorithms"
if (month <= 2) { year -= 1; month += 12; }
const A = Math.floor(year / 100);
const B = 2 - A + Math.floor(A / 4);
return Math.floor(365.25 * (year + 4716)) +
Math.floor(30.6001 * (month + 1)) +
day + B - 1524.5;
}
function isGregorianLeap(year) {
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
}
function isSextile(republicanYear) {
// The year following An N is Gregorian year 1792 + N
return isGregorianLeap(1792 + republicanYear);
}
function toRepublicanDate(utcDate) {
// JD at midnight UTC for the given date
const jd = Math.floor(gregorianToJD(
utcDate.getUTCFullYear(),
utcDate.getUTCMonth() + 1,
utcDate.getUTCDate()
) + 0.5); // +0.5 because JD starts at noon; floor gives previous midnight
const daysSinceEpoch = jd - REPUBLICAN_EPOCH_JD;
if (daysSinceEpoch < 0) {
return null; // Before the Republic
}
// Walk years: each year is 365 or 366 days
let year = 1;
let remaining = daysSinceEpoch;
while (true) {
const yearLen = isSextile(year) ? 366 : 365;
if (remaining < yearLen) break;
remaining -= yearLen;
year++;
}
let month, day, weekday, complementary;
if (remaining < 360) {
// Normal months (0-indexed day within year)
month = Math.floor(remaining / 30) + 1; // 1..12
day = (remaining % 30) + 1; // 1..30
weekday = ((remaining % 10)); // 0..9 → Primidi..Décadi
complementary = null;
} else {
// Complementary days (jours complémentaires / sans-culottides)
const compIdx = remaining - 360; // 0..4 (or 0..5 sextile)
month = null;
day = compIdx + 1;
weekday = null;
complementary = COMPLEMENTARY_NAMES[compIdx] || "Jour inconnu";
}
return { year, month, day, weekday, complementary };
}
function toRomanNumeral(n) {
const vals = [1000,900,500,400,100,90,50,40,10,9,5,4,1];
const syms = ["M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"];
let result = "";
for (let i = 0; i < vals.length; i++) {
while (n >= vals[i]) { result += syms[i]; n -= vals[i]; }
}
return result;
}
function formatRepublicanDate(r) {
if (!r) return { full: "Before the Republic", translation: "", dayOfYear: "-", year: "-" };
let full, translation;
if (r.complementary) {
const compIdx = r.day - 1;
full = `${r.complementary}, An ${toRomanNumeral(r.year)}`;
translation = `${COMPLEMENTARY_NAMES_EN[compIdx]}, Year ${r.year}`;
} else {
const weekdayName = WEEKDAY_NAMES[r.weekday];
const weekdayNameEn = WEEKDAY_NAMES_EN[r.weekday];
const monthName = MONTH_NAMES[r.month - 1];
const monthNameEn = MONTH_NAMES_EN[r.month - 1];
full = `${weekdayName}, ${r.day} ${monthName} An ${toRomanNumeral(r.year)}`;
translation = `${weekdayNameEn}, ${r.day} ${monthNameEn}, Year ${r.year}`;
}
const dayOfYear = r.complementary
? `360 + ${r.day} (Jour complémentaire)`
: `${(r.month - 1) * 30 + r.day}`;
return { full, translation, dayOfYear, year: `An ${toRomanNumeral(r.year)}` };
}
// --- Decimal time ---
// Decimal time counts from local midnight in the selected time zone.
function getLocalParts(date, zone) {
const fmt = new Intl.DateTimeFormat("en-US", {
timeZone: zone,
hour12: false,
year: "numeric", month: "2-digit", day: "2-digit",
hour: "2-digit", minute: "2-digit", second: "2-digit"
});
const parts = fmt.formatToParts(date).reduce((acc, p) => {
if (p.type !== "literal") acc[p.type] = parseInt(p.value, 10);
return acc;
}, {});
return parts; // { year, month, day, hour, minute, second }
}
function toDecimalTime(date, zone) {
const p = getLocalParts(date, zone);
// ms since local midnight (ignoring DST transitions within the day — good enough)
const msIntoDay =
p.hour * 3_600_000 +
p.minute * 60_000 +
p.second * 1_000 +
date.getMilliseconds();
const dayFraction = msIntoDay / 86_400_000;
const totalDecimalSeconds = dayFraction * 100_000; // 10h * 100m * 100s
const dHour = Math.floor(totalDecimalSeconds / 10_000);
const rem1 = totalDecimalSeconds % 10_000;
const dMin = Math.floor(rem1 / 100);
const dSec = rem1 % 100;
const dCenti = Math.floor((dSec % 1) * 100);
const dSecInt = Math.floor(dSec);
return {
str: `${dHour}:${String(dMin).padStart(2,"0")}:${String(dSecInt).padStart(2,"0")}.${String(dCenti).padStart(2,"0")}`
};
}
function toRepublicanDateLocal(date, zone) {
const p = getLocalParts(date, zone);
return toRepublicanDate(new Date(Date.UTC(p.year, p.month - 1, p.day)));
}
// --- DOM & sync ---
const zoneSelect = document.getElementById("zoneSelect");
const zoneLabel = document.getElementById("zoneLabel");
const dateTrans = document.getElementById("dateTrans");
const offsetLabel = document.getElementById("offsetLabel");
const decimalUtc = document.getElementById("decimalUtc");
const dayOfYear = document.getElementById("dayOfYear");
const repYear = document.getElementById("repYear");
const utcLabel = document.getElementById("utcLabel");
const dateLine = document.getElementById("dateLine");
const digital = document.getElementById("digital");
const zones = [
"America/New_York",
"America/Chicago",
"America/Denver",
"America/Los_Angeles",
"America/Anchorage",
"Pacific/Honolulu",
"UTC"
];
let selectedZone = Intl.DateTimeFormat().resolvedOptions().timeZone || "America/New_York";
if (!zones.includes(selectedZone)) selectedZone = "America/New_York";
for (const zone of zones) {
const option = document.createElement("option");
option.value = zone;
option.textContent = zone;
if (zone === selectedZone) option.selected = true;
zoneSelect.appendChild(option);
}
zoneSelect.addEventListener("change", () => {
selectedZone = zoneSelect.value;
zoneLabel.textContent = selectedZone;
});
let serverOffsetMs = 0;
async function syncWithServer() {
const start = Date.now();
try {
const response = await fetch(`/api/time?ts=${start}`, { cache: "no-store" });
const end = Date.now();
const data = await response.json();
const rtt = end - start;
serverOffsetMs = (data.epoch_ms + rtt / 2) - end;
offsetLabel.textContent = `Synchronized (${serverOffsetMs >= 0 ? "+" : ""}${serverOffsetMs.toFixed(0)} ms, RTT ${rtt} ms)`;
} catch (_) {
offsetLabel.textContent = "Sync failed";
}
}
function render() {
const now = new Date(Date.now() + serverOffsetMs);
const dt = toDecimalTime(now, selectedZone);
const rep = toRepublicanDateLocal(now, selectedZone);
const fmt = formatRepublicanDate(rep);
digital.textContent = dt.str;
dateLine.textContent = fmt.full;
dateTrans.textContent = fmt.translation;
decimalUtc.textContent = dt.str;
dayOfYear.textContent = fmt.dayOfYear;
repYear.textContent = fmt.year;
zoneLabel.textContent = selectedZone;
utcLabel.textContent = now.toISOString().replace("T", " ").replace("Z", " UTC");
requestAnimationFrame(render);
}
syncWithServer();
setInterval(syncWithServer, 20_000);
render();
+34
View File
@@ -0,0 +1,34 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Sethflix Official Time</title>
<link rel="icon" type="image/png" href="https://storage.googleapis.com/sethfreiberg.com/sethflix/favicon.png">
<link rel="stylesheet" href="/style.css">
</head>
<body>
<main class="wrap">
<div class="brand-row">
<img class="logo-img" src="https://storage.googleapis.com/sethfreiberg.com/sethflix/favicon.png" alt="Sethflix logo">
<span class="brand-text">SethPC Time</span>
</div>
<p class="kicker">Official U.S. Time</p>
<h1 id="dateLine">Loading date...</h1>
<p id="digital" class="digital">--:--:--.--</p>
<div class="controls">
<label class="zone-picker" for="zoneSelect">Display Time Zone</label>
<select id="zoneSelect"></select>
</div>
<div class="meta-grid">
<p><strong>Time Zone:</strong> <span id="zoneLabel">-</span></p>
<p><strong>Status:</strong> <span id="offsetLabel">calibrating...</span></p>
<p><strong>UTC:</strong> <span id="utcLabel">-</span></p>
<p><strong>Source:</strong> Hosted by SethPC</p>
</div>
</main>
<script src="/app.js"></script>
</body>
</html>
+87
View File
@@ -0,0 +1,87 @@
#!/usr/bin/env python3
import json
import time
from datetime import date, datetime, timezone
from http.server import ThreadingHTTPServer, SimpleHTTPRequestHandler
from pathlib import Path
from urllib.parse import urlparse
ROOT = Path(__file__).resolve().parent
CLOCK_CGI_PATH = "/zzz__2fbc6c3300df7e4483acd44c5044098a9fcc61d6.cgi"
def us_dst_dates_for_year(year: int) -> tuple[int, int]:
march1 = date(year, 3, 1)
first_sunday_march = 1 + ((6 - march1.weekday()) % 7)
second_sunday_march = first_sunday_march + 7
nov1 = date(year, 11, 1)
first_sunday_november = 1 + ((6 - nov1.weekday()) % 7)
return second_sunday_march, first_sunday_november
class Handler(SimpleHTTPRequestHandler):
def __init__(self, *args, **kwargs):
super().__init__(*args, directory=str(ROOT), **kwargs)
def do_GET(self):
parsed = urlparse(self.path)
if parsed.path == "/":
self.send_response(302)
self.send_header("Location", "/timegov/")
self.end_headers()
return
if parsed.path in {"/simple", "/simple/"}:
self.path = "/index.html"
return super().do_GET()
if parsed.path == "/api/time":
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Cache-Control", "no-store")
self.end_headers()
payload = {
"epoch_ms": time.time_ns() // 1_000_000,
"iso_utc": datetime.now(timezone.utc).isoformat(),
}
self.wfile.write(json.dumps(payload).encode("utf-8"))
return
if parsed.path == "/api/timegov/auxdata.xml":
now = datetime.now(timezone.utc)
dst_start_day, dst_end_day = us_dst_dates_for_year(now.year)
xml = (
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
"<timeData>"
f"<currYear>{now.year}</currYear>"
f"<DstDates>3 {dst_start_day} 11 {dst_end_day}</DstDates>"
"<LeapDate>2099 12 31</LeapDate>"
"</timeData>"
)
self.send_response(200)
self.send_header("Content-Type", "application/xml")
self.send_header("Cache-Control", "no-store")
self.end_headers()
self.wfile.write(xml.encode("utf-8"))
return
if parsed.path == CLOCK_CGI_PATH:
t2 = time.time_ns() // 1_000
t3 = time.time_ns() // 1_000
xml = f"<timestamp time2=\"{t2}\" time3=\"{t3}\"/>"
self.send_response(200)
self.send_header("Content-Type", "application/xml")
self.send_header("Cache-Control", "no-store")
self.end_headers()
self.wfile.write(xml.encode("utf-8"))
return
return super().do_GET()
if __name__ == "__main__":
server = ThreadingHTTPServer(("0.0.0.0", 8092), Handler)
server.serve_forever()
Executable
+107
View File
@@ -0,0 +1,107 @@
#!/usr/bin/env python3
import json
import time
from datetime import date, datetime, timezone
from http.server import ThreadingHTTPServer, SimpleHTTPRequestHandler
from pathlib import Path
from urllib.parse import urlparse
ROOT = Path(__file__).resolve().parent
CLOCK_CGI_PATH = "/zzz__2fbc6c3300df7e4483acd44c5044098a9fcc61d6.cgi"
def us_dst_dates_for_year(year: int) -> tuple[int, int]:
march1 = date(year, 3, 1)
first_sunday_march = 1 + ((6 - march1.weekday()) % 7)
second_sunday_march = first_sunday_march + 7
nov1 = date(year, 11, 1)
first_sunday_november = 1 + ((6 - nov1.weekday()) % 7)
return second_sunday_march, first_sunday_november
class Handler(SimpleHTTPRequestHandler):
def __init__(self, *args, **kwargs):
super().__init__(*args, directory=str(ROOT), **kwargs)
def do_GET(self):
parsed = urlparse(self.path)
if parsed.path == "/":
self.send_response(302)
self.send_header("Location", "/timegov/")
self.end_headers()
return
if parsed.path in {"/simple", "/simple/"}:
self.path = "/index.html"
return super().do_GET()
if parsed.path in {"/decimal", "/decimal/"}:
self.path = "/decimal.html"
return super().do_GET()
if parsed.path in {"/seth", "/seth/"}:
self.path = "/seth.html"
return super().do_GET()
if parsed.path in {"/calendar", "/calendar/"}:
self.path = "/calendar.html"
return super().do_GET()
if parsed.path in {"/astro", "/astro/"}:
self.path = "/astro.html"
return super().do_GET()
if parsed.path in {"/convert", "/convert/"}:
self.path = "/convert.html"
return super().do_GET()
if parsed.path == "/api/time":
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Cache-Control", "no-store")
self.end_headers()
payload = {
"epoch_ms": time.time_ns() // 1_000_000,
"iso_utc": datetime.now(timezone.utc).isoformat(),
}
self.wfile.write(json.dumps(payload).encode("utf-8"))
return
if parsed.path == "/api/timegov/auxdata.xml":
now = datetime.now(timezone.utc)
dst_start_day, dst_end_day = us_dst_dates_for_year(now.year)
xml = (
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
"<timeData>"
f"<currYear>{now.year}</currYear>"
f"<DstDates>3 {dst_start_day} 11 {dst_end_day}</DstDates>"
"<LeapDate>2099 12 31</LeapDate>"
"</timeData>"
)
self.send_response(200)
self.send_header("Content-Type", "application/xml")
self.send_header("Cache-Control", "no-store")
self.end_headers()
self.wfile.write(xml.encode("utf-8"))
return
if parsed.path == CLOCK_CGI_PATH:
t2 = time.time_ns() // 1_000
t3 = time.time_ns() // 1_000
xml = f"<timestamp time2=\"{t2}\" time3=\"{t3}\"/>"
self.send_response(200)
self.send_header("Content-Type", "application/xml")
self.send_header("Cache-Control", "no-store")
self.end_headers()
self.wfile.write(xml.encode("utf-8"))
return
return super().do_GET()
if __name__ == "__main__":
server = ThreadingHTTPServer(("0.0.0.0", 8092), Handler)
server.serve_forever()
+267
View File
@@ -0,0 +1,267 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>SethPC Seth Date</title>
<link rel="icon" type="image/png" href="https://storage.googleapis.com/sethfreiberg.com/sethflix/favicon.png">
<link rel="stylesheet" href="/style.css">
<style>
.seth-note {
margin: 0 0 1rem;
font-size: 0.85rem;
color: var(--muted);
line-height: 1.5;
}
.ref-table {
width: 100%;
border-collapse: collapse;
font-size: 0.82rem;
margin: 0.4rem 0 0.9rem;
}
.ref-table th {
text-align: left;
color: var(--accent);
border-bottom: 1px solid var(--border);
padding: 0.2rem 0.5rem 0.2rem 0;
font-weight: 600;
}
.ref-table td {
padding: 0.15rem 0.5rem 0.15rem 0;
color: var(--muted);
border-bottom: 1px solid #2a2a2a;
}
.ref-table td:first-child {
color: var(--text);
width: 2rem;
}
.time-compare {
display: flex;
flex-direction: column;
gap: 0.2rem;
}
.time-compare p {
margin: 0;
font-family: "JetBrains Mono", monospace;
font-size: 1rem;
letter-spacing: 0.02em;
color: var(--muted);
}
.seth-details {
margin: 0.75rem 0 0;
}
.seth-details summary {
cursor: pointer;
color: var(--accent);
font-size: 0.85rem;
user-select: none;
padding: 0.3rem 0;
}
.seth-details summary:hover {
color: var(--accent-hover);
}
.seth-details .seth-note {
margin-top: 0.6rem;
}
</style>
</head>
<body>
<main class="wrap">
<div class="brand-row">
<img class="logo-img" src="https://storage.googleapis.com/sethfreiberg.com/sethflix/favicon.png" alt="SethPC logo">
<span class="brand-text">SethPC Seth Date</span>
</div>
<p class="kicker">Seth Calendar &amp; Decimal Time &nbsp;·&nbsp;
<a href="/calendar/" style="color:var(--accent);text-decoration:none;font-size:0.85rem;">Calendar</a> &nbsp;·&nbsp;
<a href="/convert" style="color:var(--accent);text-decoration:none;font-size:0.85rem;">Converter</a> &nbsp;·&nbsp;
<a href="/decimal" style="color:var(--accent);text-decoration:none;font-size:0.85rem;">Decimal Time</a> &nbsp;·&nbsp;
<a href="/astro" style="color:var(--accent);text-decoration:none;font-size:0.85rem;">Astronomy</a>
</p>
<h1 id="dateLine">Loading date...</h1>
<p id="digital" class="digital">-:--.--</p>
<div class="controls">
<label class="zone-picker" for="zoneSelect">Display Time Zone</label>
<select id="zoneSelect"></select>
</div>
<div class="meta-grid">
<div class="time-compare" style="grid-column: span 2;">
<p class="mono">DECI: <span id="decimalTime">-</span></p>
<p class="mono">GREG: <span id="gregorianTime">-</span></p>
</div>
<p><strong>Time Zone:</strong> <span id="zoneLabel">-</span></p>
<p><strong>Day of Year:</strong> <span id="dayOfYear">-</span></p>
<p><strong>Status:</strong> <span id="offsetLabel">calibrating...</span></p>
<p><strong>Week of Year:</strong> <span id="weekOfYear">-</span></p>
<p><strong>Gregorian UTC:</strong> <span id="utcLabel">-</span></p>
<p><strong>Source:</strong> Hosted by SethPC</p>
</div>
<details class="seth-details">
<summary>History</summary>
<div class="seth-note">
<p>
The Seth Calendar was invented by <strong>Seth Freiberg</strong> in 2026 as a personal design project,
born out of frustration with the irregularity of the Gregorian calendar &mdash; unequal month lengths,
weeks that don't divide months evenly, and no clean mapping between days and dates.
</p>
<p>
The goal was a calendar with a simple, regular structure: 10 months of exactly 36 days,
each month divided into 6 weeks of 6 days, with a small block of holiday days at the year end
to absorb the remainder. Year numbers and the January 1 new year are kept from the Gregorian
calendar to stay compatible with the existing world.
</p>
<p>
Decimal time was paired with it for the same reason: the standard 24-hour clock is an arbitrary
Babylonian inheritance. Dividing the day into 10 hours of 100 minutes of 100 seconds gives a
fully base-10 time system that is easier to reason about and calculate with.
All units in the Seth system &mdash; months, weeks, days, hours, minutes, seconds &mdash; are zero-indexed.
</p>
<p>
Credit is due to the <strong>French Republican Calendar</strong> (1793), which pioneered both ideas:
a reformed calendar with equal 30-day months and 5&ndash;6 complementary days at year end,
and a decimal clock dividing the day into 10 hours of 100 minutes of 100 seconds.
The French system was officially used from 1793 to 1805 before Napoleon abolished it.
The Seth Calendar was designed independently in the same spirit, with a different month structure
and the addition of zero-indexing throughout.
</p>
</div>
</details>
<details class="seth-details">
<summary>About Seth Date</summary>
<div class="seth-note">
<p>
<strong>Seth Date</strong> uses the same year numbers and January 1 new year as the Gregorian
calendar. The year is divided into <strong>10 months of 36 days</strong> (6 weeks of 6 days each),
followed by <strong>5 holiday days</strong> at year end (6 on leap years).
All units are <strong>zero-indexed</strong>: months 0&ndash;9, days 0&ndash;35, weeks 0&ndash;5, weekdays 0&ndash;5.
Time uses the same decimal system: 10 hours, 100 minutes, 100 seconds per day.
The Seth second is derived from the Unix second (which equals the SI second, leap seconds aside)
at a fixed ratio: <strong>1 Seth second = 0.864 SI seconds</strong> (86,400 SI seconds per day &divide; 100,000 Seth seconds per day).
</p>
<p>
<strong>Leap Day</strong> (Gregorian Feb 29) is a special intercalary day that exists
<em>outside</em> the normal month and week structure. It is inserted between Month 1, Week 3, Day 4
and Month 1, Week 3, Day 5 &mdash; occupying DOY 60 in its own slot. After Leap Day the calendar
resumes at D5 unchanged, which is why every Seth date from March 1 onward falls on the same
Gregorian date every year (e.g. Christmas is always Month 9, Day 34). On the calendar it appears
as a split cell sharing the D4 column: the top half is the normal D4 day, the bottom half is Leap Day.
</p>
<p><strong>Reading the date:</strong> dates are written as
<em>Year M W D</em> &mdash;
e.g. <em>Month 3, Week 2, Day 4</em> means the 3rd month (0-indexed), week 2, day 4 of that week,
which is day 16 of the month (2&times;6 + 4).
Holiday days are written as <em>Holiday N</em> (N = 0&ndash;4, or 0&ndash;5 on leap years) and fall outside any month or week.
</p>
<p><strong>Months</strong> (months 0&ndash;9, days 0&ndash;35, approx. Gregorian ranges):</p>
<table class="ref-table">
<thead><tr><th>#</th><th>Days</th><th>Approx. Gregorian</th><th>Notes</th></tr></thead>
<tbody>
<tr><td>0</td><td>0&ndash;35</td><td>Jan 1 &ndash; Feb 5</td><td></td></tr>
<tr><td>1</td><td>0&ndash;35</td><td>Feb 6 &ndash; Mar 13</td><td></td></tr>
<tr><td>2</td><td>0&ndash;35</td><td>Mar 14 &ndash; Apr 18</td><td></td></tr>
<tr><td>3</td><td>0&ndash;35</td><td>Apr 19 &ndash; May 24</td><td></td></tr>
<tr><td>4</td><td>0&ndash;35</td><td>May 25 &ndash; Jun 29</td><td></td></tr>
<tr><td>5</td><td>0&ndash;35</td><td>Jun 30 &ndash; Aug 4</td><td></td></tr>
<tr><td>6</td><td>0&ndash;35</td><td>Aug 5 &ndash; Sep 9</td><td></td></tr>
<tr><td>7</td><td>0&ndash;35</td><td>Sep 10 &ndash; Oct 15</td><td></td></tr>
<tr><td>8</td><td>0&ndash;35</td><td>Oct 16 &ndash; Nov 20</td><td></td></tr>
<tr><td>9</td><td>0&ndash;35</td><td>Nov 21 &ndash; Dec 26</td><td>Ends Dec 25 on leap years</td></tr>
</tbody>
</table>
<p><strong>Weeks</strong> (weeks 0&ndash;5 within each month, days 0&ndash;5 within each week):</p>
<table class="ref-table">
<thead><tr><th>Week</th><th>Days</th></tr></thead>
<tbody>
<tr><td>0</td><td>0&ndash;5</td></tr>
<tr><td>1</td><td>6&ndash;11</td></tr>
<tr><td>2</td><td>12&ndash;17</td></tr>
<tr><td>3</td><td>18&ndash;23</td></tr>
<tr><td>4</td><td>24&ndash;29</td></tr>
<tr><td>5</td><td>30&ndash;35</td></tr>
</tbody>
</table>
<p><strong>Holiday days</strong> (after Month 9, Day 35):</p>
<table class="ref-table">
<thead><tr><th>Holiday</th><th>Normal year</th><th>Leap year</th></tr></thead>
<tbody>
<tr><td>H0</td><td>Dec 27</td><td>Dec 26 &mdash; Boxing Day</td></tr>
<tr><td>H1</td><td>Dec 28</td><td>Dec 27</td></tr>
<tr><td>H2</td><td>Dec 29</td><td>Dec 28</td></tr>
<tr><td>H3</td><td>Dec 30</td><td>Dec 29</td></tr>
<tr><td>H4</td><td>Dec 31 &mdash; New Year's Eve</td><td>Dec 30</td></tr>
<tr><td>H5*</td><td>&mdash;</td><td>Dec 31 &mdash; New Year's Eve</td></tr>
</tbody>
</table>
<p style="font-size:0.8rem;">
* Leap years only.<br>
Christmas (Dec 25) falls on Month 9, Day 34 in normal years, and Month 9, Day 35 in leap years.<br>
Dec 26 (Boxing Day) is Month 9, Day 35 in normal years, and Holiday 0 in leap years.
</p>
</div>
</details>
<details class="seth-details">
<summary>Time Conversions</summary>
<div class="seth-note">
<p>Each decimal hour = 2 hours 24 minutes Gregorian. Each Gregorian hour = ~0:41:67 decimal.</p>
<div style="display:grid; grid-template-columns: 1fr 1fr; gap: 0 2rem;">
<div>
<p><strong>Decimal &rarr; Gregorian</strong></p>
<table class="ref-table">
<thead><tr><th>Decimal hour</th><th>Gregorian (24h)</th></tr></thead>
<tbody>
<tr><td>0:00</td><td>00:00</td></tr>
<tr><td>1:00</td><td>02:24</td></tr>
<tr><td>2:00</td><td>04:48</td></tr>
<tr><td>3:00</td><td>07:12</td></tr>
<tr><td>4:00</td><td>09:36</td></tr>
<tr><td>5:00</td><td>12:00</td></tr>
<tr><td>6:00</td><td>14:24</td></tr>
<tr><td>7:00</td><td>16:48</td></tr>
<tr><td>8:00</td><td>19:12</td></tr>
<tr><td>9:00</td><td>21:36</td></tr>
</tbody>
</table>
</div>
<div>
<p><strong>Gregorian &rarr; Decimal</strong></p>
<table class="ref-table">
<thead><tr><th>Gregorian (24h)</th><th>Decimal hour</th></tr></thead>
<tbody>
<tr><td>00:00</td><td>0:00</td></tr>
<tr><td>01:00</td><td>0:41:67</td></tr>
<tr><td>02:00</td><td>0:83:33</td></tr>
<tr><td>03:00</td><td>1:25:00</td></tr>
<tr><td>04:00</td><td>1:66:67</td></tr>
<tr><td>05:00</td><td>2:08:33</td></tr>
<tr><td>06:00</td><td>2:50:00</td></tr>
<tr><td>07:00</td><td>2:91:67</td></tr>
<tr><td>08:00</td><td>3:33:33</td></tr>
<tr><td>09:00</td><td>3:75:00</td></tr>
<tr><td>10:00</td><td>4:16:67</td></tr>
<tr><td>11:00</td><td>4:58:33</td></tr>
<tr><td>12:00</td><td>5:00:00</td></tr>
<tr><td>13:00</td><td>5:41:67</td></tr>
<tr><td>14:00</td><td>5:83:33</td></tr>
<tr><td>15:00</td><td>6:25:00</td></tr>
<tr><td>16:00</td><td>6:66:67</td></tr>
<tr><td>17:00</td><td>7:08:33</td></tr>
<tr><td>18:00</td><td>7:50:00</td></tr>
<tr><td>19:00</td><td>7:91:67</td></tr>
<tr><td>20:00</td><td>8:33:33</td></tr>
<tr><td>21:00</td><td>8:75:00</td></tr>
<tr><td>22:00</td><td>9:16:67</td></tr>
<tr><td>23:00</td><td>9:58:33</td></tr>
</tbody>
</table>
</div>
</div>
</div>
</details>
</main>
<script src="/seth.js"></script>
</body>
</html>
+218
View File
@@ -0,0 +1,218 @@
// Seth Calendar
//
// Structure:
// - Same year numbers and Jan 1 epoch as Gregorian
// - 10 months of 36 days (6 weeks of 6 days each)
// - 5 holiday days at year end (6 on leap years)
// - Total: 365 or 366 days
//
// Gregorian alignment (non-leap year):
// Month 1 Day 1 = Jan 1
// Month 10 Day 35 = Dec 25 (Christmas)
// Month 10 Day 36 = Dec 26 (Boxing Day)
// Holiday 1 = Dec 27
// Holiday 5 = Dec 31 (New Year's Eve)
//
// Gregorian alignment (leap year):
// Month 10 Day 35 = Dec 24 (Christmas Eve)
// Month 10 Day 36 = Dec 25 (Christmas)
// Holiday 1 = Dec 26 (Boxing Day)
// Holiday 6 = Dec 31 (New Year's Eve)
function isLeapYear(year) {
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
}
function toSethDate(year, dayOfYear) {
// dayOfYear: 1-indexed (Jan 1 = 1)
const leap = isLeapYear(year);
// Leap Day: DOY 60 in a leap year — sits between M1 W3 D4 and M1 W3 D5
if (leap && dayOfYear === 60) {
return { type: "leapday", year, dayOfYear };
}
// After leap day, subtract 1 so the rest of the calendar stays pegged
const idx = (leap && dayOfYear > 60 ? dayOfYear - 1 : dayOfYear) - 1;
if (idx < 360) {
const month = Math.floor(idx / 36); // 0..9
const dayInM = idx % 36; // 0..35
const week = Math.floor(dayInM / 6); // 0..5
const dayInW = dayInM % 6; // 0..5
return { type: "month", year, month, day: dayInM, week, weekDay: dayInW, dayOfYear };
} else {
const holiday = idx - 360; // 0..4 (or 0..5 leap)
return { type: "holiday", year, holiday, leap, dayOfYear };
}
}
function getDayOfYear(date, zone) {
const fmt = new Intl.DateTimeFormat("en-US", {
timeZone: zone,
year: "numeric", month: "numeric", day: "numeric"
});
const parts = fmt.formatToParts(date).reduce((acc, p) => {
if (p.type !== "literal") acc[p.type] = parseInt(p.value, 10);
return acc;
}, {});
const { year, month, day } = parts;
// Day of year calculation
const cumDays = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
const leap = isLeapYear(year);
let doy = cumDays[month - 1] + day;
if (leap && month > 2) doy++;
return { year, doy };
}
function formatSethDate(sd) {
if (sd.type === "leapday") return "Leap Day";
if (sd.type === "holiday") {
const total = sd.leap ? 6 : 5;
return `Holiday ${sd.holiday} of ${total}`;
}
return `Month ${sd.month}, Week ${sd.week}, Day ${sd.weekDay}`;
}
function formatSethLong(sd) {
if (sd.type === "leapday") return `${sd.year} Leap Day — Feb 29`;
if (sd.type === "holiday") {
const lastHoliday = sd.leap ? 5 : 4;
const isNYE = sd.holiday === lastHoliday;
const isBoxingDay = sd.leap && sd.holiday === 0;
let note = "";
if (isNYE) note = " — New Year's Eve";
else if (isBoxingDay) note = " — Boxing Day";
return `${sd.year} Holiday ${sd.holiday}${note}`;
}
return `${sd.year} M${sd.month} W${sd.week} D${sd.weekDay} (Month ${sd.month}, Day ${sd.day})`;
}
// --- Decimal time (same as decimal page) ---
function getLocalParts(date, zone) {
const fmt = new Intl.DateTimeFormat("en-US", {
timeZone: zone,
hour12: false,
year: "numeric", month: "2-digit", day: "2-digit",
hour: "2-digit", minute: "2-digit", second: "2-digit"
});
return fmt.formatToParts(date).reduce((acc, p) => {
if (p.type !== "literal") acc[p.type] = parseInt(p.value, 10);
return acc;
}, {});
}
function toDecimalTime(date, zone) {
const p = getLocalParts(date, zone);
const msIntoDay =
p.hour * 3_600_000 +
p.minute * 60_000 +
p.second * 1_000 +
date.getMilliseconds();
const dayFraction = msIntoDay / 86_400_000;
const totalDecimalSeconds = dayFraction * 100_000;
const dHour = Math.floor(totalDecimalSeconds / 10_000);
const rem1 = totalDecimalSeconds % 10_000;
const dMin = Math.floor(rem1 / 100);
const dSec = rem1 % 100;
const dCenti = Math.floor((dSec % 1) * 100);
const dSecInt = Math.floor(dSec);
return `\u00a0${dHour}:${String(dMin).padStart(2,"0")}:${String(dSecInt).padStart(2,"0")}.${String(dCenti).padStart(2,"0")}`;
}
// --- DOM & sync ---
const zoneSelect = document.getElementById("zoneSelect");
const zoneLabel = document.getElementById("zoneLabel");
const offsetLabel = document.getElementById("offsetLabel");
const decimalTime = document.getElementById("decimalTime");
const gregorianTime = document.getElementById("gregorianTime");
const dayOfYear = document.getElementById("dayOfYear");
const weekOfYear = document.getElementById("weekOfYear");
const utcLabel = document.getElementById("utcLabel");
const dateLine = document.getElementById("dateLine");
const digital = document.getElementById("digital");
const zones = [
"America/New_York",
"America/Chicago",
"America/Denver",
"America/Los_Angeles",
"America/Anchorage",
"Pacific/Honolulu",
"UTC"
];
let selectedZone = Intl.DateTimeFormat().resolvedOptions().timeZone || "America/New_York";
if (!zones.includes(selectedZone)) selectedZone = "America/New_York";
for (const zone of zones) {
const option = document.createElement("option");
option.value = zone;
option.textContent = zone;
if (zone === selectedZone) option.selected = true;
zoneSelect.appendChild(option);
}
zoneSelect.addEventListener("change", () => {
selectedZone = zoneSelect.value;
zoneLabel.textContent = selectedZone;
});
let serverOffsetMs = 0;
async function syncWithServer() {
const start = Date.now();
try {
const response = await fetch(`/api/time?ts=${start}`, { cache: "no-store" });
const end = Date.now();
const data = await response.json();
const rtt = end - start;
serverOffsetMs = (data.epoch_ms + rtt / 2) - end;
offsetLabel.textContent = `Synchronized (${serverOffsetMs >= 0 ? "+" : ""}${serverOffsetMs.toFixed(0)} ms, RTT ${rtt} ms)`;
} catch (_) {
offsetLabel.textContent = "Sync failed";
}
}
function toGregorianTime(date, zone) {
const p = getLocalParts(date, zone);
// Format as HH:MM:SS.cc to match decimal time width
const cs = String(Math.floor(date.getMilliseconds() / 10)).padStart(2, "0");
return `${String(p.hour).padStart(2,"0")}:${String(p.minute).padStart(2,"0")}:${String(p.second).padStart(2,"0")}.${cs}`;
}
function render() {
const now = new Date(Date.now() + serverOffsetMs);
const { year, doy } = getDayOfYear(now, selectedZone);
const sd = toSethDate(year, doy);
const dt = toDecimalTime(now, selectedZone);
const gt = toGregorianTime(now, selectedZone);
dateLine.textContent = formatSethLong(sd);
digital.textContent = dt;
decimalTime.textContent = dt;
gregorianTime.textContent = gt;
dayOfYear.textContent = `${doy} of ${isLeapYear(year) ? 366 : 365}`;
const totalWeeks = 60; // 10 months × 6 weeks
const woy = sd.type === "month" ? sd.month * 6 + sd.week : "—";
const woyTotal = sd.type === "month" ? `${woy} of ${totalWeeks}`
: sd.type === "leapday" ? "— (leap day)"
: `— (holiday days)`;
weekOfYear.textContent = woyTotal;
zoneLabel.textContent = selectedZone;
utcLabel.textContent = now.toISOString().replace("T", " ").replace("Z", " UTC");
requestAnimationFrame(render);
}
syncWithServer();
setInterval(syncWithServer, 20_000);
render();
+119
View File
@@ -0,0 +1,119 @@
:root {
--bg-top: #000000;
--bg-bottom: #1a1a1a;
--panel: #252525;
--text: #e0e0e0;
--muted: #cccccc;
--accent: #d35400;
--accent-hover: #e65c00;
--border: #333333;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
min-height: 100vh;
font-family: "Source Sans 3", "Segoe UI", sans-serif;
color: var(--text);
background: radial-gradient(circle at 20% -10%, #2c2c2c 0%, var(--bg-bottom) 38%, var(--bg-top) 100%);
display: grid;
place-items: center;
padding: 1.2rem;
}
.wrap {
width: min(760px, 100%);
background: var(--panel);
border: 1px solid var(--border);
border-radius: 12px;
padding: clamp(1rem, 2.5vw, 2rem);
box-shadow: 0 10px 28px rgb(0 0 0 / 42%);
}
.brand-row {
display: flex;
align-items: center;
gap: 0.55rem;
margin-bottom: 0.35rem;
}
.logo-img {
width: 28px;
height: 28px;
border-radius: 6px;
}
.brand-text {
font-weight: 700;
letter-spacing: 0.02em;
color: var(--accent);
}
.kicker {
margin: 0;
color: var(--muted);
letter-spacing: 0.06em;
text-transform: uppercase;
font-size: 0.78rem;
}
h1 {
margin: 0.35rem 0 0.8rem;
font-size: clamp(1.2rem, 3vw, 1.7rem);
line-height: 1.2;
}
.digital {
margin: 0 0 1rem;
font-family: "JetBrains Mono", monospace;
font-size: clamp(2rem, 8vw, 4rem);
letter-spacing: 0.02em;
color: var(--accent);
}
.controls {
margin-bottom: 1rem;
}
.meta-grid {
display: grid;
grid-template-columns: repeat(2, minmax(180px, 1fr));
gap: 0.45rem 0.9rem;
color: var(--muted);
font-size: 0.98rem;
}
.meta-grid p {
margin: 0;
}
.zone-picker {
display: block;
margin-bottom: 0.35rem;
color: var(--muted);
}
select {
width: min(500px, 100%);
background: #2a2a2a;
color: #ffffff;
border: 1px solid #444444;
border-radius: 10px;
padding: 0.55rem 0.75rem;
font-size: 1rem;
}
select:focus {
outline: none;
border-color: var(--accent);
box-shadow: 0 0 0 3px rgb(211 84 0 / 25%);
}
@media (max-width: 760px) {
.meta-grid {
grid-template-columns: 1fr;
}
}
Symlink
+1
View File
@@ -0,0 +1 @@
/opt/clock-site/timegov-clone/time.gov
+159
View File
@@ -0,0 +1,159 @@
/**
* @file
* Header and Footer styles
*
*/
/* Header Styles */
.nist-header {
background: #000;
font-family: Helvetica, Arial, sans-serif;
padding: 10px 16px 0;
font-size: 16px;
}
.nist-header__logo-link {
display: inline-block;
height: 35px;
}
.nist-header__logo-icon {
fill: #fff;
display: inline-block;
height: 16px;
position: relative;
top: -2px;
margin-right: 2px;
}
.nist-header__logo-image {
fill: #fff;
display: inline-block;
height: 24px;
width: 90px;
}
/* Limit main content area width */
.nist-main {
margin-left: auto;
margin-right: auto;
max-width: 1200px;
padding: 0 16px;
box-sizing: border-box;
}
/* Make sure body has no margin or padding when using only this header component */
body {
padding: 0;
margin: 0;
}
/* Footer styles */
.nist-footer {
background: #333333;
position: relative;
z-index: 200;
font-family: Helvetica, Arial, sans-serif;
font-size: 16px;
box-sizing: border-box;
}
.nist-footer__inner {
margin-left: auto;
margin-right: auto;
max-width: 1200px;
padding: 0 16px;
}
.nist-footer__inner:after {
content: "";
display: table;
clear: both;
}
.nist-footer {
background: #333333;
color: white;
padding: 10px 0px;
position: relative;
}
.nist-footer a {
color: white;
text-decoration: none;
}
.nist-footer .nist-footer__logo img {
/* width: 30%; 300px;*/
height: 100px;
display: inline-block;
margin-left: 5%; /*margin-left: 10%;*/
margin-right: 5%; /*margin-right: 10%auto;*/
margin-top: 5px;
}
.nist-footer__menu {
clear: both;
margin-bottom: 1px;
}
.nist-footer__menu.first {
padding-top: 20px;
}
.nist-footer__menu ul {
margin: 0;
padding: 0;
list-style: none;
text-align: center;
}
.nist-footer__menu-item {
display: inline-block;
font-size: 14px;
padding: 0;
margin-left: 0;
}
.nist-footer__menu-item:after {
content: '|';
margin-right: 1.6px;
display: inherit;
position: static;
font: inherit;
line-height: inherit;
color: inherit;
}
.nist-footer__menu-item:last-child:after {
content: none;
}
.nist-footer__menu-item a {
font-weight: normal;
margin-right: 6px;
white-space: nowrap;
padding: 0.5em 0;
display: inline-block;
}
/**
* Stick footer to bottom of page
* Source: https://css-tricks.com/couple-takes-sticky-footer/
*/
html.nist-footer-bottom,
html.nist-footer-bottom body {
height: 100%;
}
html.nist-footer-bottom body {
display: flex;
flex-direction: column;
}
html.nist-footer-bottom #main {
flex: 1 0 auto;
}
html.nist-footer-bottom .nist-footer {
flex-shrink: 0;
}
+126
View File
@@ -0,0 +1,126 @@
///////////////////////////////////////////////////////////////////////////
////////////////////// ANALOG CLOCK FUNCTIONS ///////////////////////////
///////////////// Clock code used from W3Schools.com ////////////////////
//////// Refer to their site about applicable use of the code ///////////
///////////////////////////////////////////////////////////////////////////
var canvas = document.getElementById("analog-clock");
var ctx = canvas.getContext("2d");
var radius = canvas.height / 2;
ctx.translate(radius, radius);
radius = radius * 0.90
setInterval(drawClock, 1000);
// add 0 to single digit time min, sec
function checkTime(i) {
if (i < 10) {i = "0" + i}; // add zero in front of numbers < 10
return i;
}
function drawClock() {
drawFace(ctx, radius);
drawNumbers(ctx, radius);
drawTime(ctx, radius);
}
function drawFace(ctx, radius) {
var grad;
ctx.beginPath();
ctx.arc(0, 0, radius, 0, 2*Math.PI);
ctx.fillStyle = 'white';
ctx.fill();
grad = ctx.createRadialGradient(0,0,radius*0.95, 0,0,radius*1.05);
grad.addColorStop(0, '#333');
grad.addColorStop(0.5, 'white');
grad.addColorStop(1, '#333');
ctx.strokeStyle = grad;
ctx.lineWidth = radius*0.1;
ctx.stroke();
ctx.beginPath();
ctx.arc(0, 0, radius*0.1, 0, 2*Math.PI);
ctx.fillStyle = '#333';
ctx.fill();
}
function drawNumbers(ctx, radius) {
var ang;
var num;
ctx.font = radius*0.15 + "px arial";
ctx.textBaseline="middle";
ctx.textAlign="center";
for(num = 1; num < 13; num++){
ang = num * Math.PI / 6;
ctx.rotate(ang);
ctx.translate(0, -radius*0.85);
ctx.rotate(-ang);
ctx.fillText(num.toString(), 0, 0);
ctx.rotate(ang);
ctx.translate(0, radius*0.85);
ctx.rotate(-ang);
}
}
function drawTime(ctx, radius){
var deviceClock = new Date();
var hour = deviceClock.getHours();
var minute = deviceClock.getMinutes();
var second = deviceClock.getSeconds();
var ampm = hour >= 12 ? 'P.M.' : 'A.M.';
hour = hour % 12;
hour = hour ? hour : 12; // the hour '0' should be '12'
//minute = minute < 10 ? '0'+minute : minute;
// CHECKTIME FUNC ADDS LEADING '0' if <10
hour = checkTime(hour);
minute = checkTime(minute);
second = checkTime(second);
//digital time display
var myDigTime = hour + ":" + minute +":" + second;
document.getElementById('myTime').innerHTML = myDigTime + ' ' + ampm;
var dd = deviceClock.getDate();
var mm = deviceClock.getMonth();
mm++; //Increment because Jan is month 0
dd = checkTime(dd);
mm = checkTime(mm);
var yyyy = deviceClock.getFullYear();
var todaysDate = mm + '/' + dd + '/' + yyyy;
document.getElementById("myDate").innerHTML = "Today: " + todaysDate;
// OFFSET
var utcOffset = deviceClock.getTimezoneOffset()/60;
if (utcOffset > 0) {
utcOffset = "(UTC-" + utcOffset + ")";
} else {
utcOffset = "(UTC+" + Math.abs(utcOffset) + ")";
};
document.getElementById("myTimeTitle").innerHTML = utcOffset;
//hour
hour=hour%12;
hour=(hour*Math.PI/6)+
(minute*Math.PI/(6*60))+
(second*Math.PI/(360*60));
drawHand(ctx, hour, radius*0.5, radius*0.07);
//minute
minute=(minute*Math.PI/30)+(second*Math.PI/(30*60));
drawHand(ctx, minute, radius*0.8, radius*0.07);
// second
second=(second*Math.PI/30);
drawHand(ctx, second, radius*0.9, radius*0.02);
}
function drawHand(ctx, pos, length, width) {
ctx.beginPath();
ctx.lineWidth = width;
ctx.lineCap = "round";
ctx.moveTo(0,0);
ctx.rotate(pos);
ctx.lineTo(0, -length);
ctx.stroke();
ctx.rotate(-pos);
}
+67
View File
@@ -0,0 +1,67 @@
// ONLOAD OPERATIONS FOR THE SITE
window.onload = function() {
/* PARSE THE URL FOR 12/24 VARIABLE*/
var getT = location.search;
var tArr = getT.split("=");
var t = tArr[1];
// CREATE A VAR FOR THE CHECKBOX
var twentyFour = document.getElementById("twenty-four");
// CHECK VALUE OF 12/24 URL VAR "t" AND SET CHECKBOX ACCORDINGLY
if (t === "24") {
twentyFour.checked = true;
} else {
// DEFAULT TO 12HR DISPLAY
twentyFour.checked = false;
}
var noMoreAlerts = false;
// NOTIFICATION BOX FOR BOOKMARKING 24-HOUR SETTINGS PAGE
var twentyFour = document.getElementById("twenty-four");
twentyFour.addEventListener("click", function(event) {
var hourLabelDiv = document.getElementsByClassName("am-pm")[0];
var url = window.location.toString();
if(timeDotGov.data.twentyFour()) {
window.history.replaceState(url, "", "/timegov/?t=24");
if (!noMoreAlerts) {
alert("Bookmarking this page will save your preference for 24-hour time display.");
}
noMoreAlerts = true;
} else {
window.history.replaceState(url, "", "/timegov/");
}
timeDotGov.clockController.handleonrefresh(new Date());
});
//? timeZoneChange = function(event) {
//? timeDotGov.clockController.getnewOffset(event.target.value);
//? }
// LOAD DST DATES AND LEAP DATE
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("GET", "/api/timegov/auxdata.xml", false); // false for synchronous request
xmlHttp.send(null);
timeDotGov.auxdata = xmlHttp.responseText;
timeDotGov.clockController.auxdata();
timeDotGov.clockController.checkservertime();
document.getElementById('responseTime').innerHTML = timeDotGov.data.zoneOffset;
// SET REFRESH RATE TO CHECK FOR TOP OF NEW SECOND, SO THE DISPLAY DOES NOT HAVE TO BE REFRESHED MORE THAN NECESSARY
setInterval(function() {
if(timeDotGov.data.currentTime) {
timeDotGov.clockController.runningclocks();
}
}, 20); // 20 milliseconds
// FUNCTION REFRESHES PAGE EVERY 10 MIN
setInterval(function() {
location.reload();
}, 600000);
};
+363
View File
@@ -0,0 +1,363 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="title" content="Atermiter-X79 Official Time" />
<meta property="og:site_name" content="Atermiter-X79" />
<meta property="og:type" content="website" />
<meta property="og:url" content="https://clock.atermiter-x79.xyz/timegov/" />
<meta property="og:title" content="Official U.S. Time | Atermiter-X79" />
<meta property="og:description" content="Self-hosted official U.S. time style display with U.S. map and synchronized clocks." />
<meta property="og:image" content="/timegov/img/map-elements/united-states-map.svg" />
<meta name="dcterms.title" content="Atermiter-X79 Official Time" />
<meta name="description" content="Self-hosted official U.S. time display with synchronized clock data." />
<meta name="dcterms.subject" content="Working with industry and science to advance innovation and improve quality of life." />
<meta name="google-site-verification" content="" />
<meta name="dcterms.type" content="text" />
<meta name="dcterms.format" content="text/html" />
<meta name="dcterms.identifier" content="https://clock.atermiter-x79.xyz/timegov/" />
<meta name="twitter:title" content="Official U.S. Time | Atermiter-X79" />
<meta name="twitter:description" content="Self-hosted official U.S. time style display with synchronized clocks." />
<meta name="twitter:image" content="/img/map-elements/og_map.png" />
<title>Atermiter-X79 Official Time</title>
<link rel="shortcut icon" href="https://storage.googleapis.com/sethfreiberg.com/sethflix/favicon.png" />
<link rel="stylesheet" href="css/main.css" />
<link rel="stylesheet" href="css/NIST-styles.css" />
<!-- FUNC FOR TOP BAR INFO BTN -->
<script type="text/javascript">
function info_exp(id, id2) {
var n = document.getElementById(id);
if (n.style.display != 'none') {
n.style.display = 'none';
document.getElementById(id2).setAttribute('aria-expanded', 'false');
} else {
n.style.display = 'block';
document.getElementById(id2).setAttribute('aria-expanded', 'true');
}
}
</script>
</head>
<body>
<header>
<!-- TOP GREY BAR -->
<div id="top-grey">
<div class="inner">
<img src="img/us_flag_small.png" class="left gap" alt="U.S. Flag"><span class="left gap">Official self-hosted time service</span> <div id="info-btn"><a href="index.html#" onclick="info_exp('top-grey-exp', 'arrow');">Service details <span id="arrow" aria-expanded="false"></span></a></div>
</div>
</div>
<!-- EXPANDED TOP GREY BAR -->
<div id="top-grey-exp" style="display:none;">
<div class="inner">
<div class="col-50">
<div class="pad-4">
<img src="img/icon-dot-gov.svg" alt="icon dot gov" class="img10"><br>
<strong>Self-hosted service notice.</strong>
<p class="no-marg">This is a privately hosted clock service designed to mirror the time.gov experience in a homelab environment.</p>
</div>
</div>
<div class="col-50">
<div class="pad-4">
<img src="img/icon-https.svg" alt="icon https" class="img10"><br>
<strong>Encrypted transport.</strong>
<p class="no-marg">The <strong>https://</strong> indicates encrypted traffic between your browser and this host.</p>
</div>
</div>
<div class="clear"></div>
</div>
</div>
<!-- BLACK TITLE BAR AREA -->
<div id="title-area">
<div class="inner">
<center>
<span class="title"><img src="https://storage.googleapis.com/sethfreiberg.com/sethflix/favicon.png" id="logo" alt="Atermiter-X79"/>OFFICIAL U.S. TIME</span>
</center>
</div>
</div>
<!-- ANNOUNCEMENT GREY BAR -->
<div id="callout-bar"><p></p></div> <!-- Blank between <p> </p> unless there is an upcoming announcement -->
</header>
<!-- MAIN CONTENT AREA -->
<div id="content">
<!-- LEFT COLUMN -->
<div id="col-left">
<div class="pad-2">
<div id="noncontTitle">Non-Contiguous U.S. and Territories</div>
<div id="ak-col">
<!-- ALASKA TIME -->
<div class="clock-box blue dst-clock">
<div class="title">Alaska <span class="DSTterm">Standard</span> Time</div>
<div class="sub-title">AK<span class="DSTletter">S</span>T (UTC-<span class="DSTnum">9</span>)</div>
<div class="time-display">
<div class="color-area"></div>
<div class="clock time-text"><time zoneOffset=9></time></div>
</div>
</div>
<!-- ALASKA MAP GRAPHIC -->
<img src="img/map-elements/alaska.svg" class="side-img" alt="Alaska Map">
<!-- ALEUTIAN TIME -->
<div class="clock-box blue dst-clock">
<div class="title">Aleutian <span class="DSTterm">Standard</span> Time</div>
<div class="sub-title">HA<span class="DSTletter">S</span>T (UTC-<span class="DSTnum">10</span>)</div>
<div class="time-display">
<div class="color-area"></div>
<div class="clock time-text"><time zoneOffset=10></time></div>
</div>
<!-- <div class="null"><span class="DSTterm"></span><span class="DSTletter"></span><span class="DSTnum"></span></div> -->
</div>
</div>
<div id="hi-col">
<!-- HAWAIIAN TIME -->
<div class="clock-box purple">
<div class="title">Hawaii Standard Time</div>
<div class="sub-title">HST (UTC-10)</div>
<div class="time-display">
<div class="color-area no-saving"></div>
<div class="clock time-text"><time zoneOffset=10></time></div>
</div>
<div class="null"><span class="DSTterm"></span><span class="DSTletter"></span><span class="DSTnum"></span></div>
</div>
<!-- HAWAII MAP GRAPHIC -->
<img src="img/map-elements/hawaii.svg" class="side-img" alt="Hawaii Map">
<!-- SAMOA TIME -->
<div class="clock-box dk-blue">
<div class="title">Samoa Standard Time</div>
<div class="sub-title">SST (UTC-11)</div>
<div class="time-display">
<div class="color-area no-saving"></div>
<div class="clock time-text"><time zoneOffset=11></time></div>
</div>
<div class="null"><span class="DSTterm"></span><span class="DSTletter"></span><span class="DSTnum"></span></div>
</div>
<!-- CHAMORRO TIME -->
<div class="clock-box dk-blue">
<div class="title">Chamorro Standard Time</div>
<div class="sub-title">CHST (UTC+10)</div>
<div class="time-display">
<div class="color-area no-saving"></div>
<div class="clock time-text"><time zoneOffset=-10></time></div>
</div>
<div class="null"><span class="DSTterm"></span><span class="DSTletter"></span><span class="DSTnum"></span></div>
</div>
<!-- PUERTO RICO LT COL-->
<div id="pr-lt" class="pad-2 margTop15">
<div class="clock-box dk-blue">
<div class="title">Atlantic Standard Time</div>
<div class="sub-title"><b>Puerto Rico / US Virgin Islands</b></div>
<div class="sub-title">AST (UTC-4)</div>
<div class="time-display">
<div class="color-area no-saving"></div>
<div class="clock time-text"><time zoneOffset=4></time></div>
</div>
<div class="null"><span class="DSTterm"></span><span class="DSTletter"></span><span class="DSTnum"></span></div>
</div> </div>
</div>
</div>
</div>
<!-- MAIN COLUMN -->
<div id="col-main">
<div class="pad-2">
<div style="height: 0px;">&nbsp;</div>
<!-- ROW FOR MAINLAND US CLOCK AREA -->
<div class="main-top">
<!-- PACIFIC TIME -->
<div class="col-25">
<div class="pad-2">
<div class="clock-box orange dst-clock">
<div class="title">Pacific <span class="DSTterm">Standard</span> Time</div>
<div class="sub-title">P<span class="DSTletter">S</span>T (UTC-<span class="DSTnum">8</span>)</div>
<div class="time-display">
<div class="color-area"></div>
<div class="clock time-text"><time zoneOffset=8 />SYNCHRONIZING</time></div></div>
</div>
</div>
</div>
</div>
<!-- MOUNTAIN TIME -->
<div class="col-25">
<div class="pad-2">
<div class="clock-box green dst-clock">
<div class="title">Mountain <span class="DSTterm">Standard</span> Time</div>
<div class="sub-title">M<span class="DSTletter">S</span>T (UTC-<span class="DSTnum">7</span>)</div>
<div class="time-display">
<div class="color-area"></div>
<div class="clock time-text"><time zoneOffset=7>SYNCHRONIZING</time></div>
</div>
</div>
</div>
</div>
<!-- CENTRAL TIME -->
<div class="col-25">
<div class="pad-2">
<div class="clock-box yellow dst-clock">
<div class="title">Central <span class="DSTterm">Standard</span> Time</div>
<div class="sub-title">C<span class="DSTletter">S</span>T (UTC-<span class="DSTnum">6</span>)</div>
<div class="time-display">
<div class="color-area"></div>
<div class="clock time-text"><time zoneOffset=6>SYNCHRONIZING</time></div>
</div>
</div>
</div>
</div>
<!-- EASTERN TIME -->
<div class="col-25">
<div class="pad-2">
<div class="clock-box red dst-clock">
<div class="title">Eastern <span class="DSTterm">Standard</span> Time</div>
<div class="sub-title">E<span class="DSTletter">S</span>T (UTC-<span class="DSTnum">5</span>)</div>
<div class="time-display">
<div class="color-area"></div>
<div class="clock time-text"><time zoneOffset=5>SYNCHRONIZING</time></div>
</div>
</div>
</div>
</div>
<div class="clear"></div>
</div>
<!-- END TOP ROW -->
<div id="mainland">
<!-- ARIZONA/MOUNTAIN STANDARD TIME -->
<div id="az-clock" class="col-25">
<div class="pad-2">
<div class="clock-box green">
<div class="title">Arizona Mountain<br>Standard Time</div>
<div class="sub-title">MST (UTC-7)</div>
<div class="time-display">
<div class="color-area no-saving"></div>
<div class="clock time-text"><time zoneOffset=7></time></div>
</div>
<div class="null"><span class="DSTterm"></span><span class="DSTletter"></span><span class="DSTnum"></span></div>
</div>
</div>
</div>
<!-- BEGIN MAIN MAP AREA -->
<img src="img/map-elements/united-states-map.svg" class="full-img" alt="United States Time Zone Map">
</div>
</div>
<!-- RIGHT COLUMN -->
<div id="col-right">
<div class="pad-4">
<!-- 24HR CHECKBOX -->
<div class='hours-wrapper'>
<div class='am-pm'></div>
<input id='twenty-four' type='checkbox' class="css-checkbox">
<label class='css-label' for='twenty-four'>24-Hour Clock Display</label>
<div class="clear"></div>
</div>
<div id="clock-utc">
<p class="text-xs">Coordinated Universal Time (UTC)</p>
<div class="clock text-lg"><time id="timeUTC"></time></div>
<div class="null"><span class="DSTterm"></span><span class="DSTletter"></span><span class="DSTnum"></span></div>
</div>
<p class="sub-title ital">UTC is always displayed as a 24-hour clock.</p>
<!-- MAIN CLOCK AREA -->
<div id="main-time-area">
<p>Your Device's Clock <span id="myTimeTitle">UTC-0</span> <br>
<div id='myDate'></div>
<canvas id="analog-clock" width=200 height=200></canvas>
<br>
<span id="myTime"></span><br><br>
Your clock is off by: <br><span id="realTimeDif"></span> s</p>
</div>
<!-- PUERTO RICO RT COL-->
<div id="pr-rt" class="pad-2 margTop15">
<div class="clock-box dk-blue">
<div class="title">Atlantic Standard Time</div>
<div class="sub-title"><b>Puerto Rico / US Virgin Islands</b></div>
<div class="sub-title">AST (UTC-4)</div>
<div class="time-display">
<div class="color-area no-saving"></div>
<div class="clock time-text"><time zoneOffset=4></time></div>
</div>
<div class="null"><span class="DSTterm"></span><span class="DSTletter"></span><span class="DSTnum"></span></div>
</div>
</div>
<div id="rightColBot">
<a href="/simple/">Simple View</a>
<a href="/api/time">Time API</a>
<div class="imgSmText">
<img src="img/stripes.svg" class="striped-box" alt="Hashed image for Daylight Saving Time not observed"> = DAYLIGHT SAVING TIME NOT OBSERVED
<p>Clocks are corrected for network delay</p>
</div>
</div>
</div>
</div>
<div class="clear"></div>
</div>
<footer class="nist-footer">
<div class="nist-footer__inner">
<div class="nist-footer__menu" role="navigation">
<ul>
<li class="nist-footer__menu-item"><a href="/timegov/">Map View</a></li>
<li class="nist-footer__menu-item"><a href="/simple/">Simple View</a></li>
<li class="nist-footer__menu-item"><a href="/api/time">JSON Time API</a></li>
</ul>
</div>
</div>
<div class="nist-footer__logo">
<center><img src="https://storage.googleapis.com/sethfreiberg.com/sethflix/favicon.png" alt="Atermiter-X79 logo" /><p>Atermiter-X79 Official Time</p></center>
</div>
</footer>
<div class='time-zone-container' style='display:none'>
<div class='dropdown-container'>
<select id='dst-time-zone'>
</select>
<select id='no-dst-time-zone'>
</select>
</div>
</div>
<div id="responseTime"></div>
<script src="scripts/jquery-3.7.1.min.js" type="text/javascript"></script>
<script type="text/javascript" src="scripts/analogClock.js"></script>
<script type="text/javascript" src="scripts/application.js"></script>
<script type="text/javascript" src="scripts/zzz__bd08fcbbc8bd600a5f9e994be2de69f7cb26b9f4.js"></script>
</body>
</html>
File diff suppressed because one or more lines are too long
+550
View File
@@ -0,0 +1,550 @@
@charset "utf-8";
/* CSS Document */
body {
margin: 0;
font-size: 1em;
font-family: Source Sans Pro Web,Helvetica Neue,Helvetica,Roboto,Arial,sans-serif;
}
.inner {
padding: 0 5%;
}
.left {
float: left;
}
.gap {
margin-right: 10px;
}
#top-grey {
padding-bottom: .25rem;
padding-top: .25rem;
min-height: 0;
background-color: #f0f0f0;
}
#arrow {
background-repeat: no-repeat;
background-size: cover;
width: 10px;
height: 10px;
display: inline-block;
}
#arrow[aria-expanded=false] {
background-image: url(../img/arrow-down.svg);
}
#arrow[aria-expanded=true] {
background-image: url(../img/arrow-up.svg);
}
#top-grey-exp {
background-color: #f0f0f0;
height: auto;
}
.col-50 {
width: 50%;
float: left;
line-height: 1.5em;
}
.no-marg {
margin: 0;
}
.pad-4 {
padding: 4%;
}
.pad-2 {
padding: 2%;
}
#col-main .pad-2 {
padding: 2% 2% 0px 2%;
}
.clear {
clear: both;
}
.img10 {
width: 10%;
height: auto;
}
#title-area {
background-color: #000;
color: #fff;
padding: 4px 0;
vertical-align: middle;
}
.title {
font-size: 1.55em;
font-weight: 700;
}
#logo {
width: 1.25em;
height: 1.25em;
margin-right: 12px;
vertical-align: -0.12em;
}
#logo2 { /* added for USNO logo and increased margins */
width: 44px;
height: auto;
margin-left: 12px;
vertical-align: middle;
}
#clock-utc {
border: solid 1px #000;
text-align: center;
margin-bottom: 5px;
}
.ital {
font-style: italic;
text-align: center;
margin-bottom: 15px !important;
}
#myDate {
margin-top: 15px;
}
#myTime {
font-size: 1.4em;
font-weight: bold;
}
.imgSmText {
font-size: 0.7em;
line-height: 1.5em;
text-transform: uppercase;
}
#rightColBot a {
display: block;
margin-bottom: 10px;
color: #000;
}
#rightColBot {
display: block;
margin-top: 15px;
padding-top: 15px;
border-top: 1px solid #000;
}
.text-xs {
font-size: 0.8em;
margin: 5px 10px 0;
}
.text-lg {
font-size: 1.8em;
font-weight: bold;
margin: 0 10px;
}
#callout-bar {
color: #fff;
background-color: #999;
padding: .25rem 0;
text-align: center;
font-weight: bold;
}
#callout-bar p {
margin: 0;
}
#content{
display: table;
width: 100%;
}
#col-left {
width: 15%;
background-color: #d9e8f6;
display: table-cell;
}
#col-main {
width: 70%;
display: table-cell;
position: relative;
}
#col-right {
width: 15%;
background-color: #e2e2e2;
display: table-cell;
position: relative;
}
.col-25 {
width: 25%;
float: left;
}
#noncontTitle,
#contTitle {
display: none;
}
#pr-lt {
display: none;
}
/*
STYLES FOR CLOCK AREAS
*/
.clock-box {
margin-bottom: 15px;
}
#col-main .clock-box {
margin-bottom: 0;
}
.clock-box .title {
text-transform: uppercase;
font-size: 0.9em;
font-weight: bold;
/*color: #000000 !important;*/
}
.sub-title {
text-transform: uppercase;
font-size: 0.7em;
margin-bottom: 5px;
}
.time-display {
display: table;
width: 90%;
}
.color-area {
width: 5%;
display: table-cell;
}
.time-text {
background-color: #000;
font-weight: bold;
color: #fff;
font-size: 1.4em;
padding: 5px;
display: table-cell;
}
.side-img {
max-width: 90%;
display: block;
margin: 0 auto 15px;
}
.margTop15 {
margin-top: 15px;
}
#twenty-hour {
float: left;
}
#main-time-area {
background-color: #000;
width: 100%;
text-align: center;
color: #fff;
padding: 15px 0;
}
.full-img {
width: 94.5%;
display: block;
margin: 0 2.3%;
}
#mainland {
position: relative;
}
#az-clock {
position: absolute;
z-index: 100;
bottom: 0;
left: 5%;
}
.hours-wrapper {
padding: 5px;
background: #eee;
margin-bottom: 15px;
}
.striped-box {
max-height: .75rem;
max-width: .75rem;
}
#analog-clock {
max-width: 90%;
}
#responseTime {
display: none;
}
/* CUSTOM CHECKBOX STYLES */
input[type=checkbox].css-checkbox {
position:absolute; z-index:-1000; left:-1000px; overflow: hidden; clip: rect(0 0 0 0); height:1px; width:1px; margin:-1px; padding:0; border:0;
}
input[type=checkbox].css-checkbox + label.css-label {
padding-left:20px;
height:15px;
display:inline-block;
line-height:15px;
background-repeat:no-repeat;
background-position: 0 0;
vertical-align:middle;
cursor:pointer;
font-size: .7em;
}
input[type=checkbox].css-checkbox:checked + label.css-label {
background-position: 0 -15px;
}
label.css-label {
background-image:url(../img/csscheckbox.png);
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.no-saving {
background-image: url(../img/diagonal-white.png);
}
.blue .color-area {
background-color: #0060a8;
}
.blue .title {
color: #0060a8;
}
.purple .color-area {
background-color: #aa378d;
}
.purple .title {
color: #aa378d;
}
.dk-blue .color-area {
background-color: #1b0459;
}
.dk-blue .title {
color: #1b0459;
}
.orange .color-area {
background-color: #d66733;
}
.orange .title {
color: #d66733;
}
.green .color-area {
background-color: #63a952;
}
.green .title {
color: #63a952;
}
.yellow .color-area {
background-color: #e6a730;
}
.yellow .title {
color: #e6a730;
}
.red .color-area {
background-color: #cc2131;
}
.red .title {
color: #cc2131;
}
/* ///////////////////////////////////////////////////////////
////////////////////// RESPONSIVE STYLES //////////////////
///////////////////////////////////////////////////////////
*/
@media only screen and (min-width : 300px) and (max-width : 1260px) {
.title {
font-size: 2.0em; /* was 1.8 */
}
#content {
display: flex;
flex-flow: wrap;
display: -webkit-flex; /* Safari */
-webkit-flex-flow: wrap; /* Safari 6.1+ */
}
#col-main {
width: 100%;
display: block;
order: 1;
margin-bottom: 15px;
}
#col-left {
width: 70%;
display: block;
order: 2;
}
#noncontTitle, #contTitle {
display: block;
text-align: center;
font-size: 1.5em;
margin-bottom: 15px;
text-transform: uppercase;
}
#col-right {
width: 30%;
display: block;
order: 3;
}
#main-time-area {
display: block;
margin: 0 auto;
padding: 15px 0;
}
#ak-col, #hi-col {
width: 50%;
float: left;
}
#pr-lt {
display: block;
}
#pr-rt {
display: none;
}
#rightColBot {
border-top: 1px solid #000;
margin-top: 30px;
position: relative;
padding-top: 15px;
}
.clock-box .title {
font-size: 1em;
line-height: 1em;
}
.sub-title {
font-size: .7em;
}
.time-text {
font-size: 1.2em;
}
.text-xs {
font-size: 1em;
}
input[type=checkbox].css-checkbox + label.css-label {
font-size: 1em;
}
.imgSmText {
font-size: 1em;
}
}
@media only screen and (min-device-width : 320px) and (max-device-width : 740px) {
.title {
font-size: 2.1em; /* was 2.3 */
}
#content {
display: flex;
flex-flow: wrap;
display: -webkit-flex; /* Safari */
-webkit-flex-flow: wrap; /* Safari 6.1+ */
}
#col-main {
width: 100%;
display: block;
order: 1;
margin-bottom: 15px;
}
#col-left {
width: 70%;
display: block;
order: 2;
}
#noncontTitle, #contTitle {
display: block;
text-align: center;
font-size: 1.5em;
margin-bottom: 15px;
text-transform: uppercase;
}
#col-right {
width: 30%;
display: block;
order: 3;
}
#main-time-area {
display: block;
margin: 0 auto;
padding: 15px 0;
}
#ak-col, #hi-col {
width: 50%;
float: left;
}
#pr-lt {
display: block;
}
#pr-rt {
display: none;
}
#rightColBot {
border-top: 1px solid #000;
margin-top: 30px;
position: relative;
padding-top: 15px;
}
.clock-box .title {
font-size: 1.5em;
line-height: 1em;
}
.sub-title {
font-size: 1em;
}
.time-text {
font-size: 1.6em;
}
.text-xs {
font-size: 1.2em;
}
input[type=checkbox].css-checkbox + label.css-label {
font-size: 1.2em;
}
.imgSmText {
font-size: 1em;
}
}
/* ///////////////////////////////////////////////////////////
/////// STYLES AND DESIGN USED WITH PERMISSION /////////
///////////////// MORGANFEBREY.COM ////////////////////////
///////////////////////////////////////////////////////////
*/
+370
View File
@@ -0,0 +1,370 @@
timeDotGov = {};
timeDotGov.clock = {};
timeDotGov.clockController = {};
timeDotGov.clockController.dsClock = {}
timeDotGov.data = {
"then": null,
"serverTime": null,
"responseTime": null,
"RThalf": null,
"realTimeDif": null,
"requestTime": null,
"leapsecond": null,
"dststart": null,
"dstend": null,
"currYear": null,
"dstDates": null,
"leapDate": null,
"clockinstances": [],
"leapFlag": null,
"leapsec60": null,
"myhour0": "0",
"myhour1": "0",
"mysec0": "0",
"mysec1": "0",
"currentTime": null,
"twentyFour": function() {
twentyFour = document.getElementById("twenty-four");
return twentyFour.checked;
}
}
var offsetCheck = true;
var daylightTitles = false;
// CREATE ARRAY THAT WILL STORE WHICH CLOCK NUMS HAVE BEEN UPDATED
var dstClocksUpdated = [];
timeDotGov.clockController.getParameterByName = function(name) {
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
results = regex.exec(location.search);
return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
}
timeDotGov.clockController.getleapdate = function() {
var leap = new Date();
var temp1 = timeDotGov.data.leapdate.split(" ");
var leapdateYear = temp1[0];
var leapdateMonth = temp1[1];
leapdateMonth = leapdateMonth - 1; // month convention = 0-11 (Jan = 0)
var leapdateDay = temp1[2];
leap.setUTCFullYear(leapdateYear, leapdateMonth, leapdateDay);
leap.setUTCHours(0, 0, 0, 0);
timeDotGov.data.leapsecond = leap.getTime();
}
// GET TIME FROM SERVER
timeDotGov.clockController.dsClock.doRequest = function() {
d = new Date();
var xmlHttp = new XMLHttpRequest();
////////////////////////////////////////////////////////////////////////////////////
////// USE OF THIS .CGI BY OUTSIDE SITES OR APPLICATIONS IS STRICTLY PROHIBITED ////
// OR USING THE TIME FROM THIS SITE IN ANY WAY FOR OTHER SITES IS ALSO PROHIBITED //
///////////////////////////////////////////////////////////////////////////////////
xmlHttp.open("GET", "/zzz__2fbc6c3300df7e4483acd44c5044098a9fcc61d6.cgi?disablecache=" + d.getTime(), false);
xmlHttp.send(null);
return xmlHttp.responseText;
}
// GET t1, t2, t3, t4 in doRequest AND CALCULATE DELAYS IN doData
timeDotGov.clockController.checkservertime = function() {
var o = new Object();
var d = new Date();
var currentTimeObj = new Date();
timeDotGov.data.requestTime = currentTimeObj.getTime();
timeDotGov.data.currentTime = timeDotGov.clockController.dsClock.doRequest(); // GET NIST TIME FROM SERVER
timeDotGov.clockController.doData();
}
// PARSE DATA FROM AUXDATA.XML
timeDotGov.clockController.doAuxData = function() {
parser = new DOMParser();
xmlDoc = parser.parseFromString(timeDotGov.auxdata, "text/xml");
timeDotGov.data.curryear = xmlDoc.getElementsByTagName("currYear")[0].childNodes[0].nodeValue;
timeDotGov.data.dstdates = xmlDoc.getElementsByTagName("DstDates")[0].childNodes[0].nodeValue;
timeDotGov.data.leapdate = xmlDoc.getElementsByTagName("LeapDate")[0].childNodes[0].nodeValue;
}
timeDotGov.clockController.getDSTdates = function() {
var dstStartDate = new Date();
var dstEndDate = new Date();
var temp=timeDotGov.data.dstdates.split(" "); // makes an array of the string elements
var startmonth = temp[0];
startmonth = startmonth - 1; // to follow js convention of month = 0-11 (Jan = 0)
var startday = temp[1];
var endmonth = temp[2];
endmonth = endmonth - 1;
var endday = temp[3];
dstStartDate.setUTCFullYear(timeDotGov.data.curryear, startmonth, startday);
dstStartDate.setUTCHours(2, 0, 0, 0);
dstEndDate.setUTCFullYear(timeDotGov.data.curryear, endmonth, endday);
dstEndDate.setUTCHours(1, 0, 0, 0);
timeDotGov.data.dstStart = dstStartDate.getTime();
timeDotGov.data.dstEnd = dstEndDate.getTime();
}
timeDotGov.clockController.auxdata = function() {
this.doAuxData();
this.getDSTdates();
this.getleapdate();
}
timeDotGov.clockController.doData = function() {
var parser = new DOMParser();
var xmlDoc = parser.parseFromString(timeDotGov.data.currentTime, "text/xml");
var t2 = xmlDoc.getElementsByTagName("timestamp")[0].getAttribute("time2");
var t3 = xmlDoc.getElementsByTagName("timestamp")[0].getAttribute("time3");
var serverDelay = Math.round((t3 - t2) / 1000); // server delay in milliseconds
timeDotGov.data.serverTime = Math.round(t3/1000); // Server time in milliseconds
var currentTimeObj2 = new Date();
timeDotGov.data.responseTime = currentTimeObj2.getTime(); // t4
timeDotGov.data.RThalf = ((timeDotGov.data.responseTime - timeDotGov.data.requestTime) - serverDelay) / 2; // (t4 - t1) - (t3 -t2) NTP EQ
timeDotGov.data.realTimeDif = timeDotGov.data.serverTime - timeDotGov.data.responseTime; // t3-t4 used for correction to local clock for official time
////////////////////////////////////////////////////////////
// USE OFFSET CHECK VAR TO ONLY CHECK CLIENT CLOCK ONCE ///
////////////////////////////////////////////////////////////
if (offsetCheck) {
// ROUND TO MILLISECONDS - USE (* -1) to invert timediff pos/neg display value
var diff = (timeDotGov.data.realTimeDif/1000) * -1;
var diffDisplay = diff.toFixed(3);
if (diffDisplay > 0 ) {
diffDisplay = "+" + diffDisplay;
}
document.getElementById("realTimeDif").innerHTML = diffDisplay;
offsetCheck = false;
}
timeDotGov.clockController.runningclocks();
timeDotGov.data.leapFlag = "false";
} // END OF doData FUNCTION
// REFRESHES CLOCKS AT THE TOP OF EACH *ACTUAL* SECOND
timeDotGov.clockController.runningclocks = function() {
var deviceClock = new Date();
var fractionalZone =(Math.abs((deviceClock.getTimezoneOffset()/60))) % 1; // IF CLIENT IS IN A ZONE WITH A FRACTIONAL HOUR, MAKE ADJUSTMENT (MODULO 1 TO GET THE FRACTIONAL HOUR)
if (fractionalZone != 0) {
fractionalZone = (1 - fractionalZone); // SUBTRACT FROM 1 TO GET VALID CORRECTION FOR 30 AND 45 MIN ZONES
}
var fractionalZoneMilli = fractionalZone * 3600000; // CONVERT PARTIAL HOUR TO MILLISECONDS, TO BE SUBTRACTED FROM ADJUSTED TIME
var s = new Date(); // convert PC time and delay back to seconds (this is UTC)
s.setTime(s.getTime() + timeDotGov.data.realTimeDif + timeDotGov.data.RThalf - fractionalZoneMilli); // CORRECT FOR PC CLOCK ERROR, HALF NETWORK DELAY AND CLIENT IN PARTIAL TIME ZONE
var sec = s.getSeconds();
if (sec != timeDotGov.data.previousSec) { // call handleonrefresh as soon as you see a new second
timeDotGov.data.previousSec = sec;
timeDotGov.clockController.handleonrefresh(s);
}
}
// CHECK IF USER CHANGED CLOCK or IF NEW DST STATE or LEAP SECOND, THEN REFRESH ALL CLOCKS
timeDotGov.clockController.handleonrefresh = function(s) {
//var mins = s.getMinutes();
var now = s.getTime(); // convert time to ms since epoch (UTC)
//if (0 <= mins < 30) {
// now = now + fractionalZoneMilli;
//} else {
// now = now - fractionalZoneMilli;
//}
if (timeDotGov.data.then == null) {
timeDotGov.data.then = now; // if it's the first round, don't let it fail
}
var DidUserChangeClock = Math.abs(now - timeDotGov.data.then);
if (DidUserChangeClock >= 2000) { // if pc clock changed then reset (|now-then| should only be 1 s)
location.reload();
timeDotGov.data.then = now;
} else {
timeDotGov.data.then = now;
}
if (timeDotGov.data.leapsecond / 1000 == Math.floor(now / 1000)) {
timeDotGov.data.leapFlag = "true"
}
var clocks = document.getElementsByClassName("clock");
var clockNum; // SET TO VALUE OF 'i' IN THE FOLLOWING LOOP TO REPRESENT EACH CLOCK INSTANCE
// SET ALL OF THE CLOCKS BY CALLING setCurrentTime FOR EACH CLOCK INSTANCE
for(var i =0; i < clocks.length; i++ ){
var clock = clocks[i].getElementsByTagName("time")[0];
var zoneOffset = clocks[i].getElementsByTagName("time")[0].getAttribute("zoneOffset") || 0;
// GIVE VAR CLOCKNUM VALUE OF i
clockNum = i;
clock.innerHTML = timeDotGov.clock.setCurrentTime(timeDotGov.data.clockinstances[i], now, timeDotGov.data.twentyFour(), timeDotGov.data.dstStart, timeDotGov.data.dstEnd, zoneOffset, timeDotGov.data.leapFlag, timeDotGov.data.leapsec60, timeDotGov.data.RThalf, clockNum);
document.getElementById('timeUTC').innerHTML = timeDotGov.clock.setCurrentTime(timeDotGov.data.clockinstances[i], now, true, timeDotGov.data.dstStart, timeDotGov.data.dstEnd, 0, timeDotGov.data.leapFlag, timeDotGov.data.leapsec60, timeDotGov.data.RThalf, 999); // LAST VAR IS CLOCKNUM, PASSING AS '999' TO AVOID DUPLICATE 'i' VARIABLE VALUE SEND TO SETCURRENTTIME FUNCTION
}
} // END OF handleonrefresh FUNCTION
// CREATES CLOCK DIGITS AND DST/ST LABELS FOR EACH CLOCK INSTANCE
timeDotGov.clock.setCurrentTime = function(clock, now, twentyFour, dstStart, dstEnd, zoneOffset, leapFlag, leapsec60, RThalf, clockNum) {
var displayTime = new Date();
now = (now - (zoneOffset * 3600000));
if (leapFlag == "true"){
now = now - 1000; // if leap has occurred, show (seconds - 1) until next sync
var leapsec60 = "true"; // var to show a 60 instead of 59
}
displayTime.setTime(now);
var year = displayTime.getUTCFullYear();
var hourNum = displayTime.getUTCHours();
var minNum = displayTime.getMinutes();
var secNum = displayTime.getSeconds();
// CREATE ARRAY OF THE CLOCK NUMS WHO FOLLOW DST
// ALSO CREATE ARRAY OF DST LABEL CLASSES ////////////////////////
var DSTclocksArray = [ 0, 1, 6, 7, 8, 9 ]; // THESE ARE THE clockNums FOR THE ZONES THAT FOLLOW DST
var dstLabels = document.getElementsByClassName("DSTterm"); // CREATE ARRAY FOR DST LABELS
var dstLetters = document.getElementsByClassName("DSTletter"); // CREATE ARRAY FOR DST ABBREVIATION
var dstNums = document.getElementsByClassName("DSTnum"); // CREATE ARRAY FOR DST OFFSET NUM
if ( now >= dstStart && now <= dstEnd ) { // CHECK IF THIS SECOND IS DST OR NOT
// CHECK IF CLOCK NUM IS IN DST ARRAY, IF SO, ADD THE DST HOUR AND LABELS
// if ( DSTclocksArray.includes(clockNum) ) {
if ( DSTclocksArray.indexOf(clockNum) > -1 ) { // indexOf fixes a chance in ie
hourNum = hourNum + 1;
// AT END OF FUNC ADD/PUSH CLOCKNUM TO dstClocksUpdated ARRAY, IF CLOCKNUM IS IN THERE, DONT UPDATE AGAIN
// if ( !dstClocksUpdated.includes( clockNum ) ) {
if ( dstClocksUpdated.indexOf( clockNum ) == -1 ) { // indexOf fixes a chance in ie
dstLabels[clockNum].innerHTML = "DAYLIGHT";
dstLetters[clockNum].innerHTML = "D";
var standardNum = Number(dstNums[clockNum].innerHTML);
dstNums[clockNum].innerHTML = standardNum - 1;
dstClocksUpdated.push(clockNum); // ADD CLOCK NUM TO ARRAY OF CLOCKS UPDATED
}
}
}
// IF DST OR INCREMENTED HOUR GOES INTO NEXT DAY, RESET TO HOUR ZERO AND ADD A DAY
if (hourNum > 23) {
hourNum = 0;
now = (now + 3600000) // advance now by one hour
displayTime.setTime(now); // reset displaytime so day/date/month are correct with new day
}
// CHECK FOR AND IMPLEMENT LEAP SECOND
if (leapFlag == "true") {
if (leapsec60 == "true") {
if (minNum == "59") {
if (secNum == "59") { // if leapsec, and 59:59 then show 60 and reset
secNum = "60";
leapsec60 = "false";
}
}
}
}
var hourLabel = "";
// 12-HOUR OR 24-HOUR DISPLAY
// BOX NOT CHECKED SO 12-HOUR DISPLAY
if (!(twentyFour)) {
if (hourNum > 11) {
hourNum -= 12;
am_pm = "P.M.";
} else {
am_pm = "A.M.";
}
} else {
am_pm = "";
}
var newVal;
// SET HOURS
if(hourNum > 9) {
newVal = Number(String(hourNum).charAt(0));
if(timeDotGov.data.myhour0 != newVal) {
timeDotGov.data.myhour0 = newVal;
}
}
else if(Number(timeDotGov.data.myhour0) != 0) {
timeDotGov.data.myhour0 = "0";
}
if(Number(hourNum) > 9){
newVal=Number(String(hourNum).charAt(1));
if(timeDotGov.data.myhour1 != newVal) {
timeDotGov.data.myhour1 = newVal;
}
} else if (Number(timeDotGov.data.myhour1) != Number(hourNum)) {
timeDotGov.data.myhour1 = Number(hourNum);
}
if ((hourNum < 1) && (!(twentyFour))) { // if not 24hour time force the 12 so it's not 00
timeDotGov.data.myhour0 = 1;
timeDotGov.data.myhour1 = 2;
}
// SET MINUTES
if(minNum > 9) {
newVal = Number(String(minNum).charAt(0));
if (timeDotGov.data.mymin0 != newVal) {
timeDotGov.data.mymin0 = newVal;
}
} else if(Number(timeDotGov.data.mymin0) != 0) {
timeDotGov.data.mymin0 = 0;
}
if(Number(minNum) > 9) {
newVal = Number(String(minNum).charAt(1));
if(timeDotGov.data.mymin1 != newVal) {
timeDotGov.data.mymin1 = newVal;
}
}
else if(Number(timeDotGov.data.mymin1) != Number(minNum)) {
timeDotGov.data.mymin1 = Number(minNum);
}
// SET SECONDS
if(secNum > 9) {
newVal = Number(String(secNum).charAt(0));
if(timeDotGov.data.mysec0 != newVal) {
timeDotGov.data.mysec0 = newVal;
}
}
else if(Number(timeDotGov.data.mysec0) != 0) {
timeDotGov.data.mysec0 = 0;
}
if(Number(secNum) > 9) {
newVal=Number(String(secNum).charAt(1));
if(timeDotGov.data.mysec1 != newVal) {
timeDotGov.data.mysec1 = newVal;
}
}
else if(Number(timeDotGov.data.mysec1) != Number(secNum)) {
timeDotGov.data.mysec1 = Number(secNum);
}
// CREATE CLOCK DIGITS STRING TO DISPLAY
var clockdigits = (timeDotGov.data.myhour0 + "" + timeDotGov.data.myhour1 + ":" + timeDotGov.data.mymin0 + timeDotGov.data.mymin1 + ":" + timeDotGov.data.mysec0 + timeDotGov.data.mysec1);
clock = clockdigits + " " + am_pm;
return clock;
} // END OF setCurrentTime FUNCTION
+159
View File
@@ -0,0 +1,159 @@
/**
* @file
* Header and Footer styles
*
*/
/* Header Styles */
.nist-header {
background: #000;
font-family: Helvetica, Arial, sans-serif;
padding: 10px 16px 0;
font-size: 16px;
}
.nist-header__logo-link {
display: inline-block;
height: 35px;
}
.nist-header__logo-icon {
fill: #fff;
display: inline-block;
height: 16px;
position: relative;
top: -2px;
margin-right: 2px;
}
.nist-header__logo-image {
fill: #fff;
display: inline-block;
height: 24px;
width: 90px;
}
/* Limit main content area width */
.nist-main {
margin-left: auto;
margin-right: auto;
max-width: 1200px;
padding: 0 16px;
box-sizing: border-box;
}
/* Make sure body has no margin or padding when using only this header component */
body {
padding: 0;
margin: 0;
}
/* Footer styles */
.nist-footer {
background: #333333;
position: relative;
z-index: 200;
font-family: Helvetica, Arial, sans-serif;
font-size: 16px;
box-sizing: border-box;
}
.nist-footer__inner {
margin-left: auto;
margin-right: auto;
max-width: 1200px;
padding: 0 16px;
}
.nist-footer__inner:after {
content: "";
display: table;
clear: both;
}
.nist-footer {
background: #333333;
color: white;
padding: 10px 0px;
position: relative;
}
.nist-footer a {
color: white;
text-decoration: none;
}
.nist-footer .nist-footer__logo img {
/* width: 30%; 300px;*/
height: 100px;
display: inline-block;
margin-left: 5%; /*margin-left: 10%;*/
margin-right: 5%; /*margin-right: 10%auto;*/
margin-top: 5px;
}
.nist-footer__menu {
clear: both;
margin-bottom: 1px;
}
.nist-footer__menu.first {
padding-top: 20px;
}
.nist-footer__menu ul {
margin: 0;
padding: 0;
list-style: none;
text-align: center;
}
.nist-footer__menu-item {
display: inline-block;
font-size: 14px;
padding: 0;
margin-left: 0;
}
.nist-footer__menu-item:after {
content: '|';
margin-right: 1.6px;
display: inherit;
position: static;
font: inherit;
line-height: inherit;
color: inherit;
}
.nist-footer__menu-item:last-child:after {
content: none;
}
.nist-footer__menu-item a {
font-weight: normal;
margin-right: 6px;
white-space: nowrap;
padding: 0.5em 0;
display: inline-block;
}
/**
* Stick footer to bottom of page
* Source: https://css-tricks.com/couple-takes-sticky-footer/
*/
html.nist-footer-bottom,
html.nist-footer-bottom body {
height: 100%;
}
html.nist-footer-bottom body {
display: flex;
flex-direction: column;
}
html.nist-footer-bottom #main {
flex: 1 0 auto;
}
html.nist-footer-bottom .nist-footer {
flex-shrink: 0;
}
+550
View File
@@ -0,0 +1,550 @@
@charset "utf-8";
/* CSS Document */
body {
margin: 0;
font-size: 1em;
font-family: Source Sans Pro Web,Helvetica Neue,Helvetica,Roboto,Arial,sans-serif;
}
.inner {
padding: 0 5%;
}
.left {
float: left;
}
.gap {
margin-right: 10px;
}
#top-grey {
padding-bottom: .25rem;
padding-top: .25rem;
min-height: 0;
background-color: #f0f0f0;
}
#arrow {
background-repeat: no-repeat;
background-size: cover;
width: 10px;
height: 10px;
display: inline-block;
}
#arrow[aria-expanded=false] {
background-image: url(../img/arrow-down.svg);
}
#arrow[aria-expanded=true] {
background-image: url(../img/arrow-up.svg);
}
#top-grey-exp {
background-color: #f0f0f0;
height: auto;
}
.col-50 {
width: 50%;
float: left;
line-height: 1.5em;
}
.no-marg {
margin: 0;
}
.pad-4 {
padding: 4%;
}
.pad-2 {
padding: 2%;
}
#col-main .pad-2 {
padding: 2% 2% 0px 2%;
}
.clear {
clear: both;
}
.img10 {
width: 10%;
height: auto;
}
#title-area {
background-color: #000;
color: #fff;
padding: 4px 0;
vertical-align: middle;
}
.title {
font-size: 1.55em;
font-weight: 700;
}
#logo {
width: 1.25em;
height: 1.25em;
margin-right: 12px;
vertical-align: -0.12em;
}
#logo2 { /* added for USNO logo and increased margins */
width: 44px;
height: auto;
margin-left: 12px;
vertical-align: middle;
}
#clock-utc {
border: solid 1px #000;
text-align: center;
margin-bottom: 5px;
}
.ital {
font-style: italic;
text-align: center;
margin-bottom: 15px !important;
}
#myDate {
margin-top: 15px;
}
#myTime {
font-size: 1.4em;
font-weight: bold;
}
.imgSmText {
font-size: 0.7em;
line-height: 1.5em;
text-transform: uppercase;
}
#rightColBot a {
display: block;
margin-bottom: 10px;
color: #000;
}
#rightColBot {
display: block;
margin-top: 15px;
padding-top: 15px;
border-top: 1px solid #000;
}
.text-xs {
font-size: 0.8em;
margin: 5px 10px 0;
}
.text-lg {
font-size: 1.8em;
font-weight: bold;
margin: 0 10px;
}
#callout-bar {
color: #fff;
background-color: #999;
padding: .25rem 0;
text-align: center;
font-weight: bold;
}
#callout-bar p {
margin: 0;
}
#content{
display: table;
width: 100%;
}
#col-left {
width: 15%;
background-color: #d9e8f6;
display: table-cell;
}
#col-main {
width: 70%;
display: table-cell;
position: relative;
}
#col-right {
width: 15%;
background-color: #e2e2e2;
display: table-cell;
position: relative;
}
.col-25 {
width: 25%;
float: left;
}
#noncontTitle,
#contTitle {
display: none;
}
#pr-lt {
display: none;
}
/*
STYLES FOR CLOCK AREAS
*/
.clock-box {
margin-bottom: 15px;
}
#col-main .clock-box {
margin-bottom: 0;
}
.clock-box .title {
text-transform: uppercase;
font-size: 0.9em;
font-weight: bold;
/*color: #000000 !important;*/
}
.sub-title {
text-transform: uppercase;
font-size: 0.7em;
margin-bottom: 5px;
}
.time-display {
display: table;
width: 90%;
}
.color-area {
width: 5%;
display: table-cell;
}
.time-text {
background-color: #000;
font-weight: bold;
color: #fff;
font-size: 1.4em;
padding: 5px;
display: table-cell;
}
.side-img {
max-width: 90%;
display: block;
margin: 0 auto 15px;
}
.margTop15 {
margin-top: 15px;
}
#twenty-hour {
float: left;
}
#main-time-area {
background-color: #000;
width: 100%;
text-align: center;
color: #fff;
padding: 15px 0;
}
.full-img {
width: 94.5%;
display: block;
margin: 0 2.3%;
}
#mainland {
position: relative;
}
#az-clock {
position: absolute;
z-index: 100;
bottom: 0;
left: 5%;
}
.hours-wrapper {
padding: 5px;
background: #eee;
margin-bottom: 15px;
}
.striped-box {
max-height: .75rem;
max-width: .75rem;
}
#analog-clock {
max-width: 90%;
}
#responseTime {
display: none;
}
/* CUSTOM CHECKBOX STYLES */
input[type=checkbox].css-checkbox {
position:absolute; z-index:-1000; left:-1000px; overflow: hidden; clip: rect(0 0 0 0); height:1px; width:1px; margin:-1px; padding:0; border:0;
}
input[type=checkbox].css-checkbox + label.css-label {
padding-left:20px;
height:15px;
display:inline-block;
line-height:15px;
background-repeat:no-repeat;
background-position: 0 0;
vertical-align:middle;
cursor:pointer;
font-size: .7em;
}
input[type=checkbox].css-checkbox:checked + label.css-label {
background-position: 0 -15px;
}
label.css-label {
background-image:url(../img/csscheckbox.png);
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.no-saving {
background-image: url(../img/diagonal-white.png);
}
.blue .color-area {
background-color: #0060a8;
}
.blue .title {
color: #0060a8;
}
.purple .color-area {
background-color: #aa378d;
}
.purple .title {
color: #aa378d;
}
.dk-blue .color-area {
background-color: #1b0459;
}
.dk-blue .title {
color: #1b0459;
}
.orange .color-area {
background-color: #d66733;
}
.orange .title {
color: #d66733;
}
.green .color-area {
background-color: #63a952;
}
.green .title {
color: #63a952;
}
.yellow .color-area {
background-color: #e6a730;
}
.yellow .title {
color: #e6a730;
}
.red .color-area {
background-color: #cc2131;
}
.red .title {
color: #cc2131;
}
/* ///////////////////////////////////////////////////////////
////////////////////// RESPONSIVE STYLES //////////////////
///////////////////////////////////////////////////////////
*/
@media only screen and (min-width : 300px) and (max-width : 1260px) {
.title {
font-size: 2.0em; /* was 1.8 */
}
#content {
display: flex;
flex-flow: wrap;
display: -webkit-flex; /* Safari */
-webkit-flex-flow: wrap; /* Safari 6.1+ */
}
#col-main {
width: 100%;
display: block;
order: 1;
margin-bottom: 15px;
}
#col-left {
width: 70%;
display: block;
order: 2;
}
#noncontTitle, #contTitle {
display: block;
text-align: center;
font-size: 1.5em;
margin-bottom: 15px;
text-transform: uppercase;
}
#col-right {
width: 30%;
display: block;
order: 3;
}
#main-time-area {
display: block;
margin: 0 auto;
padding: 15px 0;
}
#ak-col, #hi-col {
width: 50%;
float: left;
}
#pr-lt {
display: block;
}
#pr-rt {
display: none;
}
#rightColBot {
border-top: 1px solid #000;
margin-top: 30px;
position: relative;
padding-top: 15px;
}
.clock-box .title {
font-size: 1em;
line-height: 1em;
}
.sub-title {
font-size: .7em;
}
.time-text {
font-size: 1.2em;
}
.text-xs {
font-size: 1em;
}
input[type=checkbox].css-checkbox + label.css-label {
font-size: 1em;
}
.imgSmText {
font-size: 1em;
}
}
@media only screen and (min-device-width : 320px) and (max-device-width : 740px) {
.title {
font-size: 2.1em; /* was 2.3 */
}
#content {
display: flex;
flex-flow: wrap;
display: -webkit-flex; /* Safari */
-webkit-flex-flow: wrap; /* Safari 6.1+ */
}
#col-main {
width: 100%;
display: block;
order: 1;
margin-bottom: 15px;
}
#col-left {
width: 70%;
display: block;
order: 2;
}
#noncontTitle, #contTitle {
display: block;
text-align: center;
font-size: 1.5em;
margin-bottom: 15px;
text-transform: uppercase;
}
#col-right {
width: 30%;
display: block;
order: 3;
}
#main-time-area {
display: block;
margin: 0 auto;
padding: 15px 0;
}
#ak-col, #hi-col {
width: 50%;
float: left;
}
#pr-lt {
display: block;
}
#pr-rt {
display: none;
}
#rightColBot {
border-top: 1px solid #000;
margin-top: 30px;
position: relative;
padding-top: 15px;
}
.clock-box .title {
font-size: 1.5em;
line-height: 1em;
}
.sub-title {
font-size: 1em;
}
.time-text {
font-size: 1.6em;
}
.text-xs {
font-size: 1.2em;
}
input[type=checkbox].css-checkbox + label.css-label {
font-size: 1.2em;
}
.imgSmText {
font-size: 1em;
}
}
/* ///////////////////////////////////////////////////////////
/////// STYLES AND DESIGN USED WITH PERMISSION /////////
///////////////// MORGANFEBREY.COM ////////////////////////
///////////////////////////////////////////////////////////
*/
Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 284.929 284.929"><path d="M282.082 76.51L267.808 62.24c-1.902-1.906-4.093-2.856-6.57-2.856-2.47 0-4.66.95-6.563 2.856L142.465 174.44 30.263 62.24c-1.903-1.905-4.093-2.855-6.567-2.855-2.475 0-4.665.95-6.567 2.856L2.856 76.516C.95 78.417 0 80.607 0 83.082c0 2.473.953 4.663 2.856 6.565L135.9 222.693c1.9 1.903 4.092 2.854 6.566 2.854s4.66-.95 6.562-2.854L282.082 89.647c1.902-1.903 2.847-4.093 2.847-6.565 0-2.475-.946-4.665-2.848-6.57z" fill="#005ea2"/></svg>

After

Width:  |  Height:  |  Size: 536 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 284.929 284.929"><path d="M282.082 195.285L149.028 62.24c-1.9-1.903-4.088-2.856-6.562-2.856s-4.665.953-6.567 2.856L2.855 195.285C.95 197.19 0 199.378 0 201.853c0 2.474.953 4.664 2.856 6.566l14.272 14.27c1.903 1.903 4.093 2.854 6.567 2.854s4.664-.95 6.567-2.854l112.204-112.202 112.208 112.21c1.902 1.902 4.093 2.847 6.563 2.847 2.478 0 4.668-.95 6.57-2.848l14.274-14.277c1.903-1.902 2.848-4.093 2.848-6.566 0-2.476-.944-4.666-2.846-6.57z" fill="#1a4480"/></svg>

After

Width:  |  Height:  |  Size: 539 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 54 54"><defs><style>.cls-1{fill:#2378c3;}.cls-2{fill:none;stroke:#005ea2;stroke-miterlimit:10;}</style></defs><title>dot gov icon</title><path class="cls-1" d="M36.5,20.91v1.36H35.15a0.71,0.71,0,0,1-.73.68H18.23a0.71,0.71,0,0,1-.73-0.68H16.14V20.91l10.18-4.07Zm0,13.57v1.36H16.14V34.48a0.71,0.71,0,0,1,.73-0.68h18.9A0.71,0.71,0,0,1,36.5,34.48ZM21.57,23.62v8.14h1.36V23.62h2.71v8.14H27V23.62h2.71v8.14h1.36V23.62h2.71v8.14h0.63a0.71,0.71,0,0,1,.73.68v0.68H17.5V32.45a0.71,0.71,0,0,1,.73-0.68h0.63V23.62h2.71Z"/><circle class="cls-2" cx="27" cy="27.12" r="26"/></svg>

After

Width:  |  Height:  |  Size: 651 B

@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 54 54"><defs><style>.cls-1{fill:#719f2a;}.cls-2{fill:none;stroke:#538200;stroke-miterlimit:10;}</style></defs><title>https icon</title><path class="cls-1" d="M34.72,34.84a1.29,1.29,0,0,1-1.29,1.29H20.57a1.29,1.29,0,0,1-1.29-1.29V27.12a1.29,1.29,0,0,1,1.29-1.29H21V23.26a6,6,0,0,1,12,0v2.57h0.43a1.29,1.29,0,0,1,1.29,1.29v7.72Zm-4.29-9V23.26a3.43,3.43,0,0,0-6.86,0v2.57h6.86Z"/><circle class="cls-2" cx="27" cy="27.12" r="26"/></svg>

After

Width:  |  Height:  |  Size: 518 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 36 KiB

@@ -0,0 +1,161 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 272 112" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-miterlimit:3.84999;" shape-rendering="geometricPrecision">
<g transform="matrix(1,0,0,1,0,-2129.92)">
<g id="Lines">
</g>
<g id="Hawaii---New-Colors" serif:id="Hawaii - New Colors" transform="matrix(1.86867,0,0,1.75,-964.299,2625.17)">
<rect x="516.034" y="-283" width="145.558" height="64" style="fill:none;"/>
<g transform="matrix(0.374018,0,0,0.399381,595.786,-396.165)">
<g transform="matrix(22.8926,0,0,22.8926,63.0186,371.75)">
<path d="M0.543,-0.311L0.543,-0L0.689,-0L0.689,-0.723L0.543,-0.723L0.543,-0.433L0.227,-0.433L0.227,-0.723L0.081,-0.723L0.081,-0L0.227,-0L0.227,-0.311L0.543,-0.311Z" style="fill-rule:nonzero;"/>
</g>
<g transform="matrix(22.8926,0,0,22.8926,80.6459,371.75)">
<rect x="0.081" y="-0.723" width="0.146" height="0.723" style="fill-rule:nonzero;"/>
</g>
</g>
<g transform="matrix(0.93185,0,0,1.12321,36.9225,37.8028)">
<g transform="matrix(0.10475,0,0,0.0927968,548.768,-284.626)">
<path d="M608.98,451.46L616.4,446.42L621.6,441.8L626,441.25L631.76,437.13L636.13,430.51L642.37,425.4L646.45,423.64L648.18,417.03L644.89,410.92L639.62,400.74L638.61,392.2L641.81,380.23L647.91,378.31L656.11,381.93L665.25,387.83L669.84,393.21L679.65,400.33L685.35,405.41L691.57,404.95L700.1,408.57L717.33,417.74L734.85,427.79L749.89,441.35L755.84,448.32L761.2,455.44L759.22,467.66L759.12,475.25L765.86,474.06L771.97,474.74L775.2,482.69L775.4,491.74L779.25,496.57L793.27,506.66L799,510.21L798.31,514.76L787.76,524.33L776.35,531.29L763.55,537.97L748.3,542.85L740.65,545.58L733.24,546.02L724.18,543.02L716.13,545.9L708.01,551.63L703.15,552.35L692.45,558.98L684.06,560.32L675.05,570.14L668.47,579.13L664.19,584.52L659.92,588.57L654.16,591.77L646.45,582.82L634.53,574.58L622.98,569.79L618.86,559.55L620.53,547.18L623.35,535.04L625.87,528.96L627.02,521.37L624.93,510.55L623.11,504.32L619.45,498.78L617.8,487.71L617.29,479.45L614.8,473.74L610.02,472.07L609.66,469.02L605.83,458.93L608.98,451.46ZM471.91,257.03L479.82,255.39L490.95,257.42L500.35,263.37L505.36,268.88L509,276.13L503.36,284.3L494.94,286.43L491.93,286.92L482.73,286.54L479.96,279.75L480.72,271.82L478.28,267.12L471.3,261.51L471.91,257.03ZM527.63,262.44L531.64,253.47L541.98,247.14L550.56,249.96L556.28,260.34L561.1,269.82L573.45,268.43L584.9,264.87L598.72,267.77L605.34,275.75L614.43,281.59L624.86,290.76L633.33,294.05L635.27,302.36L631.94,309.3L624.87,313.77L610.86,318.14L598.39,316.61L586.32,320.33L571.52,321.53L563.44,317.51L562.62,308.28L561.75,295.43L561.47,292.67L560.36,286.73L555.49,284.8L550.2,287.52L547.72,286.03L536.39,279.17L528.96,267.33L527.63,262.44ZM525.69,319.24L535.58,315.03L542.55,314.07L545.85,318.36L545.45,326.6L537.92,328.26L524.96,327.87L520.26,322.95L525.69,319.24ZM442.84,214.72L447.58,211.18L446.67,204.53L455.57,206.62L466.63,210.78L480.04,214.06L484.19,213L489.07,211.76L492.33,212.16L497.98,219.65L498.48,219.71L510.23,221.27L525.71,221.76L530.51,225.1L524.98,231.95L514.47,237.73L502.56,239.15L491.02,235.13L480.28,230.23L472.11,227.2L457.85,227.27L445.3,226.23L436.58,222.96L442.84,214.72ZM304.39,129.64L320.8,131.33L328.65,125.78L334,119.63L343.7,115.04L347.42,114.77L352.93,125.26L358.65,138.05L363.94,145.93L361.37,155.64L365.7,160.39L373.69,158.07L380.12,159.22L378.73,167.92L379.96,175.19L388.13,183.3L387.54,183.94L384.27,187.52L379.71,190.15L371.19,186.85L367.44,188.4L362.72,188.99L356.88,183.99L351.32,179.79L342.09,177.65L337.43,176.51L330.68,176.68L320.91,177.04L320.65,177.05L318.33,176.89L315.27,169.04L314.19,164.29L308.83,158.75L308.83,154.65L302.28,144.65L303.31,139.03L297.31,129.5L304.39,129.64ZM1,49.47L5.92,38.42L12.05,34.05L21.34,31.28L23.93,29.4L26.15,24.57L32.53,24.25L34.43,25.64L35.2,28.21L31.87,30.43L28.65,35.79L28.93,40.82L21.58,42.78L16.52,43.42L13.17,46.26L10.3,49.73L6.82,56.05L3.32,53.89L1,49.47ZM77.87,21.23L84.49,17.01L87.71,10.71L91.96,8.24L107.2,3.77L112.36,1L117.96,2.11L122.59,5.68L126.65,2.12L134.99,4.85L139.72,3.53L145.53,7.32L152.31,13.24L153.9,23.06L148.67,32.13L145.76,34.86L145.1,41.52L143.24,48.93L135.11,53.93L125.02,59.46L112.85,55.26L105.79,52.8L101.39,52.25L95.26,44.71L87.04,39.13L80.26,35.49L76.35,28.36L77.87,21.23Z" style="fill:rgb(211,111,175);"/>
</g>
<g id="No-Daylight-Savings" serif:id="No Daylight Savings" transform="matrix(0.689936,0,0,0.611207,461.087,-486.077)">
<clipPath id="_clip1">
<path d="M219.545,398.139L220.671,397.373L221.461,396.672L222.129,396.588L223.003,395.963L223.667,394.958L224.614,394.182L225.234,393.915L225.496,392.911L224.997,391.984L224.197,390.438L224.043,389.141L224.529,387.324L225.455,387.032L226.7,387.582L228.088,388.478L228.785,389.295L230.274,390.376L231.14,391.147L232.084,391.077L233.379,391.627L235.995,393.019L238.655,394.545L240.939,396.604L241.842,397.662L242.656,398.743L242.355,400.598L242.34,401.75L243.363,401.57L244.291,401.673L244.781,402.88L244.812,404.254L245.396,404.987L247.525,406.519L248.395,407.058L248.29,407.749L246.688,409.202L244.956,410.259L243.013,411.273L240.697,412.014L239.536,412.428L238.411,412.495L237.035,412.04L235.813,412.477L234.58,413.347L233.842,413.456L232.218,414.463L230.944,414.666L229.576,416.157L228.577,417.522L227.927,418.34L227.279,418.955L226.404,419.441L225.234,418.082L223.424,416.831L221.67,416.104L221.045,414.549L221.298,412.671L221.727,410.828L222.109,409.905L222.284,408.753L221.966,407.11L221.69,406.164L221.134,405.323L220.884,403.642L220.807,402.388L220.429,401.521L219.703,401.268L219.648,400.805L219.067,399.273L219.545,398.139ZM198.734,368.619L199.935,368.37L201.625,368.678L203.052,369.582L203.813,370.418L204.365,371.519L203.509,372.759L202.231,373.083L201.774,373.157L200.377,373.099L199.956,372.069L200.072,370.865L199.701,370.151L198.642,369.299L198.734,368.619ZM207.194,369.44L207.803,368.079L209.373,367.118L210.675,367.546L211.544,369.122L212.275,370.561L214.151,370.35L215.889,369.809L217.987,370.25L218.992,371.461L220.372,372.348L221.956,373.74L223.242,374.24L223.536,375.501L223.031,376.555L221.957,377.234L219.83,377.897L217.937,377.665L216.105,378.23L213.857,378.412L212.631,377.801L212.506,376.4L212.374,374.449L212.332,374.03L212.163,373.128L211.424,372.835L210.621,373.248L210.244,373.022L208.524,371.98L207.396,370.183L207.194,369.44ZM206.899,378.064L208.401,377.425L209.459,377.279L209.96,377.931L209.899,379.182L208.756,379.434L206.788,379.374L206.075,378.627L206.899,378.064ZM194.321,362.195L195.04,361.658L194.902,360.648L196.253,360.966L197.932,361.597L199.968,362.095L200.599,361.934L201.339,361.746L201.834,361.807L202.692,362.944L202.768,362.953L204.552,363.19L206.902,363.264L207.631,363.771L206.792,364.811L205.196,365.689L203.388,365.904L201.636,365.294L200.005,364.55L198.764,364.09L196.599,364.101L194.694,363.943L193.37,363.446L194.321,362.195ZM173.3,349.278L175.792,349.535L176.984,348.692L177.796,347.758L179.269,347.061L179.833,347.02L180.67,348.613L181.538,350.555L182.342,351.751L181.951,353.225L182.609,353.947L183.822,353.594L184.798,353.769L184.587,355.09L184.774,356.194L186.014,357.425L185.925,357.522L185.428,358.066L184.736,358.465L183.442,357.964L182.873,358.199L182.156,358.289L181.27,357.53L180.425,356.892L179.024,356.567L178.317,356.394L177.292,356.42L175.808,356.475L175.769,356.476L175.417,356.452L174.952,355.26L174.788,354.539L173.974,353.698L173.974,353.075L172.98,351.557L173.136,350.704L172.225,349.257L173.3,349.278ZM127.238,337.106L127.985,335.428L128.916,334.765L130.326,334.344L130.719,334.059L131.056,333.326L132.025,333.277L132.313,333.488L132.43,333.878L131.925,334.215L131.436,335.029L131.478,335.793L130.362,336.09L129.594,336.188L129.086,336.619L128.65,337.146L128.122,338.105L127.59,337.777L127.238,337.106ZM138.909,332.819L139.914,332.178L140.403,331.221L141.048,330.846L143.362,330.168L144.145,329.747L144.995,329.916L145.698,330.458L146.315,329.917L147.581,330.332L148.299,330.131L149.181,330.707L150.211,331.605L150.452,333.096L149.658,334.473L149.216,334.888L149.116,335.899L148.834,337.024L147.599,337.783L146.067,338.623L144.22,337.985L143.148,337.612L142.48,337.528L141.549,336.383L140.301,335.536L139.272,334.984L138.678,333.901L138.909,332.819Z"/>
</clipPath>
<g clip-path="url(#_clip1)">
<g id="Lines1" serif:id="Lines" transform="matrix(1,0,0,1,2,-7)">
<g id="No-Daylight-Savings1" serif:id="No Daylight Savings">
<g id="Lines2" serif:id="Lines">
<g transform="matrix(0.978609,-0.96912,-0.978603,-0.969126,176.003,588.009)">
<path d="M71.318,155.942L172.818,155.942" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(0.978616,-0.969113,-0.978596,-0.969133,174.834,590.801)">
<path d="M73.355,153.905L174.855,153.905" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(0.978602,-0.969127,-0.97861,-0.969119,173.67,593.592)">
<path d="M75.392,151.868L176.892,151.868" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(0.978606,-0.969124,-0.978606,-0.969123,172.5,596.385)">
<path d="M77.43,149.83L178.93,149.83" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(0.978603,-0.969126,-0.978609,-0.96912,171.333,599.177)">
<path d="M79.467,147.793L180.967,147.793" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(0.978582,-0.969147,-0.97863,-0.9691,170.172,601.968)">
<path d="M81.503,145.757L183.003,145.757" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(0.978606,-0.969123,-0.978606,-0.969123,168.998,604.759)">
<path d="M83.54,143.721L185.039,143.721" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(0.978606,-0.969123,-0.978606,-0.969123,167.831,607.549)">
<path d="M85.575,141.685L187.075,141.685" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(0.978609,-0.96912,-0.978603,-0.969127,166.663,610.339)">
<path d="M87.611,139.649L189.111,139.649" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(0.978606,-0.969123,-0.978606,-0.969123,165.497,613.129)">
<path d="M89.646,137.613L191.147,137.613" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(0.978609,-0.96912,-0.978603,-0.969126,164.329,615.919)">
<path d="M91.682,135.578L193.182,135.578" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(0.978606,-0.969124,-0.978606,-0.969123,163.163,618.709)">
<path d="M93.718,133.542L195.217,133.542" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(0.978609,-0.96912,-0.978603,-0.969127,161.996,621.499)">
<path d="M95.754,131.507L197.253,131.507" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(0.978606,-0.969123,-0.978606,-0.969123,160.829,624.289)">
<path d="M97.789,129.471L199.289,129.471" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(0.978603,-0.969126,-0.978609,-0.969121,159.663,627.079)">
<path d="M99.825,127.435L201.325,127.435" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(0.978602,-0.969127,-0.97861,-0.96912,158.497,629.869)">
<path d="M101.861,125.399L203.361,125.399" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(1.38396,-8.39218e-17,8.4743e-17,1.37055,-26.0821,190.525)">
<path d="M118.761,161.9L190.532,90.129L193.183,87.478" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(1.38396,-8.39218e-17,8.4743e-17,1.37055,-26.0821,194.05)">
<path d="M120.796,161.364L192.568,89.592L196.719,85.441" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(1.38396,-8.39218e-17,8.4743e-17,1.37055,-26.0821,197.915)">
<path d="M122.832,160.579L194.603,88.808L200.004,83.406" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(1.38396,-8.39218e-17,8.4743e-17,1.37055,-26.0821,201.782)">
<path d="M124.868,159.793L196.639,88.021L203.29,81.371" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(1.38396,-8.39218e-17,8.4743e-17,1.37055,-26.0821,205.308)">
<path d="M126.903,159.257L198.675,87.485L206.826,79.335" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(1.38396,-8.39218e-17,8.4743e-17,1.37055,-26.0821,218.253)">
<path d="M128.939,151.847L200.71,80.076L203.486,77.3" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(0.978606,-0.969123,-0.978606,-0.969123,150.327,649.399)">
<path d="M116.11,111.149L217.61,111.149" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(0.978606,-0.969124,-0.978606,-0.969123,149.159,652.189)">
<path d="M118.146,109.113L219.646,109.113" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(0.978602,-0.969127,-0.97861,-0.96912,147.994,654.979)">
<path d="M120.182,107.078L221.682,107.078" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(0.978603,-0.969126,-0.978609,-0.96912,146.826,657.769)">
<path d="M122.218,105.042L223.717,105.042" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(0.978599,-0.96913,-0.978613,-0.969117,145.66,660.559)">
<path d="M124.252,103.006L225.753,103.006" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(0.978606,-0.969123,-0.978606,-0.969123,144.492,663.349)">
<path d="M126.289,100.971L227.789,100.971" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(0.978606,-0.969123,-0.978606,-0.969123,143.325,666.139)">
<path d="M128.325,98.936L229.825,98.936" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(0.978606,-0.969123,-0.978606,-0.969123,142.158,668.929)">
<path d="M130.36,96.899L231.86,96.899" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(0.978612,-0.969117,-0.9786,-0.969129,140.989,671.719)">
<path d="M132.396,94.864L233.896,94.864" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(0.978606,-0.969123,-0.978606,-0.969123,139.824,674.509)">
<path d="M134.432,92.828L235.932,92.828" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(0.978613,-0.969116,-0.978599,-0.96913,138.655,677.298)">
<path d="M136.467,90.792L237.967,90.792" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(0.978606,-0.969123,-0.978606,-0.969123,137.489,680.089)">
<path d="M138.504,88.756L240.002,88.756" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(0.978603,-0.969126,-0.978609,-0.96912,136.323,682.88)">
<path d="M140.539,86.721L242.039,86.721" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(0.978603,-0.969127,-0.978609,-0.96912,135.157,685.67)">
<path d="M142.575,84.686L244.074,84.686" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(0.978606,-0.969123,-0.978606,-0.969123,133.989,688.46)">
<path d="M144.61,82.649L246.11,82.649" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(0.978605,-0.969124,-0.978607,-0.969123,132.822,691.249)">
<path d="M146.646,80.614L248.146,80.614" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
<g transform="matrix(0.978606,-0.969123,-0.978606,-0.969123,131.655,694.039)">
<path d="M148.682,78.578L250.182,78.578" style="fill:none;fill-rule:nonzero;stroke:rgb(158,30,126);stroke-width:1.21px;"/>
</g>
</g>
</g>
</g>
</g>
</g>
<g id="Outline" transform="matrix(0.10475,0,0,0.0927968,548.768,-284.626)">
<path d="M608.98,451.46L616.4,446.42L621.6,441.8L626,441.25L631.76,437.13L636.13,430.51L642.37,425.4L646.45,423.64L648.18,417.03L644.89,410.92L639.62,400.74L638.61,392.2L641.81,380.23L647.91,378.31L656.11,381.93L665.25,387.83L669.84,393.21L679.65,400.33L685.35,405.41L691.57,404.95L700.1,408.57L717.33,417.74L734.85,427.79L749.89,441.35L755.84,448.32L761.2,455.44L759.22,467.66L759.12,475.25L765.86,474.06L771.97,474.74L775.2,482.69L775.4,491.74L779.25,496.57L793.27,506.66L799,510.21L798.31,514.76L787.76,524.33L776.35,531.29L763.55,537.97L748.3,542.85L740.65,545.58L733.24,546.02L724.18,543.02L716.13,545.9L708.01,551.63L703.15,552.35L692.45,558.98L684.06,560.32L675.05,570.14L668.47,579.13L664.19,584.52L659.92,588.57L654.16,591.77L646.45,582.82L634.53,574.58L622.98,569.79L618.86,559.55L620.53,547.18L623.35,535.04L625.87,528.96L627.02,521.37L624.93,510.55L623.11,504.32L619.45,498.78L617.8,487.71L617.29,479.45L614.8,473.74L610.02,472.07L609.66,469.02L605.83,458.93L608.98,451.46ZM471.91,257.03L479.82,255.39L490.95,257.42L500.35,263.37L505.36,268.88L509,276.13L503.36,284.3L494.94,286.43L491.93,286.92L482.73,286.54L479.96,279.75L480.72,271.82L478.28,267.12L471.3,261.51L471.91,257.03ZM527.63,262.44L531.64,253.47L541.98,247.14L550.56,249.96L556.28,260.34L561.1,269.82L573.45,268.43L584.9,264.87L598.72,267.77L605.34,275.75L614.43,281.59L624.86,290.76L633.33,294.05L635.27,302.36L631.94,309.3L624.87,313.77L610.86,318.14L598.39,316.61L586.32,320.33L571.52,321.53L563.44,317.51L562.62,308.28L561.75,295.43L561.47,292.67L560.36,286.73L555.49,284.8L550.2,287.52L547.72,286.03L536.39,279.17L528.96,267.33L527.63,262.44ZM525.69,319.24L535.58,315.03L542.55,314.07L545.85,318.36L545.45,326.6L537.92,328.26L524.96,327.87L520.26,322.95L525.69,319.24ZM442.84,214.72L447.58,211.18L446.67,204.53L455.57,206.62L466.63,210.78L480.04,214.06L484.19,213L489.07,211.76L492.33,212.16L497.98,219.65L498.48,219.71L510.23,221.27L525.71,221.76L530.51,225.1L524.98,231.95L514.47,237.73L502.56,239.15L491.02,235.13L480.28,230.23L472.11,227.2L457.85,227.27L445.3,226.23L436.58,222.96L442.84,214.72ZM304.39,129.64L320.8,131.33L328.65,125.78L334,119.63L343.7,115.04L347.42,114.77L352.93,125.26L358.65,138.05L363.94,145.93L361.37,155.64L365.7,160.39L373.69,158.07L380.12,159.22L378.73,167.92L379.96,175.19L388.13,183.3L387.54,183.94L384.27,187.52L379.71,190.15L371.19,186.85L367.44,188.4L362.72,188.99L356.88,183.99L351.32,179.79L342.09,177.65L337.43,176.51L330.68,176.68L320.91,177.04L320.65,177.05L318.33,176.89L315.27,169.04L314.19,164.29L308.83,158.75L308.83,154.65L302.28,144.65L303.31,139.03L297.31,129.5L304.39,129.64ZM1,49.47L5.92,38.42L12.05,34.05L21.34,31.28L23.93,29.4L26.15,24.57L32.53,24.25L34.43,25.64L35.2,28.21L31.87,30.43L28.65,35.79L28.93,40.82L21.58,42.78L16.52,43.42L13.17,46.26L10.3,49.73L6.82,56.05L3.32,53.89L1,49.47ZM77.87,21.23L84.49,17.01L87.71,10.71L91.96,8.24L107.2,3.77L112.36,1L117.96,2.11L122.59,5.68L126.65,2.12L134.99,4.85L139.72,3.53L145.53,7.32L152.31,13.24L153.9,23.06L148.67,32.13L145.76,34.86L145.1,41.52L143.24,48.93L135.11,53.93L125.02,59.46L112.85,55.26L105.79,52.8L101.39,52.25L95.26,44.71L87.04,39.13L80.26,35.49L76.35,28.36L77.87,21.23Z" style="fill:none;stroke:black;stroke-width:9.59px;stroke-linejoin:round;stroke-miterlimit:2;"/>
</g>
</g>
</g>
<g id="Time_Zones1">
</g>
<g id="States">
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 25 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 390 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

+22
View File
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 11 11" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(1,0,0,1,-58.7899,-489.903)">
<g id="Lines">
</g>
<g id="Artboard5" transform="matrix(0.55,0,0,0.270127,40.6399,374.754)">
<rect x="33" y="426.278" width="20" height="40.722" style="fill:none;"/>
<clipPath id="_clip1">
<rect x="33" y="426.278" width="20" height="40.722"/>
</clipPath>
<g clip-path="url(#_clip1)">
<g transform="matrix(1.81818,0,0,3.70196,-73.8908,-1387.32)">
<rect x="58.79" y="489.903" width="11" height="11" style="fill:white;"/>
</g>
<g transform="matrix(1.98807,0,0,4.04786,-42.4869,-1558.5)">
<path d="M37.97,493.257L37.97,490.327L40.928,490.327L37.97,493.257ZM37.97,496.021L43.719,490.327L46.704,490.327L37.97,498.976L37.97,496.021ZM39.335,500.387L48.03,491.776L48.03,494.732L42.319,500.387L39.335,500.387ZM45.111,500.387L48.03,497.496L48.03,500.387L45.111,500.387Z"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 B

+369
View File
@@ -0,0 +1,369 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="title" content="SethPC Official Time" />
<meta property="og:site_name" content="SethPC" />
<meta property="og:type" content="website" />
<meta property="og:url" content="https://clock.sethpc.xyz/timegov/" />
<meta property="og:title" content="Official U.S. Time | SethPC" />
<meta property="og:description" content="Self-hosted official U.S. time style display with U.S. map and synchronized clocks." />
<meta property="og:image" content="/timegov/img/map-elements/united-states-map.svg" />
<meta name="dcterms.title" content="SethPC Official Time" />
<meta name="description" content="Self-hosted official U.S. time display with synchronized clock data." />
<meta name="dcterms.subject" content="Working with industry and science to advance innovation and improve quality of life." />
<meta name="google-site-verification" content="" />
<meta name="dcterms.type" content="text" />
<meta name="dcterms.format" content="text/html" />
<meta name="dcterms.identifier" content="https://clock.sethpc.xyz/timegov/" />
<meta name="twitter:title" content="Official U.S. Time | SethPC" />
<meta name="twitter:description" content="Self-hosted official U.S. time style display with synchronized clocks." />
<meta name="twitter:image" content="/img/map-elements/og_map.png" />
<title>SethPC Official Time</title>
<link rel="shortcut icon" href="https://storage.googleapis.com/sethfreiberg.com/sethflix/favicon.png" />
<link rel="stylesheet" href="css/main.css" />
<link rel="stylesheet" href="css/NIST-styles.css" />
<!-- FUNC FOR TOP BAR INFO BTN -->
<script type="text/javascript">
function info_exp(id, id2) {
var n = document.getElementById(id);
if (n.style.display != 'none') {
n.style.display = 'none';
document.getElementById(id2).setAttribute('aria-expanded', 'false');
} else {
n.style.display = 'block';
document.getElementById(id2).setAttribute('aria-expanded', 'true');
}
}
</script>
</head>
<body>
<header>
<!-- TOP GREY BAR -->
<div id="top-grey">
<div class="inner">
<img src="img/us_flag_small.png" class="left gap" alt="U.S. Flag"><span class="left gap">Official self-hosted time service</span> <div id="info-btn"><a href="index.html#" onclick="info_exp('top-grey-exp', 'arrow');">Service details <span id="arrow" aria-expanded="false"></span></a></div>
</div>
</div>
<!-- EXPANDED TOP GREY BAR -->
<div id="top-grey-exp" style="display:none;">
<div class="inner">
<div class="col-50">
<div class="pad-4">
<img src="img/icon-dot-gov.svg" alt="icon dot gov" class="img10"><br>
<strong>Self-hosted service notice.</strong>
<p class="no-marg">This is a privately hosted clock service designed to mirror the time.gov experience in a homelab environment.</p>
</div>
</div>
<div class="col-50">
<div class="pad-4">
<img src="img/icon-https.svg" alt="icon https" class="img10"><br>
<strong>Encrypted transport.</strong>
<p class="no-marg">The <strong>https://</strong> indicates encrypted traffic between your browser and this host.</p>
</div>
</div>
<div class="clear"></div>
</div>
</div>
<!-- BLACK TITLE BAR AREA -->
<div id="title-area">
<div class="inner">
<center>
<span class="title"><img src="https://storage.googleapis.com/sethfreiberg.com/sethflix/favicon.png" id="logo" alt="SethPC"/>OFFICIAL U.S. TIME</span>
</center>
</div>
</div>
<!-- ANNOUNCEMENT GREY BAR -->
<div id="callout-bar"><p></p></div> <!-- Blank between <p> </p> unless there is an upcoming announcement -->
</header>
<!-- MAIN CONTENT AREA -->
<div id="content">
<!-- LEFT COLUMN -->
<div id="col-left">
<div class="pad-2">
<div id="noncontTitle">Non-Contiguous U.S. and Territories</div>
<div id="ak-col">
<!-- ALASKA TIME -->
<div class="clock-box blue dst-clock">
<div class="title">Alaska <span class="DSTterm">Standard</span> Time</div>
<div class="sub-title">AK<span class="DSTletter">S</span>T (UTC-<span class="DSTnum">9</span>)</div>
<div class="time-display">
<div class="color-area"></div>
<div class="clock time-text"><time zoneOffset=9></time></div>
</div>
</div>
<!-- ALASKA MAP GRAPHIC -->
<img src="img/map-elements/alaska.svg" class="side-img" alt="Alaska Map">
<!-- ALEUTIAN TIME -->
<div class="clock-box blue dst-clock">
<div class="title">Aleutian <span class="DSTterm">Standard</span> Time</div>
<div class="sub-title">HA<span class="DSTletter">S</span>T (UTC-<span class="DSTnum">10</span>)</div>
<div class="time-display">
<div class="color-area"></div>
<div class="clock time-text"><time zoneOffset=10></time></div>
</div>
<!-- <div class="null"><span class="DSTterm"></span><span class="DSTletter"></span><span class="DSTnum"></span></div> -->
</div>
</div>
<div id="hi-col">
<!-- HAWAIIAN TIME -->
<div class="clock-box purple">
<div class="title">Hawaii Standard Time</div>
<div class="sub-title">HST (UTC-10)</div>
<div class="time-display">
<div class="color-area no-saving"></div>
<div class="clock time-text"><time zoneOffset=10></time></div>
</div>
<div class="null"><span class="DSTterm"></span><span class="DSTletter"></span><span class="DSTnum"></span></div>
</div>
<!-- HAWAII MAP GRAPHIC -->
<img src="img/map-elements/hawaii.svg" class="side-img" alt="Hawaii Map">
<!-- SAMOA TIME -->
<div class="clock-box dk-blue">
<div class="title">Samoa Standard Time</div>
<div class="sub-title">SST (UTC-11)</div>
<div class="time-display">
<div class="color-area no-saving"></div>
<div class="clock time-text"><time zoneOffset=11></time></div>
</div>
<div class="null"><span class="DSTterm"></span><span class="DSTletter"></span><span class="DSTnum"></span></div>
</div>
<!-- CHAMORRO TIME -->
<div class="clock-box dk-blue">
<div class="title">Chamorro Standard Time</div>
<div class="sub-title">CHST (UTC+10)</div>
<div class="time-display">
<div class="color-area no-saving"></div>
<div class="clock time-text"><time zoneOffset=-10></time></div>
</div>
<div class="null"><span class="DSTterm"></span><span class="DSTletter"></span><span class="DSTnum"></span></div>
</div>
<!-- PUERTO RICO LT COL-->
<div id="pr-lt" class="pad-2 margTop15">
<div class="clock-box dk-blue">
<div class="title">Atlantic Standard Time</div>
<div class="sub-title"><b>Puerto Rico / US Virgin Islands</b></div>
<div class="sub-title">AST (UTC-4)</div>
<div class="time-display">
<div class="color-area no-saving"></div>
<div class="clock time-text"><time zoneOffset=4></time></div>
</div>
<div class="null"><span class="DSTterm"></span><span class="DSTletter"></span><span class="DSTnum"></span></div>
</div> </div>
</div>
</div>
</div>
<!-- MAIN COLUMN -->
<div id="col-main">
<div class="pad-2">
<div style="height: 0px;">&nbsp;</div>
<!-- ROW FOR MAINLAND US CLOCK AREA -->
<div class="main-top">
<!-- PACIFIC TIME -->
<div class="col-25">
<div class="pad-2">
<div class="clock-box orange dst-clock">
<div class="title">Pacific <span class="DSTterm">Standard</span> Time</div>
<div class="sub-title">P<span class="DSTletter">S</span>T (UTC-<span class="DSTnum">8</span>)</div>
<div class="time-display">
<div class="color-area"></div>
<div class="clock time-text"><time zoneOffset=8 />SYNCHRONIZING</time></div></div>
</div>
</div>
</div>
</div>
<!-- MOUNTAIN TIME -->
<div class="col-25">
<div class="pad-2">
<div class="clock-box green dst-clock">
<div class="title">Mountain <span class="DSTterm">Standard</span> Time</div>
<div class="sub-title">M<span class="DSTletter">S</span>T (UTC-<span class="DSTnum">7</span>)</div>
<div class="time-display">
<div class="color-area"></div>
<div class="clock time-text"><time zoneOffset=7>SYNCHRONIZING</time></div>
</div>
</div>
</div>
</div>
<!-- CENTRAL TIME -->
<div class="col-25">
<div class="pad-2">
<div class="clock-box yellow dst-clock">
<div class="title">Central <span class="DSTterm">Standard</span> Time</div>
<div class="sub-title">C<span class="DSTletter">S</span>T (UTC-<span class="DSTnum">6</span>)</div>
<div class="time-display">
<div class="color-area"></div>
<div class="clock time-text"><time zoneOffset=6>SYNCHRONIZING</time></div>
</div>
</div>
</div>
</div>
<!-- EASTERN TIME -->
<div class="col-25">
<div class="pad-2">
<div class="clock-box red dst-clock">
<div class="title">Eastern <span class="DSTterm">Standard</span> Time</div>
<div class="sub-title">E<span class="DSTletter">S</span>T (UTC-<span class="DSTnum">5</span>)</div>
<div class="time-display">
<div class="color-area"></div>
<div class="clock time-text"><time zoneOffset=5>SYNCHRONIZING</time></div>
</div>
</div>
</div>
</div>
<div class="clear"></div>
</div>
<!-- END TOP ROW -->
<div id="mainland">
<!-- ARIZONA/MOUNTAIN STANDARD TIME -->
<div id="az-clock" class="col-25">
<div class="pad-2">
<div class="clock-box green">
<div class="title">Arizona Mountain<br>Standard Time</div>
<div class="sub-title">MST (UTC-7)</div>
<div class="time-display">
<div class="color-area no-saving"></div>
<div class="clock time-text"><time zoneOffset=7></time></div>
</div>
<div class="null"><span class="DSTterm"></span><span class="DSTletter"></span><span class="DSTnum"></span></div>
</div>
</div>
</div>
<!-- BEGIN MAIN MAP AREA -->
<img src="img/map-elements/united-states-map.svg" class="full-img" alt="United States Time Zone Map">
</div>
</div>
<!-- RIGHT COLUMN -->
<div id="col-right">
<div class="pad-4">
<!-- 24HR CHECKBOX -->
<div class='hours-wrapper'>
<div class='am-pm'></div>
<input id='twenty-four' type='checkbox' class="css-checkbox">
<label class='css-label' for='twenty-four'>24-Hour Clock Display</label>
<div class="clear"></div>
</div>
<div id="clock-utc">
<p class="text-xs">Coordinated Universal Time (UTC)</p>
<div class="clock text-lg"><time id="timeUTC"></time></div>
<div class="null"><span class="DSTterm"></span><span class="DSTletter"></span><span class="DSTnum"></span></div>
</div>
<p class="sub-title ital">UTC is always displayed as a 24-hour clock.</p>
<!-- MAIN CLOCK AREA -->
<div id="main-time-area">
<p>Your Device's Clock <span id="myTimeTitle">UTC-0</span> <br>
<div id='myDate'></div>
<canvas id="analog-clock" width=200 height=200></canvas>
<br>
<span id="myTime"></span><br><br>
Your clock is off by: <br><span id="realTimeDif"></span> s</p>
</div>
<!-- PUERTO RICO RT COL-->
<div id="pr-rt" class="pad-2 margTop15">
<div class="clock-box dk-blue">
<div class="title">Atlantic Standard Time</div>
<div class="sub-title"><b>Puerto Rico / US Virgin Islands</b></div>
<div class="sub-title">AST (UTC-4)</div>
<div class="time-display">
<div class="color-area no-saving"></div>
<div class="clock time-text"><time zoneOffset=4></time></div>
</div>
<div class="null"><span class="DSTterm"></span><span class="DSTletter"></span><span class="DSTnum"></span></div>
</div>
</div>
<div id="rightColBot">
<a href="/simple/">Simple View</a>
<a href="/decimal/">Decimal Time</a>
<a href="/seth/">Seth Date</a>
<a href="/calendar/">Seth Calendar</a>
<a href="/api/time">Time API</a>
<div class="imgSmText">
<img src="img/stripes.svg" class="striped-box" alt="Hashed image for Daylight Saving Time not observed"> = DAYLIGHT SAVING TIME NOT OBSERVED
<p>Clocks are corrected for network delay</p>
</div>
</div>
</div>
</div>
<div class="clear"></div>
</div>
<footer class="nist-footer">
<div class="nist-footer__inner">
<div class="nist-footer__menu" role="navigation">
<ul>
<li class="nist-footer__menu-item"><a href="/timegov/">Map View</a></li>
<li class="nist-footer__menu-item"><a href="/simple/">Simple View</a></li>
<li class="nist-footer__menu-item"><a href="/decimal/">Decimal Time</a></li>
<li class="nist-footer__menu-item"><a href="/seth/">Seth Date</a></li>
<li class="nist-footer__menu-item"><a href="/calendar/">Seth Calendar</a></li>
<li class="nist-footer__menu-item"><a href="/api/time">JSON Time API</a></li>
</ul>
</div>
</div>
<div class="nist-footer__logo">
<center><img src="https://storage.googleapis.com/sethfreiberg.com/sethflix/favicon.png" alt="SethPC logo" /><p>SethPC Official Time</p></center>
</div>
</footer>
<div class='time-zone-container' style='display:none'>
<div class='dropdown-container'>
<select id='dst-time-zone'>
</select>
<select id='no-dst-time-zone'>
</select>
</div>
</div>
<div id="responseTime"></div>
<script src="scripts/jquery-3.7.1.min.js" type="text/javascript"></script>
<script type="text/javascript" src="scripts/analogClock.js"></script>
<script type="text/javascript" src="scripts/application.js"></script>
<script type="text/javascript" src="scripts/zzz__bd08fcbbc8bd600a5f9e994be2de69f7cb26b9f4.js"></script>
</body>
</html>
@@ -0,0 +1,126 @@
///////////////////////////////////////////////////////////////////////////
////////////////////// ANALOG CLOCK FUNCTIONS ///////////////////////////
///////////////// Clock code used from W3Schools.com ////////////////////
//////// Refer to their site about applicable use of the code ///////////
///////////////////////////////////////////////////////////////////////////
var canvas = document.getElementById("analog-clock");
var ctx = canvas.getContext("2d");
var radius = canvas.height / 2;
ctx.translate(radius, radius);
radius = radius * 0.90
setInterval(drawClock, 1000);
// add 0 to single digit time min, sec
function checkTime(i) {
if (i < 10) {i = "0" + i}; // add zero in front of numbers < 10
return i;
}
function drawClock() {
drawFace(ctx, radius);
drawNumbers(ctx, radius);
drawTime(ctx, radius);
}
function drawFace(ctx, radius) {
var grad;
ctx.beginPath();
ctx.arc(0, 0, radius, 0, 2*Math.PI);
ctx.fillStyle = 'white';
ctx.fill();
grad = ctx.createRadialGradient(0,0,radius*0.95, 0,0,radius*1.05);
grad.addColorStop(0, '#333');
grad.addColorStop(0.5, 'white');
grad.addColorStop(1, '#333');
ctx.strokeStyle = grad;
ctx.lineWidth = radius*0.1;
ctx.stroke();
ctx.beginPath();
ctx.arc(0, 0, radius*0.1, 0, 2*Math.PI);
ctx.fillStyle = '#333';
ctx.fill();
}
function drawNumbers(ctx, radius) {
var ang;
var num;
ctx.font = radius*0.15 + "px arial";
ctx.textBaseline="middle";
ctx.textAlign="center";
for(num = 1; num < 13; num++){
ang = num * Math.PI / 6;
ctx.rotate(ang);
ctx.translate(0, -radius*0.85);
ctx.rotate(-ang);
ctx.fillText(num.toString(), 0, 0);
ctx.rotate(ang);
ctx.translate(0, radius*0.85);
ctx.rotate(-ang);
}
}
function drawTime(ctx, radius){
var deviceClock = new Date();
var hour = deviceClock.getHours();
var minute = deviceClock.getMinutes();
var second = deviceClock.getSeconds();
var ampm = hour >= 12 ? 'P.M.' : 'A.M.';
hour = hour % 12;
hour = hour ? hour : 12; // the hour '0' should be '12'
//minute = minute < 10 ? '0'+minute : minute;
// CHECKTIME FUNC ADDS LEADING '0' if <10
hour = checkTime(hour);
minute = checkTime(minute);
second = checkTime(second);
//digital time display
var myDigTime = hour + ":" + minute +":" + second;
document.getElementById('myTime').innerHTML = myDigTime + ' ' + ampm;
var dd = deviceClock.getDate();
var mm = deviceClock.getMonth();
mm++; //Increment because Jan is month 0
dd = checkTime(dd);
mm = checkTime(mm);
var yyyy = deviceClock.getFullYear();
var todaysDate = mm + '/' + dd + '/' + yyyy;
document.getElementById("myDate").innerHTML = "Today: " + todaysDate;
// OFFSET
var utcOffset = deviceClock.getTimezoneOffset()/60;
if (utcOffset > 0) {
utcOffset = "(UTC-" + utcOffset + ")";
} else {
utcOffset = "(UTC+" + Math.abs(utcOffset) + ")";
};
document.getElementById("myTimeTitle").innerHTML = utcOffset;
//hour
hour=hour%12;
hour=(hour*Math.PI/6)+
(minute*Math.PI/(6*60))+
(second*Math.PI/(360*60));
drawHand(ctx, hour, radius*0.5, radius*0.07);
//minute
minute=(minute*Math.PI/30)+(second*Math.PI/(30*60));
drawHand(ctx, minute, radius*0.8, radius*0.07);
// second
second=(second*Math.PI/30);
drawHand(ctx, second, radius*0.9, radius*0.02);
}
function drawHand(ctx, pos, length, width) {
ctx.beginPath();
ctx.lineWidth = width;
ctx.lineCap = "round";
ctx.moveTo(0,0);
ctx.rotate(pos);
ctx.lineTo(0, -length);
ctx.stroke();
ctx.rotate(-pos);
}
@@ -0,0 +1,67 @@
// ONLOAD OPERATIONS FOR THE SITE
window.onload = function() {
/* PARSE THE URL FOR 12/24 VARIABLE*/
var getT = location.search;
var tArr = getT.split("=");
var t = tArr[1];
// CREATE A VAR FOR THE CHECKBOX
var twentyFour = document.getElementById("twenty-four");
// CHECK VALUE OF 12/24 URL VAR "t" AND SET CHECKBOX ACCORDINGLY
if (t === "24") {
twentyFour.checked = true;
} else {
// DEFAULT TO 12HR DISPLAY
twentyFour.checked = false;
}
var noMoreAlerts = false;
// NOTIFICATION BOX FOR BOOKMARKING 24-HOUR SETTINGS PAGE
var twentyFour = document.getElementById("twenty-four");
twentyFour.addEventListener("click", function(event) {
var hourLabelDiv = document.getElementsByClassName("am-pm")[0];
var url = window.location.toString();
if(timeDotGov.data.twentyFour()) {
window.history.replaceState(url, "", "/timegov/?t=24");
if (!noMoreAlerts) {
alert("Bookmarking this page will save your preference for 24-hour time display.");
}
noMoreAlerts = true;
} else {
window.history.replaceState(url, "", "/timegov/");
}
timeDotGov.clockController.handleonrefresh(new Date());
});
//? timeZoneChange = function(event) {
//? timeDotGov.clockController.getnewOffset(event.target.value);
//? }
// LOAD DST DATES AND LEAP DATE
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("GET", "/api/timegov/auxdata.xml", false); // false for synchronous request
xmlHttp.send(null);
timeDotGov.auxdata = xmlHttp.responseText;
timeDotGov.clockController.auxdata();
timeDotGov.clockController.checkservertime();
document.getElementById('responseTime').innerHTML = timeDotGov.data.zoneOffset;
// SET REFRESH RATE TO CHECK FOR TOP OF NEW SECOND, SO THE DISPLAY DOES NOT HAVE TO BE REFRESHED MORE THAN NECESSARY
setInterval(function() {
if(timeDotGov.data.currentTime) {
timeDotGov.clockController.runningclocks();
}
}, 20); // 20 milliseconds
// FUNCTION REFRESHES PAGE EVERY 10 MIN
setInterval(function() {
location.reload();
}, 600000);
};
File diff suppressed because one or more lines are too long
@@ -0,0 +1,370 @@
timeDotGov = {};
timeDotGov.clock = {};
timeDotGov.clockController = {};
timeDotGov.clockController.dsClock = {}
timeDotGov.data = {
"then": null,
"serverTime": null,
"responseTime": null,
"RThalf": null,
"realTimeDif": null,
"requestTime": null,
"leapsecond": null,
"dststart": null,
"dstend": null,
"currYear": null,
"dstDates": null,
"leapDate": null,
"clockinstances": [],
"leapFlag": null,
"leapsec60": null,
"myhour0": "0",
"myhour1": "0",
"mysec0": "0",
"mysec1": "0",
"currentTime": null,
"twentyFour": function() {
twentyFour = document.getElementById("twenty-four");
return twentyFour.checked;
}
}
var offsetCheck = true;
var daylightTitles = false;
// CREATE ARRAY THAT WILL STORE WHICH CLOCK NUMS HAVE BEEN UPDATED
var dstClocksUpdated = [];
timeDotGov.clockController.getParameterByName = function(name) {
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
results = regex.exec(location.search);
return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
}
timeDotGov.clockController.getleapdate = function() {
var leap = new Date();
var temp1 = timeDotGov.data.leapdate.split(" ");
var leapdateYear = temp1[0];
var leapdateMonth = temp1[1];
leapdateMonth = leapdateMonth - 1; // month convention = 0-11 (Jan = 0)
var leapdateDay = temp1[2];
leap.setUTCFullYear(leapdateYear, leapdateMonth, leapdateDay);
leap.setUTCHours(0, 0, 0, 0);
timeDotGov.data.leapsecond = leap.getTime();
}
// GET TIME FROM SERVER
timeDotGov.clockController.dsClock.doRequest = function() {
d = new Date();
var xmlHttp = new XMLHttpRequest();
////////////////////////////////////////////////////////////////////////////////////
////// USE OF THIS .CGI BY OUTSIDE SITES OR APPLICATIONS IS STRICTLY PROHIBITED ////
// OR USING THE TIME FROM THIS SITE IN ANY WAY FOR OTHER SITES IS ALSO PROHIBITED //
///////////////////////////////////////////////////////////////////////////////////
xmlHttp.open("GET", "/zzz__2fbc6c3300df7e4483acd44c5044098a9fcc61d6.cgi?disablecache=" + d.getTime(), false);
xmlHttp.send(null);
return xmlHttp.responseText;
}
// GET t1, t2, t3, t4 in doRequest AND CALCULATE DELAYS IN doData
timeDotGov.clockController.checkservertime = function() {
var o = new Object();
var d = new Date();
var currentTimeObj = new Date();
timeDotGov.data.requestTime = currentTimeObj.getTime();
timeDotGov.data.currentTime = timeDotGov.clockController.dsClock.doRequest(); // GET NIST TIME FROM SERVER
timeDotGov.clockController.doData();
}
// PARSE DATA FROM AUXDATA.XML
timeDotGov.clockController.doAuxData = function() {
parser = new DOMParser();
xmlDoc = parser.parseFromString(timeDotGov.auxdata, "text/xml");
timeDotGov.data.curryear = xmlDoc.getElementsByTagName("currYear")[0].childNodes[0].nodeValue;
timeDotGov.data.dstdates = xmlDoc.getElementsByTagName("DstDates")[0].childNodes[0].nodeValue;
timeDotGov.data.leapdate = xmlDoc.getElementsByTagName("LeapDate")[0].childNodes[0].nodeValue;
}
timeDotGov.clockController.getDSTdates = function() {
var dstStartDate = new Date();
var dstEndDate = new Date();
var temp=timeDotGov.data.dstdates.split(" "); // makes an array of the string elements
var startmonth = temp[0];
startmonth = startmonth - 1; // to follow js convention of month = 0-11 (Jan = 0)
var startday = temp[1];
var endmonth = temp[2];
endmonth = endmonth - 1;
var endday = temp[3];
dstStartDate.setUTCFullYear(timeDotGov.data.curryear, startmonth, startday);
dstStartDate.setUTCHours(2, 0, 0, 0);
dstEndDate.setUTCFullYear(timeDotGov.data.curryear, endmonth, endday);
dstEndDate.setUTCHours(1, 0, 0, 0);
timeDotGov.data.dstStart = dstStartDate.getTime();
timeDotGov.data.dstEnd = dstEndDate.getTime();
}
timeDotGov.clockController.auxdata = function() {
this.doAuxData();
this.getDSTdates();
this.getleapdate();
}
timeDotGov.clockController.doData = function() {
var parser = new DOMParser();
var xmlDoc = parser.parseFromString(timeDotGov.data.currentTime, "text/xml");
var t2 = xmlDoc.getElementsByTagName("timestamp")[0].getAttribute("time2");
var t3 = xmlDoc.getElementsByTagName("timestamp")[0].getAttribute("time3");
var serverDelay = Math.round((t3 - t2) / 1000); // server delay in milliseconds
timeDotGov.data.serverTime = Math.round(t3/1000); // Server time in milliseconds
var currentTimeObj2 = new Date();
timeDotGov.data.responseTime = currentTimeObj2.getTime(); // t4
timeDotGov.data.RThalf = ((timeDotGov.data.responseTime - timeDotGov.data.requestTime) - serverDelay) / 2; // (t4 - t1) - (t3 -t2) NTP EQ
timeDotGov.data.realTimeDif = timeDotGov.data.serverTime - timeDotGov.data.responseTime; // t3-t4 used for correction to local clock for official time
////////////////////////////////////////////////////////////
// USE OFFSET CHECK VAR TO ONLY CHECK CLIENT CLOCK ONCE ///
////////////////////////////////////////////////////////////
if (offsetCheck) {
// ROUND TO MILLISECONDS - USE (* -1) to invert timediff pos/neg display value
var diff = (timeDotGov.data.realTimeDif/1000) * -1;
var diffDisplay = diff.toFixed(3);
if (diffDisplay > 0 ) {
diffDisplay = "+" + diffDisplay;
}
document.getElementById("realTimeDif").innerHTML = diffDisplay;
offsetCheck = false;
}
timeDotGov.clockController.runningclocks();
timeDotGov.data.leapFlag = "false";
} // END OF doData FUNCTION
// REFRESHES CLOCKS AT THE TOP OF EACH *ACTUAL* SECOND
timeDotGov.clockController.runningclocks = function() {
var deviceClock = new Date();
var fractionalZone =(Math.abs((deviceClock.getTimezoneOffset()/60))) % 1; // IF CLIENT IS IN A ZONE WITH A FRACTIONAL HOUR, MAKE ADJUSTMENT (MODULO 1 TO GET THE FRACTIONAL HOUR)
if (fractionalZone != 0) {
fractionalZone = (1 - fractionalZone); // SUBTRACT FROM 1 TO GET VALID CORRECTION FOR 30 AND 45 MIN ZONES
}
var fractionalZoneMilli = fractionalZone * 3600000; // CONVERT PARTIAL HOUR TO MILLISECONDS, TO BE SUBTRACTED FROM ADJUSTED TIME
var s = new Date(); // convert PC time and delay back to seconds (this is UTC)
s.setTime(s.getTime() + timeDotGov.data.realTimeDif + timeDotGov.data.RThalf - fractionalZoneMilli); // CORRECT FOR PC CLOCK ERROR, HALF NETWORK DELAY AND CLIENT IN PARTIAL TIME ZONE
var sec = s.getSeconds();
if (sec != timeDotGov.data.previousSec) { // call handleonrefresh as soon as you see a new second
timeDotGov.data.previousSec = sec;
timeDotGov.clockController.handleonrefresh(s);
}
}
// CHECK IF USER CHANGED CLOCK or IF NEW DST STATE or LEAP SECOND, THEN REFRESH ALL CLOCKS
timeDotGov.clockController.handleonrefresh = function(s) {
//var mins = s.getMinutes();
var now = s.getTime(); // convert time to ms since epoch (UTC)
//if (0 <= mins < 30) {
// now = now + fractionalZoneMilli;
//} else {
// now = now - fractionalZoneMilli;
//}
if (timeDotGov.data.then == null) {
timeDotGov.data.then = now; // if it's the first round, don't let it fail
}
var DidUserChangeClock = Math.abs(now - timeDotGov.data.then);
if (DidUserChangeClock >= 2000) { // if pc clock changed then reset (|now-then| should only be 1 s)
location.reload();
timeDotGov.data.then = now;
} else {
timeDotGov.data.then = now;
}
if (timeDotGov.data.leapsecond / 1000 == Math.floor(now / 1000)) {
timeDotGov.data.leapFlag = "true"
}
var clocks = document.getElementsByClassName("clock");
var clockNum; // SET TO VALUE OF 'i' IN THE FOLLOWING LOOP TO REPRESENT EACH CLOCK INSTANCE
// SET ALL OF THE CLOCKS BY CALLING setCurrentTime FOR EACH CLOCK INSTANCE
for(var i =0; i < clocks.length; i++ ){
var clock = clocks[i].getElementsByTagName("time")[0];
var zoneOffset = clocks[i].getElementsByTagName("time")[0].getAttribute("zoneOffset") || 0;
// GIVE VAR CLOCKNUM VALUE OF i
clockNum = i;
clock.innerHTML = timeDotGov.clock.setCurrentTime(timeDotGov.data.clockinstances[i], now, timeDotGov.data.twentyFour(), timeDotGov.data.dstStart, timeDotGov.data.dstEnd, zoneOffset, timeDotGov.data.leapFlag, timeDotGov.data.leapsec60, timeDotGov.data.RThalf, clockNum);
document.getElementById('timeUTC').innerHTML = timeDotGov.clock.setCurrentTime(timeDotGov.data.clockinstances[i], now, true, timeDotGov.data.dstStart, timeDotGov.data.dstEnd, 0, timeDotGov.data.leapFlag, timeDotGov.data.leapsec60, timeDotGov.data.RThalf, 999); // LAST VAR IS CLOCKNUM, PASSING AS '999' TO AVOID DUPLICATE 'i' VARIABLE VALUE SEND TO SETCURRENTTIME FUNCTION
}
} // END OF handleonrefresh FUNCTION
// CREATES CLOCK DIGITS AND DST/ST LABELS FOR EACH CLOCK INSTANCE
timeDotGov.clock.setCurrentTime = function(clock, now, twentyFour, dstStart, dstEnd, zoneOffset, leapFlag, leapsec60, RThalf, clockNum) {
var displayTime = new Date();
now = (now - (zoneOffset * 3600000));
if (leapFlag == "true"){
now = now - 1000; // if leap has occurred, show (seconds - 1) until next sync
var leapsec60 = "true"; // var to show a 60 instead of 59
}
displayTime.setTime(now);
var year = displayTime.getUTCFullYear();
var hourNum = displayTime.getUTCHours();
var minNum = displayTime.getMinutes();
var secNum = displayTime.getSeconds();
// CREATE ARRAY OF THE CLOCK NUMS WHO FOLLOW DST
// ALSO CREATE ARRAY OF DST LABEL CLASSES ////////////////////////
var DSTclocksArray = [ 0, 1, 6, 7, 8, 9 ]; // THESE ARE THE clockNums FOR THE ZONES THAT FOLLOW DST
var dstLabels = document.getElementsByClassName("DSTterm"); // CREATE ARRAY FOR DST LABELS
var dstLetters = document.getElementsByClassName("DSTletter"); // CREATE ARRAY FOR DST ABBREVIATION
var dstNums = document.getElementsByClassName("DSTnum"); // CREATE ARRAY FOR DST OFFSET NUM
if ( now >= dstStart && now <= dstEnd ) { // CHECK IF THIS SECOND IS DST OR NOT
// CHECK IF CLOCK NUM IS IN DST ARRAY, IF SO, ADD THE DST HOUR AND LABELS
// if ( DSTclocksArray.includes(clockNum) ) {
if ( DSTclocksArray.indexOf(clockNum) > -1 ) { // indexOf fixes a chance in ie
hourNum = hourNum + 1;
// AT END OF FUNC ADD/PUSH CLOCKNUM TO dstClocksUpdated ARRAY, IF CLOCKNUM IS IN THERE, DONT UPDATE AGAIN
// if ( !dstClocksUpdated.includes( clockNum ) ) {
if ( dstClocksUpdated.indexOf( clockNum ) == -1 ) { // indexOf fixes a chance in ie
dstLabels[clockNum].innerHTML = "DAYLIGHT";
dstLetters[clockNum].innerHTML = "D";
var standardNum = Number(dstNums[clockNum].innerHTML);
dstNums[clockNum].innerHTML = standardNum - 1;
dstClocksUpdated.push(clockNum); // ADD CLOCK NUM TO ARRAY OF CLOCKS UPDATED
}
}
}
// IF DST OR INCREMENTED HOUR GOES INTO NEXT DAY, RESET TO HOUR ZERO AND ADD A DAY
if (hourNum > 23) {
hourNum = 0;
now = (now + 3600000) // advance now by one hour
displayTime.setTime(now); // reset displaytime so day/date/month are correct with new day
}
// CHECK FOR AND IMPLEMENT LEAP SECOND
if (leapFlag == "true") {
if (leapsec60 == "true") {
if (minNum == "59") {
if (secNum == "59") { // if leapsec, and 59:59 then show 60 and reset
secNum = "60";
leapsec60 = "false";
}
}
}
}
var hourLabel = "";
// 12-HOUR OR 24-HOUR DISPLAY
// BOX NOT CHECKED SO 12-HOUR DISPLAY
if (!(twentyFour)) {
if (hourNum > 11) {
hourNum -= 12;
am_pm = "P.M.";
} else {
am_pm = "A.M.";
}
} else {
am_pm = "";
}
var newVal;
// SET HOURS
if(hourNum > 9) {
newVal = Number(String(hourNum).charAt(0));
if(timeDotGov.data.myhour0 != newVal) {
timeDotGov.data.myhour0 = newVal;
}
}
else if(Number(timeDotGov.data.myhour0) != 0) {
timeDotGov.data.myhour0 = "0";
}
if(Number(hourNum) > 9){
newVal=Number(String(hourNum).charAt(1));
if(timeDotGov.data.myhour1 != newVal) {
timeDotGov.data.myhour1 = newVal;
}
} else if (Number(timeDotGov.data.myhour1) != Number(hourNum)) {
timeDotGov.data.myhour1 = Number(hourNum);
}
if ((hourNum < 1) && (!(twentyFour))) { // if not 24hour time force the 12 so it's not 00
timeDotGov.data.myhour0 = 1;
timeDotGov.data.myhour1 = 2;
}
// SET MINUTES
if(minNum > 9) {
newVal = Number(String(minNum).charAt(0));
if (timeDotGov.data.mymin0 != newVal) {
timeDotGov.data.mymin0 = newVal;
}
} else if(Number(timeDotGov.data.mymin0) != 0) {
timeDotGov.data.mymin0 = 0;
}
if(Number(minNum) > 9) {
newVal = Number(String(minNum).charAt(1));
if(timeDotGov.data.mymin1 != newVal) {
timeDotGov.data.mymin1 = newVal;
}
}
else if(Number(timeDotGov.data.mymin1) != Number(minNum)) {
timeDotGov.data.mymin1 = Number(minNum);
}
// SET SECONDS
if(secNum > 9) {
newVal = Number(String(secNum).charAt(0));
if(timeDotGov.data.mysec0 != newVal) {
timeDotGov.data.mysec0 = newVal;
}
}
else if(Number(timeDotGov.data.mysec0) != 0) {
timeDotGov.data.mysec0 = 0;
}
if(Number(secNum) > 9) {
newVal=Number(String(secNum).charAt(1));
if(timeDotGov.data.mysec1 != newVal) {
timeDotGov.data.mysec1 = newVal;
}
}
else if(Number(timeDotGov.data.mysec1) != Number(secNum)) {
timeDotGov.data.mysec1 = Number(secNum);
}
// CREATE CLOCK DIGITS STRING TO DISPLAY
var clockdigits = (timeDotGov.data.myhour0 + "" + timeDotGov.data.myhour1 + ":" + timeDotGov.data.mymin0 + timeDotGov.data.mymin1 + ":" + timeDotGov.data.mysec0 + timeDotGov.data.mysec1);
clock = clockdigits + " " + am_pm;
return clock;
} // END OF setCurrentTime FUNCTION