Diff -- /var/www/vhosts/3dshawn.com/site1/assets/js/tz_localize.js
Diff
/var/www/vhosts/3dshawn.com/site1/assets/js/tz_localize.js
added on local at 2026-07-10 23:21:48
Added
+77
lines
Removed
-0
lines
Context
0
unchanged
Blobs
from
to e24b30de2fde
to e24b30de2fde
Unified diff
Naive LCS-based line diff. Additions in emerald, deletions in rose. Both sides decompressed live from BLOB_STORE.| 1 | /* | |
| 2 | * DriftSense -- viewer-local time renderer. | |
| 3 | * | |
| 4 | * Server stores everything in server-local time (or UTC). Perl emits every | |
| 5 | * user-facing timestamp as: | |
| 6 | * | |
| 7 | * <span class="ts" data-ts="1720658362" data-fmt="datetime">2026-07-10 22:19:22</span> | |
| 8 | * | |
| 9 | * data-ts = UNIX epoch seconds (single source of truth, no ambiguity). | |
| 10 | * data-fmt = "datetime" | "date" | "time" | "relative" | "date-short" | |
| 11 | * | |
| 12 | * This script rewrites the visible text to the viewer's browser locale + | |
| 13 | * timezone. Runs once at DOMContentLoaded and again whenever `ts-refresh` | |
| 14 | * is dispatched (for AJAX-inserted rows). | |
| 15 | * | |
| 16 | * Original server-side text stays inside as the graceful no-JS fallback. | |
| 17 | */ | |
| 18 | (function () { | |
| 19 | 'use strict'; | |
| 20 | ||
| 21 | function fmt(el) { | |
| 22 | var raw = el.getAttribute('data-ts'); | |
| 23 | if (!raw) return; | |
| 24 | var epoch = parseInt(raw, 10); | |
| 25 | if (!epoch || isNaN(epoch)) return; | |
| 26 | var d = new Date(epoch * 1000); | |
| 27 | var kind = el.getAttribute('data-fmt') || 'datetime'; | |
| 28 | var out; | |
| 29 | switch (kind) { | |
| 30 | case 'date': | |
| 31 | out = d.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' }); | |
| 32 | break; | |
| 33 | case 'date-short': | |
| 34 | out = d.toLocaleDateString(undefined, { month: 'short', day: 'numeric' }); | |
| 35 | break; | |
| 36 | case 'time': | |
| 37 | out = d.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' }); | |
| 38 | break; | |
| 39 | case 'relative': | |
| 40 | out = relative(epoch); | |
| 41 | break; | |
| 42 | default: | |
| 43 | out = d.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' }) | |
| 44 | + ' ' + d.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' }); | |
| 45 | } | |
| 46 | el.textContent = out; | |
| 47 | if (!el.title) el.title = d.toString(); // full timezone-aware tooltip | |
| 48 | } | |
| 49 | ||
| 50 | function relative(epoch) { | |
| 51 | var now = Math.floor(Date.now() / 1000); | |
| 52 | var s = now - epoch; | |
| 53 | var future = s < 0; | |
| 54 | s = Math.abs(s); | |
| 55 | var unit, n; | |
| 56 | if (s < 60) { n = s; unit = 'second'; } | |
| 57 | else if (s < 3600) { n = Math.floor(s / 60); unit = 'minute'; } | |
| 58 | else if (s < 86400) { n = Math.floor(s / 3600); unit = 'hour'; } | |
| 59 | else if (s < 2592000){n = Math.floor(s / 86400); unit = 'day'; } | |
| 60 | else if (s < 31536000){n= Math.floor(s / 2592000); unit = 'month';} | |
| 61 | else { n = Math.floor(s / 31536000); unit = 'year';} | |
| 62 | var plural = n === 1 ? '' : 's'; | |
| 63 | return future ? ('in ' + n + ' ' + unit + plural) : (n + ' ' + unit + plural + ' ago'); | |
| 64 | } | |
| 65 | ||
| 66 | function localizeAll(root) { | |
| 67 | (root || document).querySelectorAll('span.ts[data-ts], time[data-ts]').forEach(fmt); | |
| 68 | } | |
| 69 | ||
| 70 | if (document.readyState === 'loading') { | |
| 71 | document.addEventListener('DOMContentLoaded', function () { localizeAll(); }); | |
| 72 | } else { | |
| 73 | localizeAll(); | |
| 74 | } | |
| 75 | window.addEventListener('ts-refresh', function () { localizeAll(); }); | |
| 76 | window.tzLocalize = localizeAll; // for programmatic use | |
| 77 | })(); |