Hijri/Gregorian calendar conversion for Dart/Flutter. Umm al-Qura and FCNA engines, zero dependencies.
Find a file
Aric Camarata c79368a4ee fix: interpret DateTime by UTC calendar day for exact round-trips
toHijri in both UAQ and FCNA engines read .year/.month/.day from the
input DateTime — local calendar components — then passed them to
DateTime.utc(). On hosts west of UTC a UTC-midnight local DateTime
resolves to the previous local day, causing toHijri(toGregorian(y,m,d))
to return the wrong Hijri day.

Fix: call date.toUtc() first in both engines before extracting calendar
components. This is symmetric with toGregorian which always returns
DateTime.utc(). Adds 3 regression tests covering the round-trip and the
local-DateTime-on-UTC-west-host case.
2026-06-13 10:28:16 -04:00
.github chore: polish pubspec, add wiki docs and CHANGELOG 2026-05-29 06:34:30 -04:00
lib fix: interpret DateTime by UTC calendar day for exact round-trips 2026-06-13 10:28:16 -04:00
test fix: interpret DateTime by UTC calendar day for exact round-trips 2026-06-13 10:28:16 -04:00
.editorconfig style: replace em dashes with colons; add .editorconfig 2026-03-08 17:28:09 -04:00
.gitignore chore: polish pubspec, add wiki docs and CHANGELOG 2026-05-29 06:34:30 -04:00
analysis_options.yaml Initial release: hijri_core v1.0.0 2026-03-08 13:03:11 -04:00
CHANGELOG.md fix: interpret DateTime by UTC calendar day for exact round-trips 2026-06-13 10:28:16 -04:00
LICENSE Initial commit 2026-03-08 12:53:03 -04:00
pubspec.yaml fix: remove invalid publisher field from pubspec 2026-05-29 15:53:36 -04:00
README.md style: replace em dashes with colons; add .editorconfig 2026-03-08 17:28:09 -04:00

hijri_core

pub package CI License: MIT

Hijri/Gregorian calendar conversion for Dart and Flutter. Zero dependencies.

Installation

dependencies:
  hijri_core: ^1.0.0

Quick Start

import 'package:hijri_core/hijri_core.dart';

// Gregorian to Hijri (Umm al-Qura by default)
final hijri = toHijri(DateTime.utc(2025, 3, 1));
print('${hijri!.hy}/${hijri.hm}/${hijri.hd}'); // 1446/9/1

// Hijri to Gregorian
final greg = toGregorian(1446, 9, 1);
print(greg!.toIso8601String().substring(0, 10)); // 2025-03-01

// Use FCNA calendar instead
final fcna = toHijri(
  DateTime.utc(2025, 3, 1),
  options: const ConversionOptions(calendar: 'fcna'),
);

// Validate a Hijri date
final valid = isValidHijriDate(1444, 9, 1); // true

// Days in a Hijri month
final days = daysInHijriMonth(1444, 9); // 29

API

Top-Level Functions

Function Description
toHijri(DateTime date, {ConversionOptions? options}) Convert Gregorian to Hijri. Returns HijriDate?.
toGregorian(int hy, int hm, int hd, {ConversionOptions? options}) Convert Hijri to Gregorian. Returns DateTime? (UTC).
isValidHijriDate(int hy, int hm, int hd, {ConversionOptions? options}) Check if a Hijri date is valid. Returns bool.
daysInHijriMonth(int hy, int hm, {ConversionOptions? options}) Days in a Hijri month (29 or 30). Throws RangeError if out of range.

Registry Functions

Function Description
registerCalendar(String name, CalendarEngine engine) Register a custom calendar engine.
getCalendar(String name) Retrieve a registered engine by name.
listCalendars() List all registered engine names.

Data

Export Description
hDatesTable 184-entry Umm al-Qura reference table (Hijri 1318-1501).
hmLong, hmMedium, hmShort Hijri month names (12 entries each).
hwLong, hwShort, hwNumeric Hijri weekday names (7 entries each).

Engines

UAQ (Umm al-Qura)

The official Saudi Arabian Islamic calendar. Table-driven conversions covering Hijri years 1318-1500 (Gregorian 1900-2076). Returns null for dates outside that range.

This is the default engine.

FCNA (Fiqh Council of North America)

Astronomical calculation using Meeus Chapter 49 new moon algorithm. The FCNA criterion: if the new moon conjunction occurs before 12:00 noon UTC on day D, the new Hijri month begins at midnight starting day D+1. Otherwise it begins at midnight starting day D+2.

No fixed date range. Works for any Hijri year >= 1.

final h = toHijri(
  DateTime.utc(2025, 3, 1),
  options: const ConversionOptions(calendar: 'fcna'),
);

Custom Engine

Implement the CalendarEngine abstract class and register it:

class MyEngine extends CalendarEngine {
  @override
  String get id => 'custom';

  @override
  HijriDate? toHijri(DateTime date) { /* ... */ }

  @override
  DateTime? toGregorian(int hy, int hm, int hd) { /* ... */ }

  @override
  bool isValid(int hy, int hm, int hd) { /* ... */ }

  @override
  int daysInMonth(int hy, int hm) { /* ... */ }
}

registerCalendar('custom', MyEngine());

final h = toHijri(
  DateTime.utc(2025, 1, 1),
  options: const ConversionOptions(calendar: 'custom'),
);

Architecture

The UAQ engine performs a binary search over the 184-entry table: O(log 183) per conversion. The FCNA engine computes new moon times using the Meeus Ch. 49 algorithm. The registry pattern lets consumers add custom calendar engines at runtime.

Compatibility

  • Dart SDK >= 3.7.0
  • Works with Flutter
  • Zero external dependencies

Acknowledgments

The Umm al-Qura calendar table is derived from data published by the King Abdulaziz City for Science and Technology (KACST), Saudi Arabia. The FCNA new moon algorithm follows Jean Meeus, "Astronomical Algorithms," 2nd ed., Chapter 49.

License

MIT. Copyright (c) 2026 Aric Camarata.