One instant, two local times
HTML for this example
Select the code and use your copy shortcut. Changes in this preview are temporary.
<!doctype html><html lang="en-US"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>One instant, two local times</title><style>*{box-sizing:border-box}html{color-scheme:light}body{margin:0;background:var(--bg,#f3f4f5);color:var(--ink,#192838);font:16px/1.5 system-ui,-apple-system,Segoe UI,sans-serif}main{max-width:1040px;margin:auto;padding:28px 28px 48px}header{margin-bottom:20px}h1{font-size:clamp(25px,4vw,36px);line-height:1.15;letter-spacing:-.035em;margin:6px 0 9px}h2{font-size:21px;line-height:1.25;margin:0 0 16px}h3{font-size:17px;margin:0 0 10px}p{margin:8px 0 16px}.eyebrow{font-size:12px;font-weight:750;text-transform:uppercase;letter-spacing:.12em;color:var(--accent,#275e86)}.subtle,small{color:#536170}.panel{padding:24px;background:white;border:1px solid var(--line,#d5dce2);border-radius:12px;margin:18px 0}.result{background:var(--result,#132f47);color:white;padding:26px;border-radius:14px;margin:18px 0}.result .subtle,.result small{color:#d7e3ed}.big{font-size:clamp(32px,6vw,56px);font-weight:750;line-height:1.15;letter-spacing:-.035em;margin:8px 0 14px;overflow-wrap:anywhere}.mono,pre,code{font-family:ui-monospace,Consolas,monospace;font-variant-numeric:tabular-nums}.mono{overflow-wrap:anywhere}pre{white-space:pre-wrap;overflow-wrap:anywhere;font-size:14px;line-height:1.7;margin:0}.grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:20px}.triple{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:16px}.fields{display:flex;gap:16px;flex-wrap:wrap;align-items:end}.fields>label{flex:1;min-width:130px}label{display:block;font-size:14px;font-weight:650}input,select,textarea{display:block;width:100%;font:inherit;font-size:16px;color:inherit;background:white;border:1px solid #a9b4bf;border-radius:6px;padding:11px 12px;margin-top:6px;min-width:0}input[type=checkbox]{display:inline;width:auto;margin:0 9px 0 0;accent-color:var(--accent,#275e86)}textarea{min-height:100px;resize:vertical}button{font:inherit;font-weight:650;padding:10px 17px;border:1px solid var(--accent,#275e86);background:var(--accent,#275e86);color:white;border-radius:6px;cursor:pointer;min-height:44px}button.secondary{background:white;color:var(--accent,#275e86)}button:disabled{opacity:.5;cursor:default}button:hover{filter:brightness(.94)}:focus-visible{outline:3px solid #e9a733;outline-offset:3px}.actions{display:flex;gap:10px;flex-wrap:wrap;margin-top:18px}.error{color:#a32325;font-weight:650;min-height:0}.error:not(:empty){padding:12px;border-left:4px solid #ae3237;background:#fff2f0;margin:14px 0}[hidden]{display:none!important}fieldset{border:1px solid #ccd4da;border-radius:8px;padding:18px;margin:0;min-width:0}legend{padding:0 7px;font-weight:750}table{width:100%;border-collapse:collapse;font-size:14px;table-layout:fixed}th{text-align:left;font-size:12px;text-transform:uppercase;letter-spacing:.04em;color:#536170}td,th{padding:12px 9px;border-bottom:1px solid #d9dfe4;vertical-align:top;overflow-wrap:anywhere}th:first-child,td:first-child{padding-left:0}dl{margin:0}dl>div{padding:10px 0;border-bottom:1px solid #d9dfe4}dt{font-size:13px;color:#536170}dd{margin:4px 0 0;font-weight:650}details{margin:20px 0;padding:18px 0;border-top:1px solid #cbd2d8}summary{cursor:pointer;font-weight:700}details p{margin-top:16px}a{color:var(--accent,#275e86)}.tag{display:inline-block;font-size:12px;font-weight:750;letter-spacing:.03em;border:1px solid currentColor;border-radius:5px;padding:4px 8px}.rule{border-top:1px solid #d9dfe4;padding-top:20px}.line{height:30px;border-bottom:1px solid #aab4be}.split-stat{display:flex;justify-content:space-between;gap:20px;align-items:baseline;flex-wrap:wrap}.result h2{color:white}.result table td,.result table th{border-color:#ffffff35;color:white}.result .eyebrow{color:#d7e3ed}.readout{padding:16px;background:#f0f4f6;border-radius:6px;margin-top:14px}.empty{padding:22px;border:1px dashed #a6b0ba;border-radius:8px;color:#536170;margin:18px 0}footer{font-size:13px;color:#536170;margin-top:24px}@media(max-width:720px){main{padding:20px}.panel,.result{padding:20px}.grid{gap:14px}.triple{gap:10px}}@media(max-width:540px){main{padding:16px}.grid{grid-template-columns:1fr}.panel,.result{padding:18px;border-radius:9px}.fields{gap:12px}.fields>label{min-width:100px}h1{font-size:27px}th,td{padding:10px 5px;font-size:13px}.triple{grid-template-columns:1fr}.big{font-size:38px}}.clock{padding-top:20px;border-top:1px solid #ffffff35}.clock h2{font-size:20px}.clock p{font-size:17px}</style></head><body style="--bg:#edf1f7;--accent:#35578e;--result:#192e52"><main><header><div class="eyebrow">Working example</div><h1>One instant, two local times</h1><p class="subtle">Keep the date and UTC offset beside each city’s clock.</p></header>
<section id="result" class="result" aria-live="polite"><div class="eyebrow">Exact source · UTC</div><p id="utc" class="mono"></p><div id="clocks" class="grid"></div></section><p id="error" class="error" role="alert"></p><p id="empty" class="empty" hidden>Inputs changed. Calculate to see the current result.</p>
<form id="form" class="panel" novalidate><h2>Convert an explicit instant</h2><label for="instant">Timestamp with seconds and Z or ±HH:MM<input id="instant" value="2026-11-01T05:30:00Z" inputmode="decimal" autocomplete="off"></label><label for="claim">Optional named source zone <select id="claim"><option value="">No zone claim — use the written offset</option><option>America/New_York</option><option>America/Los_Angeles</option></select></label><div class="actions"><button>Convert</button><button type="button" id="swap" class="secondary">Reverse city order</button></div></form>
<section class="panel"><h2>Which New York 1:30 a.m.?</h2><div class="actions"><button type="button" data-sample="0" class="secondary">Earlier 1:30 · UTC−04</button><button type="button" data-sample="1" class="secondary">Later 1:30 · UTC−05</button><button type="button" data-sample="2" class="secondary">Next day · Nov 2</button></div><div class="readout"><table><caption>November 1, 2026: same New York clock, different instants</caption><thead><tr><th>UTC instant</th><th>New York</th><th>Los Angeles</th></tr></thead><tbody><tr><td>Nov 1, 05:30</td><td>Nov 1, 01:30<br>UTC−04:00</td><td>Oct 31, 22:30<br>UTC−07:00</td></tr><tr><td>Nov 1, 06:30</td><td>Nov 1, 01:30<br>UTC−05:00</td><td>Oct 31, 23:30<br>UTC−07:00</td></tr></tbody></table></div></section><section class="panel"><h2>Calculation record</h2><pre id="trace"></pre></section>
<details><summary>Formats, date rules, and scheduling limits</summary><p>Use YYYY-MM-DDTHH:MM:SSZ or an explicit ±HH:MM offset, years 2000–2099. A wall time without an offset is incomplete: New York’s November 1, 2026, 01:30 needs an earlier −04:00 or later −05:00 choice. A nonexistent spring-forward time needs correction. If you name a source zone, its dated offset must match your timestamp.</p><p>Only America/New_York and America/Los_Angeles are displayed. Results use this browser’s Intl time-zone data; the three 2026 cases are demonstration checks, not proof of all dates. No meeting is booked. Confirm the intended instant in the calendar workflow.</p><p><a href="https://www.nist.gov/pml/time-and-frequency-division/popular-links/daylight-saving-time-dst">NIST transition rules</a> · <a href="https://www.timeanddate.com/time/zone/usa/new-york">New York dates and offsets</a></p></details><footer>Local review copy · No data is sent or saved.</footer></main><script>const $=id=>document.getElementById(id);const esc=s=>String(s).replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));function round(n,d){return (2n*n+d)/(2n*d)}function dec(n,d,p=6){let x=round(n*10n**BigInt(p),d).toString().padStart(p+1,'0');return p?x.slice(0,-p)+'.'+x.slice(-p):x}function exact(n,d){let a=n/d,r=n%d,s='';for(let i=0;r&&i<36;i++){r*=10n;s+=r/d;r%=d}return a+(s?'.'+s:'')+(r?'…':'')}function num(s,name,max=1000000000,places=9,zero=false){if(!new RegExp('^[0-9]+(?:[.][0-9]{1,'+places+'})?$').test(s)||s.length>35)throw Error(name+': use a '+(zero?'nonnegative':'positive')+' dot-decimal number.');let [a,b='']=s.split('.'),d=10n**BigInt(b.length),n=BigInt(a+b);if((zero?n<0n:n<=0n)||n>BigInt(max)*d)throw Error(name+': value outside the supported range.');return {n,d,v:Number(n)/Number(d)}}function clear(){ $('result').hidden=true;$('trace').textContent='';$('empty').hidden=false;$('error').textContent='';}function reveal(){ $('result').hidden=false;$('empty').hidden=true;$('error').textContent='';}function error(e){clear();$('error').textContent=e.message;}function bindForm(run){$('form').addEventListener('submit',e=>{e.preventDefault();run()});$('form').addEventListener('input',clear);$('form').addEventListener('change',clear);}
const zones=['America/New_York','America/Los_Angeles'];let reversed=false;const samples=['2026-11-01T05:30:00Z','2026-11-01T06:30:00Z','2026-11-02T05:30:00Z'];
function local(ms,z){const p=Object.fromEntries(new Intl.DateTimeFormat('en-US',{timeZone:z,year:'numeric',month:'2-digit',day:'2-digit',hour:'2-digit',minute:'2-digit',second:'2-digit',hourCycle:'h23',timeZoneName:'longOffset'}).formatToParts(new Date(ms)).map(x=>[x.type,x.value]));return {date:p.year+'-'+p.month+'-'+p.day,time:p.hour+':'+p.minute+':'+p.second,offset:p.timeZoneName.replace('GMT','UTC')}}
function convert(s,claim=''){const m=/^(20\d{2})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(Z|([+-])(\d{2}):(\d{2}))$/.exec(s);if(!m)throw Error('Timestamp: include a valid date, seconds, and explicit Z or offset. For repeated 01:30, choose earlier −04:00 or later −05:00.');const [y,mo,day,h,mi,se]=m.slice(1,7).map(Number);if(mo<1||mo>12||day<1||h>23||mi>59||se>59||day>new Date(Date.UTC(y,mo,0)).getUTCDate())throw Error('Timestamp: invalid calendar date or clock time.');const oh=Number(m[9]||0),om=Number(m[10]||0);if(oh>14||om>59||(oh===14&&om))throw Error('Timestamp: offset must be within ±14:00.');const offset=(m[8]==='-'?-1:1)*(oh*60+om);const ms=Date.UTC(y,mo-1,day,h,mi,se)-offset*60000;if(claim){if(!zones.includes(claim))throw Error('Unsupported source zone.');const v=local(ms,claim);if(v.date!==s.slice(0,10)||v.time!==s.slice(11,19))throw Error('Named source zone does not match the dated offset, or this local time does not exist. Correct the timestamp.');}return {utc:new Date(ms).toISOString(),rows:zones.map(z=>({zone:z,...local(ms,z)}))};}
function run(){try{const r=convert($('instant').value,$('claim').value);reveal();$('utc').textContent=r.utc;$('clocks').innerHTML=(reversed?[...r.rows].reverse():r.rows).map(x=>'<article class="clock"><h2>'+x.zone.split('/')[1].replace('_',' ')+'</h2><div class="big mono">'+x.time.slice(0,5)+'</div><p>'+x.date+' · '+x.offset+'</p><small>'+x.zone+'</small></article>').join('');$('trace').textContent='UTC: '+r.utc+'\n'+r.rows.map(x=>x.zone+' | '+x.date+' '+x.time+' | '+x.offset).join('\n');}catch(e){error(e)}}
bindForm(run);$('swap').onclick=()=>{reversed=!reversed;run()};document.querySelectorAll('[data-sample]').forEach(b=>b.onclick=()=>{$('instant').value=samples[Number(b.dataset.sample)];$('claim').value='';run()});run();</script>
<script>
// Local calculation controls: no native form submission or sandbox permissions required.
(()=>{const form=document.getElementById('form');if(!form)throw Error('Calculation form missing');const dispatch=()=>form.dispatchEvent(new Event('submit',{bubbles:true,cancelable:true}));const buttons=[...form.querySelectorAll('button')].filter(button=>button.type==='submit');if(buttons.length!==1)throw Error('Expected one calculation control');for(const button of buttons){button.type='button';button.addEventListener('click',dispatch);}form.addEventListener('keydown',event=>{if(event.key==='Enter'&&!event.isComposing&&['INPUT','SELECT'].includes(event.target.tagName)){event.preventDefault();dispatch();}});})();
</script>
</body></html>