I made a small PopClip extension to help with calendar invitations and event listings that use another time zone.
Select a string such as:
Wednesday, September 16 • 10am – 12pm PDT
Then choose the TZ action. It shows the start time converted to Melbourne (Australia) time, without replacing or altering the selected text.
For the example above, it shows:
3:00 am Melbourne time (AEST)
The extension currently recognises these common abbreviations:
PDT, PST, EDT, EST, CDT, CST, MDT, MST, BST, GMT, UTC,
AEST, AEDT, ACST, ACDT, and AWST.
It is deliberately preview-only: it uses PopClip’s show-result behaviour, so the original selected text remains unchanged.
Current limitations:
- It converts the start of a time range, rather than displaying both start and end times.
- It uses fixed offsets for abbreviations. That is usually fine for an explicitly stated daylight/standard abbreviation such as PDT or AEST, but it does not yet infer daylight-saving status from the calendar date.
- Some abbreviations are globally ambiguous, particularly CST and IST.
I’m sharing it in case it is useful to others, and I’d welcome suggestions—especially on supported input formats, target-zone configuration, and a better icon/title.
Extension code follows:
// #popclip
// name: Time Zone Preview
// icon: TZ
// after: show-result
// language: javascript
const text = popclip.input.text.trim();
const match = text.match(
/\\b(\\d{1,2})(?::(\\d{2}))?\\s\*(am|pm)\\s\*(?:–|-|to)\\s\*(\\d{1,2})(?::(\\d{2}))?\\s\*(am|pm)?\\s+(PDT|PST|EDT|EST|CDT|CST|MDT|MST|BST|GMT|UTC|AEST|AEDT|ACST|ACDT|AWST)\\b/i,
);
if (!match) {
return "Select a time range such as ‘10am – 12pm PDT’.";
}
let [, hourText, minuteText, ampmText, , , , zoneText] = match;
let hour = Number(hourText);
const minute = Number(minuteText || 0);
const ampm = ampmText.toLowerCase();
const zone = zoneText.toUpperCase();
if (ampm === "pm" && hour !== 12) hour += 12;
if (ampm === "am" && hour === 12) hour = 0;
const offsets = {
PDT: -7,
PST: -8,
EDT: -4,
EST: -5,
CDT: -5,
CST: -6,
MDT: -6,
MST: -7,
BST: 1,
GMT: 0,
UTC: 0,
AEST: 10,
AEDT: 11,
ACST: 9.5,
ACDT: 10.5,
AWST: 8,
};
const sourceOffset = offsets[zone];
const melbourneOffset = 10;
const convertedMinutes =
(hour * 60 + minute + (melbourneOffset - sourceOffset) * 60 + 1440) % 1440;
const convertedHour = Math.floor(convertedMinutes / 60);
const convertedMinute = convertedMinutes % 60;
const convertedAmPm = convertedHour >= 12 ? "pm" : "am";
const displayHour = convertedHour % 12 || 12;
const displayMinute = String(convertedMinute).padStart(2, "0");
return `${displayHour}:${displayMinute} ${convertedAmPm} Melbourne time (AEST)`;