pphatdev/lunar-date

A PHP library for converting Gregorian dates to Khmer (Cambodian) calendar dates and vice versa, inspired by momentkh

v0.1.2 2025-08-07 12:01 UTC

This package is auto-updated.

Last update: 2025-09-07 12:46:33 UTC


README

CI License: MIT PHP Version Latest Stable Version Total Downloads

A comprehensive PHP library for converting between Gregorian and Khmer (Cambodian) calendar dates. This is a faithful port of the popular JavaScript momentkh library by ThyrithSor, enhanced with modern PHP best practices and comprehensive testing.

Features

Core Calendar Functions

  • Gregorian to Khmer Lunar Date Conversion - Full bidirectional date conversion with lunar calendar support
  • Buddhist Era (BE) Year Support - Accurate BE year calculations and conversion utilities
  • Khmer New Year Calculations - Precise calculation of Khmer New Year moments using advanced algorithms
  • Animal Year System - Complete 12-year animal cycle (ឆ្នាំសត្វ) support
  • Era Year System - 10-year era cycle (ស័ក) calculations
  • Leap Year Support - Accurate lunar calendar leap year handling

Formatting & Localization

  • Flexible Date Formatting - Customizable output with Khmer formatting tokens
  • Number Conversion - Bidirectional Arabic ↔ Khmer numeral conversion
  • Khmer Text Support - Full Unicode Khmer character support and validation
  • Multiple Calendar Systems - Solar and lunar month name support

Developer Experience

  • Modern PHP (7.4+) - Strict types, comprehensive type hints, and modern syntax
  • Comprehensive Testing - Full PHPUnit test suite with edge case coverage
  • Static Analysis - PHPStan ready with high code quality standards
  • PSR-12 Standards - Follows PHP coding standards and best practices
  • Rich Documentation - Detailed PHPDoc annotations and usage examples

Requirements

  • PHP 7.4 or higher
  • ext-json (usually included)

Installation

Install via Composer:

composer require pphatdev/lunar-date

Quick Start

<?php
require_once 'vendor/autoload.php';

use PPhatDev\LunarDate\KhmerDate;

// Create Khmer date from current time
$today = new KhmerDate();
echo $today->toLunarDate();
// Output: ថ្ងៃអាទិត្យ ៨កើត ខែមិគសិរ ឆ្នាំជូត សំរឹទ្ធិស័ក ពុទ្ធសករាជ ២៥៦៧

// Convert specific Gregorian date
$date = new KhmerDate('1996-09-24');
echo $date->toLunarDate();
// Output: ថ្ងៃអង្គារ ១២កើត ខែអស្សុជ ឆ្នាំច សំរឹទ្ធិស័ក ពុទ្ធសករាជ ២៥៣៩

// Custom formatting
echo $date->toLunarDate('dN ថ្ងៃW ខែm ព.ស. b');
// Output: ១២កើត ថ្ងៃអង្គារ ខែអស្សុជ ព.ស. ២៥៣៩

// Khmer Gregorian date
echo $date->toKhmerDate();
// Output: ២៤ ខែកញ្ញា ឆ្នាំ១៩៩៦

API Reference

KhmerDate Class

The main class for working with Khmer calendar dates.

Creating KhmerDate Objects

// Current date and time
$now = new KhmerDate();

// From DateTime object
$dateTime = new DateTime('2024-01-15');
$khmerDate = new KhmerDate($dateTime);

// From string (various formats supported)
$khmerDate = new KhmerDate('2024-01-15');
$khmerDate = new KhmerDate('2024-01-15 14:30:00');

// From timestamp
$khmerDate = new KhmerDate(1705315800);

// Static factory methods
$khmerDate = KhmerDate::create('2024-01-15');
$khmerDate = KhmerDate::createFromDateTime($dateTime);

Core Methods

$date = new KhmerDate('2024-01-15');

// Lunar date conversion
echo $date->toLunarDate();                    // Full Khmer lunar date
echo $date->toLunarDate('dN ខែm ព.ស. b');     // Custom format

// Khmer Gregorian date
echo $date->toKhmerDate();                    // Khmer numerals with Gregorian calendar

// Calendar components
echo $date->khDay();      // Lunar day (0-29): 8
echo $date->khMonth();    // Lunar month index: 2  
echo $date->khYear();     // Buddhist Era year: 2567

// Standard DateTime operations
echo $date->format('Y-m-d H:i:s');           // Standard formatting
echo $date->getTimestamp();                  // Unix timestamp
$copy = $date->copy();                       // Create copy

Date Arithmetic

$date = new KhmerDate('2024-01-15');

// Add/subtract time intervals
$futureDate = $date->add('P1M');      // Add 1 month
$pastDate = $date->subtract('P7D');   // Subtract 7 days

// The original date object is modified, use copy() if needed
$newDate = $date->copy()->add('P1Y'); // Add 1 year to copy

Core Methods

$date = new KhmerDate('2024-01-15');

// Lunar date conversion
echo $date->toLunarDate();                    // Full Khmer lunar date
echo $date->toLunarDate('dN ខែm ព.ស. b');     // Custom format

// Khmer Gregorian date
echo $date->toKhmerDate();                    // Khmer numerals with Gregorian calendar

// Calendar components
echo $date->khDay();      // Lunar day (0-29): 8
echo $date->khMonth();    // Lunar month index: 2  
echo $date->khYear();     // Buddhist Era year: 2567

// Standard DateTime operations
echo $date->format('Y-m-d H:i:s');           // Standard formatting
echo $date->getTimestamp();                  // Unix timestamp
$copy = $date->copy();                       // Create copy

Date Arithmetic

$date = new KhmerDate('2024-01-15');

// Add/subtract time intervals
$futureDate = $date->add('P1M');      // Add 1 month
$pastDate = $date->subtract('P7D');   // Subtract 7 days

// The original date object is modified, use copy() if needed
$newDate = $date->copy()->add('P1Y'); // Add 1 year to copy

Static Methods & Utilities

Khmer New Year Calculation

// Get exact Khmer New Year moment for any year
$newYear2024 = KhmerDate::getKhNewYearMoment(2024);
echo $newYear2024->format('Y-m-d H:i:s');  // 2024-04-14 06:46:00

// Calculate for multiple years
foreach (range(2020, 2030) as $year) {
    $newYear = KhmerDate::getKhNewYearMoment($year);
    echo "Khmer New Year $year: " . $newYear->format('Y-m-d H:i') . "\n";
}

Number Conversion

// Arabic to Khmer numerals
echo KhmerDate::arabicToKhmerNumber('2024');   // ២០២៤
echo KhmerDate::arabicToKhmerNumber('15');     // ១៥

// Khmer to Arabic numerals  
echo KhmerDate::khmerToArabicNumber('២០២៤');  // 2024
echo KhmerDate::khmerToArabicNumber('១៥');     // 15

// Helper method
echo KhmerDate::getKhmerNumber(2024);          // ២០២៤

Calendar Information

// Get month names
$lunarMonths = KhmerDate::getKhmerMonthNames();
// ['មិគសិរ', 'បុស្ស', 'មាឃ', 'ផល្គុន', 'ចេត្រ', 'ពិសាខ', 'ជេស្ឋ', 'អាសាឍ', 'ស្រាពណ៍', 'ភទ្របទ', 'អស្សុជ', 'កត្ដិក', 'បឋមាសាឍ', 'ទុតិយាសាឍ']

// Get animal year names (12-year cycle)
$animalYears = KhmerDate::getAnimalYearNames();
// ['ជូត', 'ឆ្លូវ', 'ខាល', 'ថោះ', 'រោង', 'ម្សាញ់', 'មមី', 'មមែ', 'វក', 'រកា', 'ច', 'កុរ']

// Get era year names (10-year cycle)
$eraYears = KhmerDate::getEraYearNames();
// ['សំរឹទ្ធិស័ក', 'ឯកស័ក', 'ទោស័ក', 'ត្រីស័ក', 'ចត្វាស័ក', 'បញ្ចស័ក', 'ឆស័ក', 'សប្តស័ក', 'អដ្ឋស័ក', 'នព្វស័ក']

Utility Functions (Utils Class)

use PPhatDev\LunarDate\Utils;

// Parse Khmer date string
$parsed = Utils::parseKhmerDate('១២កើត ខែអស្សុជ ព.ស. ២៥៦៧');
var_dump($parsed); // Returns parsed date components

// Get Khmer month date range
$monthRange = Utils::getKhmerMonthRange(5, 2567); // ពិសាខ month in BE 2567
foreach ($monthRange as $day) {
    echo "Day {$day['day']}: {$day['lunar']} - {$day['gregorian']}\n";
}

// Find specific lunar days in a year
$fullMoons = Utils::findLunarDayOccurrences(15, 0, 2024); // All 15កើត in 2024
$newMoons = Utils::findLunarDayOccurrences(15, 1, 2024);  // All 15រោច in 2024

// Date difference in Khmer terms
$date1 = new KhmerDate('2024-01-01');
$date2 = new KhmerDate('2024-12-31');
$diff = Utils::diffInKhmer($date1, $date2);
echo "Difference: {$diff['days']} days, {$diff['months']} months";

// Buddhist holidays for a specific year
$holidays = Utils::getBuddhistHolidays(2024);
foreach ($holidays as $holiday) {
    echo "{$holiday['name']}: {$holiday['date']}\n";
}

// Era conversion utilities
$beYear = Utils::convertEra(2024, 'AD', 'BE');    // 2567
$adYear = Utils::convertEra(2567, 'BE', 'AD');    // 2024
$jsYear = Utils::convertEra(2024, 'AD', 'JS');    // 1385

// Date validation
$isValid = Utils::isValidKhmerDate(15, 5, 2567); // true
$isValid = Utils::isValidKhmerDate(31, 5, 2567); // false (invalid lunar day)

// Season information based on lunar calendar
$date = new KhmerDate('2024-07-15');
$season = Utils::getSeason($date);
echo $season['name'];        // រដូវវស្សា (Rainy Season)
echo $season['name_en'];     // Rainy Season

Formatting System

The library supports extensive formatting tokens inspired by momentkh:

Formatting Tokens

Token Description Example Output
W ថ្ងៃនៃសប្ដាហ៍ (Day of week full) អង្គារ
w ថ្ងៃនៃសប្ដាហ៍កាត់ (Day of week short)
d ថ្ងៃទី (Lunar day count) ១២
D ថ្ងៃទី ០១-១៥ (Lunar day with leading zero) ០៨
n កើត ឬ រោច (Moon status short)
N កើត ឬ រោច (Moon status full) កើត
m ខែចន្ទគតិ (Lunar month) មិគសិរ
M ខែសុរិយគតិ (Solar month) មករា
a ឆ្នាំសត្វ (Animal year) ជូត
e ស័ក (Era year) សំរឹទ្ធិស័ក
b ឆ្នាំពុទ្ធសករាជ (Buddhist Era year) ២៥៦៧
c ឆ្នាំគ្រិស្តសករាជ (Gregorian year) ២០២៤
j ឆ្នាំចុល្លសករាជ (Jolak Sakaraj year) ១៣៨៥

Advanced Formatting Examples

$date = new KhmerDate('2024-01-15');

// Basic formats
echo $date->toLunarDate('W');                           // អាទិត្យ
echo $date->toLunarDate('dN');                          // ៨កើត  
echo $date->toLunarDate('dN ខែm');                      // ៨កើត ខែមិគសិរ

// Complete formats
echo $date->toLunarDate('ថ្ងៃW dN ខែm ឆ្នាំa e');          // ថ្ងៃអាទិត្យ ៨កើត ខែមិគសិរ ឆ្នាំជូត សំរឹទ្ធិស័ក
echo $date->toLunarDate('dN ថ្ងៃW ខែm ព.ស. b');           // ៨កើត ថ្ងៃអាទិត្យ ខែមិគសិរ ព.ស. ២៥៦៧

// Mixed calendar formats
echo $date->toLunarDate('ថ្ងៃW ទី d ខែM ឆ្នាំ c');          // Mixed lunar/solar format
echo $date->toLunarDate('ព.ស. b (គ.ស. c)');              // ព.ស. ២៥៦៧ (គ.ស. ២០២៤)

KhmerFormatter Class

For advanced formatting needs, use the KhmerFormatter class directly:

use PPhatDev\LunarDate\KhmerFormatter;

$formatter = new KhmerFormatter();

// Number formatting
echo $formatter->toKhmerNumber('2024');           // ២០២៤
echo $formatter->fromKhmerNumber('២០២៤');        // 2024
echo $formatter->formatNumber(1234.56, 2);       // ១,២៣៤.៥៦

// Date formatting
$date = new DateTime('2024-01-15');
echo $formatter->formatDate($date, 'full');      // Full Khmer Gregorian date
echo $formatter->getDayName($date);              // អាទិត្យ
echo $formatter->getMonthName($date);            // មករា
echo $formatter->getLunarMonthName(5);           // ពិសាខ

// Currency and time formatting
echo $formatter->formatCurrency(1500.50);       // ១,៥០០.៥០ រៀល
echo $formatter->formatTime($date, true);       // 24-hour format
echo $formatter->formatTime($date, false);      // 12-hour format

// Text validation and utilities
$isKhmer = $formatter->isKhmerText('ភាសាខ្មែរ');  // true
$isKhmer = $formatter->isKhmerText('English');   // false
echo $formatter->formatOrdinal(21);             // ២១

// Format lunar date data
$lunarData = KhmerDate::findLunarDate(new DateTime('2024-01-15'));
echo $formatter->formatLunarDate($lunarData, 'full');

Comparison with Original MomentKH

This library is a faithful port of the JavaScript momentkh library. Here's how the APIs compare:

JavaScript (momentkh)

const moment = require('moment');
require('@thyrith/momentkh')(moment);

let today = moment();
console.log(today.toLunarDate());

let birthday = moment('1996-09-24');
console.log(birthday.toLunarDate('dN ថ្ងៃW ខែm ព.ស. b'));

console.log(moment.getKhNewYearMoment(2024));

PHP (lunar-date)

use PPhatDev\LunarDate\KhmerDate;

$today = new KhmerDate();
echo $today->toLunarDate();

$birthday = new KhmerDate('1996-09-24');
echo $birthday->toLunarDate('dN ថ្ងៃW ខែm ព.ស. b');

echo KhmerDate::getKhNewYearMoment(2024)->format('Y-m-d H:i');

Understanding the Khmer Calendar System

The Khmer calendar is a sophisticated lunisolar system that combines lunar phases with solar year calculations:

Calendar Structure

Months (ខែ)

The Khmer calendar has 14 possible months per year:

Regular Months (12 months):

  • មិគសិរ, បុស្ស, មាឃ, ផល្គុន, ចេត្រ, ពិសាខ, ជេស្ឋ, អាសាឍ, ស្រាពណ៍, ភទ្របទ, អស្សុជ, កត្ដិក

Leap Months (occur in leap years):

  • បឋមាសាឍ (first អាសាឍ)
  • ទុតិយាសាឍ (second អាសាឍ)

Days (ថ្ងៃ)

Each lunar month follows the moon phases:

  • Waxing Moon (កើត): ១កើត, ២កើត, ... ១៥កើត (15 days)
  • Waning Moon (រោច): ១រោច, ២រោច, ... ១៤រោច or ១៥រោច (14-15 days)

Year Systems (ឆ្នាំ)

Animal Years (12-year cycle): ជូត, ឆ្លូវ, ខាល, ថោះ, រោង, ម្សាញ់, មមី, មមែ, វក, រកា, ច, កុរ

Era Years (10-year cycle): សំរឹទ្ធិស័ក, ឯកស័ក, ទោស័ក, ត្រីស័ក, ចត្វាស័ក, បញ្ចស័ក, ឆស័ក, សប្តស័ក, អដ្ឋស័ក, នព្វស័ក

Buddhist Era (BE): Gregorian year + 543/544 (depending on the time of year)

Leap Year Types

The Khmer calendar has three types of years:

  1. Regular Year (354 days): 12 months, normal lunar cycle
  2. Leap Month Year (អធិកមាស): 13 months (384 days) - adds បឋមាសាឍ/ទុតិយាសាឍ
  3. Leap Day Year (ចន្ទ្រាធិមាស): Extra day added to ជេស្ឋ month (355 days)

Key Calculations

The library implements complex calculations including:

  • Bodithey (បូតិថី): Lunar calendar adjustments
  • Avoman (អវមាន): Leap month calculations
  • Ahargun (អហរគុណ): Leap day determinations
  • Soriyatra Lerng Sak: New Year timing calculations

Advanced Usage Examples

Working with Lunar Days

// Find all full moon days (15កើត) in 2024
$fullMoons = Utils::findLunarDayOccurrences(15, 0, 2024);
foreach ($fullMoons as $fullMoon) {
    echo "Full Moon: {$fullMoon['date']} - {$fullMoon['khmer']}\n";
}

// Find all new moon days (15រោច) in 2024  
$newMoons = Utils::findLunarDayOccurrences(15, 1, 2024);

Buddhist Holiday Calculations

// Get all Buddhist holidays for 2024
$holidays = Utils::getBuddhistHolidays(2024);
foreach ($holidays as $holiday) {
    echo "{$holiday['name']}: {$holiday['date']} ({$holiday['type']})\n";
}

Season Information

$date = new KhmerDate('2024-07-15');
$season = Utils::getSeason($date);

echo "Season: {$season['name']} ({$season['name_en']})\n";
echo "Description: {$season['description']}\n";

Era Conversion Examples

// Convert between different calendar systems
$currentYear = 2024;

$beYear = Utils::convertEra($currentYear, 'AD', 'BE');    // 2567
$jsYear = Utils::convertEra($currentYear, 'AD', 'JS');    // 1385

// Convert back
$adFromBE = Utils::convertEra($beYear, 'BE', 'AD');       // 2024

echo "Gregorian: $currentYear\n";
echo "Buddhist Era: $beYear\n";
echo "Jolak Sakaraj: $jsYear\n";

Date Range Operations

// Get all days in a specific Khmer month
$monthData = Utils::getKhmerMonthRange(5, 2567); // ពិសាខ month, BE 2567

foreach ($monthData as $day) {
    echo "Day {$day['day']}: {$day['lunar']} - {$day['gregorian']}\n";
}

// Validate Khmer dates
$isValid = Utils::isValidKhmerDate(15, 5, 2567);  // true - valid full moon
$isValid = Utils::isValidKhmerDate(31, 5, 2567);  // false - invalid lunar day

Development & Testing

Development Setup

# Clone the repository
git clone https://github.com/pphatdev/lunar-date.git
cd lunar-date

# Install dependencies
composer install

# Run examples to test functionality
php examples/demo.php
php examples/momentkh_examples.php
php examples/new_year.php
php examples/current_be_year.php

Quality Assurance

This library maintains high code quality standards:

# Run complete test suite
composer test
# or
make test

# Generate code coverage report
composer test:coverage
# or
make coverage

# Run all quality checks
make quality

Testing Coverage

The library includes comprehensive test coverage:

  • Unit Tests: All classes and methods tested with PHPUnit
  • Integration Tests: End-to-end calendar conversion testing
  • Edge Case Testing: Boundary conditions and error scenarios
  • Simple Test: Basic functionality verification with tests/simple_test.php

Code Quality Metrics

  • CI/CD Pipeline: GitHub Actions with multi-version PHP testing (7.4, 8.0, 8.1, 8.2, 8.3)
  • PSR-12 Compliance: Modern PHP coding standards
  • Type Safety: Comprehensive type hints and strict types
  • Error Handling: Proper exception handling and validation
  • Documentation: Complete PHPDoc annotations

Project Structure

src/
├── KhmerDate.php           # Main date class with conversion methods
├── KhmerFormatter.php      # Formatting and localization utilities
├── KhmerCalculator.php     # Core calendar calculations and algorithms
├── SoriyatraLerngSak.php   # Khmer New Year calculations
├── Utils.php               # Utility functions and helper methods
└── Constants.php           # Calendar constants and definitions

examples/
├── demo.php               # Basic usage examples and demonstrations
├── momentkh_examples.php  # MomentKH compatibility demos
├── new_year.php          # Khmer New Year calculation examples
└── current_be_year.php   # Buddhist Era year examples

tests/
├── KhmerDateTest.php      # Main class tests
├── KhmerCalculatorTest.php # Calculation tests  
├── ConstantsTest.php      # Constants validation
└── simple_test.php        # Basic functionality test

.github/workflows/
└── ci.yml                 # GitHub Actions CI configuration

Contributing

We welcome contributions! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

Contribution Guidelines

  1. Fork the repository and create a feature branch
  2. Follow PSR-12 coding standards
  3. Add tests for new functionality
  4. Update documentation as needed
  5. Run quality checks before submitting
# Before submitting PR
make quality     # Run all quality checks
composer test    # Ensure all tests pass

Development Workflow

See CONTRIBUTING.md for detailed contribution guidelines.

Development Priorities

  • Performance Optimization: Improve calculation efficiency
  • Extended Formatting: More formatting options and tokens
  • Historical Accuracy: Enhance historical date accuracy
  • Documentation: Expand examples and use cases

Requirements

  • PHP 7.4+: Modern PHP with type hints support
  • No External Dependencies: Uses only PHP standard library
  • Composer: For autoloading and dependency management

License

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

Credits & Acknowledgments

Original Work

  • ThyrithSor - Creator of the original momentkh JavaScript library
  • Moment.js Team - Inspiration for the API design philosophy

Academic References

  • "Pratitin Soryakkatik-Chankatik 1900-1999" by Mr. Roath Kim Soeun
  • cam-cc.org - Cambodian calendar calculations
  • dahlina.com - Historical calendar references

Special Thanks

Special appreciation to ThyrithSor for creating the foundational momentkh library and making Khmer calendar calculations accessible to developers worldwide. This PHP port aims to bring the same functionality to the PHP ecosystem while maintaining compatibility with the original JavaScript API.

Related Projects

Support

  • Documentation: This README and inline PHPDoc comments
  • Examples: Check the examples/ directory for usage patterns
  • Issues: Report bugs and request features via GitHub Issues
  • Discussions: Use GitHub Discussions for questions and community support

Note: This library focuses on historical accuracy and cultural authenticity for Khmer calendar calculations. While extensively tested, please verify critical date calculations for important applications.