Turn the messy Hebrew dates people actually type — "Chof Gimmel Shvat", "ר״ח אדר", "Chof Hey Nissan (Isru Chag Pesach) ~ April 21" — into structured day / month / year components.
npm install parse-hebrew-dateMIT, and no runtime dependencies.
import { parseHebrewDate } from 'parse-hebrew-date';
parseHebrewDate('Chof Gimmel Shvat');
// { day: 23, month: 'Shvat', input: 'Chof Gimmel Shvat', strategy: 'transliterated' }
parseHebrewDate('כ״ג בשבט תשפ״ג');
// { day: 23, month: 'Shvat', year: 5783, input: 'כ״ג בשבט תשפ״ג', strategy: 'gematriya' }
parseHebrewDate('not a date');
// nullHebrew dates in real datasets — genealogy sheets, shul yahrzeit lists, family calendars — are rarely clean Hebrew script. They are phonetic English typed by whoever kept the list, mixed with abbreviations, parenthetical notes and a Gregorian date stapled on with a tilde.
Libraries like @hebcal/core handle everything downstream of a clean date — conversion, holidays, candle-lighting — and handle it far better than this package ever would. What is missing is the layer underneath: turning what somebody actually typed into a date at all.
| Layer | Handled |
|---|---|
"כ״ג בשבט תשפ״ג" — clean gematria, nikud, implied thousands digit |
✅ |
"Chof Gimmel Shvat" — transliterated phonetic English |
✅ |
"ר״ח אדר" / "Rosh Chodesh Nissan" — Rosh Chodesh shorthand |
✅ |
"מנחם אב", "Adar Sheini" — compound and qualified month names |
✅ |
"… (Isru Chag Pesach) ~ April 21" — notes, separators, stray years |
✅ |
| Gregorian conversion, holidays, zmanim | ❌ — use @hebcal/core |
The output is deliberately plain data, not a date object. Feed it to @hebcal/core or anything else once you know which Hebrew year you are projecting it onto.
| Input | Output |
|---|---|
Chof Gimmel Shvat |
23 Shvat |
Tes Vov Shvat |
15 Shvat |
Chai Elul |
18 Elul |
Yud Beis Menachem Av |
12 Av |
Alef Adar Sheini |
1 Adar II |
23 Shvat / 15th Shvat |
23 Shvat / 15 Shvat |
כ״ג בשבט תשפ״ג |
23 Shvat 5783 |
כ״ז בְּתַמּוּז תשע״ג |
27 Tamuz 5773 |
כ"ג בשבט תשפ"ג (ASCII quotes) |
23 Shvat 5783 |
ט״ו בשבט |
15 Shvat |
חי אלול |
18 Elul |
ה׳ מנחם אב תשנ״ח |
5 Av 5758 |
ר״ח אדר |
1 Adar |
ר״ח אדר ב |
1 Adar II |
Rosh Chodesh Nissan |
1 Nisan |
Chof Hey Nissan (Isru Chag Pesach) |
25 Nisan, note Isru Chag Pesach |
Chof Gimmel Shvat 5758 |
23 Shvat 5758 |
ח' שבט ~ January 23 |
8 Shvat |
January 23 ~ ח' שבט |
8 Shvat |
garbage |
null |
Handled along the way: mixed case, hyphenated numerals, nikud, geresh/gershayim typed as ASCII ' and ", the ב prefix on a month name, the many spellings of one month (Shvat / Shevat / Shevet, Cheshvan / Heshvan / Marcheshvan), extra whitespace, and the backslash-escaped \~ that CSV exports leave behind.
Returns null for anything it cannot read. It never throws, including on null, undefined or a non-string — safe to map straight over a spreadsheet column.
The same, but throws HebrewDateParseError (which carries the offending .input) instead of returning null.
interface HebrewDateParts {
day: number; // 1–30
month: HebrewMonthName; // canonical name
year?: number; // Hebrew year, only when the input carried one
note?: string; // text found in parentheses
input: string; // the original string, untouched
strategy: ParseStrategy; // which layer read it
}strategy is 'gematriya' (delegated to @hebcal/hdate), 'hebrew-script', 'transliterated' or 'rosh-chodesh'. It is useful for triaging an import: a column that comes back entirely 'gematriya' is clean data, one full of 'transliterated' is not.
The fourteen canonical month names, in calendar order from Tishrei:
Tishrei, Cheshvan, Kislev, Tevet, Shvat, Adar, Adar I, Adar II, Nisan, Iyyar, Sivan, Tamuz, Av, Elul.
- Adar. An unqualified Adar stays
'Adar'unless the input also carries a Hebrew year, in which case it becomes'Adar II'in a leap year — the Adar that Purim falls in — and'Adar'otherwise. Named outright,'Adar I'/'Adar II'are always honoured as written, in either spelling (אדר א/אדר ראשון,Adar Sheini), and in a common year too. - Years. Only Hebrew years are reported. A plain-digit year is read in the range 5700–5999; a Hebrew-script year in 5400–5999 (1640–2239 CE). A trailing Gregorian year is dropped rather than reported.
- Days are validated. A day outside 1–30 makes the whole parse fail rather than returning an impossible date.
- No calendar maths. This package extracts components. It does not convert to Gregorian, resolve a recurrence, or tell you whether 30 Cheshvan exists in a given year — use
@hebcal/corefor that. A date that cannot exist comes back exactly as written:ל׳ אלול תשפ״גparses to30 Elul 5783rather than being silently rolled forward to1 Tishrei 5784. Validating against a real calendar is the caller's job, and needs to stay visible.
Node >= 20, and nothing else — zero runtime dependencies. Ships ESM and CommonJS builds with TypeScript declarations for both.
Versions up to and including 0.1.1 depended on @hebcal/hdate for gematria and clean-date parsing. That library is GPL-2.0, which put a copyleft obligation on everyone installing this MIT package, so those few primitives — a gematria table, the Metonic leap-year rule, and a strict clean-date reader — are implemented directly in src/gematria.ts as of 0.2.0. See the changelog; the rewrite also fixed four dating bugs.
The shape of this package, and the decision to keep it separate from the calendar libraries, both owe a lot to hebcal by Michael J. Radwin and contributors — still the right tool for everything downstream of this one.
MIT © 2026 Shmuel Holzman.
Maintained by Holzman AI & Automations. Extracted from Luach, a family Hebrew-calendar app, where it was written to survive a real family spreadsheet.