Files
new-year-countdown/www/countdown.js
2022-12-31 01:41:29 +00:00

304 lines
8.2 KiB
JavaScript

// This is deliberately browser-compatible JS, so we avoid fat arrows etc.
var locale = "EN-GB";
var now = new Date();
var nextNewYearUTC = new Date(Date.UTC(now.getFullYear()+1, 0, 1));
var secsRemaining = 0;
var nextZone;
var counter = document.getElementById("counter");
var loc = document.getElementById("location");
var time = document.getElementById("localtime");
var untilDate = document.getElementById("until-date");
var countDownSound = new Audio('get-ready.ogg');
// Convert the next new year time to 'date, hours:mins:secs' in the target timezone
function zoneTime(zone, refTime) {
return refTime.toLocaleString('EN-US', {
hour: '2-digit',
hour12: false,
minute: '2-digit',
second: '2-digit',
year: 'numeric',
month: 'numeric',
day: 'numeric',
timeZone: zone,
});
}
function zoneOffsetMillis(timestr, baseDate) {
var v = timestr.split(/[/ :,]+/)
.map(function(str) { return Number(str); });
var date = new Date(v[2],v[0]-1,v[1],v[3],v[4],v[5]);
return date.valueOf() - baseDate.valueOf();
}
// Compile a list of timezones from Intl.supportedValuesOf
var zones = Intl.supportedValuesOf('timeZone')
.map(function(zone) { return {
zone: zone,
offset: zoneOffsetMillis(zoneTime(zone, nextNewYearUTC), nextNewYearUTC)
}; })
.sort(function(a,b) { return b.offset - a.offset; });
// Now concoct an index of timezone hour offsets to timezone info.
// Each zone has:
// { abbrevs, offset, hOffset, nydDate }
var zoneIndex = zones
.reduce((a, v) => {
var hOffset = v.offset/3600000;
var zoneInfo = a[hOffset];
if (zoneInfo)
zoneInfo.abbrevs.push(v.zone);
else {
a[hOffset] = {
abbrevs: [v.zone],
offset: v.offset,
hOffset: hOffset,
nydDate: new Date(nextNewYearUTC.valueOf() + v.offset),
};
}
return a;
}, {});
console.debug(nextNewYearUTC, zoneIndex);
// This info is just used to say what cities / countries are in the timezone
var zoneInfo = {
"-12": {
country: "small region of U.S.A.",
city: "Baker Island, Howland Island"
},
"-11": {
country: "American Samoa, Midway Atoll/U.S.A. and 1 more",
city: "Alofi, Midway, Pago Pago"
},
"-10": {
country: "Cook Islands, small region of U.S.A. and 2 more",
city: "Honolulu, Rarotonga, Adak, Papeete"
},
"-9.5": {
country: "French Polynesia, Marquesas Islands",
city: "Taiohae"
},
"-9": {
country: "Alaska/U.S.A. and French Polynesia",
city: "Anchorage, Fairbanks, Unalaska, Juneau"
},
"-8": {
country: "Pitcairn Islands, regions of U.S.A. Mexico, and Canada",
city: "Los Angeles, San Francisco, Las Vegas, Seattle"
},
"-7": {
country: "some regions of U.S.A., Mexico, some regions of Canada",
city: "Calgary, Denver, Edmonton, Phoenix"
},
"-6": {
country: "Belize, regions of U.S.A., Mexico, some regions of Canada and 8 more",
city: "Mexico City, Chicago, Guatemala, Dallas"
},
"-5": {
country: "regions of U.S.A., regions of Canada and 12 more",
city: "New York, Washington DC, Detroit, Havana"
},
"-4": {
country: "Venezuela",
city: "Caracas, Barquisimeto, Maracaibo, Maracay"
},
"-3.5": {
country: "Newfoundland and Labrador/Canada",
city: "St. John's, Conception Bay South, Corner Brook, Gander"
},
"-3": {
country: "regions of Brazil, Argentina and 7 more",
city: "Buenos Aires, Santiago, Asuncion, Paramaribo"
},
"-2": {
country: "South Georgia and the South Sandwich Islands",
city: "Grytviken",
},
"-1": {
country: "Cape Verde, some regions of Greenland and 1 more",
city: "Praia, Ponta Delgada (Azores), Ittoqqortoormiit, Mindelo"
},
"0": {
country: "United Kingdom and 24 more",
city: "London, Casablanca, Dublin, Lisbon"
},
"1": {
country: "Germany and 43 more",
city: "Brussels, Madrid, Paris, Rome"
},
"2": {
country: "Greece and 30 more",
city: "Cairo, Ankara, Athens, Bucharest"
},
"3": {
country: "Iraq and 20 more",
city: "Baghdad, Khartoum, Nairobi, Addis Ababa"
},
"3.5": {
country: "Iran",
city: "Tehran, Rasht, Esfahãn, Bandar-Abbas"
},
"4": {
country: "UAE, parts of Russia, Georgia, Armenia, Mauritius",
city: "Tblisi, Dubai, Abu Dhabi",
},
"4.5": {
country: "Afghanistan",
city: "Kabul, Kandahar, Mazari Sharif, Herat"
},
"5": {
country: "Pakistan and 8 more",
city: "Tashkent, Islamabad, Lahore, Karachi"
},
"5.5": {
country: "India and Sri Lanka",
city: "New Delhi, Mumbai, Kolkata, Bangalore"
},
"5.75": {
country: "Nepal",
city: "Kathmandu, Biratnagar, Pokhara"
},
"6": {
country: "Bangladesh, some regions of Russia and 4 more",
city: "Dhaka, Almaty, Bishkek, Thimphu"
},
"6.5": {
country: "Myanmar and Cocos Islands",
city: "Yangon, Naypyidaw, Mandalay, Bantam"
},
"7": {
country: "much of Indonesia, Thailand and 7 more",
city: "Jakarta, Bangkok, Hanoi, Phnom Penh"
},
"8": {
country: "China and 12 more",
city: "Beijing, Hong Kong, Manila, Singapore"
},
"8.75": {
country: "Western Australia/Australia",
city: "Eucla"
},
"9": {
country: "Japan and 6 more",
city: "Tokyo, Seoul, Pyongyang, Dili"
},
"9.5": {
country: "Northern Territory/Australia",
city: "Darwin, Alice Springs, Uluru"
},
"10": {
country: "Queensland/Australia and 5 more",
city: "Brisbane, Port Moresby, Guam (Hagåtña), Cairns"
},
"10.5": {
country: "small region of Australia",
city: "Adelaide, Broken Hill"
},
"11": {
country: "Vanuatu, Solomon Islands, much of Australia and 5 more",
city: "Melbourne, Sydney, Canberra, Honiara"
},
"12": {
country: "Kiribati, Marshall Islands, Norfolk Island, Tuvalu, Fiji",
city: "Kingston",
},
"13": {
country: "New Zealand with exceptions and 5 more",
city: "Auckland, Suva, Wellington, Nukualofa"
},
"13.75": {
country: "Chatham Islands/New Zealand",
city: "Chatham Islands"
},
};
console.debug(zoneInfo);
var zoneOrder = Object.keys(zoneIndex)
.map(Number)
.sort(function(a,b) { return a-b; });
console.debug(zoneOrder);
function pad(num) {
return (num<10? "0"+num : ""+num);
}
function counterClass(millisecsRemaining) {
if (millisecsRemaining > 3600000) return "hours";
if (millisecsRemaining > 60000) return "minutes";
if (millisecsRemaining > 10000) return "seconds";
return "countdown";
}
function timeRemaining(millisecsRemaining) {
// var days = Math.floor(millisecsRemaining / millisInDay)
// millisecsRemaining -= days * millisInDay
var hours = Math.floor(millisecsRemaining / 3600000)
millisecsRemaining -= hours * 3600000
var mins = Math.floor(millisecsRemaining / 60000)
millisecsRemaining -= mins * 60000
var secs = Math.round(millisecsRemaining / 1000)
return hours+"h "+pad(mins)+"m "+pad(secs)+"s"
}
var playing = false;
function handleTick(now) {
console.debug(now, now.getMilliseconds());
while (!nextZone || zoneIndex[nextZone].nydDate <= now) {
if (zoneOrder.length == 0) return;
nextZone = zoneOrder.shift();
console.debug("next up: zone", nextZone);
}
var millisecsRemaining = zoneIndex[nextZone].nydDate.getTime() - now.getTime();
var excl = millisecsRemaining < 1000*5? "!" : "";
console.debug("seconds",now.getSeconds());
if (millisecsRemaining < 2*60*1000) {
if (!playing) {
countDownSound.play();
playing = true;
}
}
else
// resets the announcement
playing = false;
var info = zoneInfo[nextZone];
var zone = zoneIndex[nextZone];
untilDate.innerHTML = nextNewYearUTC.toLocaleDateString(locale, {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric",
});
counter.innerHTML = timeRemaining(millisecsRemaining);
loc.innerHTML = info? info.city || info.country : zone.abbrev[0];
time.innerHTML = zone.nydDate.toLocaleDateString(locale, {
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
});
counter.className = counterClass(millisecsRemaining);
}
function ticker() {
var now = new Date();
handleTick(now);
var millis = now.getMilliseconds()
var millisTilNextSecond = 1000-millis;
setTimeout(ticker, millisTilNextSecond);
}
ticker();