F2had/hijri-native

React Native TurboModule for Hijri (Umm al-Qura) calendar conversion using native iOS/Android APIs

โ˜… 0Forks 0TypeScriptGitHub โ†—Compare
calendarexpohijriiosislamic-calendarreact-nativeturbomoduletypescriptumm-al-qura

README

hijri-native

CI npm version npm downloads License: MIT

Gregorian โ†” Hijri (Umm al-Qura) calendar conversion for React Native using native OS APIs.

  • ๐ŸŽ iOS: NSCalendar.islamicUmmAlQura
  • ๐Ÿค– Android: java.time.chrono.HijrahChronology

โœ… No JavaScript math
โœ… No lookup tables
โœ… Zero dependencies
โœ… TypeScript support


Requirements

  • React Native 0.76+ (New Architecture / TurboModules)
  • iOS 13+
  • Android API 24+

Installation

npm:

npm install hijri-native

yarn:

yarn add hijri-native

bun:

bun add hijri-native

Core API

Conversion Functions

import {
  toHijri, // Gregorian โ†’ Hijri
  toGregorian, // Hijri โ†’ Gregorian
  fromTimestamp, // Unix timestamp โ†’ Hijri
  getDaysInMonth, // Get days in Hijri month
  today, // Today's Hijri date
} from 'hijri-native';

// Gregorian โ†’ Hijri
const hijri = toHijri(2026, 2, 18);
// { year: 1447, month: 8, day: 20 }

// Hijri โ†’ Gregorian
const greg = toGregorian(1447, 8, 20);
// { year: 2026, month: 2, day: 18 }

// Unix timestamp (seconds) + timezone โ†’ Hijri
const hijriNow = fromTimestamp(Math.floor(Date.now() / 1000), 'Asia/Riyadh');

// Today's Hijri date in a timezone
const todayHijri = today('Asia/Riyadh');

// Days in a Hijri month (29 or 30)
const days = getDaysInMonth(8, 1447);

Utility Functions

import {
  isEqual,
  isBefore,
  isAfter,
  differenceInDays,
  addDays,
} from 'hijri-native';

const a = { year: 1447, month: 8, day: 20 };
const b = { year: 1447, month: 9, day: 1 };

isEqual(a, b); // false
isBefore(a, b); // true
isAfter(a, b); // false
differenceInDays(a, b); // ~10
addDays(a, 5); // { year: 1447, month: 8, day: 25 }

isEqual, isBefore and isAfter are pure TypeScript and need no native module. Import them from hijri-native/pure to use them anywhere โ€” tests, server code, web bundles โ€” with no native dependency at all:

import { isEqual, isBefore, isAfter } from 'hijri-native/pure';

Testing

Importing this package is side-effect free: the native module is resolved with TurboModuleRegistry.get, so nothing throws until you call a conversion function. A test that merely imports a module which re-exports hijri-native therefore needs no mock.

To exercise the conversion functions themselves, mock the package:

jest.mock('hijri-native', () => ({
  today: () => ({ year: 1447, month: 8, day: 20 }),
  toHijri: () => ({ year: 1447, month: 8, day: 20 }),
  toGregorian: () => ({ year: 2026, month: 2, day: 8 }),
}));

Use isAvailable() to branch at runtime where the native module may be missing:

import { isAvailable, today } from 'hijri-native';

const hijri = isAvailable() ? today('Asia/Riyadh') : null;

Types

import type { HijriDate } from 'hijri-native';
// { year: number; month: number; day: number }

Why hijri-native?

Feature hijri-native JS Libraries
Accuracy โœ… Native OS APIs โš ๏ธ Algorithm-based
Performance โœ… Native speed โš ๏ธ JS calculations
Bundle size โœ… Zero deps โš ๏ธ Large tables
Umm al-Qura โœ… iOS & Android โš ๏ธ Limited support

License

MIT ยฉ Fahad

Contributors

F2had

Issues