codryn/phpcalendar

Configurable calendar system supporting real-world and fantasy calendars with date parsing, formatting, and temporal arithmetic

Maintainers

Package info

github.com/codryn/phpcalendar

pkg:composer/codryn/phpcalendar

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 2

0.1.0 2026-01-12 07:43 UTC

This package is auto-updated.

Last update: 2026-03-12 08:14:30 UTC


README

PHP Version PHPStan Level 10 CI Latest Stable Version License

RPG Calendar System for PHP - Gregorian & Fantasy Calendars with Internationalization

PHPCalendar is a powerful, type-safe calendar system for PHP supporting both real-world (Gregorian) and fantasy setting calendars. Perfect for RPG applications, game development, and custom calendar implementations.

Features

  • πŸ—“οΈ Built-in Calendar Profiles: Gregorian + fantasy RPG calendars (FaerΓ»n, Golarion, DSA, Eberron, Dragonlance, Greyhawk, ...)
  • πŸ”„ Calendar Mapping: Convert dates between different calendar systems with correlation dates and optional valid ranges
  • 🌍 Internationalization: Support for different languages (English, German, French, Spanish, Italian, ...) for month names, epochs, and calendar names
  • πŸ“… Flexible Date Parsing: Parse dates from natural language and various formats
  • 🎨 Customizable Formatting: Format dates with custom patterns
  • ⏱️ Date Arithmetic: Add/subtract time spans, calculate differences between dates
  • 🎯 Custom Calendar Support: Create your own calendars with custom months, days, and leap year rules
  • ❗ Error Handling: Clear, specific error messages with location information
  • πŸ”’ Type Safe: Full PHP 8.1+ type declarations and strict mode
  • ⚑ Zero Dependencies: Pure PHP 8.1+ implementation using only stdlib
  • πŸ§ͺ Well Tested: Unit tests with comprehensive coverage
  • πŸ“¦ PSR-12 Compliant: Modern PHP coding standards

Requirements

  • PHP 8.1 or higher
  • No additional PHP extensions required

Installation

composer require codryn/phpcalendar

Quick Start and Usage

use Codryn\PHPCalendar\Calendar\Calendar;

// Create a calendar from built-in profile
$calendar = Calendar::fromProfile('gregorian');

// Parse a date
$date = $calendar->parse('December 25, 2024');

// Format the date
echo $calendar->format($date, 'F d, Y'); // December 25, 2024

// Add 7 days
use Codryn\PHPCalendar\Calendar\TimeSpan;
$future = $date->add(TimeSpan::fromSeconds(86400 * 7));
echo $calendar->format($future, 'F d, Y'); // January 1, 2025

// Calculate difference
$start = $calendar->parse('2024-01-01');
$end = $calendar->parse('2024-12-31');
$span = $calendar->diff($start, $end);
echo $span->getTotalDays(); // 365

Calendar Mapping and Conversion

Convert dates between different calendar systems:

use Codryn\PHPCalendar\Calendar\CalendarMapping;
use Codryn\PHPCalendar\Calendar\CalendarMappingConfiguration;
use Codryn\PHPCalendar\Calendar\TimePoint;

// Setup calendars
$gregorian = Calendar::fromProfile('gregorian');
$faerun = Calendar::fromProfile('faerun');

// Define correlation: Jan 1, 2024 = 1 Hammer 1492 DR
$config = new CalendarMappingConfiguration(
    sourceCalendarName: 'gregorian',
    targetCalendarName: 'faerun',
    correlationDate: [
        'source' => ['year' => 2024, 'month' => 1, 'day' => 1],
        'target' => ['year' => 1492, 'month' => 1, 'day' => 1],
    ]
);

$mapping = new CalendarMapping($config, $gregorian, $faerun);

// Convert dates between calendars
$gregorianDate = new TimePoint($gregorian, 2024, 12, 25);
$faerunDate = $mapping->convert($gregorianDate);
echo $faerun->format($faerunDate); // Nightal 24, 1492

// Convert back
$backToGregorian = $mapping->reverseConvert($faerunDate);
echo $gregorian->format($backToGregorian); // December 25, 2024

Internationalization

PHPCalendar supports internationalization for all built-in calendar profiles. Calendar names, month names, epoch notations, and nameless day names can be displayed in 5 languages:

  • en-us (English - default)
  • de (German)
  • fr (French)
  • es (Spanish)
  • it (Italian)

Usage

use Codryn\PHPCalendar\Calendar\Calendar;

// Create calendar with German locale
$calendar = Calendar::fromProfile('gregorian', 'de');
echo $calendar->getDisplayName(); // "Gregorianischer Kalender"

// Create calendar with French locale
$calendar = Calendar::fromProfile('gregorian', 'fr');
echo $calendar->getDisplayName(); // "Calendrier GrΓ©gorien"

// Get localized month names
$profile = new \Codryn\PHPCalendar\Profile\GregorianProfile();
$profile->setLocale('es');
$months = $profile->getMonthNames();
echo $months[1]; // "Enero" (January in Spanish)

// Get localized epoch notation
$epoch = $profile->getEpochNotation();
echo $epoch['after']; // "d.C." (Spanish for AD/CE)

Supported Translations

Calendar Month Names Display Name Epoch Notation Nameless Days
Gregorian βœ… All 5 βœ… All 5 βœ… All 5 N/A
FaerΓ»n Proper nouns* βœ… All 5 βœ… All 5 βœ… All 5
DSA Proper nouns* βœ… All 5 βœ… All 5 βœ… All 5
Dragonlance βœ… All 5 βœ… All 5 βœ… All 5 N/A
Eberron Proper nouns* βœ… All 5 βœ… All 5 N/A
Golarion Proper nouns* βœ… All 5 βœ… All 5 N/A
Greyhawk βœ… All 5 βœ… All 5 βœ… All 5 N/A

*Proper nouns (fantasy deity names, dragonmarks, etc.) remain untranslated across all languages.

Locale Fallback

If an unsupported or invalid locale is provided, the system automatically falls back to English (en-us):

$calendar = Calendar::fromProfile('gregorian', 'unsupported-locale');
echo $calendar->getDisplayName(); // "Gregorian Calendar" (English fallback)

Calendar Profiles

Gregorian Calendar

Standard international calendar with proper leap year rules.

$calendar = Calendar::fromProfile('gregorian');

$christmas = $calendar->parse('2024-12-25');
echo $calendar->format($christmas, 'l, F j, Y'); 
// Wednesday, December 25, 2024

echo $calendar->isLeapYear(2024) ? 'Leap' : 'Normal'; // Leap

Specifications:

  • 12 months with varying days (28-31)
  • Leap year: Divisible by 4, except centuries unless divisible by 400
  • Epoch: BCE/CE

FaerΓ»n (Forgotten Realms)

Harptos calendar from D&D's Forgotten Realms setting.

$calendar = Calendar::fromProfile('faerun');

$date = $calendar->parse('1 Hammer 1492 DR');
echo $calendar->format($date, 'd F Y'); // 1 Hammer 1492

// 12 months of 30 days + 5 festival days
echo $calendar->getMonthCount(); // 17

Specifications:

  • 12 regular months (30 days each) + 5 festival days
  • Leap year: Every 4 years (Shieldmeet)
  • Epoch: DR (Dale Reckoning)
  • Total: 365 days (366 with Shieldmeet)

Golarion (Pathfinder)

Absalom Reckoning calendar from Pathfinder RPG.

$calendar = Calendar::fromProfile('golarion');

$date = $calendar->parse('15 Rova 4724 AR');
echo $calendar->format($date, 'd F Y'); // 15 Rova 4724

echo $calendar->isLeapYear(4720) ? 'Leap' : 'Normal'; // Leap

Specifications:

  • 12 months with varying days (28-31)
  • Leap year: Every 8 years
  • Epoch: AR (Absalom Reckoning)
  • Total: 365 days (366 in leap years)

Das Schwarze Auge

Aventurian calendar from The Dark Eye RPG.

$calendar = Calendar::fromProfile('dsa');

$date = $calendar->parse('12 Praios 1045 BF');
echo $calendar->format($date, 'd F Y'); // 12 Praios 1045

// Always 365 days - no leap years
echo $calendar->isLeapYear(1045) ? 'Leap' : 'Normal'; // Normal

Specifications:

  • 12 months (30 days each) + 5 nameless days
  • No leap years
  • Epoch: BF (Bosparans Fall)
  • Total: 365 days always

Eberron

Galifar Calendar from D&D's Eberron setting.

$calendar = Calendar::fromProfile('eberron');

$date = $calendar->parse('10 Olarune 998 YK');
echo $calendar->format($date, 'd F Y'); // 10 Olarune 998

// Perfect 28-day months
echo $calendar->getDaysInMonth(1, 998); // 28 (always)

Specifications:

  • 12 months of exactly 28 days each
  • No leap years
  • Epoch: YK (Years of Kingdom)
  • Total: 336 days

Dragonlance

Krynn calendar from D&D's Dragonlance setting.

$calendar = Calendar::fromProfile('dragonlance');

$preCataclysm = $calendar->parse('100 Phoenix 0 PC');
$postCataclysm = $calendar->parse('1 Phoenix 1 AC');

echo $calendar->format($preCataclysm, 'd F Y'); // 100 Phoenix 0
echo $calendar->format($postCataclysm, 'd F Y'); // 1 Phoenix 1

Specifications:

  • 12 months with varying days (28-31)
  • Leap year: Gregorian rules
  • Epoch: PC/AC (Pre/After Cataclysm)
  • Total: 365 days (366 in leap years)

Greyhawk

Common Year calendar from D&D's World of Greyhawk.

$calendar = Calendar::fromProfile('greyhawk');

$date = $calendar->parse('1 Needfest 591 CY');
echo $calendar->format($date, 'd F Y'); // 1 Needfest 591

// 12 regular months + 4 festival weeks
echo $calendar->getMonthCount(); // 16

Specifications:

  • 12 months (28 days each) + 4 festival weeks (7 days each)
  • No leap years
  • Epoch: CY (Common Year)
  • Total: 364 days

Custom Calendars

Create your own calendar with custom configuration:

use Codryn\PHPCalendar\Calendar\Calendar;
use Codryn\PHPCalendar\Calendar\CalendarConfiguration;

$config = new CalendarConfiguration(
    name: 'mars-sol',
    displayName: 'Martian Solar Calendar',
    monthNames: [
        1 => 'Ares', 2 => 'Phobos', 3 => 'Deimos',
        4 => 'Olympus', 5 => 'Valles', 6 => 'Mariner',
        7 => 'Viking', 8 => 'Sojourner', 9 => 'Spirit',
        10 => 'Opportunity', 11 => 'Curiosity', 12 => 'Perseverance'
    ],
    daysPerMonth: [
        1 => 31, 2 => 31, 3 => 31, 4 => 31,
        5 => 31, 6 => 31, 7 => 31, 8 => 31,
        9 => 31, 10 => 31, 11 => 31, 12 => 36
    ],
    leapYearRule: fn(int $year) => $year % 10 === 0,
    epochNotation: ['before' => 'BL', 'after' => 'AL'], // Before/After Landing
    formatPatterns: ['d F Y AL', 'Y-m-d']
);

$marsCalendar = Calendar::fromConfiguration($config);
$landingDay = $marsCalendar->parse('1 Ares 1 AL');
echo $marsCalendar->format($landingDay); // 1 Ares 1 AL

API Reference

PHPCalendar provides a clean, fluent API for calendar operations:

// Create calendar
$calendar = Calendar::fromProfile('gregorian');
$calendar = Calendar::fromProfile('gregorian', 'de'); // With locale
$calendar = Calendar::fromConfiguration($config);

// Set locale on profile directly
$profile = new GregorianProfile();
$profile->setLocale('fr');
$locale = $profile->getLocale(); // 'fr'

// Parse dates
$date = $calendar->parse('December 25, 2024');
$date = $calendar->parse('2024-12-25');

// Format dates
$formatted = $calendar->format($date, 'F d, Y');
$formatted = $calendar->format($date, 'Y-m-d H:i:s');

// Date arithmetic
$future = $date->add(TimeSpan::fromSeconds(86400 * 7)); // +7 days
$past = $date->subtract(TimeSpan::fromSeconds(3600));   // -1 hour

// Calculate differences
$span = $calendar->diff($startDate, $endDate);
$days = $span->getTotalDays();
$hours = $span->getTotalHours();

// Calendar metadata
$calendar->getName();              // 'gregorian'
$calendar->getDisplayName();       // 'Gregorian Calendar'
$calendar->getMonthNames();        // [1 => 'January', ...]
$calendar->getDaysInMonth(2, 2024); // 29
$calendar->isLeapYear(2024);       // true

Documentation

Comprehensive documentation is available:

Development

See CONTRIBUTING.md for development guidelines.

Architecture

PHPCalendar is structured around key components:

  • Calendar: Main class for calendar operations
  • CalendarProfile: Defines calendar rules and metadata
  • TimePoint: Represents specific dates in a calendar
  • TimeSpan: Represents time intervals for date arithmetic

Performance

TODO: Add performance benchmarks and optimizations.

Quality Standards

  • βœ… PHPStan Level 10: Strictest static analysis level from PHPSTan 2.1
  • βœ… PSR-12: PHP coding standards compliance
  • βœ… Strict Types: declare(strict_types=1) in all files
  • βœ… TDD: Test-driven development methodology
  • βœ… Type Hints: Full type declarations on all methods
  • βœ… PHPDoc: Complete documentation blocks

Contributing

Contributions are welcome! See CONTRIBUTING.md for:

  • Development workflow
  • Coding standards
  • Testing requirements
  • Pull request process

License

This project is licensed under the MIT License - see the LICENSE file for details.

Changelog

See CHANGELOG.md for version history and updates.

Support

Credits

Created and maintained by Marco for Codryn.

Special thanks to:

  • The PHP community
  • Contributors to fantasy RPG calendar systems
  • PHPUnit, PHPStan, and PHP-CS-Fixer maintainers

Game Systems Copyright

This library implements calendars from various tabletop RPG systems for non-commercial use. All game system names, mechanics, and related intellectual property remain the property of their respective copyright holders. See GAME_SYSTEMS_COPYRIGHT.md for detailed copyright notices and attributions.

Built for the tabletop RPG community 🎲