XKCD 6/6 Time

by Ondřej Žára

HTML

<span id="clock"></span>
<ul>
  <li>Browser requirements: async/await, allowed geolocation</li>
  <li>Implements <a href="https://xkcd.com/2050/">https://xkcd.com/2050/</a></li>
  <li>Uses <a href="https://sunrise-sunset.org/api">https://sunrise-sunset.org/api</a></li>
  <li>Created by <a href="https://ondras.zarovi.cz/">Ondřej Žára</a></li>
</ul>

JavaScript

let lat = 0, lon = 0;
let before = null, after = null, h0 = null;

const S = 1000;
const M = 60*S;
const H = 60*M;

async function getSun(date = "today") {
  let response = await fetch(`https://api.sunrise-sunset.org/json?lat=${lat}&lng=${lng}&formatted=0&date=${date}`);
  let data = await response.json();
  return {
    sunrise: new Date(data.results.sunrise).getTime(),
    sunset: new Date(data.results.sunset).getTime()
  }
}

async function onPosition(position) {
	lat = position.coords.latitude;
  lng = position.coords.longitude;
  update();
}

function onError(e) {
  alert(e.message);
}

function show(h, m, s) {
  let node = document.querySelector("#clock");
  let h_ = (h+h0) % 24;
  let m_ = m.toString().padStart(2, "0");
  let s_ = s.toString().padStart(2, "0");
  node.textContent = `${h_}:${m_}:${s_}`;
}

async function updateBeforeAfter() {
  // fixme before sunrise, after sunset
  let sun = await getSun();

  let now = Date.now();
  if (now < sun.sunrise) {
    after = sun.sunrise;
    sun = await getSun("yesterday");
    before = sun.sunset;
    h0 = 18;
  } else if (now > sun.sunset) {
    before = sun.sunset
    sun = await getSun("tomorrow");
    after = sun.sunrise;
    h0 = 18;
  } else {
    before = sun.sunrise;
    after = sun.sunset;
    h0 = 6;
  }
  update();
}

function update() {
  let now = Date.now();
  if (before === null || now < before || now > after) {
    return updateBeforeAfter();
  }

  const totalMsReal = 12 * H;
  const totalMsXkcd = after-before;
  
  let elapsedMsReal = (now - before);
  let elapsedMsXkcd = elapsedMsReal * (totalMsXkcd / totalMsReal);
  
  let ms = elapsedMsXkcd;
  let h = Math.floor(ms / H);
  ms = ms % H;
  let m = Math.floor(ms / M);
  ms = ms % M;
  let s = Math.round(ms / S);
  
  show(h, m, s);
  setTimeout(update, 500);
}

navigator.geolocation.getCurrentPosition(onPosition, onError);

/* date mock
Date.now = () => {
  return new Date().getTime() - 12*H;
}
*/