[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"verticals":3,"search-suggestions":44,"article-temporal-api-date-mutation-trap":91,"related-temporal-api-date-mutation-trap":269,"code:js:true:xu5fsd":397,"code:js:true:1bhs4wc":398,"code:js:true:u0lclk":399,"code:js:true:hdhe8j":400,"code:js:true:5tmsa9":401,"comments-01a07bbf-a18a-766f-9824-295cdf509230":402},[4,20,32],{"id":5,"slug":6,"name":7,"tagline":8,"description":9,"accentFrom":10,"accentTo":11,"icon":12,"defaultLocale":13,"locales":14,"features":16,"position":19},"019fe637-3d33-714b-b57f-23e163ffca0c","dev","Web Development","Read it. Run it. Prove it.","A post a day on modern web development — most with an editable playground and a quiz that explains every answer. Free, no account needed.","violet-500","cyan-400","◇","en",[13,15],"fa",{"courses":17,"paths":17,"articles":17,"exams":18,"flashcards":18,"packages":17,"community":17,"certificates":17,"teams":17,"commerce":17},true,false,0,{"id":21,"slug":22,"name":23,"tagline":24,"description":25,"accentFrom":26,"accentTo":10,"icon":27,"defaultLocale":13,"locales":28,"features":30,"position":31},"019fe637-3dc2-754c-8657-0f175bfee7c6","lang","Languages","Learn a language the way you learn a codebase.","Grammar explained the way good documentation explains an API — one idea at a time, each with a quiz.","amber-400","⌘",[13,15,29],"es",{"courses":18,"paths":18,"articles":17,"exams":18,"flashcards":17,"packages":18,"community":17,"certificates":17,"teams":18,"commerce":18},2,{"id":33,"slug":34,"name":35,"tagline":36,"description":37,"accentFrom":38,"accentTo":39,"icon":40,"defaultLocale":13,"locales":41,"features":42,"position":43},"7b3c16f2-931d-410e-802e-e1fa4edab7de","soft","Soft Skills","The half of the job nobody wrote documentation for.","Weekly, on the parts of working life that decide more than your code does — first weeks, meetings, interviews, promotions, and the people around you. Written from what actually happens, and recorded as a podcast you can listen to on the walk.","emerald-400","teal-300","◉",[13],{"courses":18,"paths":18,"articles":17,"exams":18,"flashcards":18,"packages":18,"community":17,"certificates":18,"teams":18,"commerce":18},3,[45,49,53,57,61,65,69,72,76,80,84,88],{"slug":46,"name":47,"articles":48},"webdev","Webdev",90,{"slug":50,"name":51,"articles":52},"javascript","Javascript",74,{"slug":54,"name":55,"articles":56},"frontend","Frontend",69,{"slug":58,"name":59,"articles":60},"css","Css",31,{"slug":62,"name":63,"articles":64},"tutorial","Tutorial",20,{"slug":66,"name":67,"articles":68},"typescript","Typescript",12,{"slug":70,"name":71,"articles":68},"performance","Performance",{"slug":73,"name":74,"articles":75},"react","React",11,{"slug":77,"name":78,"articles":79},"browser","Browser",7,{"slug":81,"name":82,"articles":83},"grammar","Grammar",6,{"slug":85,"name":86,"articles":87},"html","Html",5,{"slug":89,"name":90,"articles":87},"programming","Programming",{"id":92,"slug":93,"title":94,"subtitle":95,"excerpt":96,"coverUrl":97,"locale":13,"readingMinutes":87,"publishedAt":98,"viewCount":99,"likeCount":19,"commentCount":19,"author":100,"vertical":105,"topic":106,"tags":108,"_count":113,"playground":115,"body":117,"bodyMd":253,"seo":254,"translationGroupId":257,"series":95,"podcastUrl":95,"verticalId":5,"thread":258,"assessments":260,"translations":265,"quiz":267},"01a07bbf-a18a-766f-9824-295cdf509230","temporal-api-date-mutation-trap","addDays() Mutated a Date Three Components Away From Where I Called It",null,"A date utility that calls .setDate() and returns the same object looks pure. It isn't — Date is mutable, so the caller and everyone else holding that reference get changed out from under them. Temporal, now shipping in real browsers, fixes it by making dates immutable.","\u002Fmedia\u002Fcovers\u002Ftemporal-api-date-mutation-trap.png","2026-09-12T10:24:23.676Z",19,{"id":101,"name":102,"username":103,"avatarUrl":95,"headline":104},"019fe637-3c25-7088-9034-39c9f15dc3c8","Parsa Jiravand","parsa","Frontend engineer · building bestpractic",{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":50,"name":107},"JavaScript",[109,110,111,112],{"slug":50,"name":51,"color":95},{"slug":46,"name":47,"color":95},{"slug":89,"name":90,"color":95},{"slug":66,"name":67,"color":95},{"assessments":114},1,{"slug":93,"title":116},"Date vs Temporal — the mutation trap",{"blocks":118,"version":114},[119,123,126,132,135,140,143,146,150,153,156,159,163,166,169,172,175,178,182,185,188,191,194,197,200,203,207,210,213,216,219,222,225,228,231,234,237,240,243,246],{"id":120,"html":121,"type":122},"b1","\u003Cp>Someone on the team clicked &quot;remind me&quot; on a Monday standup. Ten minutes later, someone else reported the standup card itself had silently moved to Sunday — on a completely different page, rendered by a completely different component, nowhere near the reminder code.\u003C\u002Fp>","paragraph",{"id":124,"html":125,"type":122},"b2","\u003Cp>Nobody had touched the standup&#39;s date. The bug was three function calls upstream, in a helper that had shipped, unremarkably, eight months earlier:\u003C\u002Fp>",{"id":127,"code":128,"type":129,"language":130,"highlight":131},"b3","function nextOccurrence(date, days) {\n  date.setDate(date.getDate() + days);\n  return date;\n}","code","js",[],{"id":133,"html":134,"type":122},"b4","\u003Cp>It looks pure. Takes a date, takes a number, returns a date. It is not pure, and the reason is \u003Ccode>Date\u003C\u002Fcode>.\u003C\u002Fp>",{"id":136,"html":137,"text":138,"type":139,"level":31},"b5","Where&#39;s the bug? Look before you scroll","Where's the bug? Look before you scroll","heading",{"id":141,"html":142,"type":122},"b6","\u003Cp>\u003Ccode>nextOccurrence\u003C\u002Fcode> takes a \u003Ccode>Date\u003C\u002Fcode> object and returns one. Nothing about the signature says &quot;and also, permanently, edits the object you handed me.&quot; But that&#39;s exactly what \u003Ccode>.setDate()\u003C\u002Fcode> does — it mutates the receiver in place and returns a plain number (the new timestamp), which this function throws away before returning \u003Ccode>date\u003C\u002Fcode> itself.\u003C\u002Fp>",{"id":144,"html":145,"type":122},"b7","\u003Cp>Here&#39;s the shape that broke:\u003C\u002Fp>",{"id":147,"code":148,"type":129,"language":130,"highlight":149},"b8","const standup = new Date(\"2026-09-14\"); \u002F\u002F Monday\nconst upcomingEvents = [standup];       \u002F\u002F rendered on the events page\n\nfunction reminderFor(event) {\n  return nextOccurrence(event, -1); \u002F\u002F \"remind a day before\"\n}\n\nconst reminder = reminderFor(standup);\n\nconsole.log(reminder.toDateString());        \u002F\u002F Sun Sep 13 2026 — correct\nconsole.log(upcomingEvents[0].toDateString()); \u002F\u002F Sun Sep 13 2026 — also changed!",[],{"id":151,"html":152,"type":122},"b9","\u003Cp>\u003Ccode>reminderFor(standup)\u003C\u002Fcode> passes the \u003Cem>same object reference\u003C\u002Fem> into \u003Ccode>nextOccurrence\u003C\u002Fcode>. The \u003Ccode>.setDate()\u003C\u002Fcode> call inside it edits that object directly. \u003Ccode>standup\u003C\u002Fcode> and \u003Ccode>upcomingEvents[0]\u003C\u002Fcode> were never two dates — they were one \u003Ccode>Date\u003C\u002Fcode> object with two names, and \u003Ccode>nextOccurrence\u003C\u002Fcode> rewrote it out from under everything else holding a reference. The events page didn&#39;t have a rendering bug. It rendered the mutated object perfectly.\u003C\u002Fp>",{"id":154,"html":155,"text":155,"type":139,"level":31},"b10","The fix everyone reaches for — and where it stops scaling",{"id":157,"html":158,"type":122},"b11","\u003Cp>The immediate patch is to clone before mutating:\u003C\u002Fp>",{"id":160,"code":161,"type":129,"language":130,"highlight":162},"b12","function nextOccurrence(date, days) {\n  const copy = new Date(date); \u002F\u002F clone first\n  copy.setDate(copy.getDate() + days);\n  return copy;\n}",[],{"id":164,"html":165,"type":122},"b13","\u003Cp>This works, for this function. It doesn&#39;t work as a policy. Every date helper in the codebase now has to remember to clone before it calls any \u003Ccode>set*\u003C\u002Fcode> method, and there&#39;s no error, lint rule, or type that catches the one you forget. \u003Ccode>setDate\u003C\u002Fcode>, \u003Ccode>setMonth\u003C\u002Fcode>, \u003Ccode>setHours\u003C\u002Fcode>, \u003Ccode>setFullYear\u003C\u002Fcode>, \u003Ccode>setMinutes\u003C\u002Fcode> — eight mutator methods, and each one is a place a future contributor can reintroduce this exact bug without knowing the convention exists.\u003C\u002Fp>",{"id":167,"html":168,"type":122},"b14","\u003Cp>And cloning doesn&#39;t touch the other classic \u003Ccode>Date\u003C\u002Fcode> trap hiding one line up: \u003Ccode>new Date(&quot;2026-09-14&quot;)\u003C\u002Fcode> — a date-only ISO string — is parsed as \u003Cstrong>UTC midnight\u003C\u002Fstrong>. Call \u003Ccode>.getDate()\u003C\u002Fcode> on it in a browser west of UTC and, depending on the reader&#39;s local timezone, you can get the day \u003Cem>before\u003C\u002Fem> the one in the string. \u003Ccode>Date\u003C\u002Fcode> doesn&#39;t distinguish &quot;a calendar date with no timezone&quot; from &quot;a specific instant in UTC&quot; — it collapses both into the same object and makes you guess which one you meant.\u003C\u002Fp>",{"id":170,"html":171,"text":171,"type":139,"level":31},"b15","What actually fixes the class of bug",{"id":173,"html":174,"type":122},"b16","\u003Cp>\u003Ccode>Temporal\u003C\u002Fcode> — now part of the language, Stage 4 in TC39 and folded into the ES2026 spec — replaces \u003Ccode>Date\u003C\u002Fcode> with a set of value types that are \u003Cstrong>immutable by construction\u003C\u002Fstrong>. Every method that &quot;changes&quot; a date returns a new one; none of them can touch the object you handed in.\u003C\u002Fp>",{"id":176,"html":177,"type":122},"b17","\u003Cp>Same helper, same reminder logic, rewritten on \u003Ccode>Temporal.PlainDate\u003C\u002Fcode>:\u003C\u002Fp>",{"id":179,"code":180,"type":129,"language":130,"highlight":181},"b18","const standup = Temporal.PlainDate.from(\"2026-09-14\"); \u002F\u002F Monday, no timezone at all\nconst upcomingEvents = [standup];\n\nfunction nextOccurrence(date, days) {\n  return date.add({ days }); \u002F\u002F always a NEW PlainDate — date itself can't change\n}\n\nconst reminder = nextOccurrence(standup, -1);\n\nconsole.log(reminder.toString());          \u002F\u002F 2026-09-13\nconsole.log(upcomingEvents[0].toString()); \u002F\u002F 2026-09-14 — untouched",[],{"id":183,"html":184,"type":122},"b19","\u003Cp>\u003Ccode>.add()\u003C\u002Fcode> (and \u003Ccode>.subtract()\u003C\u002Fcode>, \u003Ccode>.with()\u003C\u002Fcode>) hand back a brand-new \u003Ccode>Temporal.PlainDate\u003C\u002Fcode> and leave \u003Ccode>standup\u003C\u002Fcode> exactly as it was. There&#39;s no clone-before-mutating convention to forget, because there&#39;s no mutation to forget it before. And \u003Ccode>Temporal.PlainDate\u003C\u002Fcode> has no timezone at all — it&#39;s a calendar date, full stop — so the UTC-midnight-versus-local-day ambiguity that bit \u003Ccode>new Date(&quot;2026-09-14&quot;)\u003C\u002Fcode> doesn&#39;t exist here; there was never a timezone to disagree about.\u003C\u002Fp>",{"id":186,"html":187,"type":122},"b20","\u003C!-- playground:start -->",{"id":189,"html":190,"text":190,"type":139,"level":31},"b21","🎮 Try it yourself",{"id":192,"html":193,"type":122},"b22","\u003Cp>\u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Ftemporal-api-date-mutation-trap\u002Fplayground\">▶️ Open the interactive playground →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":195,"html":196,"type":122},"b23","\u003Cp>\u003Cem>Runs right in your browser — poke at it and watch the concept react live.\u003C\u002Fem>\u003C\u002Fp>",{"id":198,"html":199,"type":122},"b24","\u003C!-- playground:end -->",{"id":201,"html":202,"type":122},"b25","\u003Cp>When you do need to compare two dates rather than reach into their fields by hand, \u003Ccode>Temporal.PlainDate.compare()\u003C\u002Fcode> gives you a real answer instead of falling back to arithmetic on timestamps:\u003C\u002Fp>",{"id":204,"code":205,"type":129,"language":130,"highlight":206},"b26","Temporal.PlainDate.compare(\n  Temporal.PlainDate.from(\"2026-09-14\"),\n  Temporal.PlainDate.from(\"2026-09-21\"),\n); \u002F\u002F -1 — the first date is earlier",[],{"id":208,"html":209,"text":209,"type":139,"level":31},"b27","Where Temporal actually stands today",{"id":211,"html":212,"type":122},"b28","\u003Cp>This isn&#39;t a future-tense proposal anymore. Temporal reached \u003Cstrong>TC39 Stage 4\u003C\u002Fstrong> in March 2026 and shipped as part of \u003Cstrong>ES2026\u003C\u002Fstrong>. It&#39;s already running natively: \u003Cstrong>Chrome and Edge 144\u003C\u002Fstrong> (January 2026) and \u003Cstrong>Firefox 139+\u003C\u002Fstrong> ship it without a flag. Safari doesn&#39;t yet — it&#39;s behind a flag in Technology Preview, not in a stable release, which is also why MDN can&#39;t mark it &quot;Baseline&quot; yet: that label needs the widely-used browsers in agreement, and Safari hasn&#39;t caught up.\u003C\u002Fp>",{"id":214,"html":215,"type":122},"b29","\u003Cp>For anything shipping today, that means: reach for it directly if your audience is Chromium- or Firefox-heavy, or pull in \u003Ccode>temporal-polyfill\u003C\u002Fcode> (or the TC39-maintained \u003Ccode>@js-temporal\u002Fpolyfill\u003C\u002Fcode>) for the rest — both implement the same spec, so the code you write against the polyfill today keeps working unchanged once Safari catches up and you drop it.\u003C\u002Fp>",{"id":217,"html":218,"type":122},"b30","\u003C!-- quiz:start -->",{"id":220,"html":221,"text":221,"type":139,"level":31},"b31","🧠 Test yourself",{"id":223,"html":224,"type":122},"b32","\u003Cp>Think it clicked? \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Ftemporal-api-date-mutation-trap\u002Fquiz\">Take the 7-question quiz →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":226,"html":227,"type":122},"b33","\u003Cp>\u003Cem>Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.\u003C\u002Fem>\u003C\u002Fp>",{"id":229,"html":230,"type":122},"b34","\u003C!-- quiz:end -->",{"id":232,"html":233,"type":122},"b35","\u003Cp>The standup card wasn&#39;t a rendering bug, and \u003Ccode>nextOccurrence\u003C\u002Fcode> wasn&#39;t a badly written function — it was a completely reasonable function written against an object model that lets \u003Cem>any\u003C\u002Fem> caller rewrite \u003Cem>any\u003C\u002Fem> other caller&#39;s data by accident. That&#39;s not a bug you fix once. It&#39;s a bug class you either keep guarding against by hand, function by function, forever — or hand to a type that structurally can&#39;t have it.\u003C\u002Fp>",{"id":235,"html":236,"type":122},"b36","\u003Cp>Grep your own codebase for a helper that calls \u003Ccode>setDate\u003C\u002Fcode>, \u003Ccode>setMonth\u003C\u002Fcode>, \u003Ccode>setHours\u003C\u002Fcode>, \u003Ccode>setFullYear\u003C\u002Fcode>, or \u003Ccode>setMinutes\u003C\u002Fcode> on an argument and then returns it. What did you find?\u003C\u002Fp>",{"id":238,"type":239},"b37","divider",{"id":241,"html":242,"type":122},"b38","\u003Cp>🚀 \u003Cstrong>Want more like this?\u003C\u002Fstrong> Every guide, playground, and quiz lives on \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002F\">bestpractic.org\u003C\u002Fa>\u003C\u002Fstrong> — open it and \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002F\">sign up free\u003C\u002Fa>\u003C\u002Fstrong> so the next one finds you.\u003C\u002Fp>",{"id":244,"html":245,"type":122},"b39","\u003Cp>\u003Cem>Thanks for reading! Let&#39;s stay connected:\u003C\u002Fem>\u003C\u002Fp>",{"id":247,"type":248,"items":249,"ordered":18},"b40","list",[250,251,252],"⭐ \u003Cstrong>GitHub\u003C\u002Fstrong> — follow me and star the projects: \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fparsajiravand\">github.com\u002Fparsajiravand\u003C\u002Fa>","💬 \u003Cstrong>Discord\u003C\u002Fstrong> — join the frontend best-practices community: \u003Ca href=\"https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ\">discord.gg\u002Fd9KRhuAwQ\u003C\u002Fa>","📸 \u003Cstrong>Instagram\u003C\u002Fstrong> — frontend best practices, daily: \u003Ca href=\"https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F\">@bestpractice___\u003C\u002Fa>","Someone on the team clicked \"remind me\" on a Monday standup. Ten minutes later, someone else reported the standup card itself had silently moved to Sunday — on a completely different page, rendered by a completely different component, nowhere near the reminder code.\n\nNobody had touched the standup's date. The bug was three function calls upstream, in a helper that had shipped, unremarkably, eight months earlier:\n\n```js\nfunction nextOccurrence(date, days) {\n  date.setDate(date.getDate() + days);\n  return date;\n}\n```\n\nIt looks pure. Takes a date, takes a number, returns a date. It is not pure, and the reason is `Date`.\n\n## Where's the bug? Look before you scroll\n\n`nextOccurrence` takes a `Date` object and returns one. Nothing about the signature says \"and also, permanently, edits the object you handed me.\" But that's exactly what `.setDate()` does — it mutates the receiver in place and returns a plain number (the new timestamp), which this function throws away before returning `date` itself.\n\nHere's the shape that broke:\n\n```js\nconst standup = new Date(\"2026-09-14\"); \u002F\u002F Monday\nconst upcomingEvents = [standup];       \u002F\u002F rendered on the events page\n\nfunction reminderFor(event) {\n  return nextOccurrence(event, -1); \u002F\u002F \"remind a day before\"\n}\n\nconst reminder = reminderFor(standup);\n\nconsole.log(reminder.toDateString());        \u002F\u002F Sun Sep 13 2026 — correct\nconsole.log(upcomingEvents[0].toDateString()); \u002F\u002F Sun Sep 13 2026 — also changed!\n```\n\n`reminderFor(standup)` passes the *same object reference* into `nextOccurrence`. The `.setDate()` call inside it edits that object directly. `standup` and `upcomingEvents[0]` were never two dates — they were one `Date` object with two names, and `nextOccurrence` rewrote it out from under everything else holding a reference. The events page didn't have a rendering bug. It rendered the mutated object perfectly.\n\n## The fix everyone reaches for — and where it stops scaling\n\nThe immediate patch is to clone before mutating:\n\n```js\nfunction nextOccurrence(date, days) {\n  const copy = new Date(date); \u002F\u002F clone first\n  copy.setDate(copy.getDate() + days);\n  return copy;\n}\n```\n\nThis works, for this function. It doesn't work as a policy. Every date helper in the codebase now has to remember to clone before it calls any `set*` method, and there's no error, lint rule, or type that catches the one you forget. `setDate`, `setMonth`, `setHours`, `setFullYear`, `setMinutes` — eight mutator methods, and each one is a place a future contributor can reintroduce this exact bug without knowing the convention exists.\n\nAnd cloning doesn't touch the other classic `Date` trap hiding one line up: `new Date(\"2026-09-14\")` — a date-only ISO string — is parsed as **UTC midnight**. Call `.getDate()` on it in a browser west of UTC and, depending on the reader's local timezone, you can get the day *before* the one in the string. `Date` doesn't distinguish \"a calendar date with no timezone\" from \"a specific instant in UTC\" — it collapses both into the same object and makes you guess which one you meant.\n\n## What actually fixes the class of bug\n\n`Temporal` — now part of the language, Stage 4 in TC39 and folded into the ES2026 spec — replaces `Date` with a set of value types that are **immutable by construction**. Every method that \"changes\" a date returns a new one; none of them can touch the object you handed in.\n\nSame helper, same reminder logic, rewritten on `Temporal.PlainDate`:\n\n```js\nconst standup = Temporal.PlainDate.from(\"2026-09-14\"); \u002F\u002F Monday, no timezone at all\nconst upcomingEvents = [standup];\n\nfunction nextOccurrence(date, days) {\n  return date.add({ days }); \u002F\u002F always a NEW PlainDate — date itself can't change\n}\n\nconst reminder = nextOccurrence(standup, -1);\n\nconsole.log(reminder.toString());          \u002F\u002F 2026-09-13\nconsole.log(upcomingEvents[0].toString()); \u002F\u002F 2026-09-14 — untouched\n```\n\n`.add()` (and `.subtract()`, `.with()`) hand back a brand-new `Temporal.PlainDate` and leave `standup` exactly as it was. There's no clone-before-mutating convention to forget, because there's no mutation to forget it before. And `Temporal.PlainDate` has no timezone at all — it's a calendar date, full stop — so the UTC-midnight-versus-local-day ambiguity that bit `new Date(\"2026-09-14\")` doesn't exist here; there was never a timezone to disagree about.\n\n\u003C!-- playground:start -->\n\n## 🎮 Try it yourself\n\n**[▶️ Open the interactive playground →](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Ftemporal-api-date-mutation-trap\u002Fplayground)**\n\n_Runs right in your browser — poke at it and watch the concept react live._\n\n\u003C!-- playground:end -->\n\nWhen you do need to compare two dates rather than reach into their fields by hand, `Temporal.PlainDate.compare()` gives you a real answer instead of falling back to arithmetic on timestamps:\n\n```js\nTemporal.PlainDate.compare(\n  Temporal.PlainDate.from(\"2026-09-14\"),\n  Temporal.PlainDate.from(\"2026-09-21\"),\n); \u002F\u002F -1 — the first date is earlier\n```\n\n## Where Temporal actually stands today\n\nThis isn't a future-tense proposal anymore. Temporal reached **TC39 Stage 4** in March 2026 and shipped as part of **ES2026**. It's already running natively: **Chrome and Edge 144** (January 2026) and **Firefox 139+** ship it without a flag. Safari doesn't yet — it's behind a flag in Technology Preview, not in a stable release, which is also why MDN can't mark it \"Baseline\" yet: that label needs the widely-used browsers in agreement, and Safari hasn't caught up.\n\nFor anything shipping today, that means: reach for it directly if your audience is Chromium- or Firefox-heavy, or pull in `temporal-polyfill` (or the TC39-maintained `@js-temporal\u002Fpolyfill`) for the rest — both implement the same spec, so the code you write against the polyfill today keeps working unchanged once Safari catches up and you drop it.\n\n\u003C!-- quiz:start -->\n\n## 🧠 Test yourself\n\nThink it clicked? **[Take the 7-question quiz →](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Ftemporal-api-date-mutation-trap\u002Fquiz)**\n\n_Instant feedback, a hint on every question, and an explanation for each answer — right or wrong._\n\n\u003C!-- quiz:end -->\n\nThe standup card wasn't a rendering bug, and `nextOccurrence` wasn't a badly written function — it was a completely reasonable function written against an object model that lets *any* caller rewrite *any* other caller's data by accident. That's not a bug you fix once. It's a bug class you either keep guarding against by hand, function by function, forever — or hand to a type that structurally can't have it.\n\nGrep your own codebase for a helper that calls `setDate`, `setMonth`, `setHours`, `setFullYear`, or `setMinutes` on an argument and then returns it. What did you find?\n\n---\n\n🚀 **Want more like this?** Every guide, playground, and quiz lives on **[bestpractic.org](https:\u002F\u002Fbestpractic.org\u002F)** — open it and **[sign up free](https:\u002F\u002Fbestpractic.org\u002F)** so the next one finds you.\n\n*Thanks for reading! Let's stay connected:*\n\n- ⭐ **GitHub** — follow me and star the projects: [github.com\u002Fparsajiravand](https:\u002F\u002Fgithub.com\u002Fparsajiravand)\n- 💬 **Discord** — join the frontend best-practices community: [discord.gg\u002Fd9KRhuAwQ](https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ)\n- 📸 **Instagram** — frontend best practices, daily: [@bestpractice___](https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F)",{"title":94,"canonical":255,"description":256},"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Ftemporal-api-date-mutation-trap","A date utility that calls .setDate() and returns the same object looks pure. It isn't — Date is mutable, so the caller and everyone else holding that reference get changed out from","01a07bbf-a18a-766f-9824-2db8e4a0dfbb",{"id":259,"locked":18},"01a07bbf-a1ed-72ab-b0ad-5cbb1c596bd9",[261],{"id":262,"slug":93,"title":263,"_count":264},"01a07bbf-a20f-7298-b529-e9280d9e5bd3","Date's mutation trap, and what Temporal fixes",{"questions":79},[266],{"locale":13,"slug":93},{"id":262,"slug":93,"title":263,"_count":268,"questionCount":79},{"questions":79},{"items":270,"meta":396},[271,282,300,322,342,360,378],{"id":92,"slug":93,"title":94,"subtitle":95,"excerpt":96,"coverUrl":97,"locale":13,"readingMinutes":87,"publishedAt":98,"viewCount":64,"likeCount":19,"commentCount":19,"author":272,"vertical":273,"topic":274,"tags":275,"_count":280,"playground":281,"hasQuiz":17,"hasPlayground":17},{"id":101,"name":102,"username":103,"avatarUrl":95,"headline":104},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":50,"name":107},[276,277,278,279],{"slug":50,"name":51,"color":95},{"slug":46,"name":47,"color":95},{"slug":89,"name":90,"color":95},{"slug":66,"name":67,"color":95},{"assessments":114},{"slug":93},{"id":283,"slug":284,"title":285,"subtitle":95,"excerpt":286,"coverUrl":287,"locale":13,"readingMinutes":288,"publishedAt":289,"viewCount":75,"likeCount":19,"commentCount":19,"author":290,"vertical":291,"topic":292,"tags":293,"_count":298,"playground":299,"hasQuiz":17,"hasPlayground":17},"01a08734-5c8d-73c5-95d1-e6b4ae366c25","react-weekly-form-actions-pending-state","React Form Actions: useActionState & useFormStatus Guide","How React 19 form Actions manage pending state automatically — useActionState, useFormStatus, and useOptimistic explained with the bug they replace.","\u002Fmedia\u002Fcovers\u002Freact-weekly-form-actions-pending-state.png",13,"2026-09-12T10:23:52.928Z",{"id":101,"name":102,"username":103,"avatarUrl":95,"headline":104},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":73,"name":74},[294,295,296,297],{"slug":73,"name":74,"color":95},{"slug":50,"name":51,"color":95},{"slug":62,"name":63,"color":95},{"slug":46,"name":47,"color":95},{"assessments":114},{"slug":284},{"id":301,"slug":302,"title":303,"subtitle":95,"excerpt":304,"coverUrl":305,"locale":13,"readingMinutes":83,"publishedAt":306,"viewCount":307,"likeCount":19,"commentCount":19,"author":308,"vertical":309,"topic":310,"tags":313,"_count":320,"playground":321,"hasQuiz":17,"hasPlayground":17},"01a07112-8d09-7178-b258-b899a0eb496e","webauthn-passkeys-discoverable-credentials","I Shipped 'Passwordless Login.' It Still Asked for a Username.","WebAuthn's own demo code creates a credential the browser can't find on its own — so your passkey button quietly turns into a fancier security key instead of an actual passwordless sign-in. One option fixes it.","\u002Fmedia\u002Fcovers\u002Fwebauthn-passkeys-discoverable-credentials.png","2026-09-10T10:57:59.771Z",91,{"id":101,"name":102,"username":103,"avatarUrl":95,"headline":104},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":311,"name":312},"security","Security",[314,315,316,317],{"slug":46,"name":47,"color":95},{"slug":311,"name":312,"color":95},{"slug":50,"name":51,"color":95},{"slug":318,"name":319,"color":95},"webauthn","Webauthn",{"assessments":114},{"slug":302},{"id":323,"slug":324,"title":325,"subtitle":95,"excerpt":326,"coverUrl":327,"locale":13,"readingMinutes":83,"publishedAt":328,"viewCount":329,"likeCount":19,"commentCount":19,"author":330,"vertical":331,"topic":332,"tags":333,"_count":340,"playground":341,"hasQuiz":17,"hasPlayground":17},"01a06c11-0cae-7489-8373-0e3cfee9f0fb","web-share-api-native-sharing","Those Share Buttons Are Guessing What's On My Phone","Every blog footer hardcodes the same row of share icons — X, Facebook, copy link — and none of them know what's actually installed on your phone. The Web Share API hands the job to the OS instead, and it can do something a custom row never could: share an actual file.","\u002Fmedia\u002Fcovers\u002Fweb-share-api-native-sharing.png","2026-09-09T11:00:52.000Z",160,{"id":101,"name":102,"username":103,"avatarUrl":95,"headline":104},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":50,"name":107},[334,335,336,337],{"slug":50,"name":51,"color":95},{"slug":46,"name":47,"color":95},{"slug":77,"name":78,"color":95},{"slug":338,"name":339,"color":95},"apis","Apis",{"assessments":114},{"slug":324},{"id":343,"slug":344,"title":345,"subtitle":95,"excerpt":346,"coverUrl":347,"locale":13,"readingMinutes":68,"publishedAt":348,"viewCount":349,"likeCount":19,"commentCount":19,"author":350,"vertical":351,"topic":352,"tags":353,"_count":358,"playground":359,"hasQuiz":17,"hasPlayground":17},"01a018ca-67d5-7020-bce5-9816c38a7a54","weekly-web-workers","Web Workers in JavaScript: The Complete Guide","Learn JavaScript Web Workers step by step: message passing, transferable objects, error handling, and a copy-paste cheat sheet for offloading work.","\u002Fmedia\u002Fcovers\u002Fweekly-web-workers.png","2026-09-07T12:03:05.323Z",239,{"id":101,"name":102,"username":103,"avatarUrl":95,"headline":104},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":50,"name":107},[354,355,356,357],{"slug":50,"name":51,"color":95},{"slug":46,"name":47,"color":95},{"slug":62,"name":63,"color":95},{"slug":70,"name":71,"color":95},{"assessments":114},{"slug":344},{"id":361,"slug":362,"title":363,"subtitle":95,"excerpt":364,"coverUrl":365,"locale":13,"readingMinutes":87,"publishedAt":366,"viewCount":367,"likeCount":19,"commentCount":19,"author":368,"vertical":369,"topic":370,"tags":371,"_count":376,"playground":377,"hasQuiz":17,"hasPlayground":17},"01a05cb5-e732-76ca-8865-14b97c64f2ba","screen-wake-lock-api-keep-screen-on","Your Recipe App Is Hiding a Silent Video","Cooking sites, workout timers, and boarding-pass pages used to autoplay an invisible, muted, looping video for one job only: stopping your phone from locking its screen. The Screen Wake Lock API replaces that hack with three lines of JavaScript — and it comes with one gotcha the docs bury.","\u002Fmedia\u002Fcovers\u002Fscreen-wake-lock-api-keep-screen-on.png","2026-09-06T10:37:22.891Z",161,{"id":101,"name":102,"username":103,"avatarUrl":95,"headline":104},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":50,"name":107},[372,373,374,375],{"slug":50,"name":51,"color":95},{"slug":46,"name":47,"color":95},{"slug":77,"name":78,"color":95},{"slug":338,"name":339,"color":95},{"assessments":114},{"slug":362},{"id":379,"slug":380,"title":381,"subtitle":95,"excerpt":382,"coverUrl":383,"locale":13,"readingMinutes":288,"publishedAt":384,"viewCount":385,"likeCount":19,"commentCount":19,"author":386,"vertical":387,"topic":388,"tags":389,"_count":394,"playground":395,"hasQuiz":17,"hasPlayground":17},"01a0632c-7248-73e3-9951-e8c6eaa578c7","react-weekly-compiler-memoization","React Compiler 1.0: What useMemo You Can Delete","React Compiler 1.0 is stable. Here's the re-render model it automates, which useMemo\u002FuseCallback\u002Fmemo calls you can delete, and what still needs you.","\u002Fmedia\u002Fcovers\u002Freact-weekly-compiler-memoization.png","2026-09-05T10:17:49.849Z",216,{"id":101,"name":102,"username":103,"avatarUrl":95,"headline":104},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":73,"name":74},[390,391,392,393],{"slug":73,"name":74,"color":95},{"slug":50,"name":51,"color":95},{"slug":62,"name":63,"color":95},{"slug":46,"name":47,"color":95},{"assessments":114},{"slug":380},{"page":114,"perPage":79,"total":52,"totalPages":75},"\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">nextOccurrence\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>date\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> days\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  date\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">setDate\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>date\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">getDate\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">+\u003C\u002Fspan> days\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> date;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> standup \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">new\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Date\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"2026-09-14\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F Monday\n\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> upcomingEvents \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>standup\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>;       \u003Cspan class=\"shj-cmnt\">\u002F\u002F rendered on the events page\n\u003C\u002Fspan>\n\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">reminderFor\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>event\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-func\">nextOccurrence\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>event\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">-\u003C\u002Fspan>\u003Cspan class=\"shj-num\">1\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F \"remind a day before\"\n\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> reminder \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-func\">reminderFor\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>standup\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\nconsole\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">log\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>reminder\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">toDateString\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;        \u003Cspan class=\"shj-cmnt\">\u002F\u002F Sun Sep 13 2026 — correct\n\u003C\u002Fspan>console\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">log\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>upcomingEvents\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-num\">0\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">toDateString\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F Sun Sep 13 2026 — also changed!\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">nextOccurrence\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>date\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> days\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> copy \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">new\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Date\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>date\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F clone first\n\u003C\u002Fspan>  copy\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">setDate\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>copy\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">getDate\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">+\u003C\u002Fspan> days\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> copy;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> standup \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Temporal\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-class\">PlainDate\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">from\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"2026-09-14\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F Monday, no timezone at all\n\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> upcomingEvents \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>standup\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>;\n\n\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">nextOccurrence\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>date\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> days\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> date\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">add\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> days \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F always a NEW PlainDate — date itself can't change\n\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> reminder \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-func\">nextOccurrence\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>standup\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">-\u003C\u002Fspan>\u003Cspan class=\"shj-num\">1\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\nconsole\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">log\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>reminder\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">toString\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;          \u003Cspan class=\"shj-cmnt\">\u002F\u002F 2026-09-13\n\u003C\u002Fspan>console\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">log\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>upcomingEvents\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-num\">0\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">toString\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F 2026-09-14 — untouched\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-class\">Temporal\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-class\">PlainDate\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">compare\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\n  \u003Cspan class=\"shj-class\">Temporal\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-class\">PlainDate\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">from\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"2026-09-14\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n  \u003Cspan class=\"shj-class\">Temporal\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-class\">PlainDate\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">from\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"2026-09-21\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F -1 — the first date is earlier\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>",{"locked":18,"total":19,"comments":403},[]]