@thyrith/momentkh 3.0.0 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/momentkh.ts CHANGED
@@ -24,20 +24,20 @@ export enum MoonPhase {
24
24
  }
25
25
 
26
26
  export enum MonthIndex {
27
- Mikasar = 0, // មិគសិរ
28
- Bos = 1, // បុស្ស
27
+ Migasir = 0, // មិគសិរ
28
+ Boss = 1, // បុស្ស
29
29
  Meak = 2, // មាឃ
30
- Phalgun = 3, // ផល្គុន
31
- Chetr = 4, // ចេត្រ
32
- Visakh = 5, // ពិសាខ
30
+ Phalkun = 3, // ផល្គុន
31
+ Cheit = 4, // ចេត្រ
32
+ Pisakh = 5, // ពិសាខ
33
33
  Jesth = 6, // ជេស្ឋ
34
- Asath = 7, // អាសាឍ
34
+ Asadh = 7, // អាសាឍ
35
35
  Srap = 8, // ស្រាពណ៍
36
- Photrobot = 9, // ភទ្របទ
36
+ Phatrabot = 9, // ភទ្របទ
37
37
  Assoch = 10, // អស្សុជ
38
- Kadek = 11, // កត្ដិក
39
- BothmakAsath = 12, // បឋមាសាឍ
40
- TutiyakAsath = 13 // ទុតិយាសាឍ
38
+ Kadeuk = 11, // កត្ដិក
39
+ Pathamasadh = 12, // បឋមាសាឍ
40
+ Tutiyasadh = 13 // ទុតិយាសាឍ
41
41
  }
42
42
 
43
43
  export enum AnimalYear {
@@ -55,17 +55,17 @@ export enum AnimalYear {
55
55
  Kor = 11 // កុរ - Pig
56
56
  }
57
57
 
58
- export enum EraYear {
59
- Samridhisak = 0, // សំរឹទ្ធិស័ក
60
- Ekasak = 1, // ឯកស័ក
61
- Tosak = 2, // ទោស័ក
62
- Tresak = 3, // ត្រីស័ក
63
- Chatvasak = 4, // ចត្វាស័ក
64
- Panchasak = 5, // បញ្ចស័ក
65
- Chhasak = 6, // ឆស័ក
66
- Saptasak = 7, // សប្តស័ក
67
- Atthasak = 8, // អដ្ឋស័ក
68
- Novvasak = 9 // នព្វស័ក
58
+ export enum Sak {
59
+ SamridhiSak = 0, // សំរឹទ្ធិស័ក
60
+ AekSak = 1, // ឯកស័ក
61
+ ToSak = 2, // ទោស័ក
62
+ TreiSak = 3, // ត្រីស័ក
63
+ ChattvaSak = 4, // ចត្វាស័ក
64
+ PanchaSak = 5, // បញ្ចស័ក
65
+ ChhaSak = 6, // ឆស័ក
66
+ SappaSak = 7, // សប្តស័ក
67
+ AtthaSak = 8, // អដ្ឋស័ក
68
+ NappaSak = 9 // នព្វស័ក
69
69
  }
70
70
 
71
71
  export enum DayOfWeek {
@@ -94,12 +94,12 @@ export interface KhmerDateInfo {
94
94
  moonPhaseName: string; // String name: "កើត" or "រោច"
95
95
  monthIndex: MonthIndex; // Enum: MonthIndex (0-13)
96
96
  monthName: string; // String name of the month
97
- beYear: number; // Buddhist Era year
97
+ beYear: number; // Buddhist Sak
98
98
  jsYear: number; // Jolak Sakaraj year
99
99
  animalYear: AnimalYear; // Enum: AnimalYear (0-11)
100
100
  animalYearName: string; // String name of animal year
101
- eraYear: EraYear; // Enum: EraYear (0-9)
102
- eraYearName: string; // String name of era year
101
+ sak: Sak; // Enum: Sak (0-9)
102
+ sakName: string; // String name of Sak
103
103
  dayOfWeek: DayOfWeek; // Enum: DayOfWeek (0-6)
104
104
  dayOfWeekName: string; // String name of day of week
105
105
  }
@@ -159,10 +159,18 @@ export interface Constants {
159
159
  LunarMonths: Record<string, number>;
160
160
  LunarMonthNames: string[];
161
161
  SolarMonthNames: string[];
162
+ SolarMonthAbbreviationNames: string[];
163
+ LunarMonthAbbreviationNames: string[];
162
164
  AnimalYearNames: string[];
163
- EraYearNames: string[];
165
+ AnimalYearEmojis: string[];
166
+ SakNames: string[];
164
167
  WeekdayNames: string[];
165
- MoonStatusNames: string[];
168
+ WeekdayNamesShort: string[];
169
+ MoonPhaseNames: string[];
170
+ MoonPhaseShort: string[];
171
+ MoonDaySymbols: string[];
172
+ KhmerNumerals: Record<string, string>;
173
+ khNewYearMoments: Record<string, string>;
166
174
  }
167
175
 
168
176
  // ============================================================================
@@ -187,12 +195,28 @@ const SolarMonthNames: string[] = [
187
195
  'កក្កដា', 'សីហា', 'កញ្ញា', 'តុលា', 'វិច្ឆិកា', 'ធ្នូ'
188
196
  ];
189
197
 
198
+ const SolarMonthAbbreviationNames: string[] = [
199
+ 'មក', 'កម', 'មន', 'មស', 'ឧស', 'មថ',
200
+ 'កដ', 'សហ', 'កញ', 'តល', 'វក', 'ធន'
201
+ ];
202
+
203
+ const LunarMonthAbbreviationNames: string[] = [
204
+ 'មិ', 'បុ', 'មា', 'ផល', 'ចេ', 'ពិ',
205
+ 'ជេ', 'អា', 'ស្រ', 'ភ', 'អ', 'ក',
206
+ 'បឋ', 'ទុតិ'
207
+ ];
208
+
190
209
  const AnimalYearNames: string[] = [
191
210
  'ជូត', 'ឆ្លូវ', 'ខាល', 'ថោះ', 'រោង', 'ម្សាញ់',
192
211
  'មមី', 'មមែ', 'វក', 'រកា', 'ច', 'កុរ'
193
212
  ];
194
213
 
195
- const EraYearNames: string[] = [
214
+ const AnimalYearEmojis: string[] = [
215
+ '🐀', '🐂', '🐅', '🐇', '🐉', '🐍',
216
+ '🐎', '🐐', '🐒', '🐓', '🐕', '🐖'
217
+ ];
218
+
219
+ const SakNames: string[] = [
196
220
  'សំរឹទ្ធិស័ក', 'ឯកស័ក', 'ទោស័ក', 'ត្រីស័ក', 'ចត្វាស័ក',
197
221
  'បញ្ចស័ក', 'ឆស័ក', 'សប្តស័ក', 'អដ្ឋស័ក', 'នព្វស័ក'
198
222
  ];
@@ -203,8 +227,8 @@ const WeekdayNames: string[] = [
203
227
 
204
228
  const WeekdayNamesShort: string[] = ['អា', 'ច', 'អ', 'ព', 'ព្រ', 'សុ', 'ស'];
205
229
 
206
- const MoonStatusNames: string[] = ['កើត', 'រោច'];
207
- const MoonStatusShort: string[] = ['ក', 'រ'];
230
+ const MoonPhaseNames: string[] = ['កើត', 'រោច'];
231
+ const MoonPhaseShort: string[] = ['ក', 'រ'];
208
232
 
209
233
  const MoonDaySymbols: string[] = [
210
234
  '᧡', '᧢', '᧣', '᧤', '᧥', '᧦', '᧧', '᧨', '᧩', '᧪',
@@ -531,7 +555,7 @@ function getNumberOfDaysInKhmerMonth(monthIndex: MonthIndex | number, beYear: nu
531
555
  if (idx === MonthIndex.Jesth && leapType === 2) { // ជេស្ឋ with leap day
532
556
  return 30;
533
557
  }
534
- if (idx === MonthIndex.BothmakAsath || idx === MonthIndex.TutiyakAsath) { // បឋមាសាឍ, ទុតិយាសាឍ
558
+ if (idx === MonthIndex.Pathamasadh || idx === MonthIndex.Tutiyasadh) { // បឋមាសាឍ, ទុតិយាសាឍ
535
559
  return leapType === 1 ? 30 : 0;
536
560
  }
537
561
  // Alternating pattern: even months = 29 days, odd months = 30 days
@@ -551,11 +575,11 @@ function nextMonthOf(monthIndex: MonthIndex | number, beYear: number): MonthInde
551
575
  const idx = typeof monthIndex === 'number' ? monthIndex : monthIndex as number;
552
576
 
553
577
  if (idx === MonthIndex.Jesth && leapType === 1) { // ជេស្ឋ in leap month year
554
- return MonthIndex.BothmakAsath; // បឋមាសាឍ
578
+ return MonthIndex.Pathamasadh; // បឋមាសាឍ
555
579
  }
556
- if (idx === MonthIndex.Kadek) return MonthIndex.Mikasar; // កត្ដិក -> មិគសិរ
557
- if (idx === MonthIndex.BothmakAsath) return MonthIndex.TutiyakAsath; // បឋមាសាឍ -> ទុតិយាសាឍ
558
- if (idx === MonthIndex.TutiyakAsath) return MonthIndex.Srap; // ទុតិយាសាឍ -> ស្រាពណ៍
580
+ if (idx === MonthIndex.Kadeuk) return MonthIndex.Migasir; // កត្ដិក -> មិគសិរ
581
+ if (idx === MonthIndex.Pathamasadh) return MonthIndex.Tutiyasadh; // បឋមាសាឍ -> ទុតិយាសាឍ
582
+ if (idx === MonthIndex.Tutiyasadh) return MonthIndex.Srap; // ទុតិយាសាឍ -> ស្រាពណ៍
559
583
 
560
584
  return (idx + 1) as MonthIndex;
561
585
  }
@@ -564,10 +588,10 @@ function previousMonthOf(monthIndex: MonthIndex | number, beYear: number): Month
564
588
  const leapType = getLeapType(beYear);
565
589
  const idx = typeof monthIndex === 'number' ? monthIndex : monthIndex as number;
566
590
 
567
- if (idx === MonthIndex.Mikasar) return MonthIndex.Kadek; // មិគសិរ -> កត្ដិក
568
- if (idx === MonthIndex.Srap && leapType === 1) return MonthIndex.TutiyakAsath; // ស្រាពណ៍ -> ទុតិយាសាឍ (leap)
569
- if (idx === MonthIndex.TutiyakAsath) return MonthIndex.BothmakAsath; // ទុតិយាសាឍ -> បឋមាសាឍ
570
- if (idx === MonthIndex.BothmakAsath) return MonthIndex.Jesth; // បឋមាសាឍ -> ជេស្ឋ
591
+ if (idx === MonthIndex.Migasir) return MonthIndex.Kadeuk; // មិគសិរ -> កត្ដិក
592
+ if (idx === MonthIndex.Srap && leapType === 1) return MonthIndex.Tutiyasadh; // ស្រាពណ៍ -> ទុតិយាសាឍ (leap)
593
+ if (idx === MonthIndex.Tutiyasadh) return MonthIndex.Pathamasadh; // ទុតិយាសាឍ -> បឋមាសាឍ
594
+ if (idx === MonthIndex.Pathamasadh) return MonthIndex.Jesth; // បឋមាសាឍ -> ជេស្ឋ
571
595
 
572
596
  return (idx - 1) as MonthIndex;
573
597
  }
@@ -784,7 +808,7 @@ class KhmerDate {
784
808
  const newDay = KhmerDate.fromDayNumber(newDayNum);
785
809
 
786
810
  let newBeYear = result.beYear;
787
- if (result.monthIndex === MonthIndex.Visakh) { // ពិសាខ
811
+ if (result.monthIndex === MonthIndex.Pisakh) { // ពិសាខ
788
812
  if (result.moonPhase === MoonPhase.Waxing && newDay.moonPhase === MoonPhase.Waning) {
789
813
  newBeYear++;
790
814
  }
@@ -795,7 +819,7 @@ class KhmerDate {
795
819
  } else {
796
820
  remaining -= (daysLeftInMonth + 1);
797
821
  const nextMonth = nextMonthOf(result.monthIndex, result.beYear);
798
- const newBeYear = (result.monthIndex === MonthIndex.Chetr) ? result.beYear + 1 : result.beYear;
822
+ const newBeYear = (result.monthIndex === MonthIndex.Cheit) ? result.beYear + 1 : result.beYear;
799
823
  result = new KhmerDate(1, MoonPhase.Waxing, nextMonth, newBeYear); // Start at 1កើត
800
824
  }
801
825
  }
@@ -817,7 +841,7 @@ class KhmerDate {
817
841
  const newDay = KhmerDate.fromDayNumber(newDayNum);
818
842
 
819
843
  let newBeYear = result.beYear;
820
- if (result.monthIndex === MonthIndex.Visakh) { // ពិសាខ
844
+ if (result.monthIndex === MonthIndex.Pisakh) { // ពិសាខ
821
845
  if (result.moonPhase === MoonPhase.Waning && newDay.moonPhase === MoonPhase.Waxing) {
822
846
  newBeYear--;
823
847
  }
@@ -828,7 +852,7 @@ class KhmerDate {
828
852
  } else {
829
853
  remaining -= (currentDayNum + 1);
830
854
  const prevMonth = previousMonthOf(result.monthIndex, result.beYear);
831
- const newBeYear = (result.monthIndex === MonthIndex.Visakh) ? result.beYear - 1 : result.beYear;
855
+ const newBeYear = (result.monthIndex === MonthIndex.Pisakh) ? result.beYear - 1 : result.beYear;
832
856
  const daysInPrevMonth = getNumberOfDaysInKhmerMonth(prevMonth, newBeYear);
833
857
  const newDay = KhmerDate.fromDayNumber(daysInPrevMonth - 1);
834
858
  result = new KhmerDate(newDay.day, newDay.moonPhase, prevMonth, newBeYear);
@@ -839,7 +863,7 @@ class KhmerDate {
839
863
  }
840
864
 
841
865
  toString(): string {
842
- return `${this.day}${MoonStatusNames[this.moonPhase]} ខែ${LunarMonthNames[this.monthIndex]} ព.ស.${this.beYear}`;
866
+ return `${this.day}${MoonPhaseNames[this.moonPhase]} ខែ${LunarMonthNames[this.monthIndex]} ព.ស.${this.beYear}`;
843
867
  }
844
868
  }
845
869
 
@@ -857,7 +881,7 @@ function getMaybeBEYear(year: number, month: number): number {
857
881
  }
858
882
  }
859
883
 
860
- // Cache for Visakha Bochea dates by year
884
+ // Cache for Pisakha Bochea dates by year
861
885
  const visakhaBocheaCache: Record<number, number> = {};
862
886
 
863
887
  // Cache for New Year Full Info
@@ -868,7 +892,7 @@ const newYearInfoCache: Record<number, NewYearFullInfo> = {};
868
892
  * BE year increases on ១រោច ខែពិសាខ (1st waning day of Pisakh = dayNumber 15 of month 5)
869
893
  * Returns timestamp in milliseconds at midnight of that day
870
894
  */
871
- function getVisakhaBochea(year: number, isSearching: boolean = false): number {
895
+ function getPisakhaBochea(year: number, isSearching: boolean = false): number {
872
896
  if (visakhaBocheaCache[year]) {
873
897
  return visakhaBocheaCache[year];
874
898
  }
@@ -879,7 +903,7 @@ function getVisakhaBochea(year: number, isSearching: boolean = false): number {
879
903
  for (let searchDay = 1; searchDay <= daysInMonth; searchDay++) {
880
904
  // Avoid infinite recursion by using simplified BE year during search
881
905
  const result = gregorianToKhmerInternal(year, searchMonth, searchDay, 12, 0, 0, true);
882
- if (result.khmer.monthIndex === MonthIndex.Visakh && result._khmerDateObj.getDayNumber() === 15) {
906
+ if (result.khmer.monthIndex === MonthIndex.Pisakh && result._khmerDateObj.getDayNumber() === 15) {
883
907
  // Found 1រោច Pisakh - return timestamp at midnight (start of BE year change day)
884
908
  // BE year changes at 00:00 on this day
885
909
  const timestamp = new Date(year, searchMonth - 1, searchDay, 0, 0, 0, 0).getTime();
@@ -982,7 +1006,7 @@ function gregorianToKhmerInternal(
982
1006
  } else {
983
1007
  // Normal mode: compare against exact BE year transition datetime (1រោច Pisakh at 00:00)
984
1008
  const inputTimestamp = new Date(year, month - 1, day, hour, minute, second).getTime();
985
- const beYearTransitionTimestamp = getVisakhaBochea(year);
1009
+ const beYearTransitionTimestamp = getPisakhaBochea(year);
986
1010
 
987
1011
  if (inputTimestamp >= beYearTransitionTimestamp) {
988
1012
  // On or after 1រោច Pisakh (new BE year)
@@ -998,15 +1022,15 @@ function gregorianToKhmerInternal(
998
1022
  let animalYearIndex = ((beYear + 4) % 12 + 12) % 12;
999
1023
 
1000
1024
  // Adjust Era and Animal Year based on Khmer New Year logic
1001
- // They should change at New Year, not wait for Visakha Bochea (which changes BE)
1025
+ // They should change at New Year, not wait for Pisakha Bochea (which changes BE)
1002
1026
  if (!isSearching) {
1003
1027
  const newYearInfo = getNewYearFullInfo(year);
1004
1028
  const inputTimestamp = new Date(year, month - 1, day, hour, minute, second).getTime();
1005
- const visakhaBocheaTimestamp = getVisakhaBochea(year);
1029
+ const visakhaBocheaTimestamp = getPisakhaBochea(year);
1006
1030
 
1007
1031
  // Animal Year changes at Moha Songkran (exact New Year time)
1008
- // Only apply manual increment if we are in the gap between New Year and Visakha Bochea
1009
- // (After Visakha Bochea, the BE year increments, so the formula based on BE automatically gives the new Animal Year)
1032
+ // Only apply manual increment if we are in the gap between New Year and Pisakha Bochea
1033
+ // (After Pisakha Bochea, the BE year increments, so the formula based on BE automatically gives the new Animal Year)
1010
1034
  if (inputTimestamp >= newYearInfo.newYearMoment.getTime() && inputTimestamp <= visakhaBocheaTimestamp) {
1011
1035
  animalYearIndex = (animalYearIndex + 1) % 12;
1012
1036
  }
@@ -1017,7 +1041,7 @@ function gregorianToKhmerInternal(
1017
1041
  }
1018
1042
  }
1019
1043
 
1020
- const eraYearIndex = ((jsYear % 10) + 10) % 10;
1044
+ const sakIndex = ((jsYear % 10) + 10) % 10;
1021
1045
  const dayOfWeek = getDayOfWeek(year, month, day);
1022
1046
 
1023
1047
  const khmerDate = new KhmerDate(khmerDayInfo.day, khmerDayInfo.moonPhase, khmerMonth as MonthIndex, beYear);
@@ -1027,15 +1051,15 @@ function gregorianToKhmerInternal(
1027
1051
  khmer: {
1028
1052
  day: khmerDayInfo.day,
1029
1053
  moonPhase: khmerDayInfo.moonPhase,
1030
- moonPhaseName: MoonStatusNames[khmerDayInfo.moonPhase],
1054
+ moonPhaseName: MoonPhaseNames[khmerDayInfo.moonPhase],
1031
1055
  monthIndex: khmerMonth as MonthIndex,
1032
1056
  monthName: LunarMonthNames[khmerMonth],
1033
1057
  beYear: beYear,
1034
1058
  jsYear: jsYear,
1035
1059
  animalYear: animalYearIndex as AnimalYear,
1036
1060
  animalYearName: AnimalYearNames[animalYearIndex],
1037
- eraYear: eraYearIndex as EraYear,
1038
- eraYearName: EraYearNames[eraYearIndex],
1061
+ sak: sakIndex as Sak,
1062
+ sakName: SakNames[sakIndex],
1039
1063
  dayOfWeek: dayOfWeek as DayOfWeek,
1040
1064
  dayOfWeekName: WeekdayNames[dayOfWeek]
1041
1065
  },
@@ -1068,7 +1092,7 @@ function khmerToGregorian(day: number, moonPhase: MoonPhase | number, monthIndex
1068
1092
  for (let gDay = 1; gDay <= daysInMonth; gDay++) {
1069
1093
  // For BE year transition day (1រោច Pisakh) and the day before (15កើត Pisakh),
1070
1094
  // check multiple times during the day because BE year can change during this period
1071
- const isAroundBEYearChange = monthIndexNum === MonthIndex.Visakh &&
1095
+ const isAroundBEYearChange = monthIndexNum === MonthIndex.Pisakh &&
1072
1096
  ((day === 15 && moonPhaseNum === MoonPhase.Waxing) || (day === 1 && moonPhaseNum === MoonPhase.Waning));
1073
1097
  const timesToCheck = isAroundBEYearChange
1074
1098
  ? [0, 6, 12, 18, 23] // Check at different hours
@@ -1225,13 +1249,13 @@ function getKhmerNewYear(ceYear: number): NewYearInfo {
1225
1249
  // Formatting Functions
1226
1250
  // ============================================================================
1227
1251
 
1228
- function format(khmerData: KhmerConversionResult, formatString?: string): string {
1252
+ function formatKhmer(khmerData: KhmerConversionResult, formatString?: string): string {
1229
1253
  if (!formatString) {
1230
1254
  // Default format
1231
1255
  const { khmer } = khmerData;
1232
1256
  const moonDay = `${khmer.day}${khmer.moonPhaseName}`;
1233
1257
  return toKhmerNumeral(
1234
- `ថ្ងៃ${khmer.dayOfWeekName} ${moonDay} ខែ${khmer.monthName} ឆ្នាំ${khmer.animalYearName} ${khmer.eraYearName} ពុទ្ធសករាជ ${khmer.beYear}`
1258
+ `ថ្ងៃ${khmer.dayOfWeekName} ${moonDay} ខែ${khmer.monthName} ឆ្នាំ${khmer.animalYearName} ${khmer.sakName} ពុទ្ធសករាជ ${khmer.beYear}`
1235
1259
  );
1236
1260
  }
1237
1261
 
@@ -1239,24 +1263,38 @@ function format(khmerData: KhmerConversionResult, formatString?: string): string
1239
1263
  const formatRules: Record<string, () => string | number> = {
1240
1264
  'W': () => khmerData.khmer.dayOfWeekName,
1241
1265
  'w': () => WeekdayNamesShort[khmerData.gregorian.dayOfWeek],
1242
- 'd': () => khmerData.khmer.day,
1243
- 'D': () => (khmerData.khmer.day < 10 ? '0' : '') + khmerData.khmer.day,
1244
- 'n': () => MoonStatusShort[khmerData.khmer.moonPhase],
1266
+ 'd': () => toKhmerNumeral(khmerData.khmer.day),
1267
+ 'D': () => toKhmerNumeral((khmerData.khmer.day < 10 ? '0' : '') + khmerData.khmer.day),
1268
+ 'dr': () => khmerData.khmer.day,
1269
+ 'Dr': () => (khmerData.khmer.day < 10 ? '0' : '') + khmerData.khmer.day,
1270
+ 'n': () => MoonPhaseShort[khmerData.khmer.moonPhase],
1245
1271
  'N': () => khmerData.khmer.moonPhaseName,
1246
1272
  'o': () => MoonDaySymbols[khmerData._khmerDateObj.getDayNumber()],
1247
1273
  'm': () => khmerData.khmer.monthName,
1248
1274
  'M': () => SolarMonthNames[khmerData.gregorian.month - 1],
1249
1275
  'a': () => khmerData.khmer.animalYearName,
1250
- 'e': () => khmerData.khmer.eraYearName,
1251
- 'b': () => khmerData.khmer.beYear,
1252
- 'c': () => khmerData.gregorian.year,
1253
- 'j': () => khmerData.khmer.jsYear
1276
+ 'as': () => AnimalYearEmojis[khmerData.khmer.animalYear],
1277
+ 'e': () => khmerData.khmer.sakName,
1278
+ 'b': () => toKhmerNumeral(khmerData.khmer.beYear),
1279
+ 'br': () => khmerData.khmer.beYear,
1280
+ 'c': () => toKhmerNumeral(khmerData.gregorian.year),
1281
+ 'cr': () => khmerData.gregorian.year,
1282
+ 'j': () => toKhmerNumeral(khmerData.khmer.jsYear),
1283
+ 'jr': () => khmerData.khmer.jsYear,
1284
+ 'Ms': () => SolarMonthAbbreviationNames[khmerData.gregorian.month - 1],
1285
+ 'ms': () => LunarMonthAbbreviationNames[khmerData.khmer.monthIndex]
1254
1286
  };
1255
1287
 
1256
- const regex = new RegExp(Object.keys(formatRules).join('|'), 'g');
1257
- const result = formatString.replace(regex, match => {
1258
- const value = formatRules[match]();
1259
- return toKhmerNumeral(String(value));
1288
+ // Sort keys by length descending to ensure longer tokens (like 'Ms', 'ms') are matched before shorter ones (like 'M', 'm')
1289
+ const sortedKeys = Object.keys(formatRules).sort((a, b) => b.length - a.length);
1290
+ const regex = new RegExp(`\\[([^\\]]+)\\]|(${sortedKeys.join('|')})`, 'g');
1291
+
1292
+ const result = formatString.replace(regex, (match, escaped, token) => {
1293
+ if (escaped) {
1294
+ return escaped;
1295
+ }
1296
+ const value = formatRules[token]();
1297
+ return String(value);
1260
1298
  });
1261
1299
 
1262
1300
  return result;
@@ -1274,64 +1312,70 @@ function gregorianToKhmer(year: number, month: number, day: number, hour: number
1274
1312
  // Public API
1275
1313
  // ============================================================================
1276
1314
 
1277
- export const momentkh = {
1278
- // Conversion functions
1279
- fromGregorian(year: number, month: number, day: number, hour: number = 0, minute: number = 0, second: number = 0): KhmerConversionResult {
1280
- return gregorianToKhmer(year, month, day, hour, minute, second);
1281
- },
1282
-
1283
- fromKhmer(day: number, moonPhase: MoonPhase | number, monthIndex: MonthIndex | number, beYear: number): GregorianDate {
1284
- return khmerToGregorian(day, moonPhase, monthIndex, beYear);
1285
- },
1286
-
1287
- // New Year function
1288
- getNewYear(ceYear: number): NewYearInfo {
1289
- return getKhmerNewYear(ceYear);
1290
- },
1291
-
1292
- // Format function
1293
- format(khmerData: KhmerConversionResult, formatString?: string): string {
1294
- return format(khmerData, formatString);
1295
- },
1296
-
1297
- // Utility for creating date from Date object
1298
- fromDate(date: Date): KhmerConversionResult {
1299
- // Validate Date object
1300
- validateDateObject(date);
1301
- return gregorianToKhmer(
1302
- date.getFullYear(),
1303
- date.getMonth() + 1,
1304
- date.getDate(),
1305
- date.getHours(),
1306
- date.getMinutes(),
1307
- date.getSeconds()
1308
- );
1309
- },
1310
-
1311
- // Convert Khmer to Date object
1312
- toDate(day: number, moonPhase: MoonPhase | number, monthIndex: MonthIndex | number, beYear: number): Date {
1313
- const greg = khmerToGregorian(day, moonPhase, monthIndex, beYear);
1314
- return new Date(greg.year, greg.month - 1, greg.day);
1315
- },
1316
-
1317
- // Constants export
1318
- constants: {
1319
- LunarMonths,
1320
- LunarMonthNames,
1321
- SolarMonthNames,
1322
- AnimalYearNames,
1323
- EraYearNames,
1324
- WeekdayNames,
1325
- MoonStatusNames
1326
- },
1327
-
1328
- // Enums export for easier usage
1315
+ // Conversion functions
1316
+ export function fromGregorian(year: number, month: number, day: number, hour: number = 0, minute: number = 0, second: number = 0): KhmerConversionResult {
1317
+ return gregorianToKhmer(year, month, day, hour, minute, second);
1318
+ }
1319
+
1320
+ export function fromKhmer(day: number, moonPhase: MoonPhase | number, monthIndex: MonthIndex | number, beYear: number): GregorianDate {
1321
+ return khmerToGregorian(day, moonPhase, monthIndex, beYear);
1322
+ }
1323
+
1324
+ // New Year function
1325
+ export function getNewYear(ceYear: number): NewYearInfo {
1326
+ return getKhmerNewYear(ceYear);
1327
+ }
1328
+
1329
+ // Format function
1330
+ export function format(khmerData: KhmerConversionResult, formatString?: string): string {
1331
+ return formatKhmer(khmerData, formatString);
1332
+ }
1333
+ // Utility for creating date from Date object
1334
+ export function fromDate(date: Date): KhmerConversionResult {
1335
+ // Validate Date object
1336
+ validateDateObject(date);
1337
+ return gregorianToKhmer(
1338
+ date.getFullYear(),
1339
+ date.getMonth() + 1,
1340
+ date.getDate(),
1341
+ date.getHours(),
1342
+ date.getMinutes(),
1343
+ date.getSeconds()
1344
+ );
1345
+ }
1346
+
1347
+ // Convert Khmer to Date object
1348
+ export function toDate(day: number, moonPhase: MoonPhase | number, monthIndex: MonthIndex | number, beYear: number): Date {
1349
+ const greg = khmerToGregorian(day, moonPhase, monthIndex, beYear);
1350
+ return new Date(greg.year, greg.month - 1, greg.day);
1351
+ }
1352
+
1353
+ // Constants export
1354
+ export const constants = {
1355
+ LunarMonths,
1356
+ LunarMonthNames,
1357
+ SolarMonthNames,
1358
+ SolarMonthAbbreviationNames,
1359
+ LunarMonthAbbreviationNames,
1360
+ AnimalYearNames,
1361
+ AnimalYearEmojis,
1362
+ SakNames,
1363
+ WeekdayNames,
1364
+ MoonPhaseNames
1365
+ };
1366
+
1367
+ // Default export - aggregate all exports for convenience
1368
+ export default {
1369
+ fromGregorian,
1370
+ fromKhmer,
1371
+ getNewYear,
1372
+ format,
1373
+ fromDate,
1374
+ toDate,
1375
+ constants,
1329
1376
  MoonPhase,
1330
1377
  MonthIndex,
1331
1378
  AnimalYear,
1332
- EraYear,
1379
+ Sak,
1333
1380
  DayOfWeek
1334
1381
  };
1335
-
1336
- // Default export
1337
- export default momentkh;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thyrith/momentkh",
3
- "version": "3.0.0",
3
+ "version": "3.0.2",
4
4
  "description": "Working on khmer calendar by implementting moment js",
5
5
  "license": "MIT",
6
6
  "type": "component",
@@ -16,7 +16,8 @@
16
16
  "LICENSE"
17
17
  ],
18
18
  "scripts": {
19
- "build": "tsc",
19
+ "build": "tsc && node build-browser.js",
20
+ "build:dist": "tsc",
20
21
  "test": "npm run test:validation && npm run test:gregorian && npm run test:khmer && npm run test:newyear && npm run test:verify-conversion && npm run test:verify-newyear && npm run test:verify-roundtrip",
21
22
  "test:validation": "node test/validation.test.js",
22
23
  "test:gregorian": "node test/gregorian-to-khmer.test.js",