How would you represent this schedule as data?
Payday is on the 25th of every month. If the 25th is not a business day, move it to the previous business day.
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.
I wanted the schedule itself to preserve that meaning.
So I created Yarunoka, a language-independent specification for a small JSON DSL called Yrnk.
- Website: https://yarunoka.dev/
- GitHub organization: https://github.com/yarunoka-dev
The name comes from the Japanese question やるのか? (yaru no ka?), roughly meaning:
So, do we do it?
That is also the question the evaluator exists to answer.
A schedule should carry its own meaning
Here is the payday example in Yrnk:
{
"label": "Payday transfer",
"description": "At 10:00 on the 25th of every month. If the 25th is not a business day, move it to the previous business day.",
"days": [25],
"shift": ["prev", "or_same", "business_day"],
"times": ["10:00"]
}
Even without reading the implementation, the general meaning is visible in the data.
The exact semantics are defined by the specification, including details that could otherwise be interpreted differently.
label and description are human-facing annotations. They are carried with the schedule, but they never affect evaluation.
Yarunoka is not a job scheduler
Yarunoka does not execute anything.
It only handles two responsibilities:
- Describe when occurrences exist.
- Answer whether a given instant or interval contains an occurrence.
The caller decides what to do with the answer.
It might:
- execute a job;
- send a notification;
- display an event in a calendar;
- start an event in a game;
- or do nothing at all.
Yarunoka does not know about queues, retries, catch-up behavior, job state, or the last successful execution. Those are application concerns.
This separation is important. The same schedule document can be used by very different applications without being tied to one execution system.
Why I made it
I have always found the distinction between calendars and to-do lists uncomfortable.
A to-do list usually describes what to do. A calendar describes when 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.
I was also building a personal AI agent. I wanted the agent to understand my schedules and remember recurring activities.
For that, I did not only want a list of generated dates. I wanted a representation that preserved the rule behind those dates:
- every third Monday;
- every 90 minutes during business hours;
- the last business day of the month;
- a collection day that is skipped on public holidays.
In other words, I wanted schedules with meaning.
The document model
A Yrnk document is built from three main concepts:
- Calendar: definitions such as holidays, business holidays, extra business days, workweeks, business hours, and named date sets.
- Schedule: rules that describe occurrences.
- Resolver: a date set supplied by the host application at runtime.
Here is a complete document:
{
"label": "Internal company schedule",
"description": "Company schedules evaluated with Japanese public holidays and a company-specific closure date.",
"version": "1.0",
"timezone": "Asia/Tokyo",
"resolvers": ["yasumi-Japan"],
"calendar": {
"holidays": "yasumi-Japan",
"business_holidays": "founding-day",
"business_days": [],
"date_sets": {
"founding-day": ["2026-10-01"]
}
},
"schedules": [
{
"label": "Company anniversary",
"description": "A company-specific all-day closure.",
"days": ["founding-day"],
"allday": true
},
{
"label": "Payday transfer",
"description": "At 10:00 on the 25th of every month. If the 25th is not a business day, move it to the previous business day.",
"days": [25],
"shift": ["prev", "or_same", "business_day"],
"times": ["10:00"]
}
]
}
In this example:
- Japanese public holidays are supplied by an external resolver.
-
founding-dayis a company-specific business holiday. - the company anniversary is an all-day occurrence;
- payday is normally the 25th at 10:00;
- if the 25th is not a business day, payday moves to the previous business day.
The PHP implementation can integrate with Yasumi to provide holiday dates.
More examples
The third Monday of every month at 10:00
{
"label": "Monthly meeting",
"description": "Held at 10:00 on the third Monday of every month.",
"days": [
["3rd", "mon"]
],
"times": ["10:00"]
}
Every 90 minutes during business hours on business days
{
"label": "Periodic business-hours process",
"description": "Occurs every 90 minutes during configured business hours on business days.",
"days": ["business_day"],
"times": {
"every": [90, "minute"],
"between": "business_hour"
}
}
Only on the business day before a break
{
"label": "Pre-break check",
"description": "Occurs at 08:00 on a business day when the following day is a business holiday.",
"days": ["business_day"],
"if": ["next", "business_holiday"],
"times": ["08:00"]
}
if filters candidate days without moving them.
The first and third Friday, skipped on public holidays
{
"label": "Recycling collection",
"description": "At 07:30 on the first and third Friday of each month, except on public holidays.",
"days": [
["1st", "fri"],
["3rd", "fri"]
],
"if": ["not", "holiday"],
"times": ["07:30"]
}
A matching holiday is removed rather than moved to another date.
Why a DSL?
I wanted a format whose intent could be understood by both humans and generative AI.
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.
This also makes workflows such as these possible:
- generate Yrnk from natural-language input;
- explain a Yrnk document in natural language;
- validate schedules in a UI;
- store and edit rules without turning them into application code;
- implement the same semantics in multiple programming languages.
The DSL describes the meaning. Language implementations provide parsing, validation, and occurrence queries.
The PHP implementation
The first implementation is yarunoka/core.
composer require yarunoka/core
It requires PHP 8.4 or newer and has no runtime dependencies.
A simplified example:
use Yarunoka\YrnkEvaluator;
use Yarunoka\YrnkParser;
$document = (new YrnkParser())->parse($json);
$payday = $document->schedules[0];
$evaluator = YrnkEvaluator::fromYrnk($document);
$matches = $evaluator->matches(
$payday,
new DateTimeImmutable('2026-07-24T10:00:00+09:00'),
);
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.
Possible use cases
I designed Yarunoka as a general-purpose schedule representation rather than a feature for one specific application.
Possible uses include:
- business systems involving holidays and business-day adjustments;
- adapters for job execution platforms such as Apache Airflow;
- calendar and reminder applications;
- recurring routines for AI agents;
- time-based events in games;
- test data generation and schedule verification.
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.
Current status
The project currently provides:
- version 1.0 of the language specification;
- JSON Schemas;
- a conformance test kit for language implementations;
- the PHP implementation;
- documentation.
The conformance tests exist so that different language implementations can evaluate the same document with the same meaning.
What comes next
The current plans include:
- a Laravel bridge;
- a TypeScript implementation;
- UI-oriented validation and editing libraries;
- a Python implementation;
- more abstract calendar vocabulary.
The PHP implementation came first because I am a PHP developer. The specification itself is not tied to PHP.
Conclusion
Schedules are rules, not lists of timestamps.
Yarunoka is an attempt to carry those rules—and their meaning—between humans, generative AI, and applications.
The project is still young, and feedback is very welcome.
- Website: https://yarunoka.dev/
- GitHub: https://github.com/yarunoka-dev
- Original Japanese article: https://zenn.dev/chatii/articles/c4cff5831cd621
Top comments (0)