<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: azathothx</title>
    <description>The latest articles on DEV Community by azathothx (@azathothx).</description>
    <link>https://dev.to/azathothx</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4082345%2Fd9c56e7a-4c6d-45fb-af68-fa19b5173ee4.png</url>
      <title>DEV Community: azathothx</title>
      <link>https://dev.to/azathothx</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/azathothx"/>
    <language>en</language>
    <item>
      <title>"One day later" and "24 hours later" are different quantities — the batch job that drifts an hour every spring</title>
      <dc:creator>azathothx</dc:creator>
      <pubDate>Tue, 18 Aug 2026 12:03:09 +0000</pubDate>
      <link>https://dev.to/azathothx/one-day-later-and-24-hours-later-are-different-quantities-the-batch-job-that-drifts-an-hour-184i</link>
      <guid>https://dev.to/azathothx/one-day-later-and-24-hours-later-are-different-quantities-the-batch-job-that-drifts-an-hour-184i</guid>
      <description>&lt;h2&gt;
  
  
  The code didn't change. The schedule did.
&lt;/h2&gt;

&lt;p&gt;There's a batch job on a US East Coast server that runs every morning at 9:00. It has been&lt;br&gt;
running fine for months — until the second Sunday of March, when it quietly starts running&lt;br&gt;
at 10:00 every morning. Nobody deployed anything.&lt;/p&gt;

&lt;p&gt;The cause usually looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;next = prev + 24 * 60 * 60 * 1000;   // "one day later"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The US East Coast switches to daylight saving time at 02:00 on the second Sunday of March.&lt;br&gt;
The clock jumps forward one hour, so &lt;strong&gt;that civil day is only 23 hours long&lt;/strong&gt;. "Previous&lt;br&gt;
time + 86,400 seconds" is physically exactly 24 hours later — but read on a wall clock,&lt;br&gt;
it's an hour off. And it stays off until the clocks fall back in November.&lt;/p&gt;

&lt;p&gt;The interesting part is not how to fix the bug. It's the fact that &lt;strong&gt;the phrase "one day"&lt;br&gt;
names two different quantities&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A civil day&lt;/strong&gt; — one square on the calendar. It sticks to the wall clock, and on a
transition day it is 23 or 25 hours long.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;24 elapsed hours&lt;/strong&gt; — 86,400 physical seconds, regardless of what the wall clock does.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both are legitimate readings of "one day"; which one you want depends on the requirement.&lt;br&gt;
A daily 9:00 report wants the former. "Inspect the equipment every 24 hours of runtime"&lt;br&gt;
wants the latter. The accident starts the moment your code holds both in &lt;strong&gt;the same type&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  In Kairos, they are different literals
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/azathothx/kairos-lang" rel="noopener noreferrer"&gt;Kairos&lt;/a&gt; is a schedule definition language I've&lt;br&gt;
been writing about (in Japanese) for a while. It separates the two quantities at the level&lt;br&gt;
of &lt;em&gt;width literals&lt;/em&gt;: &lt;code&gt;1d&lt;/code&gt; is a civil day, &lt;code&gt;24h&lt;/code&gt; is elapsed time. Here is "every day from&lt;br&gt;
9:00" written both ways, evaluated across the 2026 spring-forward date (March 8) in&lt;br&gt;
&lt;code&gt;America/New_York&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;premise NY {
  calendar-system: Gregorian
  tz: "America/New_York"
  wkst: Mon
}

@NY
everyInstant |&amp;gt; strideBy(1d, from: 2026-03-06T09:00)
everyInstant |&amp;gt; strideBy(24h, from: 2026-03-06T09:00)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# expression 1 (4 points)
2026-03-06T09:00
2026-03-07T09:00
2026-03-08T09:00
2026-03-09T09:00
# expression 2 (4 points)
2026-03-06T09:00
2026-03-07T09:00
2026-03-08T10:00
2026-03-09T10:00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://kairos-lang.org/en/playground/#s=cHJlbWlzZSBOWSB7CiAgY2FsZW5kYXItc3lzdGVtOiBHcmVnb3JpYW4KICB0ejogIkFtZXJpY2EvTmV3X1lvcmsiCiAgd2tzdDogTW9uCn0KCkBOWQpldmVyeUluc3RhbnQgfD4gc3RyaWRlQnkoMWQsIGZyb206IDIwMjYtMDMtMDZUMDk6MDApCmV2ZXJ5SW5zdGFudCB8PiBzdHJpZGVCeSgyNGgsIGZyb206IDIwMjYtMDMtMDZUMDk6MDAp&amp;amp;f=2026-03-06&amp;amp;t=2026-03-10&amp;amp;z=America%2FNew_York" rel="noopener noreferrer"&gt;▶ Open in Playground&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Expression 1 (&lt;code&gt;1d&lt;/code&gt;) sticks to 09:00 every day. Expression 2 (&lt;code&gt;24h&lt;/code&gt;) drifts to 10:00 on&lt;br&gt;
the transition day — and &lt;strong&gt;stays there&lt;/strong&gt;. That's the batch job from the opening,&lt;br&gt;
reproduced. Neither expression is wrong: &lt;strong&gt;they are different questions&lt;/strong&gt;, and the&lt;br&gt;
language returns different answers.&lt;/p&gt;

&lt;p&gt;And if you try to write a width that mixes the two:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;字句エラー(8:26): 市民時と経過時間の幅は混合できない: 1d12h（ADR-28）
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;— a lexical error: &lt;em&gt;"civil-time and elapsed-time widths cannot be mixed: &lt;code&gt;1d12h&lt;/code&gt;"&lt;/em&gt;.&lt;br&gt;
It is rejected &lt;strong&gt;at parse time&lt;/strong&gt;. "One day and twelve hours" is a quantity whose length&lt;br&gt;
is undefined until you know how the civil day stretches; if you could write it, its&lt;br&gt;
behavior on transition days would be implementation-defined. So you can't write it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Writing a time that doesn't exist
&lt;/h2&gt;

&lt;p&gt;A transition day also has a wall-clock hole: 02:00–03:00 never happens that morning.&lt;br&gt;
Write 02:30 of that day as &lt;em&gt;data&lt;/em&gt; — a named, literal time — and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x = [2026-03-08T02:30] covering: 2026..2026
x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;存在しない時刻: 2026-03-08T02:30（tz "America/New_York" の DST の隙間に落ちる——実在の壁時計で書く。ADR-33）
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;— &lt;em&gt;"nonexistent time: 2026-03-08T02:30 (falls into the DST gap of tz "America/New_York" —&lt;br&gt;
write a wall-clock time that exists)"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://kairos-lang.org/en/playground/#s=cHJlbWlzZSBOWSB7CiAgY2FsZW5kYXItc3lzdGVtOiBHcmVnb3JpYW4KICB0ejogIkFtZXJpY2EvTmV3X1lvcmsiCiAgd2tzdDogTW9uCn0KCkBOWQp4ID0gWzIwMjYtMDMtMDhUMDI6MzBdIGNvdmVyaW5nOiAyMDI2Li4yMDI2Cng&amp;amp;f=2026-03-06&amp;amp;t=2026-03-10&amp;amp;z=America%2FNew_York" rel="noopener noreferrer"&gt;▶ Open in Playground&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No silent forward-shift to 03:30, no silent drop. &lt;strong&gt;You named a specific wall-clock time;&lt;br&gt;
that time doesn't exist; therefore your data is wrong&lt;/strong&gt; — and the language says so.&lt;br&gt;
Contrast this with points that are &lt;em&gt;derived&lt;/em&gt; by evaluation (say, a "daily at 02:30" rule&lt;br&gt;
hitting the transition day): those resolve deterministically to "the first instant after&lt;br&gt;
the gap", by convention. &lt;strong&gt;Named times are strict; derived times follow conventions.&lt;/strong&gt;&lt;br&gt;
Nothing you didn't write ever runs; nothing you did write is silently rewritten.&lt;/p&gt;

&lt;h2&gt;
  
  
  "We don't have DST here"
&lt;/h2&gt;

&lt;p&gt;Half true. Japan (where I live) hasn't had DST since 1951; our civil days are always&lt;br&gt;
24 hours. But "the server is on UTC, the business runs on Tokyo mornings" is an everyday&lt;br&gt;
setup, and inside it someone is doing the civil-day ↔ elapsed-time conversion — every&lt;br&gt;
single day. A cron line &lt;code&gt;0 9 * * *&lt;/code&gt; means "9:00 &lt;em&gt;wherever the server is&lt;/em&gt;", and its meaning&lt;br&gt;
changes the day you migrate the box. Same species of bug: not "which quantity", but&lt;br&gt;
&lt;strong&gt;"whose wall clock"&lt;/strong&gt; left unstated.&lt;/p&gt;

&lt;p&gt;That's why a Kairos premise &lt;strong&gt;requires&lt;/strong&gt; the timezone declaration (&lt;code&gt;tz: "America/New_York"&lt;/code&gt;&lt;br&gt;
above): no schedule definition can be written without saying whose wall clock it reads.&lt;br&gt;
Move the server; the definition means what it always meant.&lt;/p&gt;

&lt;p&gt;cron's minefield — I've written before about what &lt;code&gt;0 9 13 * 5&lt;/code&gt; actually does — is rarely&lt;br&gt;
a lack of power. It's &lt;strong&gt;polysemy&lt;/strong&gt;: "one day" that names two quantities, "9:00" that&lt;br&gt;
doesn't say whose. What a language can do is split the ambiguous words into distinct&lt;br&gt;
spellings, and make the confusion &lt;em&gt;unwritable&lt;/em&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Kairos is a schedule definition language in RC, developed documentation-first: the spec,&lt;br&gt;
operator reference, and stdlib guides are canonical in Japanese with a full&lt;br&gt;
&lt;a href="https://github.com/azathothx/kairos-lang/tree/main/en/spec" rel="noopener noreferrer"&gt;English mirror&lt;/a&gt;, and every&lt;br&gt;
example in the docs is executed and verified against the reference implementation in CI.&lt;br&gt;
The &lt;a href="https://kairos-lang.org/en/playground/" rel="noopener noreferrer"&gt;Playground&lt;/a&gt; runs that same reference&lt;br&gt;
implementation in your browser (nothing leaves the page); evaluator error messages are the&lt;br&gt;
implementation's canonical Japanese, as you saw above — the English spec defines their exact&lt;br&gt;
semantics. If you came for the "schedules cron and RRULE can't express" angle, the&lt;br&gt;
&lt;a href="https://kairos-lang.org/en/recipes/" rel="noopener noreferrer"&gt;recipes&lt;/a&gt; walk the greatest hits — last business day&lt;br&gt;
of the month, Easter as pure arithmetic, the 4-4-5 fiscal calendar — one page per&lt;br&gt;
requirement, each with a runnable Playground link.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>scheduling</category>
      <category>timezone</category>
      <category>dst</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
