aboutsummaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
authorMitsuo Tokumori <[email protected]>2025-03-09 20:30:25 +0900
committerMitsuo Tokumori <[email protected]>2025-03-09 20:30:25 +0900
commitedb9e94a98458fbac00075a3a407246668123eb4 (patch)
tree2eecc2d720411b0e0a3ba1f4d23afad0608e88f3 /app
parent4987626c394ca07c3ded98d9ed7095986553863e (diff)
downloadmasu-edb9e94a98458fbac00075a3a407246668123eb4.tar.gz
masu-edb9e94a98458fbac00075a3a407246668123eb4.tar.bz2
masu-edb9e94a98458fbac00075a3a407246668123eb4.zip
Add world clocks to block_timeHEADmaster
Diffstat (limited to 'app')
-rw-r--r--app/static/block_time.js85
-rw-r--r--app/static/script.js18
-rw-r--r--app/static/style.css33
-rw-r--r--app/templates/index.html5
4 files changed, 120 insertions, 21 deletions
diff --git a/app/static/block_time.js b/app/static/block_time.js
index d6510ae..8841e7b 100644
--- a/app/static/block_time.js
+++ b/app/static/block_time.js
@@ -1,13 +1,86 @@
function init() {
- initTimeVisualizer('timeVisualizer');
+ initTimeVisualizer("time-timeVisualizer");
}
export function updateTime() {
- fetch("/time")
- .then(res => res.json())
- .then(data => {
- document.getElementById("weatherSummary").innerText = data.time;
- });
+ const cities = [
+ { name: "Tokyo", tz: "Asia/Tokyo", note: "JST" },
+ { name: "Berlin", tz: "Europe/Berlin", note: "CET, CEST from last Sunday in March at 02:00 to last Sunday in October at 03:00" },
+ { name: "London", tz: "Europe/London", note: "GMT, BST from last Sunday of March to last Sunday of October" },
+ { name: "Lima", tz: "America/Lima", note: "aka: \"PET\""},
+ { name: "Santiago", tz: "America/Santiago", note: "aka: \"CLT\""},
+ { name: "New York", tz: "America/New_York", note: "EST, EDT from second Sunday in March at 02:00 to first Sunday in November at 02:00" },
+ { name: "Chicago", tz: "America/Chicago", note: "CST, CDT from second Sunday in March at 02:00 to first Sunday in November at 02:00" },
+ { name: "Denver", tz: "America/Denver", note: "MST, MDT from second Sunday in March at 02:00 to first Sunday in November at 02:00" },
+ { name: "Los Angeles", tz: "America/Los_Angeles", note: "PST, PDT from second Sunday in March at 02:00 to first Sunday in November at 02:00" },
+ { name: "Arizona", tz: "America/Denver", note: "MST" },
+ { name: "Anchorage", tz: "America/Anchorage", note: "AKST, AKDT from second Sunday in March at 02:00 to first Sunday in November at 02:00" },
+ { name: "Honolulu", tz: "Pacific/Honolulu", note: "HST" },
+ ];
+
+ const escapeHtml = str => str.replace(/[&<>"']/g, c => `&#${c.charCodeAt(0)};`);
+
+ const now = new Date();
+ const locale = "en-US";
+ let output = "<ul>";
+
+ // Tokyo datetime header
+ const tokyoDate = new Intl.DateTimeFormat("ja-JP", {
+ calendar: 'japanese', era: 'long',
+ year: "numeric", month: "numeric", day: "numeric", weekday: "long",
+ hour: "2-digit", minute: "2-digit", hour12: false,
+ timeZone: "Asia/Tokyo"
+ }).format(now);
+ const qw = getDateInfo(now); // quarter and ISO8601 week
+ let s = `Tokyo: (Q${qw.quarter}) (W${qw.isoWeek.toString().padStart(2, '0')}) ${tokyoDate}\n`;
+ document.getElementById("time-localClock").innerText = s;
+
+ // Time in other cities
+ cities.forEach(city => {
+ const formatter = new Intl.DateTimeFormat(locale, {
+ year: "numeric", month: "2-digit", day: "2-digit", weekday: "short",
+ hour: "2-digit", minute: "2-digit", hour12: false,
+ timeZoneName: "shortOffset",
+ timeZone: city.tz
+ })
+ const parts = formatter.formatToParts(now);
+ const t = {
+ time: parts.slice(8, -1).map(part => part.value).join('').trim(),
+ offset: parts.find(part => part.type === "timeZoneName")?.value.replace("GMT", "UTC"),
+ timeZoneName: new Intl.DateTimeFormat(locale, { timeZoneName: 'long', timeZone: city.tz })
+ .formatToParts(now).find(part => part.type === "timeZoneName")?.value,
+ }
+ t.dst = ["Daylight", "Summer"].some(sub => t.timeZoneName.includes(sub));
+ //const timeData = parts.reduce((obj, part) => { obj[part.type] = obj[part.value]; return obj; }, {})
+ const offsetAndDST = `${t.offset}` + (t.dst ? "+1" : "");
+ output += `<li>${city.name}: ${t.time} (<span title="${escapeHtml(city.note)}">${offsetAndDST}</span>) </li>`;
+ });
+ output += "</ul>"
+
+ document.getElementById("time-worldClock").innerHTML = output.trim();
+}
+
+function getQuarter(date) {
+ const month = date.getMonth(); // 0-11 (January is 0)
+ return Math.floor(month / 3) + 1; // Returns 1-4
+}
+
+function getISOWeek(date) {
+ const tempDate = new Date(date);
+ tempDate.setHours(0, 0, 0, 0);
+ tempDate.setDate(tempDate.getDate() + 3 - ((tempDate.getDay() + 6) % 7)); // Move to nearest Thursday
+ const firstThursday = new Date(tempDate.getFullYear(), 0, 4); // First Thursday of the year
+ firstThursday.setDate(firstThursday.getDate() + 3 - ((firstThursday.getDay() + 6) % 7));
+ const weekNumber = Math.round(((tempDate - firstThursday) / 86400000) / 7) + 1;
+ return weekNumber;
+}
+
+function getDateInfo(date) {
+ return {
+ quarter: getQuarter(date),
+ isoWeek: getISOWeek(date),
+ year: date.getFullYear()
+ };
}
function initTimeVisualizer(containerId) {
diff --git a/app/static/script.js b/app/static/script.js
index efd6836..84230a5 100644
--- a/app/static/script.js
+++ b/app/static/script.js
@@ -1,24 +1,22 @@
+"use strict";
import { updateTime } from './block_time.js';
import { updateWeather } from './block_weather.js';
const config = {
- polling: true
+ polling: true,
+ pollingDelay: 500, // in ms. To be light on CPU make it >= 500ms
}
function init() {
const blocks = {
- time: { interval: 30 * 1000, lastUpdate: 0, update: updateTime }, // 30s
+ time: { interval: 30 * 1000, lastUpdate: 0, update: updateTime }, // 30s
weather: { interval: 30 * 60000, lastUpdate: 0, update: updateWeather } // 30min
// Add more: { interval: X, lastUpdate: 0, update: updateFunction }
};
initHeaderControls(blocks)
-
- // Initial load
- // Poll every 500ms to check intervals (fast enough for 1s updates, light on CPU)
- // maybe the 1s updates should be special case, but for now let's keep it simple
reloadAll(blocks);
- setInterval(() => pollUpdates(blocks), 500);
+ setInterval(() => pollUpdates(blocks), config.pollingDelay);
}
function reloadAll(blocks) {
@@ -45,6 +43,12 @@ function initHeaderControls(blocks) {
document.getElementById("pause").addEventListener("change", (e) => {
config.polling = e.target.checked;
});
+ config.polling = document.getElementById("pause").checked;
+ document.getElementById("city").addEventListener("keypress", (e) => {
+ if (e.key === "Enter") {
+ updateWeather()
+ }
+ });
}
init()
diff --git a/app/static/style.css b/app/static/style.css
index a186f24..9089bc6 100644
--- a/app/static/style.css
+++ b/app/static/style.css
@@ -1,4 +1,12 @@
/* MASU */
+/* Design decisions:
+ * * Minimum viewport resolution: 440x440px
+ * * simple is better
+ *
+ * Good and simple UI sites:
+ * * https://archlinux.org/packages/ (https://github.com/archlinux/archweb)
+ * * https://suckless.org/
+ */
body {
margin: 0;
padding: 0;
@@ -47,12 +55,25 @@ header button, header label {
}
}
+/* tooltip: span element with "title" attribute (KISS) */
+span[title] {
+ /*border-bottom: 1px dotted;*/
+ text-decoration: dotted underline;
+}
+
/* block_time */
#time .block {
justify-content: center;
}
-#timeVisualizer .grid {
+#time-localClock {
+}
+
+#time-worldClock {
+ font-size: 12px;
+}
+
+#time-timeVisualizer .grid {
display: grid;
grid-template-columns: repeat(8, 42px);
grid-template-rows: repeat(3, 42px);
@@ -63,7 +84,7 @@ header button, header label {
margin: 0 auto;
}
-#timeVisualizer .cell {
+#time-timeVisualizer .cell {
width: 42px;
height: 42px;
background: #ffffff;
@@ -71,15 +92,15 @@ header button, header label {
border: 1px solid #ddd;
}
-#timeVisualizer .sleep {
+#time-timeVisualizer .sleep {
background: #e6f3ff;
}
-#timeVisualizer .past {
+#time-timeVisualizer .past {
background: #cccccc;
}
-#timeVisualizer .current::after {
+#time-timeVisualizer .current::after {
content: '';
position: absolute;
left: 0;
@@ -89,7 +110,7 @@ header button, header label {
background: #cccccc;
}
-#timeVisualizer .timeline {
+#time-timeVisualizer .timeline {
position: absolute;
width: 2px;
height: 100%;
diff --git a/app/templates/index.html b/app/templates/index.html
index 4647619..0d10afa 100644
--- a/app/templates/index.html
+++ b/app/templates/index.html
@@ -14,8 +14,9 @@
<div id="container">
<!-- TODO: Use Jinja2 blocks -->
<div class="block" id="time">
- <p id="weatherSummary"></p>
- <div id="timeVisualizer"></div>
+ <div id="time-localClock"></div>
+ <div id="time-worldClock"></div>
+ <div id="time-timeVisualizer"></div>
</div>
<div class="block" id="weather">
<img id="weather-icon" src="" alt="weather icon">