NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Zendesk SLA Threshold Override (Pacific-only) // @namespace http://tampermonkey.net/ // @version 0.2 // @description Turn SLA badges yellow at 1 h remaining and red at 15 min, interpreting times in Pacific Time regardless of your local zone. // @match https://*.zendesk.com/* // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; // thresholds const ONE_HOUR = 60 * 60 * 1000; const FIFTEEN_MIN= 15 * 60 * 1000; // returns true if PT was on DST for the given date function isPacificDST(year, monthIndex, day) { // monthIndex: 0=Jan … 11=Dec if (monthIndex < 2 || monthIndex > 10) return false; // Jan, Feb, Dec → no DST if (monthIndex > 2 && monthIndex < 10) return true; // Apr–Oct → DST // Mar or Nov → compute the “nth Sunday” if (monthIndex === 2) { // March: starts 2nd Sunday const mar1 = new Date(Date.UTC(year,2,1)); const firstSun = 1 + ((7 - mar1.getUTCDay()) % 7); return day >= (firstSun + 7); } // November: ends on the 1st Sunday const nov1 = new Date(Date.UTC(year,10,1)); const firstSunNov = 1 + ((7 - nov1.getUTCDay()) % 7); return day < firstSunNov; } // parse a datetime like "2025-05-16T17:00:00" **as** Pacific Time function parseAsPacific(dtstr) { // only match bare datetimes (no offset suffix) const m = dtstr.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2}))?$/); if (!m) return new Date(dtstr); // fallback to built-in const [, ys, ms, ds, hs, mins, ss] = m; const y = +ys, mi = +ms - 1, d = +ds, h = +hs, n = +mins, s = ss ? +ss : 0; const dst = isPacificDST(y, mi, d); const offsetMin = dst ? 420 : 480; // getTimezoneOffset minutes for PT // Date.UTC() treats the components as UTC; adding offsetMin minutes shifts it into PT’s actual wall-clock const utcMs = Date.UTC(y, mi, d, h, n, s) + offsetMin * 60_000; return new Date(utcMs); } function updateBadges() { const now = Date.now(); document .querySelectorAll('span[data-garden-id="tags.tag_view"] time[datetime]') .forEach(timeEl => { const raw = timeEl.getAttribute('datetime'); // if it has a “Z” or explicit ±HH:MM, native parsing is already correct const expiry = /Z$|[+\-]\d{2}:\d{2}$/.test(raw) ? new Date(raw) : parseAsPacific(raw); const remaining = expiry.getTime() - now; const badge = timeEl.closest('span[data-garden-id="tags.tag_view"]'); if (!badge) return; // reset badge.style.backgroundColor = ''; badge.style.color = ''; if (remaining <= 0 || remaining <= FIFTEEN_MIN) { // ≤ 15 min or overdue → RED badge.style.backgroundColor = '#d9534f'; badge.style.color = 'white'; } else if (remaining <= ONE_HOUR) { // ≤ 1 h → YELLOW badge.style.backgroundColor = '#f0ad4e'; badge.style.color = 'black'; } else { // > 1 h → GREEN badge.style.backgroundColor = '#5cb85c'; badge.style.color = 'white'; } }); } // run once shortly after page load… setTimeout(updateBadges, 2000); // …and every 30 s thereafter setInterval(updateBadges, 30000); })();