Skip to content

Instantly share code, notes, and snippets.

@myersjustinc
Last active November 12, 2021 20:11
Show Gist options
  • Save myersjustinc/6de3a20ef5a41088764edbb1ac37f4ec to your computer and use it in GitHub Desktop.
Save myersjustinc/6de3a20ef5a41088764edbb1ac37f4ec to your computer and use it in GitHub Desktop.
Bookmarklet to add time zone labels to Outlook 365's web-based calendar view
javascript:(function(){"use strict";const e='\n [role="main"] [aria-hidden="true"] > \n div > [aria-hidden="true"] time',t=`${e} abbr`;if(document.querySelectorAll(t).length)return;const r=new Date,n=document.querySelector('[role="main"]').getAttribute("aria-label"),o=parseInt(/\d{2}(?=:\d{2})/.exec(n)[0],10),a=[{formalName:%22America/Chicago%22,shortName:%22Central%22,abbr:%22CT%22},{formalName:%22America/New_York%22,shortName:%22Eastern%22,abbr:%22ET%22},{formalName:%22America/Los_Angeles%22,shortName:%22Pacific%22,abbr:%22PT%22}];a.forEach(function(e){const%20t=new%20Intl.DateTimeFormat(%22en-US%22,{timeZone:e.formalName,hour:%22numeric%22,minute:%22numeric%22,hour12:!1}).format(r),n=parseInt(/\d{2}(?=:\d{2})/.exec(t)[0],10);e.localHour=n,e.offsetFromOutlook=n-o}),document.querySelectorAll(e).forEach(function(e){if(null!=e.querySelector(%22abbr%22))return;const%20t=parseInt(e.textContent.trim(),10),r=document.createDocumentFragment();a.forEach(function(e,n){r.appendChild(function(e,t,r){const%20n=function(e,t){const%20r=t+e.offsetFromOutlook;return%20r%3C0?r+24:r%3E23?r-24:r}(e,t),o=document.createElement(%22abbr%22);return%20o.title=`\n%20%20%20%20%20%20${n.toString(10).padStart(2,%220%22)}:00\n%20%20%20%20%20%20${e.shortName}`.trim().replace(/\s+/g,%22%20%22),o.textContent=`\n%20%20%20%20%20%20${n}\n%20%20%20%20%20%20${e.abbr}`.trim().replace(/\s+/g,%22%20%22),o.style.display=%22block%22,o.style.textDecoration=%22none%22,r||(o.style.fontSize=%220.75em%22),o}(e,t,0===n))}),e.innerHTML=%22%22,e.appendChild(r)})})();
(function() {
'use strict'
// Licensed ISC:
//
// Copyright 2021 Justin Myers
//
// Permission to use, copy, modify, and/or distribute this
// software for any purpose with or without fee is hereby
// granted, provided that the above copyright notice and this
// permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS
// ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
// RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
// OF THIS SOFTWARE.
// Find all the labels we want to augment
const timeSelector = `
[role="main"] [aria-hidden="true"] >
div > [aria-hidden="true"] time`
const timeAbbrSelector = `${timeSelector} abbr`
if (document.querySelectorAll(timeAbbrSelector).length) {
return
}
// Figure out what time Outlook thinks it is
const now = new Date()
const outlookLabel = document.querySelector(
'[role="main"]').getAttribute('aria-label')
const outlookHour = parseInt(
/\d{2}(?=:\d{2})/.exec(outlookLabel)[0], 10)
// Figure out what time it is in each of our desired time zones,
// along with how far it is from Outlook's own time
const timeZones = [
{
formalName: 'America/Chicago',
shortName: 'Central',
abbr: 'CT'
},
{
formalName: 'America/New_York',
shortName: 'Eastern',
abbr: 'ET'
},
{
formalName: 'America/Los_Angeles',
shortName: 'Pacific',
abbr: 'PT'
}
]
timeZones.forEach(function(zone) {
const zoneFormat = new Intl.DateTimeFormat('en-US', {
timeZone: zone.formalName,
hour: 'numeric',
minute: 'numeric',
hour12: false
})
const localTime = zoneFormat.format(now)
const localHour = parseInt(
/\d{2}(?=:\d{2})/.exec(localTime)[0], 10)
zone.localHour = localHour
zone.offsetFromOutlook = localHour - outlookHour
})
// Build hour labels
function getZoneHour(zone, hourToLabel) {
const rawZoneHour = hourToLabel + zone.offsetFromOutlook
if (rawZoneHour < 0) {
return rawZoneHour + 24
}
if (rawZoneHour > 23) {
return rawZoneHour - 24
}
return rawZoneHour
}
function buildLabel(zone, hourToLabel, emphasize) {
const currentHour = getZoneHour(zone, hourToLabel)
const labelElem = document.createElement('abbr')
labelElem.title = `
${currentHour.toString(10).padStart(2, '0')}:00
${zone.shortName}`.trim().replace(/\s+/g, ' ')
labelElem.textContent = `
${currentHour}
${zone.abbr}`.trim().replace(/\s+/g, ' ')
labelElem.style.display = 'block'
labelElem.style.textDecoration = 'none'
if (!emphasize) {
labelElem.style.fontSize = '0.75em'
}
return labelElem
}
// Update every hour label
const timeElements = document.querySelectorAll(timeSelector)
timeElements.forEach(function(timeElem) {
if (timeElem.querySelector('abbr') != null) {
return
}
const localHour = parseInt(timeElem.textContent.trim(), 10)
const labelsFragment = document.createDocumentFragment()
timeZones.forEach(function(zone, zoneIndex) {
labelsFragment.appendChild(buildLabel(
zone, localHour, zoneIndex === 0))
})
timeElem.innerHTML = ''
timeElem.appendChild(labelsFragment)
})
}())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment