A product release initially belongs to January, but the team moves it to February. Updating the UTC month preserves its time and normally its day, giving a small calendar edit that you can inspect before saving.
Moving January fifteenth to February is a straightforward case: both months contain the fifteenth. The code therefore preserves that day and the 09:30 UTC time while changing the month. The form uses February's human number two, so the setter receives index one. The result includes the new epoch timestamp because that number is the setter's return, not a newly created Date object.

Follow the value through the operation
The form supplies a human month number, which the setter converts to a zero-based index. setUTCMonth mutates the Date and returns epoch milliseconds. Showing the ISO timestamp alongside that return makes the changed month visible without losing the method's actual contract.
The object created from the source string is mutated in place. If another variable points to that Date, it observes the changed month too. The source JSON remains unchanged because the code constructs a fresh Date before applying the setter. Separating these two layers prevents a misleading claim that setUTCMonth is nonmutating merely because the original input string still looks the same in the source panel.
Month-end dates are where the business rule becomes important. January thirty-first cannot fit in February. Keeping the day while changing the month causes the extra days to normalize into March. A subscription system might instead require the final day of February, but that is an additional clamping policy; the basic setter does not infer it for you. Explain and test that policy before using this small example for billing renewals.
Change the source to January thirty-first in the same year, leave the target month at two, and inspect the ISO output. It should reveal rollover rather than silently choosing February's final day. Restore the fifteenth to see the ordinary case again. When a teammate checks the example, ask them to compare those two cases, because a harmless-looking middle-of-month test cannot establish correct month-end behavior.

<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="nos-demo-layout" content="responsive-lab"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Reschedule a monthly release</title><style>*{box-sizing:border-box}body{margin:0;background:#edf1f0;color:#172d36;font:15px/1.5 system-ui,sans-serif}main{max-width:1040px;margin:auto;padding:24px}header{display:flex;justify-content:space-between;gap:12px;align-items:center;margin-bottom:20px}.eyebrow{font-size:11px;font-weight:800;letter-spacing:2px;color:#55716b;text-transform:uppercase}h1{font-size:clamp(23px,4vw,34px);line-height:1.15;margin:8px 0}h2{font-size:16px;margin:0 0 12px}.tag{border:1px solid #bfceca;border-radius:99px;padding:6px 12px;font-size:12px;white-space:nowrap}.panel{background:white;border:1px solid #d4dfdb;border-radius:18px;padding:20px;margin-bottom:14px;box-shadow:0 9px 30px #1d393407}.grid{display:grid;grid-template-columns:1fr 1fr;gap:14px}.result{background:#17352f;color:#f7fffb}.result h2{color:#c9ded5}.cards{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(135px,1fr))}.card{background:#f2ffc2;color:#17352f;padding:15px;border-radius:12px;min-width:0;overflow-wrap:anywhere}.card small{display:block;color:#536f60;font-size:11px;font-weight:700;text-transform:uppercase;margin-bottom:4px}.field{display:block;margin:8px 0}.field small{margin:0 0 3px;text-transform:none}.value{font-size:15px;font-weight:650;white-space:pre-wrap}.hero{font-size:clamp(28px,5vw,46px);font-weight:750;line-height:1.2;overflow-wrap:anywhere}.muted{color:#587169;font-size:13px}.result .muted{color:#bed7cb}pre{margin:0;white-space:pre-wrap;overflow-wrap:anywhere;font:13px/1.6 ui-monospace,monospace;max-height:220px;overflow:auto}.toolbar{display:flex;flex-wrap:wrap;gap:8px;margin:12px 0}button{background:#17352f;color:white;border:0;border-radius:10px;padding:10px 14px;font:600 13px system-ui;cursor:pointer}button.secondary{background:#edf3ea;color:#244337;border:1px solid #d0ddd0}button:hover{filter:brightness(.94)}button:focus-visible,textarea:focus-visible,summary:focus-visible{outline:3px solid #749919;outline-offset:3px}textarea{display:block;width:100%;height:126px;border:1px solid #bccfc5;border-radius:10px;background:#fafcf9;padding:12px;font:13px/1.5 ui-monospace,monospace;resize:vertical;color:#15362c}label{font-weight:650;font-size:13px}details{margin-top:16px}summary{cursor:pointer;font-size:13px;font-weight:650}#status{min-height:20px;font-size:12px;color:#526f60;margin-top:8px}.error{color:#ffcfbf}.footer{font-size:12px;color:#627b72;margin-top:16px}.empty{font-size:18px;color:#c7dacd}.source-card{background:#f4f7f3;border:1px solid #dbe4d8;border-radius:10px;padding:12px;overflow-wrap:anywhere;white-space:pre-wrap}.source-list{display:flex;flex-wrap:wrap;gap:8px;max-height:170px;overflow:auto}.source-list .source-card{flex:1;min-width:100px}.code{background:#f5f8f2}mark{background:#f2ffc2;color:#183a2d;padding:2px 5px;border-radius:4px}@media(max-width:620px){main{padding:15px}.grid{grid-template-columns:1fr}.panel{padding:16px}header{align-items:flex-start}.tag{display:none}h1{font-size:25px}.cards{grid-template-columns:repeat(auto-fit,minmax(125px,1fr))}.source-list{max-height:120px}pre{max-height:150px}}</style></head><body><main>
<header><div><div class="eyebrow">JavaScript • Date setUTCMonth</div><h1>Reschedule a monthly release</h1><div class="muted">Change the data. See the actual result.</div></div><span class="tag">Interactive prototype</span></header>
<section class="panel"><label for="input">Input data (JSON)</label><textarea id="input" maxlength="10000" spellcheck="false"></textarea><div class="toolbar"><button id="run" type="button">Update preview</button><button class="secondary" id="original" type="button">Original data</button><button class="secondary" id="changed" type="button">Changed data</button><button class="secondary" id="edge" type="button">Empty / zero case</button></div><div id="status" role="status" aria-live="polite"></div></section>
<div class="grid"><section class="panel"><h2>Source after the operation</h2><div id="source" class="source-list"></div><p id="source-status" class="muted"></p></section><section class="panel result"><h2>Returned result</h2><div id="result" aria-live="polite"></div></section></div><details class="panel code"><summary>Inspect the exact values</summary><pre id="inspect"></pre></details>
<p class="footer">Local sample data only. No server request, account, or installation is needed.</p></main><script>
'use strict';
const initial = "{\"date\":\"2026-01-15T09:30:00Z\",\"month\":2}";
const alternate = "{\n \"date\": \"2026-01-16T09:30:00.000Z\",\n \"month\": 3\n}";
const empty = "{}";
const input = document.getElementById('input');
const output = document.getElementById('result');
const source = document.getElementById('source');
const status = document.getElementById('status');
function transform(data) { return (()=>{const date=new Date(data.date);const timestamp=date.setUTCMonth(data.month-1);return {iso:date.toISOString(),timestamp};})(); }
function readable(value) {
if (value === undefined) return 'undefined';
if (typeof value === 'number' && Object.is(value, -0)) return '-0';
return JSON.stringify(value, (_, v) => typeof v === 'number' && !Number.isFinite(v) ? String(v) : v, 2);
}
function renderCards(value, host) {
host.replaceChildren();
if (Array.isArray(value)) {
if (!value.length) {const p=document.createElement('p');p.className='empty';p.textContent='No items returned';host.append(p);return;}
const grid=document.createElement('div');grid.className='cards';
value.slice(0,40).forEach((item,index)=>{const card=document.createElement('div');card.className='card';const label=document.createElement('small');label.textContent='Item '+(index+1);card.append(label);appendValue(item,card);grid.append(card);});host.append(grid);
if(value.length>40){const p=document.createElement('p');p.textContent='Showing 40 of '+value.length+' items.';host.append(p);}
} else if(value && typeof value==='object'){const card=document.createElement('div');card.className='card';appendValue(value,card);host.append(card);}
else {const p=document.createElement('div');p.className='hero';p.textContent=typeof value==='boolean'?(value?'Yes · true':'No · false'):value===undefined?'No value · undefined':value===''?'Empty string':String(value);host.append(p);}
}
function appendValue(value,host){
if(value && typeof value==='object' && !Array.isArray(value))Object.entries(value).slice(0,40).forEach(([key,item])=>{const row=document.createElement('div');row.className='field';const label=document.createElement('small');label.textContent=key;const text=document.createElement('span');text.className='value';text.textContent=typeof item==='string'?item:readable(item);row.append(label,text);host.append(row);});
else {const text=document.createElement('div');text.className='value';text.textContent=typeof value==='string'?value:readable(value);host.append(text);}
}
function update(){
try{
if(input.value.length>10000)throw new Error('Keep this sample below 10,000 characters.');
const data=JSON.parse(input.value);const before=readable(data);const result=transform(data);const after=readable(data);
renderCards(result,output);source.replaceChildren();(Array.isArray(data)?data.slice(0,40):[data]).forEach(item=>{const card=document.createElement('div');card.className='source-card';card.textContent=typeof item==='string'?item:readable(item);source.append(card);});document.getElementById('source-status').textContent=before===after?'Data values unchanged':'Data values changed in place';document.getElementById('inspect').textContent='Input before:\n'+before+'\n\nReturned result:\n'+readable(result)+'\n\nSource after:\n'+after;
output.classList.remove('error');status.textContent='Preview updated. '+(before===after?'Data values unchanged.':'Source data changed in place.');
}catch(error){output.replaceChildren();output.classList.add('error');output.textContent=error.name+': '+error.message;source.replaceChildren();document.getElementById('source-status').textContent='';document.getElementById('inspect').textContent='';status.textContent='Check the JSON and the input shape. Restore Original data to start again.';}
}
document.getElementById('run').addEventListener('click',update);
[['original',initial],['changed',alternate],['edge',empty]].forEach(([id,value])=>document.getElementById(id).addEventListener('click',()=>{input.value=value;update();}));
input.value=initial;update();
</script></body></html>
Run the same calculation in your code
The JavaScript below isolates this example’s calculation. The complete HTML also includes its editable input and result panels.
const data = {"date":"2026-01-15T09:30:00Z","month":2};
const result = (()=>{const date=new Date(data.date);const timestamp=date.setUTCMonth(data.month-1);return {iso:date.toISOString(),timestamp};})();
console.log(result);
The original input produces this result:
{
"iso": "2026-02-15T09:30:00.000Z",
"timestamp": 1771147800000
}
Keep the timestamp unit and UTC convention visible while checking the result. Those are part of this example’s contract, rather than settings inferred from whichever machine opens the page.
Check the boundary before reusing it
A January thirty-first date can overflow into March when assigned February. The setter does not clamp automatically to the target month's final day, so billing rules need explicit handling.
Try January thirty-first with February as the target. Compare that overflow with the fifteenth used initially, then decide which behavior your scheduling rule actually requires.
Changed begins on January sixteenth and requests March. The result should retain the sixteenth and 09:30 UTC while moving to March. Use your separate January thirty-first experiment to inspect overflow rather than expecting it from this preset.

<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="nos-demo-layout" content="responsive-lab"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Reschedule a monthly release</title><style>*{box-sizing:border-box}body{margin:0;background:#edf1f0;color:#172d36;font:15px/1.5 system-ui,sans-serif}main{max-width:1040px;margin:auto;padding:24px}header{display:flex;justify-content:space-between;gap:12px;align-items:center;margin-bottom:20px}.eyebrow{font-size:11px;font-weight:800;letter-spacing:2px;color:#55716b;text-transform:uppercase}h1{font-size:clamp(23px,4vw,34px);line-height:1.15;margin:8px 0}h2{font-size:16px;margin:0 0 12px}.tag{border:1px solid #bfceca;border-radius:99px;padding:6px 12px;font-size:12px;white-space:nowrap}.panel{background:white;border:1px solid #d4dfdb;border-radius:18px;padding:20px;margin-bottom:14px;box-shadow:0 9px 30px #1d393407}.grid{display:grid;grid-template-columns:1fr 1fr;gap:14px}.result{background:#17352f;color:#f7fffb}.result h2{color:#c9ded5}.cards{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(135px,1fr))}.card{background:#f2ffc2;color:#17352f;padding:15px;border-radius:12px;min-width:0;overflow-wrap:anywhere}.card small{display:block;color:#536f60;font-size:11px;font-weight:700;text-transform:uppercase;margin-bottom:4px}.field{display:block;margin:8px 0}.field small{margin:0 0 3px;text-transform:none}.value{font-size:15px;font-weight:650;white-space:pre-wrap}.hero{font-size:clamp(28px,5vw,46px);font-weight:750;line-height:1.2;overflow-wrap:anywhere}.muted{color:#587169;font-size:13px}.result .muted{color:#bed7cb}pre{margin:0;white-space:pre-wrap;overflow-wrap:anywhere;font:13px/1.6 ui-monospace,monospace;max-height:220px;overflow:auto}.toolbar{display:flex;flex-wrap:wrap;gap:8px;margin:12px 0}button{background:#17352f;color:white;border:0;border-radius:10px;padding:10px 14px;font:600 13px system-ui;cursor:pointer}button.secondary{background:#edf3ea;color:#244337;border:1px solid #d0ddd0}button:hover{filter:brightness(.94)}button:focus-visible,textarea:focus-visible,summary:focus-visible{outline:3px solid #749919;outline-offset:3px}textarea{display:block;width:100%;height:126px;border:1px solid #bccfc5;border-radius:10px;background:#fafcf9;padding:12px;font:13px/1.5 ui-monospace,monospace;resize:vertical;color:#15362c}label{font-weight:650;font-size:13px}details{margin-top:16px}summary{cursor:pointer;font-size:13px;font-weight:650}#status{min-height:20px;font-size:12px;color:#526f60;margin-top:8px}.error{color:#ffcfbf}.footer{font-size:12px;color:#627b72;margin-top:16px}.empty{font-size:18px;color:#c7dacd}.source-card{background:#f4f7f3;border:1px solid #dbe4d8;border-radius:10px;padding:12px;overflow-wrap:anywhere;white-space:pre-wrap}.source-list{display:flex;flex-wrap:wrap;gap:8px;max-height:170px;overflow:auto}.source-list .source-card{flex:1;min-width:100px}.code{background:#f5f8f2}mark{background:#f2ffc2;color:#183a2d;padding:2px 5px;border-radius:4px}@media(max-width:620px){main{padding:15px}.grid{grid-template-columns:1fr}.panel{padding:16px}header{align-items:flex-start}.tag{display:none}h1{font-size:25px}.cards{grid-template-columns:repeat(auto-fit,minmax(125px,1fr))}.source-list{max-height:120px}pre{max-height:150px}}</style></head><body><main>
<header><div><div class="eyebrow">JavaScript • Date setUTCMonth</div><h1>Reschedule a monthly release</h1><div class="muted">One input, one operation, one observable result.</div></div><span class="tag">Starter example</span></header>
<section class="panel"><label for="input">Input data (JSON)</label><textarea id="input" maxlength="10000" spellcheck="false"></textarea><div class="toolbar"><button id="run" type="button">Update preview</button><button class="secondary" id="original" type="button">Original data</button><button class="secondary" id="changed" type="button">Changed data</button><button class="secondary" id="edge" type="button">Empty / zero case</button></div><div id="status" role="status" aria-live="polite"></div></section>
<div class="grid"><section class="panel code"><h2>The operation</h2><pre id="expression"></pre></section><section class="panel result"><h2>Returned result</h2><pre id="result" aria-live="polite"></pre></section></div><section class="panel"><h2>Source after the operation</h2><pre id="source"></pre></section>
<p class="footer">Local sample data only. No server request, account, or installation is needed.</p></main><script>
'use strict';
const initial = "{\"date\":\"2026-01-15T09:30:00Z\",\"month\":2}";
const alternate = "{\n \"date\": \"2026-01-16T09:30:00.000Z\",\n \"month\": 3\n}";
const empty = "{}";
const input = document.getElementById('input');
const output = document.getElementById('result');
const source = document.getElementById('source');
const status = document.getElementById('status');
function transform(data) { return (()=>{const date=new Date(data.date);const timestamp=date.setUTCMonth(data.month-1);return {iso:date.toISOString(),timestamp};})(); }
function readable(value) {
if (value === undefined) return 'undefined';
if (typeof value === 'number' && Object.is(value, -0)) return '-0';
return JSON.stringify(value, (_, v) => typeof v === 'number' && !Number.isFinite(v) ? String(v) : v, 2);
}
function renderCards(value, host) {
host.replaceChildren();
if (Array.isArray(value)) {
if (!value.length) {const p=document.createElement('p');p.className='empty';p.textContent='No items returned';host.append(p);return;}
const grid=document.createElement('div');grid.className='cards';
value.slice(0,40).forEach((item,index)=>{const card=document.createElement('div');card.className='card';const label=document.createElement('small');label.textContent='Item '+(index+1);card.append(label);appendValue(item,card);grid.append(card);});host.append(grid);
if(value.length>40){const p=document.createElement('p');p.textContent='Showing 40 of '+value.length+' items.';host.append(p);}
} else if(value && typeof value==='object'){const card=document.createElement('div');card.className='card';appendValue(value,card);host.append(card);}
else {const p=document.createElement('div');p.className='hero';p.textContent=typeof value==='boolean'?(value?'Yes · true':'No · false'):value===undefined?'No value · undefined':value===''?'Empty string':String(value);host.append(p);}
}
function appendValue(value,host){
if(value && typeof value==='object' && !Array.isArray(value))Object.entries(value).slice(0,40).forEach(([key,item])=>{const row=document.createElement('div');row.className='field';const label=document.createElement('small');label.textContent=key;const text=document.createElement('span');text.className='value';text.textContent=typeof item==='string'?item:readable(item);row.append(label,text);host.append(row);});
else {const text=document.createElement('div');text.className='value';text.textContent=typeof value==='string'?value:readable(value);host.append(text);}
}
function update(){
try{
if(input.value.length>10000)throw new Error('Keep this sample below 10,000 characters.');
const data=JSON.parse(input.value);const before=readable(data);const result=transform(data);const after=readable(data);
output.textContent=readable(result);source.textContent=after;
output.classList.remove('error');status.textContent='Preview updated. '+(before===after?'Data values unchanged.':'Source data changed in place.');
}catch(error){output.replaceChildren();output.classList.add('error');output.textContent=error.name+': '+error.message;source.replaceChildren();status.textContent='Check the JSON and the input shape. Restore Original data to start again.';}
}
document.getElementById('run').addEventListener('click',update);
[['original',initial],['changed',alternate],['edge',empty]].forEach(([id,value])=>document.getElementById(id).addEventListener('click',()=>{input.value=value;update();}));
document.getElementById('expression').textContent="(()=>{const date=new Date(data.date);const timestamp=date.setUTCMonth(data.month-1);return {iso:date.toISOString(),timestamp};})()";
input.value=initial;update();
</script></body></html>
The editor expects valid JSON. If punctuation prevents a run, check the JSON parsing lesson before changing the calculation. If your copied page does not execute at all, check how JavaScript connects to HTML. The official MDN reference documents the underlying operation.
A monthly renewal deserves both a middle-of-month test and a month-end test. Share this runnable HTML as a link so the person reviewing the scheduling rule can try January fifteenth and thirty-first side by side. Agree on rollover or clamping before adapting the code to real renewal dates.