codryn / phpcalendar
Configurable calendar system supporting real-world and fantasy calendars with date parsing, formatting, and temporal arithmetic
Requires
- php: ^8.1
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.0
- phpstan/phpstan: ^2.1
- phpstan/phpstan-strict-rules: ^2.0
- phpunit/phpunit: ^10.5
This package is auto-updated.
Last update: 2026-03-12 08:14:30 UTC
README
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:
- API Documentation - Complete class and method reference
- Usage Guide - Common patterns and examples
- Calendar Profiles - Details on all 7 built-in profiles
- Calendar Mapping - Convert dates between calendar systems
- Custom Calendars - Guide to creating custom calendars
- Contributing Guide - Development workflow and standards
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
- π Documentation
- π Issue Tracker
- π¬ Discussions
- π§ Email
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 π²