File size: 5,136 Bytes
cf86710
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
import type { FormatOptions as DateFnsFormatOptions } from "date-fns";

import type { DateLibOptions } from "../../classes/DateLib.js";
import { getMonthCode } from "../utils/calendarMath.js";
import { toHebrewDate } from "../utils/dateConversion.js";
import { hebrewMonthNumber } from "../utils/serial.js";

const fallbackMonthNames: Record<string, { en: string; he: string }> = {
  tishrei: { en: "Tishrei", he: "转砖专讬" },
  cheshvan: { en: "Cheshvan", he: "讞砖讜讜谉" },
  kislev: { en: "Kislev", he: "讻住诇讜" },
  tevet: { en: "Tevet", he: "讟讘转" },
  shevat: { en: "Shevat", he: "砖讘讟" },
  adarI: { en: "Adar I", he: "讗讚专 讗壮" },
  adar: { en: "Adar", he: "讗讚专" },
  nisan: { en: "Nisan", he: "谞讬住谉" },
  iyar: { en: "Iyar", he: "讗讬讬专" },
  sivan: { en: "Sivan", he: "住讬讜讜谉" },
  tamuz: { en: "Tammuz", he: "转诪讜讝" },
  av: { en: "Av", he: "讗讘" },
  elul: { en: "Elul", he: "讗诇讜诇" },
};

const fallbackWeekdayNames = {
  long: {
    en: [
      "Sunday",
      "Monday",
      "Tuesday",
      "Wednesday",
      "Thursday",
      "Friday",
      "Saturday",
    ],
    he: [
      "讬讜诐 专讗砖讜谉",
      "讬讜诐 砖谞讬",
      "讬讜诐 砖诇讬砖讬",
      "讬讜诐 专讘讬注讬",
      "讬讜诐 讞诪讬砖讬",
      "讬讜诐 砖讬砖讬",
      "砖讘转",
    ],
  },
  narrow: {
    en: ["S", "M", "T", "W", "T", "F", "S"],
    he: ["讗", "讘", "讙", "讚", "讛", "讜", "砖"],
  },
};

const getLocaleCode = (options?: DateLibOptions): string => {
  return options?.locale?.code ?? "he";
};

const getMonthCodeForDate = (date: Date) => {
  const hebrew = toHebrewDate(date);
  return getMonthCode(hebrew.year, hebrew.monthIndex);
};

const formatMonthName = (date: Date, localeCode: string): string => {
  try {
    return new Intl.DateTimeFormat(localeCode, {
      month: "long",
      calendar: "hebrew",
    }).format(date);
  } catch {
    const code = getMonthCodeForDate(date);
    const isHebrew = localeCode.startsWith("he");
    return isHebrew ? fallbackMonthNames[code].he : fallbackMonthNames[code].en;
  }
};

const formatWeekdayName = (
  date: Date,
  localeCode: string,
  width: "long" | "narrow",
): string => {
  try {
    return new Intl.DateTimeFormat(localeCode, {
      weekday: width,
      calendar: "hebrew",
    }).format(date);
  } catch {
    const index = date.getDay();
    const isHebrew = localeCode.startsWith("he");
    return isHebrew
      ? fallbackWeekdayNames[width].he[index]
      : fallbackWeekdayNames[width].en[index];
  }
};

const formatDateStyle = (
  date: Date,
  localeCode: string,
  style: "long" | "full",
): string => {
  try {
    return new Intl.DateTimeFormat(localeCode, {
      dateStyle: style,
      calendar: "hebrew",
    }).format(date);
  } catch {
    const hebrew = toHebrewDate(date);
    const month = formatMonthName(date, localeCode);
    if (style === "full") {
      const weekday = formatWeekdayName(date, localeCode, "long");
      return `${weekday}, ${month} ${hebrew.day}, ${hebrew.year}`;
    }
    return `${month} ${hebrew.day}, ${hebrew.year}`;
  }
};

const formatNumber = (value: number): string => {
  return value.toString();
};

const buildTimeFormat = (
  date: Date,
  localeCode: string,
  formatStr: string,
): string => {
  const hour12 = formatStr.includes("a");
  return new Intl.DateTimeFormat(localeCode, {
    hour: "numeric",
    minute: "numeric",
    hour12,
  }).format(date);
};

/** Hebrew calendar formatting override. */
export function format(
  date: Date,
  formatStr: string,
  options?: DateFnsFormatOptions,
): string {
  const extendedOptions = options as DateLibOptions | undefined;
  const localeCode = getLocaleCode(extendedOptions);
  const hebrew = toHebrewDate(date);
  const monthNumber = hebrewMonthNumber(hebrew.monthIndex);

  switch (formatStr) {
    case "LLLL y":
    case "LLLL yyyy":
      return `${formatMonthName(date, localeCode)} ${formatNumber(hebrew.year)}`;
    case "LLLL":
      return formatMonthName(date, localeCode);
    case "PPP":
      return formatDateStyle(date, localeCode, "long");
    case "PPPP":
      return formatDateStyle(date, localeCode, "full");
    case "cccc":
      return formatWeekdayName(date, localeCode, "long");
    case "cccccc":
      return formatWeekdayName(date, localeCode, "narrow");
    case "yyyy":
    case "y":
      return formatNumber(hebrew.year);
    case "yyyy-MM":
      return `${formatNumber(hebrew.year)}-${formatNumber(monthNumber).padStart(2, "0")}`;
    case "yyyy-MM-dd":
      return `${formatNumber(hebrew.year)}-${formatNumber(monthNumber).padStart(2, "0")}-${formatNumber(hebrew.day).padStart(2, "0")}`;
    case "MM":
      return formatNumber(monthNumber).padStart(2, "0");
    case "M":
      return formatNumber(monthNumber);
    case "dd":
      return formatNumber(hebrew.day).padStart(2, "0");
    case "d":
      return formatNumber(hebrew.day);
    default:
      if (/[Hh]/.test(formatStr) && /m/.test(formatStr)) {
        return buildTimeFormat(date, localeCode, formatStr);
      }
      return `${formatNumber(hebrew.day)}/${formatNumber(monthNumber)}/${formatNumber(hebrew.year)}`;
  }
}