@kalcodes/ethiopian-calendar 1.1.0 → 2.0.0

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/dist/index.d.ts CHANGED
@@ -1,15 +1,58 @@
1
- import { type SimpleDate } from "./utils.js";
2
- declare const ETH_JDN_OFFSET = 1724221;
3
- declare const GREG_JDN_OFFSET = 1721426;
4
- declare function ethToJDN(date: SimpleDate): number;
5
- declare function jdnToEth(jdn: number): SimpleDate;
6
- declare function gregToJDN(date: SimpleDate): number;
7
- declare function jdnToGreg(jdn: number): {
1
+ type DateStr = `${number}-${number}-${number}` | `${number}/${number}/${number}`;
2
+ type DateObj = {
8
3
  year: number;
9
4
  month: number;
10
5
  day: number;
11
6
  };
12
- declare function gregToEth(date: SimpleDate): SimpleDate;
13
- declare function ethToGreg(date: SimpleDate): SimpleDate;
14
- export { ETH_JDN_OFFSET, GREG_JDN_OFFSET, ethToJDN, jdnToEth, ethToGreg, gregToJDN, jdnToGreg, gregToEth, type SimpleDate, };
7
+ declare class CalendarError extends Error {
8
+ constructor(message: string);
9
+ }
10
+ declare class BaseCalendar {
11
+ year: number;
12
+ month: number;
13
+ day: number;
14
+ constructor(date: DateStr | DateObj);
15
+ _parseDateString(str: DateStr): DateObj;
16
+ static _today(): {
17
+ year: number;
18
+ month: number;
19
+ day: number;
20
+ };
21
+ toString(): string;
22
+ }
23
+ declare class EthiopianCalendar extends BaseCalendar {
24
+ static type: "ethiopian";
25
+ static jdnOffset: 1724221;
26
+ constructor(date: DateStr | DateObj | Date);
27
+ static isLeapYear(year: number): boolean;
28
+ get isLeapYear(): boolean;
29
+ get isValid(): boolean;
30
+ toJDN(): number;
31
+ toGregorian(): {
32
+ year: number;
33
+ month: number;
34
+ day: number;
35
+ };
36
+ static today(): EthiopianCalendar;
37
+ static epoch(): EthiopianCalendar;
38
+ static fromJDN(jdn: number): EthiopianCalendar;
39
+ }
40
+ declare class GregorianCalendar extends BaseCalendar {
41
+ static type: "gregorian";
42
+ static jdnOffset: 1721426;
43
+ constructor(date: DateStr | DateObj | Date);
44
+ static isLeapYear(year: number): boolean;
45
+ get isLeapYear(): boolean;
46
+ get isValid(): boolean;
47
+ toJDN(): number;
48
+ toEthiopian(): EthiopianCalendar;
49
+ static today(): GregorianCalendar;
50
+ static epoch(): GregorianCalendar;
51
+ static fromJDN(jdn: number): {
52
+ year: number;
53
+ month: number;
54
+ day: number;
55
+ };
56
+ }
57
+ export { CalendarError, EthiopianCalendar, GregorianCalendar };
15
58
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,UAAU,EAIhB,MAAM,YAAY,CAAC;AAEpB,QAAA,MAAM,cAAc,UAAY,CAAC;AACjC,QAAA,MAAM,eAAe,UAAY,CAAC;AAElC,iBAAS,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAU1C;AAED,iBAAS,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAmBzC;AAED,iBAAS,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAmB3C;AAED,iBAAS,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb,CAeA;AAED,iBAAS,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,CAK/C;AAED,iBAAS,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,CAK/C;AAED,OAAO,EACL,cAAc,EACd,eAAe,EACf,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,SAAS,EACT,SAAS,EACT,SAAS,EACT,KAAK,UAAU,GAChB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,KAAK,OAAO,GACR,GAAG,MAAM,IAAI,MAAM,IAAI,MAAM,EAAE,GAC/B,GAAG,MAAM,IAAI,MAAM,IAAI,MAAM,EAAE,CAAC;AAEpC,KAAK,OAAO,GAAG;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,cAAM,aAAc,SAAQ,KAAK;IAC/B,YAAY,OAAO,EAAE,MAAM,EAE1B;CACF;AAED,cAAM,YAAY;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IAEZ,YAAY,IAAI,EAAE,OAAO,GAAG,OAAO,EAYlC;IAED,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAatC;IAED,MAAM,CAAC,MAAM;QAGT,IAAI;QACJ,KAAK;QACL,GAAG;MAEN;IAED,QAAQ,WAIP;CACF;AAED,cAAM,iBAAkB,SAAQ,YAAY;IAC1C,MAAM,CAAC,IAAI,EAAG,WAAW,CAAU;IACnC,MAAM,CAAC,SAAS,EAAG,OAAS,CAAU;IAEtC,YAAY,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,IAAI,EAUzC;IAED,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,WAE7B;IACD,IAAI,UAAU,YAEb;IAED,IAAI,OAAO,YAUV;IAED,KAAK,WAUJ;IAED,WAAW;cAmGH,MAAM;eACL,MAAM;aACR,MAAM;MAnGZ;IAED,MAAM,CAAC,KAAK,sBAGX;IAED,MAAM,CAAC,KAAK,sBAEX;IAED,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,qBAmBzB;CACF;AAED,cAAM,iBAAkB,SAAQ,YAAY;IAC1C,MAAM,CAAC,IAAI,EAAG,WAAW,CAAU;IACnC,MAAM,CAAC,SAAS,EAAG,OAAS,CAAU;IAEtC,YAAY,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,IAAI,EAazC;IAED,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,WAE7B;IAED,IAAI,UAAU,YAEb;IAED,IAAI,OAAO,IAAI,OAAO,CAMrB;IAED,KAAK,WAcJ;IAED,WAAW,sBAEV;IAED,MAAM,CAAC,KAAK,sBAEX;IAED,MAAM,CAAC,KAAK,sBAEX;IAED,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG;QAC3B,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,GAAG,EAAE,MAAM,CAAC;KACb,CAeA;CACF;AAED,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,CAAC"}
package/dist/index.js CHANGED
@@ -1,64 +1,174 @@
1
- import { toDateString, isValidEthDate, isValidGregDate, } from "./utils.js";
2
- const ETH_JDN_OFFSET = 1_724_221;
3
- const GREG_JDN_OFFSET = 1_721_426;
4
- function ethToJDN(date) {
5
- const { year, month, day } = date;
6
- if (!isValidEthDate(date))
7
- throw new Error(`The date ${toDateString(date)} is invalid!`);
8
- const leapDays = Math.floor((year - 1) / 4);
9
- const daysInYear = 30 * (month - 1) + (day - 1);
10
- return ETH_JDN_OFFSET + 365 * (year - 1) + leapDays + daysInYear;
11
- }
12
- function jdnToEth(jdn) {
13
- const ethDays = jdn - ETH_JDN_OFFSET;
14
- const cycles = Math.floor(ethDays / 1461);
15
- const daysRemaining = ethDays % 1461;
16
- let yearsInCycle = Math.floor(daysRemaining / 365);
17
- let daysInYear = daysRemaining % 365;
18
- if (daysRemaining === 1460) {
19
- yearsInCycle = 3;
20
- daysInYear = 365;
21
- }
22
- const year = cycles * 4 + yearsInCycle + 1;
23
- const month = Math.floor(daysInYear / 30) + 1;
24
- const day = (daysInYear % 30) + 1;
25
- return { year, month, day };
26
- }
27
- function gregToJDN(date) {
28
- const { year, month, day } = date;
29
- if (!isValidGregDate(date))
30
- throw new Error(`The date ${toDateString(date)} is invalid!`);
31
- const a = Math.floor((14 - month) / 12);
32
- const y = year + 4800 - a;
33
- const m = month + 12 * a - 3;
34
- return (day +
35
- Math.floor((153 * m + 2) / 5) +
36
- 365 * y +
37
- Math.floor(y / 4) -
38
- Math.floor(y / 100) +
39
- Math.floor(y / 400) -
40
- 32045);
1
+ class CalendarError extends Error {
2
+ constructor(message) {
3
+ super(message);
4
+ }
41
5
  }
42
- function jdnToGreg(jdn) {
43
- const a = jdn + 32044;
44
- const b = Math.floor((4 * a + 3) / 146097);
45
- const c = a - Math.floor((146097 * b) / 4);
46
- const d = Math.floor((4 * c + 3) / 1461);
47
- const e = c - Math.floor((1461 * d) / 4);
48
- const m = Math.floor((5 * e + 2) / 153);
49
- const day = e - Math.floor((153 * m + 2) / 5) + 1;
50
- const month = m + 3 - 12 * Math.floor(m / 10);
51
- const year = 100 * b + d - 4800 + Math.floor(m / 10);
52
- return { year, month, day };
6
+ class BaseCalendar {
7
+ year;
8
+ month;
9
+ day;
10
+ constructor(date) {
11
+ if (typeof date === "string") {
12
+ date = this._parseDateString(date);
13
+ }
14
+ this.year = date.year;
15
+ this.month = date.month;
16
+ this.day = date.day;
17
+ // WARNING: Negative years may fail
18
+ if (this.year < 1) {
19
+ console.warn("Proeleptic (negative) years are not supported yet!");
20
+ }
21
+ }
22
+ _parseDateString(str) {
23
+ const separator = str.includes("/") ? "/" : "-";
24
+ const pattern = new RegExp(`^\\d+${separator}\\d+${separator}\\d+$`);
25
+ if (!str.match(pattern))
26
+ throw new CalendarError(`${str} doesn't seem to be a valid date string! \n` +
27
+ `Date String must be of a form yy-mm-dd or yy/mm/dd.`);
28
+ const [year, month, day] = str.split("-").map((item) => Number(item));
29
+ return { year, month, day };
30
+ }
31
+ static _today() {
32
+ const today = new Date();
33
+ return {
34
+ year: today.getFullYear(),
35
+ month: today.getMonth() + 1,
36
+ day: today.getDate(),
37
+ };
38
+ }
39
+ toString() {
40
+ const month = this.month.toString().padStart(2, "0");
41
+ const day = this.day.toString().padStart(2, "0");
42
+ return `${this.year}-${month}-${day}`;
43
+ }
53
44
  }
54
- function gregToEth(date) {
55
- if (!isValidGregDate(date))
56
- throw new Error(`The date ${toDateString(date)} is invalid!`);
57
- return jdnToEth(gregToJDN(date));
45
+ class EthiopianCalendar extends BaseCalendar {
46
+ static type = "ethiopian";
47
+ static jdnOffset = 1_724_221;
48
+ constructor(date) {
49
+ if (date instanceof Date) {
50
+ super(new GregorianCalendar(date).toEthiopian());
51
+ }
52
+ else {
53
+ super(date);
54
+ }
55
+ // Validate input
56
+ if (!this.isValid)
57
+ throw new CalendarError(`The date ${this.toString()} is invalid!`);
58
+ }
59
+ static isLeapYear(year) {
60
+ return year % 4 === 0;
61
+ }
62
+ get isLeapYear() {
63
+ return EthiopianCalendar.isLeapYear(this.year);
64
+ }
65
+ get isValid() {
66
+ if (this.day < 1 || this.day > 30)
67
+ return false;
68
+ if (this.month < 1 || this.month > 13)
69
+ return false;
70
+ if (this.month === 13) {
71
+ if (this.isLeapYear)
72
+ return this.day <= 6;
73
+ return this.day <= 5;
74
+ }
75
+ return true;
76
+ }
77
+ toJDN() {
78
+ const leapDays = Math.floor((this.year - 1) / 4);
79
+ const daysInYear = 30 * (this.month - 1) + (this.day - 1);
80
+ return (EthiopianCalendar.jdnOffset +
81
+ 365 * (this.year - 1) +
82
+ leapDays +
83
+ daysInYear);
84
+ }
85
+ toGregorian() {
86
+ return GregorianCalendar.fromJDN(this.toJDN());
87
+ }
88
+ static today() {
89
+ const gregorian = new GregorianCalendar(BaseCalendar._today());
90
+ return EthiopianCalendar.fromJDN(gregorian.toJDN());
91
+ }
92
+ static epoch() {
93
+ return new EthiopianCalendar("1-1-1");
94
+ }
95
+ static fromJDN(jdn) {
96
+ const ethDays = jdn - EthiopianCalendar.jdnOffset;
97
+ const cycles = Math.floor(ethDays / 1461);
98
+ const daysRemaining = ethDays % 1461;
99
+ let yearsInCycle = Math.floor(daysRemaining / 365);
100
+ let daysInYear = daysRemaining % 365;
101
+ if (daysRemaining === 1460) {
102
+ yearsInCycle = 3;
103
+ daysInYear = 365;
104
+ }
105
+ const year = cycles * 4 + yearsInCycle + 1;
106
+ const month = Math.floor(daysInYear / 30) + 1;
107
+ const day = (daysInYear % 30) + 1;
108
+ return new EthiopianCalendar({ year, month, day });
109
+ }
58
110
  }
59
- function ethToGreg(date) {
60
- if (!isValidEthDate(date))
61
- throw new Error(`The date ${toDateString(date)} is invalid!`);
62
- return jdnToGreg(ethToJDN(date));
111
+ class GregorianCalendar extends BaseCalendar {
112
+ static type = "gregorian";
113
+ static jdnOffset = 1_721_426;
114
+ constructor(date) {
115
+ if (date instanceof Date) {
116
+ date = {
117
+ year: date.getFullYear(),
118
+ month: date.getMonth() + 1,
119
+ day: date.getDate(),
120
+ };
121
+ }
122
+ super(date);
123
+ // Validate input
124
+ if (!this.isValid)
125
+ throw new CalendarError(`The date ${this.toString()} is invalid!`);
126
+ }
127
+ static isLeapYear(year) {
128
+ return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
129
+ }
130
+ get isLeapYear() {
131
+ return GregorianCalendar.isLeapYear(this.year);
132
+ }
133
+ get isValid() {
134
+ if (this.month < 1 || this.month > 12)
135
+ return false;
136
+ const feb = this.isLeapYear ? 29 : 28;
137
+ const daysInMonth = [31, feb, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
138
+ return this.day >= 1 && this.day <= daysInMonth[this.month - 1];
139
+ }
140
+ toJDN() {
141
+ const a = Math.floor((14 - this.month) / 12);
142
+ const y = this.year + 4800 - a;
143
+ const m = this.month + 12 * a - 3;
144
+ return (this.day +
145
+ Math.floor((153 * m + 2) / 5) +
146
+ 365 * y +
147
+ Math.floor(y / 4) -
148
+ Math.floor(y / 100) +
149
+ Math.floor(y / 400) -
150
+ 32045);
151
+ }
152
+ toEthiopian() {
153
+ return EthiopianCalendar.fromJDN(this.toJDN());
154
+ }
155
+ static today() {
156
+ return new GregorianCalendar(BaseCalendar._today());
157
+ }
158
+ static epoch() {
159
+ return new GregorianCalendar("1-1-1");
160
+ }
161
+ static fromJDN(jdn) {
162
+ const a = jdn + 32044;
163
+ const b = Math.floor((4 * a + 3) / 146097);
164
+ const c = a - Math.floor((146097 * b) / 4);
165
+ const d = Math.floor((4 * c + 3) / 1461);
166
+ const e = c - Math.floor((1461 * d) / 4);
167
+ const m = Math.floor((5 * e + 2) / 153);
168
+ const day = e - Math.floor((153 * m + 2) / 5) + 1;
169
+ const month = m + 3 - 12 * Math.floor(m / 10);
170
+ const year = 100 * b + d - 4800 + Math.floor(m / 10);
171
+ return new GregorianCalendar({ year, month, day });
172
+ }
63
173
  }
64
- export { ETH_JDN_OFFSET, GREG_JDN_OFFSET, ethToJDN, jdnToEth, ethToGreg, gregToJDN, jdnToGreg, gregToEth, };
174
+ export { CalendarError, EthiopianCalendar, GregorianCalendar };
package/package.json CHANGED
@@ -1,14 +1,10 @@
1
1
  {
2
2
  "name": "@kalcodes/ethiopian-calendar",
3
- "version": "1.1.0",
3
+ "version": "2.0.0",
4
4
  "description": "Simple ethiopian ⟵⟶ gregorian calendar converter.",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
7
7
  "author": "kalcodes",
8
- "exports": {
9
- ".": "./dist/index.js",
10
- "./utils": "./dist/utils.js"
11
- },
12
8
  "repository": {
13
9
  "type": "git",
14
10
  "url": "https://github.com/kalcodes/ethiopian-calendar.git"
@@ -26,13 +22,6 @@
26
22
  "kalcodes"
27
23
  ],
28
24
  "license": "ISC",
29
- "devEngines": {
30
- "packageManager": {
31
- "name": "pnpm",
32
- "version": "^11.9.0",
33
- "onFail": "download"
34
- }
35
- },
36
25
  "devDependencies": {
37
26
  "@types/node": "^26.2.0",
38
27
  "typescript": "^7.0.2"
package/dist/utils.d.ts DELETED
@@ -1,14 +0,0 @@
1
- type Separator = "-" | "/";
2
- type SimpleDate = {
3
- year: number;
4
- month: number;
5
- day: number;
6
- };
7
- declare function isEthLeapYear(year: number): boolean;
8
- declare function isGregLeapYear(year: number): boolean;
9
- declare function isValidEthDate(date: SimpleDate): boolean;
10
- declare function isValidGregDate(date: SimpleDate): boolean;
11
- declare function toDateString(date: SimpleDate, separator?: Separator): string;
12
- declare function toDateObject(dateString: string, separator?: Separator): SimpleDate;
13
- export { type SimpleDate, type Separator, toDateObject, toDateString, isEthLeapYear, isGregLeapYear, isValidEthDate, isValidGregDate, };
14
- //# sourceMappingURL=utils.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,KAAK,SAAS,GAAG,GAAG,GAAG,GAAG,CAAC;AAC3B,KAAK,UAAU,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/D,iBAAS,aAAa,CAAC,IAAI,EAAE,MAAM,WAElC;AAED,iBAAS,cAAc,CAAC,IAAI,EAAE,MAAM,WAEnC;AAED,iBAAS,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAYjD;AAED,iBAAS,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAQlD;AAED,iBAAS,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,SAAS,GAAE,SAAe,UAGjE;AAED,iBAAS,YAAY,CACnB,UAAU,EAAE,MAAM,EAClB,SAAS,GAAE,SAAe,GACzB,UAAU,CAcZ;AAED,OAAO,EACL,KAAK,UAAU,EACf,KAAK,SAAS,EACd,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,cAAc,EACd,cAAc,EACd,eAAe,GAChB,CAAC"}
package/dist/utils.js DELETED
@@ -1,42 +0,0 @@
1
- function isEthLeapYear(year) {
2
- return year % 4 === 0;
3
- }
4
- function isGregLeapYear(year) {
5
- return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
6
- }
7
- function isValidEthDate(date) {
8
- const { year, month, day } = date;
9
- if (day < 1 || day > 30)
10
- return false;
11
- if (month < 1 || month > 13)
12
- return false;
13
- if (month === 13) {
14
- if (isEthLeapYear(year))
15
- return day <= 6;
16
- return day <= 5;
17
- }
18
- return true;
19
- }
20
- function isValidGregDate(date) {
21
- const { year, month, day } = date;
22
- if (month < 1 || month > 12)
23
- return false;
24
- const feb = isGregLeapYear(year) ? 29 : 28;
25
- const daysInMonth = [31, feb, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
26
- return day <= daysInMonth[month - 1];
27
- }
28
- function toDateString(date, separator = "-") {
29
- const { year, month, day } = date;
30
- return `${year}${separator}${month}${separator}${day}`;
31
- }
32
- function toDateObject(dateString, separator = "-") {
33
- const pattern = new RegExp(`^\d+${separator}\d+${separator}\d+$`);
34
- if (!dateString.match(pattern))
35
- throw new Error(`${dateString} doesn't seem to be a valid date string! \n` +
36
- `Date String should year-month-day or year/month/day.`);
37
- const [year, month, day] = dateString
38
- .split(separator)
39
- .map((item) => Number(item));
40
- return { year, month, day };
41
- }
42
- export { toDateObject, toDateString, isEthLeapYear, isGregLeapYear, isValidEthDate, isValidGregDate, };