<?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: chatii</title>
    <description>The latest articles on DEV Community by chatii (@chatii).</description>
    <link>https://dev.to/chatii</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%2F46583%2F48308d6a-4a7e-4cf1-af84-f5d439901658.png</url>
      <title>DEV Community: chatii</title>
      <link>https://dev.to/chatii</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chatii"/>
    <language>en</language>
    <item>
      <title>Ask Your Eloquent Model "Is It Due?": Yarunoka's Laravel Bridge Is Out</title>
      <dc:creator>chatii</dc:creator>
      <pubDate>Tue, 11 Aug 2026 13:30:11 +0000</pubDate>
      <link>https://dev.to/chatii/ask-your-eloquent-model-is-it-due-yarunokas-laravel-bridge-is-out-37k</link>
      <guid>https://dev.to/chatii/ask-your-eloquent-model-is-it-due-yarunokas-laravel-bridge-is-out-37k</guid>
      <description>&lt;p&gt;In my previous post, I introduced &lt;strong&gt;Yarunoka&lt;/strong&gt;, a language-independent JSON DSL for calendar-aware schedules:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/chatii/schedules-are-rules-not-lists-of-timestamps-introducing-yarunoka-98i"&gt;https://dev.to/chatii/schedules-are-rules-not-lists-of-timestamps-introducing-yarunoka-98i&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The core idea was: &lt;strong&gt;schedules are rules, not lists of timestamps&lt;/strong&gt;. A rule like "the 25th of every month, moved to the previous business day if needed" should live in the data itself, not be scattered across conditionals in application code.&lt;/p&gt;

&lt;p&gt;At the end of that post, I listed a Laravel bridge as one of the next steps. It is now released.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require yarunoka/laravel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Packagist: &lt;a href="https://packagist.org/packages/yarunoka/laravel" rel="noopener noreferrer"&gt;https://packagist.org/packages/yarunoka/laravel&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Documentation: &lt;a href="https://yarunoka.dev/docs/php/laravel/1.0/" rel="noopener noreferrer"&gt;https://yarunoka.dev/docs/php/laravel/1.0/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Yarunoka itself actually started inside a personal Laravel application—I extracted the specification and the evaluation engine first, and this bridge brings it back to where it came from.&lt;/p&gt;

&lt;h2&gt;
  
  
  A schedule as a model attribute
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;yarunoka/core&lt;/code&gt; gives you a parser, a validator, and an evaluator. But in a Laravel application, I do not want to assemble those by hand every time. If I have a &lt;code&gt;Routine&lt;/code&gt; model that represents something recurring, I want this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$routine&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;schedule&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The bridge provides an Eloquent cast that stores a Yrnk Schedule directly in a JSON column:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Yarunoka\Laravel\Schedule&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Routine&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Model&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;casts&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'schedule'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Schedule&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;schedule&lt;/code&gt; column holds plain JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Recurring process"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Runs at 10:00 on business days."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"days"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"business_day"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"times"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"10:00"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And reading the attribute gives you a &lt;code&gt;Schedule&lt;/code&gt; object back. The rule stays as data in the database, readable by humans, editable through a UI, and usable by the application—without turning into code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Asking "is it due?"
&lt;/h2&gt;

&lt;p&gt;Once the schedule is a model attribute, you can ask the question the whole project is named after (&lt;em&gt;yaru no ka?&lt;/em&gt; — "so, do we do it?"):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$routine&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;schedule&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;isDue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;since&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$routine&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;last_run_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// do it&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the &lt;code&gt;since&lt;/code&gt; argument. A periodic checker rarely wakes up at exactly &lt;code&gt;10:00:00&lt;/code&gt;. If the previous check was at 09:55 and the current one is at 10:05, the 10:00 occurrence happened &lt;em&gt;in between&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;So &lt;code&gt;isDue()&lt;/code&gt; does not ask "is now the scheduled time?" It asks "&lt;strong&gt;was there an occurrence between the last check and now?&lt;/strong&gt;" That is the question a real application needs answered.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why core has no &lt;code&gt;isDue()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This method only exists in the bridge, and that is intentional.&lt;/p&gt;

&lt;p&gt;As I wrote in the previous post, Yarunoka is not a job scheduler. It does not execute anything, and it knows nothing about job state or last successful runs. The core evaluator only offers a pure query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$evaluator&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasMatchIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$schedule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$since&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$at&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"Did this interval contain an occurrence?" What happens after &lt;code&gt;true&lt;/code&gt; is the application's business—run a job, send a notification, or do nothing.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;isDue()&lt;/code&gt; is a thin, application-flavored word on top of &lt;code&gt;hasMatchIn()&lt;/code&gt;. Convenient vocabulary belongs in the bridge; the core stays a pure query engine.&lt;/p&gt;

&lt;h2&gt;
  
  
  The calendar lives in your environment
&lt;/h2&gt;

&lt;p&gt;A Yrnk Schedule can say &lt;code&gt;"days": ["business_day"]&lt;/code&gt;, but &lt;em&gt;what counts as a business day&lt;/em&gt; is a separate concern. In most Laravel applications, that definition is shared across the whole app, not stored per record. So the bridge lets you put the Calendar in &lt;code&gt;config/yarunoka.php&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'timezone'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Asia/Tokyo'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'calendar'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'holidays'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'yasumi-Japan'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'business_holidays'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'2026-08-14'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="s1"&gt;'business_days'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s1"&gt;'resolvers'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this in place, a stored schedule only needs the rule:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"days"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"shift"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"prev"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"or_same"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"business_day"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"times"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"10:00"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"What is a business day?" is answered by the environment. If you have &lt;a href="https://www.yasumi.dev/" rel="noopener noreferrer"&gt;Yasumi&lt;/a&gt; installed, &lt;code&gt;'yasumi-Japan'&lt;/code&gt; supplies Japanese public holidays.&lt;/p&gt;

&lt;h3&gt;
  
  
  Date lists can be names
&lt;/h3&gt;

&lt;p&gt;Look at the config again: &lt;code&gt;business_holidays&lt;/code&gt; holds a list of dates, but &lt;code&gt;holidays&lt;/code&gt; holds a string. Every date-list position in the Calendar accepts either the dates themselves or the &lt;strong&gt;name&lt;/strong&gt; of something that resolves them. &lt;code&gt;yasumi-Japan&lt;/code&gt; is one such name.&lt;/p&gt;

&lt;p&gt;You can add your own names with Resolvers. This is for date sets that cannot be fixed values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;company holidays managed in the database;&lt;/li&gt;
&lt;li&gt;extra business days editable from an admin panel;&lt;/li&gt;
&lt;li&gt;holiday data fetched from an external API.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="s1"&gt;'resolvers'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'company-holidays'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;CompanyHolidayResolver&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Resolvers are built by Laravel's service container, so ordinary dependency injection works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CompanyHolidayResolver&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;YrnkResolverInterface&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;CompanyHolidayRepository&lt;/span&gt; &lt;span class="nv"&gt;$repository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now &lt;code&gt;company-holidays&lt;/code&gt; is usable anywhere the DSL accepts a name—in the config Calendar, or inside schedules stored in the database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="s1"&gt;'calendar'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'business_holidays'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'company-holidays'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From the core's point of view, this is just "resolve the date set named &lt;code&gt;company-holidays&lt;/code&gt;". Whether that means a database query or an API call is the bridge's problem, not the core's.&lt;/p&gt;

&lt;h3&gt;
  
  
  Or bind a layer directly
&lt;/h3&gt;

&lt;p&gt;There is a second route. Each Calendar layer—holidays, business holidays, extra business days—can be supplied straight from the container, bypassing the config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Yarunoka\Resolvers\YrnkBusinessHolidaysResolverInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;YrnkBusinessHolidaysResolverInterface&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nc"&gt;DatabaseBusinessHolidaysResolver&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A binding takes precedence over the same layer in the config. If the source of your business holidays already exists as an application service, binding it directly is more natural than registering a name in the config. The config describes the environment; the container supplies the implementations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validation is real validation
&lt;/h2&gt;

&lt;p&gt;Since schedules are stored as JSON, invalid schedules must not reach the database. The bridge ships a validation rule:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Yarunoka\Laravel\Rules\ValidYrnkSchedule&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$validated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'schedule'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'required'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ValidYrnkSchedule&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is not a simplified lookalike validator. It loads the input as actual Yarunoka and checks the structure, the values, the Calendar vocabulary, and whether every Resolver name can be resolved. The Eloquent cast validates on save as well, so even a direct assignment cannot sneak a broken schedule into the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  One Schedule, or Schedules
&lt;/h2&gt;

&lt;p&gt;A small design story. A Yrnk document can hold multiple schedules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"schedules"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"days"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"mon"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"times"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"10:00"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"days"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"fri"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"times"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"18:00"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pulled along by that structure, my first version of the Laravel &lt;code&gt;Schedule&lt;/code&gt; cast held this whole &lt;code&gt;schedules&lt;/code&gt; array. So &lt;code&gt;$routine-&amp;gt;schedule&lt;/code&gt;—singular—contained multiple schedules inside. I caught it right before release.&lt;/p&gt;

&lt;p&gt;What an Eloquent attribute named &lt;code&gt;schedule&lt;/code&gt; should hold is, obviously, one schedule. The fact that a Yrnk document &lt;em&gt;can&lt;/em&gt; hold many is a separate question from what one model attribute holds. The final design:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="s1"&gt;'schedule'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Schedule&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// one schedule&lt;/span&gt;
&lt;span class="s1"&gt;'schedules'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Schedules&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// a combination of schedules&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is also a cast for a whole Yrnk Document, for cases where each record should carry its own timezone and Calendar. One column can store a Schedule, a Schedules, or a full Document—whichever unit fits your use case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the bridge is separate
&lt;/h2&gt;

&lt;p&gt;Yarunoka started inside a Laravel application, so I could have released it as a Laravel-only library from day one. But the schedule rules themselves and "how to use them in Laravel" are different things.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yarunoka/core&lt;/code&gt; reads Yrnk, writes it, validates it, and answers occurrence queries. &lt;code&gt;yarunoka/laravel&lt;/code&gt; adds the config, the container integration, the Eloquent casts, the validation rule, and &lt;code&gt;isDue()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The previous post said Yarunoka is indifferent to execution. In the same way, the core is indifferent to Laravel. You get to use it in a Laravel-native way, without the DSL itself becoming Laravel-specific.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require yarunoka/laravel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The service provider is auto-discovered. To customize the config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan vendor:publish &lt;span class="nt"&gt;--tag&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;yarunoka-config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Documentation: &lt;a href="https://yarunoka.dev/docs/php/laravel/1.0/" rel="noopener noreferrer"&gt;https://yarunoka.dev/docs/php/laravel/1.0/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Packagist: &lt;a href="https://packagist.org/packages/yarunoka/laravel" rel="noopener noreferrer"&gt;https://packagist.org/packages/yarunoka/laravel&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/yarunoka-dev" rel="noopener noreferrer"&gt;https://github.com/yarunoka-dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Website: &lt;a href="https://yarunoka.dev/" rel="noopener noreferrer"&gt;https://yarunoka.dev/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A TypeScript implementation and more are still on the roadmap. Feedback is very welcome.&lt;/p&gt;

</description>
      <category>php</category>
      <category>laravel</category>
      <category>calendar</category>
      <category>schedule</category>
    </item>
    <item>
      <title>Schedules Are Rules, Not Lists of Timestamps: Introducing Yarunoka</title>
      <dc:creator>chatii</dc:creator>
      <pubDate>Thu, 06 Aug 2026 10:49:20 +0000</pubDate>
      <link>https://dev.to/chatii/schedules-are-rules-not-lists-of-timestamps-introducing-yarunoka-98i</link>
      <guid>https://dev.to/chatii/schedules-are-rules-not-lists-of-timestamps-introducing-yarunoka-98i</guid>
      <description>&lt;p&gt;How would you represent this schedule as data?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Payday is on the 25th of every month. If the 25th is not a business day, move it to the previous business day.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A plain timestamp cannot express it. A cron expression cannot carry the business-day rule. In many applications, the meaning ends up scattered across conditionals in application code.&lt;/p&gt;

&lt;p&gt;I wanted the schedule itself to preserve that meaning.&lt;/p&gt;

&lt;p&gt;So I created &lt;strong&gt;Yarunoka&lt;/strong&gt;, a language-independent specification for a small JSON DSL called &lt;strong&gt;Yrnk&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Website: &lt;a href="https://yarunoka.dev/" rel="noopener noreferrer"&gt;https://yarunoka.dev/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub organization: &lt;a href="https://github.com/yarunoka-dev" rel="noopener noreferrer"&gt;https://github.com/yarunoka-dev&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The name comes from the Japanese question &lt;strong&gt;やるのか？&lt;/strong&gt; (&lt;em&gt;yaru no ka?&lt;/em&gt;), roughly meaning:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;So, do we do it?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is also the question the evaluator exists to answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  A schedule should carry its own meaning
&lt;/h2&gt;

&lt;p&gt;Here is the payday example in Yrnk:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Payday transfer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"At 10:00 on the 25th of every month. If the 25th is not a business day, move it to the previous business day."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"days"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"shift"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"prev"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"or_same"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"business_day"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"times"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"10:00"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even without reading the implementation, the general meaning is visible in the data.&lt;/p&gt;

&lt;p&gt;The exact semantics are defined by the specification, including details that could otherwise be interpreted differently.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;label&lt;/code&gt; and &lt;code&gt;description&lt;/code&gt; are human-facing annotations. They are carried with the schedule, but they never affect evaluation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Yarunoka is not a job scheduler
&lt;/h2&gt;

&lt;p&gt;Yarunoka does not execute anything.&lt;/p&gt;

&lt;p&gt;It only handles two responsibilities:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Describe when occurrences exist.&lt;/li&gt;
&lt;li&gt;Answer whether a given instant or interval contains an occurrence.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The caller decides what to do with the answer.&lt;/p&gt;

&lt;p&gt;It might:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;execute a job;&lt;/li&gt;
&lt;li&gt;send a notification;&lt;/li&gt;
&lt;li&gt;display an event in a calendar;&lt;/li&gt;
&lt;li&gt;start an event in a game;&lt;/li&gt;
&lt;li&gt;or do nothing at all.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Yarunoka does not know about queues, retries, catch-up behavior, job state, or the last successful execution. Those are application concerns.&lt;/p&gt;

&lt;p&gt;This separation is important. The same schedule document can be used by very different applications without being tied to one execution system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I made it
&lt;/h2&gt;

&lt;p&gt;I have always found the distinction between calendars and to-do lists uncomfortable.&lt;/p&gt;

&lt;p&gt;A to-do list usually describes &lt;strong&gt;what&lt;/strong&gt; to do. A calendar describes &lt;strong&gt;when&lt;/strong&gt; something happens. But to me, both are still plans, and deciding whether something belongs in a calendar or a to-do list often feels like unnecessary work.&lt;/p&gt;

&lt;p&gt;I was also building a personal AI agent. I wanted the agent to understand my schedules and remember recurring activities.&lt;/p&gt;

&lt;p&gt;For that, I did not only want a list of generated dates. I wanted a representation that preserved the rule behind those dates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;every third Monday;&lt;/li&gt;
&lt;li&gt;every 90 minutes during business hours;&lt;/li&gt;
&lt;li&gt;the last business day of the month;&lt;/li&gt;
&lt;li&gt;a collection day that is skipped on public holidays.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other words, I wanted schedules with meaning.&lt;/p&gt;

&lt;h2&gt;
  
  
  The document model
&lt;/h2&gt;

&lt;p&gt;A Yrnk document is built from three main concepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Calendar&lt;/strong&gt;: definitions such as holidays, business holidays, extra business days, workweeks, business hours, and named date sets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Schedule&lt;/strong&gt;: rules that describe occurrences.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resolver&lt;/strong&gt;: a date set supplied by the host application at runtime.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is a complete document:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Internal company schedule"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Company schedules evaluated with Japanese public holidays and a company-specific closure date."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timezone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Asia/Tokyo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"resolvers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"yasumi-Japan"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"calendar"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"holidays"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yasumi-Japan"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"business_holidays"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"founding-day"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"business_days"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"date_sets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"founding-day"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"2026-10-01"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"schedules"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Company anniversary"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A company-specific all-day closure."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"days"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"founding-day"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"allday"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Payday transfer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"At 10:00 on the 25th of every month. If the 25th is not a business day, move it to the previous business day."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"days"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"shift"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"prev"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"or_same"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"business_day"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"times"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"10:00"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Japanese public holidays are supplied by an external resolver.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;founding-day&lt;/code&gt; is a company-specific business holiday.&lt;/li&gt;
&lt;li&gt;the company anniversary is an all-day occurrence;&lt;/li&gt;
&lt;li&gt;payday is normally the 25th at 10:00;&lt;/li&gt;
&lt;li&gt;if the 25th is not a business day, payday moves to the previous business day.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The PHP implementation can integrate with &lt;a href="https://www.yasumi.dev/" rel="noopener noreferrer"&gt;Yasumi&lt;/a&gt; to provide holiday dates.&lt;/p&gt;

&lt;h2&gt;
  
  
  More examples
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The third Monday of every month at 10:00
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Monthly meeting"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Held at 10:00 on the third Monday of every month."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"days"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"3rd"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mon"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"times"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"10:00"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Every 90 minutes during business hours on business days
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Periodic business-hours process"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Occurs every 90 minutes during configured business hours on business days."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"days"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"business_day"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"times"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"every"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"minute"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"between"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"business_hour"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Only on the business day before a break
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Pre-break check"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Occurs at 08:00 on a business day when the following day is a business holiday."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"days"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"business_day"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"if"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"next"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"business_holiday"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"times"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"08:00"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;if&lt;/code&gt; filters candidate days without moving them.&lt;/p&gt;

&lt;h3&gt;
  
  
  The first and third Friday, skipped on public holidays
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Recycling collection"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"At 07:30 on the first and third Friday of each month, except on public holidays."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"days"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"1st"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fri"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"3rd"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fri"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"if"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"not"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"holiday"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"times"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"07:30"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A matching holiday is removed rather than moved to another date.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a DSL?
&lt;/h2&gt;

&lt;p&gt;I wanted a format whose intent could be understood by both humans and generative AI.&lt;/p&gt;

&lt;p&gt;A reader should be able to inspect the JSON and understand the broad schedule without first studying an implementation. When the meaning could be ambiguous, the specification defines the exact interpretation.&lt;/p&gt;

&lt;p&gt;This also makes workflows such as these possible:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;generate Yrnk from natural-language input;&lt;/li&gt;
&lt;li&gt;explain a Yrnk document in natural language;&lt;/li&gt;
&lt;li&gt;validate schedules in a UI;&lt;/li&gt;
&lt;li&gt;store and edit rules without turning them into application code;&lt;/li&gt;
&lt;li&gt;implement the same semantics in multiple programming languages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The DSL describes the meaning. Language implementations provide parsing, validation, and occurrence queries.&lt;/p&gt;

&lt;h2&gt;
  
  
  The PHP implementation
&lt;/h2&gt;

&lt;p&gt;The first implementation is &lt;a href="https://github.com/yarunoka-dev/php-core" rel="noopener noreferrer"&gt;&lt;code&gt;yarunoka/core&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;composer require yarunoka/core
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It requires PHP 8.4 or newer and has no runtime dependencies.&lt;/p&gt;

&lt;p&gt;A simplified example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Yarunoka\YrnkEvaluator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Yarunoka\YrnkParser&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$document&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;YrnkParser&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$json&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$payday&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$document&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;schedules&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="nv"&gt;$evaluator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;YrnkEvaluator&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fromYrnk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$document&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$matches&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$evaluator&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nv"&gt;$payday&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DateTimeImmutable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'2026-07-24T10:00:00+09:00'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The evaluator can also answer whether an interval contains an occurrence. The result is still only a query result; the application decides what happens next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Possible use cases
&lt;/h2&gt;

&lt;p&gt;I designed Yarunoka as a general-purpose schedule representation rather than a feature for one specific application.&lt;/p&gt;

&lt;p&gt;Possible uses include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;business systems involving holidays and business-day adjustments;&lt;/li&gt;
&lt;li&gt;adapters for job execution platforms such as Apache Airflow;&lt;/li&gt;
&lt;li&gt;calendar and reminder applications;&lt;/li&gt;
&lt;li&gt;recurring routines for AI agents;&lt;/li&gt;
&lt;li&gt;time-based events in games;&lt;/li&gt;
&lt;li&gt;test data generation and schedule verification.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some ideas, such as abstract expressions like “the first half of March” or “early in the month,” are not covered by version 1.0 yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current status
&lt;/h2&gt;

&lt;p&gt;The project currently provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;version 1.0 of the language specification;&lt;/li&gt;
&lt;li&gt;JSON Schemas;&lt;/li&gt;
&lt;li&gt;a conformance test kit for language implementations;&lt;/li&gt;
&lt;li&gt;the PHP implementation;&lt;/li&gt;
&lt;li&gt;documentation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The conformance tests exist so that different language implementations can evaluate the same document with the same meaning.&lt;/p&gt;

&lt;h2&gt;
  
  
  What comes next
&lt;/h2&gt;

&lt;p&gt;The current plans include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a Laravel bridge;&lt;/li&gt;
&lt;li&gt;a TypeScript implementation;&lt;/li&gt;
&lt;li&gt;UI-oriented validation and editing libraries;&lt;/li&gt;
&lt;li&gt;a Python implementation;&lt;/li&gt;
&lt;li&gt;more abstract calendar vocabulary.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The PHP implementation came first because I am a PHP developer. The specification itself is not tied to PHP.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Schedules are rules, not lists of timestamps.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yarunoka is an attempt to carry those rules—and their meaning—between humans, generative AI, and applications.&lt;/p&gt;

&lt;p&gt;The project is still young, and feedback is very welcome.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Website: &lt;a href="https://yarunoka.dev/" rel="noopener noreferrer"&gt;https://yarunoka.dev/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/yarunoka-dev" rel="noopener noreferrer"&gt;https://github.com/yarunoka-dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Original Japanese article: &lt;a href="https://zenn.dev/chatii/articles/c4cff5831cd621" rel="noopener noreferrer"&gt;https://zenn.dev/chatii/articles/c4cff5831cd621&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>opensource</category>
      <category>programming</category>
      <category>php</category>
      <category>json</category>
    </item>
    <item>
      <title>My Days at Laravel Live Japan 2026</title>
      <dc:creator>chatii</dc:creator>
      <pubDate>Tue, 02 Jun 2026 15:48:53 +0000</pubDate>
      <link>https://dev.to/chatii/my-days-at-laravel-live-japan-2026-2gl2</link>
      <guid>https://dev.to/chatii/my-days-at-laravel-live-japan-2026-2gl2</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Japanese version available on &lt;a href="https://note.com/chatii/n/ne1f5e470eaad" rel="noopener noreferrer"&gt;note&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Hi, I'm chatii &lt;a href="https://x.com/chatii" rel="noopener noreferrer"&gt;@chatii&lt;/a&gt;.&lt;br&gt;
I recently attended Laravel Live Japan 2026.&lt;br&gt;
Here's what inspired me and what I took home from the conference.&lt;/p&gt;
&lt;h2&gt;
  
  
  Profile
&lt;/h2&gt;

&lt;p&gt;Organizer of &lt;a href="https://phpcon.kagawa.jp" rel="noopener noreferrer"&gt;PHP Conference Kagawa&lt;/a&gt;&lt;br&gt;
Encountered PHP back in the 4.x era&lt;br&gt;
Freelancer&lt;br&gt;
English level: "Can read reasonably well," "Can write a little," "Can listen a bit," "Cannot speak at all."&lt;/p&gt;
&lt;h2&gt;
  
  
  5/23 PHP×Tokyo - Laravel Live Japan PRE-PARTY
&lt;/h2&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://phpxtky.connpass.com/event/388121/" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia.connpass.com%2Fthumbs%2Fec%2F42%2Fec42a33910d1d3cf9e433903727303a8.png" height="150" class="m-0" width="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://phpxtky.connpass.com/event/388121/" rel="noopener noreferrer" class="c-link"&gt;
            PHP×Tokyo - Laravel Live Japan PRE-PARTY - connpass
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            (English follows Japanese)  PHP×Tokyoは、PHPやLaravelが好きなエンジニアのためのインターナショナルなミートアップです。  日英のライブ翻訳付きなので、英語が得意でなくても大丈夫です！言語の壁を越えて、PHP/Laravelについて語り合いましょう！  登壇者も募集中です！登壇を希望する方はこちらからご応募ください。 登壇は日本語・英語どちらでも大丈夫です。  #### タイムテーブル    * 13:00 - 13:30 受付 &amp;amp; ネットワーキング   * 13:30 - 13:40 オープニング   * 13:40 - 14:10 "Man...
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fconnpass.com%2Fstatic%2F76ece7b%2Fimg%2Ffavicon.ico" width="16" height="16"&gt;
          phpxtky.connpass.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;I first participated in "PHP×Tokyo March 2026." It was my first time attending a meetup with international participants.&lt;br&gt;
I couldn't speak English, but I hoped to be able to communicate somehow.&lt;/p&gt;

&lt;p&gt;Back in March, &lt;a href="https://x.com/offskip_dave" rel="noopener noreferrer"&gt;David&lt;/a&gt; helped me immensely with translation, which made me feel a bit apologetic...&lt;/p&gt;

&lt;p&gt;At the PRE-PARTY, I took the plunge. During the networking session, I managed to approach &lt;a href="https://x.com/theveeq" rel="noopener noreferrer"&gt;Victor Ukam&lt;/a&gt;, who gave the talk "Manage AI Prompts as Versioned Files in PHP," and said in English, "I have a question...!"&lt;/p&gt;

&lt;p&gt;Well... communication after that relied on Google Translate, but I was able to overcome the "first hurdle." You could say I successfully executed &lt;code&gt;&amp;lt;?= "Hello, World" ?&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Also, it was great to see Ivan again, who came from Russia. I first met him in March, and I was so happy he came over to say hello!&lt;/p&gt;
&lt;h2&gt;
  
  
  5/25 Eve of the Conference, Gyoza Restaurant
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://x.com/zumi_engineer" rel="noopener noreferrer"&gt;Zumi&lt;/a&gt; organized an unofficial pre-party via the &lt;code&gt;laravel-live-jp&lt;/code&gt; channel on the "Laravel Japan" Discord.&lt;br&gt;
Participating in these "fringe events" around a conference is always fun. I had booked a hotel from the day before, so I joined in.&lt;/p&gt;

&lt;p&gt;The real-time translation app that &lt;a href="https://x.com/albert_cht" rel="noopener noreferrer"&gt;Albert Chen&lt;/a&gt; built was incredibly high-performance...&lt;/p&gt;
&lt;h2&gt;
  
  
  5/26 Day 1
&lt;/h2&gt;

&lt;p&gt;...Actually, I couldn't sleep at all after the gyoza party. I already suffer from insomnia, and even though I tried my best to sleep, it didn't work.&lt;br&gt;
So, unable to sleep, I was asking Claude for "Quick English phrases for tech conferences" and checking if my pronunciation was understandable using ChatGPT's voice input. Just trying to build some makeshift confidence.&lt;/p&gt;

&lt;p&gt;A tech conference with such a glamorous stage felt very fresh. As a conference organizer myself, there were many points I could use for reference.&lt;br&gt;
Furthermore, that real-time translation was a godsend. While I was occasionally concerned about the "Reconnecting..." messages, I thought I'd love to use a translation system like that in Kagawa—one that handles such specialized vocabulary.&lt;/p&gt;

&lt;p&gt;Also, the international speakers were so good at "showmanship," smoothly handling live demos while delivering great talks.&lt;/p&gt;
&lt;h3&gt;
  
  
  After Party
&lt;/h3&gt;

&lt;p&gt;I had one specific goal: to say hello to &lt;a href="https://x.com/matthieunapoli" rel="noopener noreferrer"&gt;Matthieu Napoli&lt;/a&gt;. He was the catalyst for starting my own meetup, "PHP Lovers Meetup"!&lt;/p&gt;

&lt;p&gt;To explain the background, I'm a big fan of &lt;a href="https://bref.sh" rel="noopener noreferrer"&gt;Bref&lt;/a&gt; (running PHP on AWS Lambda), which he developed. In 2023, I heard on the user Slack that he was "going to visit Japan." After many twists and turns, we held:&lt;br&gt;
"&lt;a href="https://php-lovers-meetup.connpass.com/event/280822/" rel="noopener noreferrer"&gt;PHP Lovers Meetup vol.0 ~ It starts from &lt;code&gt;&amp;lt;?php&lt;/code&gt;&lt;/a&gt;"&lt;/p&gt;

&lt;p&gt;At that time, I never dreamed I would see him again. Then I saw on Twitter that he was participating in Laravel Live Japan! I just had to tell him, "Thank you for that time..."&lt;/p&gt;

&lt;p&gt;And... I did it! I successfully said hello.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-2059229348788310420-116" src="https://platform.twitter.com/embed/Tweet.html?id=2059229348788310420"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-2059229348788310420-116');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=2059229348788310420&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;(Wait, English...? Well, just a little, maybe the first two sentences... I tried my best, okay?)&lt;/p&gt;

&lt;p&gt;Also, at the smoking area, I managed a "multi-turn" conversation in English with someone from Singapore.&lt;/p&gt;

&lt;p&gt;Everyone communicated with me in earnest, even someone with my limited English. I'm truly grateful. Just like writing code, I realized the importance of actually &lt;em&gt;speaking&lt;/em&gt; it.&lt;/p&gt;

&lt;h2&gt;
  
  
  5/27 Day 2 and Post-Event
&lt;/h2&gt;

&lt;p&gt;Due to the lack of sleep, I was in "energy-saving mode." I still managed to get a thorough demo of LaraCopilot and see a demo of Sentry.&lt;/p&gt;

&lt;p&gt;After the main event, a group of us went to a bar on the 4th floor of the venue, Tachikawa Stage Garden, and later joined &lt;a href="https://x.com/asumikam" rel="noopener noreferrer"&gt;@asumikam&lt;/a&gt;'s group.&lt;br&gt;
We ended up at a HUB and had even more great conversations.&lt;br&gt;
Before I knew it, it was 3 AM. Spending time together until that hour was a precious experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;I want to be able to speak English so bad!&lt;/p&gt;

&lt;p&gt;Also, for a while after coming back, my brain was stuck in "English mode" even when speaking Japanese.&lt;br&gt;
Talking to my kids, I sounded like I was speaking broken, machine-translated Japanese. (Maybe other non-native speakers know this feeling!)&lt;/p&gt;

&lt;p&gt;Thanks to Generative AI, we don't have to do everything ourselves anymore, but being able to use English "on the fly" still feels essential.&lt;br&gt;
Above all, I just want to communicate more!&lt;/p&gt;

&lt;p&gt;And someday, I hope to welcome international participants and speakers to PHP Conference Kagawa.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>conference</category>
      <category>community</category>
    </item>
    <item>
      <title>Hello dev.to. I attended Laravel Live Japan over the past 2 days. I'm starting to write posts (or other outputs) in English because I'm excited about conversation with international developers. But my English is not good. It's my challenge.</title>
      <dc:creator>chatii</dc:creator>
      <pubDate>Thu, 28 May 2026 12:59:39 +0000</pubDate>
      <link>https://dev.to/chatii/hello-devto-i-attended-laravel-live-japan-over-the-past-2-days-im-starting-to-write-posts-or-542g</link>
      <guid>https://dev.to/chatii/hello-devto-i-attended-laravel-live-japan-over-the-past-2-days-im-starting-to-write-posts-or-542g</guid>
      <description></description>
      <category>devjournal</category>
      <category>laravel</category>
      <category>learning</category>
      <category>writing</category>
    </item>
  </channel>
</rss>
