On this page
What is compared#
morilog/jalali v3.5.0, the most widely used Jalali package, against RTLY-Kit. Three operations:
| Scenario | RTLY-Kit | morilog/jalali |
|---|---|---|
Gregorian string to Jalali Y/m/d | Jalali::make('2025-03-21')->format('Y/m/d') | Jalalian::fromDateTime('2025-03-21')->format('Y/m/d') |
Jalali y/m/d to Gregorian Y-m-d | Jalali::create(1404, m, d)->toGregorian()->format('Y-m-d') | (new Jalalian(1404, m, d))->toCarbon()->format('Y-m-d') |
addDays then format | Jalali::create(1404, 1, 1)->addDays(n)->format('Y/m/d') | (new Jalalian(1404, 1, 1))->addDays(n)->format('Y/m/d') |
The RTLY-Kit side of each scenario, as you would write it:
<?php
require __DIR__.'/vendor/autoload.php';
use RtlyKit\Calendar\Jalali;
echo Jalali::make('2025-03-21')->format('Y/m/d'), "\n"; // 1404/01/01
echo Jalali::create(1404, 1, 1)->toGregorian()->format('Y-m-d'), "\n"; // 2025-03-21
echo Jalali::create(1404, 1, 1)->addDays(100)->format('Y/m/d'), "\n"; // 1404/04/08
Results#
One run, median of 5 repeats of 20,000 iterations each. Environment: PHP 8.2.30 CLI in Docker on Windows 11, opcache.enable_cli=1.
| Scenario | RTLY-Kit (µs/op) | morilog/jalali (µs/op) | Ratio |
|---|---|---|---|
Gregorian string to Jalali Y/m/d | 13.33 | 153.84 | 11.5x |
Jalali y/m/d to Gregorian Y-m-d | 12.80 | 58.55 | 4.6x |
addDays + format | 33.18 | 479.04 | 14.4x |
Peak memory for the whole process, with both libraries loaded, was 4 MiB.
An earlier ad-hoc run, without opcache and with a different mix of scenarios, showed a larger gap (roughly 25x). The spread between runs shows that the ratio depends on the scenario and on the PHP settings.
Correctness cross-check#
For every day from 1900-01-01 to 2099-12-27 (73,000 days in a row), the Jalali date from RTLY-Kit is the same as the one from morilog's CalendarUtils::toJalali. There are no mismatches. Both use the arithmetic 33-year leap rule, so this shows that the two agree with each other. For the match with the official calendar, see Accuracy and data.
Why the difference?#
We have not profiled this, so read the following as likely and not proven. RTLY-Kit wraps a plain DateTimeImmutable and converts with integer arithmetic. morilog builds and changes Carbon objects, which are much larger (parsing and locale code). So part of the gap comes from that design choice and not only from the conversion algorithm. If you already use Carbon everywhere, the gap that matters to you may be smaller.
Reproduce#
The benchmark is in tools/benchmark in the repository. Its composer.json installs RTLY-Kit from the working tree plus morilog/jalali in its own vendor/ folder, which is not part of the package. The script prints the PHP version, the opcache and JIT settings and the median of the repeats.
docker compose -f tools/docker-compose.yml run --rm php sh -c 'cd tools/benchmark && composer install -n && php -d opcache.enable_cli=1 run.php 20000 5'
Numbers from different machines cannot be compared.
What the benchmark does not cover#
- Three scenarios. No formatting-heavy, parsing-heavy or bulk-array workloads.
- One machine and one PHP build. Docker on Windows adds noise.
- No memory-per-object measurement, only the total peak.
- Only morilog/jalali was measured. Other packages were not.