aboutsummaryrefslogtreecommitdiffstats
path: root/app/static/block_time.js
blob: 8841e7bae81fb42c7d2a3cd9a50b2d38c46e3561 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
function init() {
  initTimeVisualizer("time-timeVisualizer");
}

export function updateTime() {
  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) {
  const grid = document.createElement('div');
  grid.className = 'grid';
  document.getElementById(containerId).appendChild(grid);

  function updateTime() {
    grid.innerHTML = '';
    const now = new Date();
    const hours = now.getHours();
    const minutes = now.getMinutes();

    for (let i = 0; i < 24; i++) {
      const cell = document.createElement('div');
      cell.className = 'cell';
      
      if (i >= 20 || i < 4) cell.classList.add('sleep');
      if (i < hours) cell.classList.add('past');
      
      if (i === hours) {
        cell.classList.add('current');
        const fillPercentage = (minutes / 60) * 100;
        cell.style.setProperty('--fill', `${fillPercentage}%`);
        const line = document.createElement('div');
        line.className = 'timeline';
        line.style.left = `calc(${fillPercentage}% - 1px)`;
        cell.appendChild(line);
      }
      
      grid.appendChild(cell);
    }
  }

  updateTime();
  setInterval(updateTime, 60000);   // 1min
}

init()