Skip to content

Commit aeabb8d

Browse files
authored
feat: add method parseEnglishDate for English date parsing (#95)
1 parent 6cf9d14 commit aeabb8d

File tree

8 files changed

+361
-22
lines changed

8 files changed

+361
-22
lines changed

README.md

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,23 @@ import NepaliDate from 'nepali-datetime'
1717

1818
// Create a NepaliDate object for the current date and time
1919
const now = new NepaliDate()
20-
console.log(now.toString()) // Outputs: "2080-03-23 15:32:03.643"
20+
console.log(now.toString()) // 2080-03-23 15:32:03.643
2121

2222
// Create a NepaliDate object from a Nepali date string
2323
const date1 = new NepaliDate('2079-02-15 23:11')
24-
console.log(date1.toString()) // Outputs: "2079-02-15 23:11:00"
24+
console.log(date1.toString()) // 2079-02-15 23:11:00
2525

2626
// Parse Nepali date string
2727
const date2 = new NepaliDate('Baisakh 18, 2080', 'MMMM D, YYYY')
28-
console.log(date1.toString()) // Outputs: "2080-01-18 00:00:00"
28+
console.log(date2.toString()) // 2080-01-18 00:00:00
2929

3030
// Format a NepaliDate object
3131
const formattedDate = now.format('YYYY-MM-DD')
32-
console.log(formattedDate) // Outputs: "2080-03-23"
32+
console.log(formattedDate) // 2080-03-23
3333

34-
// Create a NepaliDate object from an English date
35-
const date3 = NepaliDate.fromEnglishDate(2023, 6, 8)
36-
console.log(englishDate.toString()) // Outputs: "2080-03-23 00:00:00"
34+
// Create a NepaliDate object from an English date string
35+
const date3 = NepaliDate.parseEnglishDate('2023-07-08', 'YYYY-MM-DD')
36+
console.log(date3.toString()) // 2080-03-23 00:00:00
3737
```
3838

3939
## Installation
@@ -143,9 +143,9 @@ Additionally, you can convert the corresponding English date to a string using t
143143
- `formatEnglishDateInNepali(formatStr)`: Returns a string representation in the Nepali (Devanagari script) of the English Date in the specified format.
144144

145145
```javascript
146-
const now = new NepaliDate(2079, 5, 3, 16, 14)
147-
console.log(now.format('YYYY-MM-DD hh:mm A')) // Outputs: 2079-06-03 04:14 PM
148-
console.log(now.formatEnglishDate('YYYY-MM-DD hh:mm A')) // Outputs: 2022-08-19 04:14 PM
146+
const date = new NepaliDate(2079, 5, 3, 16, 14)
147+
console.log(date.format('YYYY-MM-DD hh:mm A')) // 2079-06-03 04:14 PM
148+
console.log(date.formatEnglishDate('YYYY-MM-DD hh:mm A')) // 2022-09-19 04:14 PM
149149
```
150150

151151
The date formatting will follow the format codes mentioned below, which are similar to the date formats used in day.js.
@@ -205,19 +205,24 @@ console.log(now.getDateObject()) // Date 2022-09-18T18:15:00.000Z
205205

206206
#### Creating a NepaliDate object from an English date
207207

208-
You can create a `NepaliDate` object from an English calendar date using the `fromEnglishDate` method.
208+
You can create a `NepaliDate` object from an English calendar date using the `parseEnglishDate` or `fromEnglishDate` method.
209209

210210
```javascript
211-
const date = NepaliDate.fromEnglishDate(2023, 6, 8)
212-
console.log(date.toString()) // Outputs: "2080-03-23 00:00:00"
211+
const date1 = NepaliDate.parseEnglishDate('2023-07-08', 'YYYY-MM-DD')
212+
console.log(date1.toString()) // 2080-03-23 00:00:00
213+
214+
const date2 = NepaliDate.fromEnglishDate(2023, 6, 8, 10, 15)
215+
console.log(date2.toString()) // 2080-03-23 10:15:00
213216
```
214217

215218
### dateConverter
216219

217-
The `dateConverter` module provides functions for converting dates between the Nepali and English calendars.
220+
The `dateConverter` module provides core functions for converting dates between the Nepali and English calendars.
221+
222+
- `englishToNepali(year, month, day)`: Converts an English calendar date to a Nepali calendar date. Returns an array `[npYear, npMonth, npDay]` representing the Nepali date.
223+
- `nepaliToEnglish(year, month, day)`: Converts a Nepali calendar date to an English calendar date. Returns an array `[enYear, enYear, enDay]` representing the English date.
218224

219-
- `englishToNepali(year, month, day)`: Converts an English calendar date to a Nepali calendar date. Returns an array `[yearNp, monthNp, dayNp]` representing the Nepali date.
220-
- `nepaliToEnglish(year, month, day)`: Converts a Nepali calendar date to an English calendar date. Returns an array `[yearEn, monthEn, dayEn]` representing the English date.
225+
> Note: Use 0 as the value for the months Baisakh and January (Javascript Logic 🤷).
221226

222227
```javascript
223228
import dateConverter from 'nepali-datetime/dateConverter'
@@ -229,6 +234,26 @@ const [npYear, npMonth, npDay] = dateConverter.englishToNepali(2023, 5, 27)
229234
const [enYear, enMonth, enDay] = dateConverter.nepaliToEnglish(2080, 2, 15)
230235
```
231236

237+
#### Quick Date conversion using NepaliDate
238+
239+
The `NepaliDate` class can also be used for direct string-to-string date conversions, eliminating the need for custom parsing or formatting logic.
240+
241+
**English Date to Nepali Date**
242+
243+
```javascript
244+
const enDate = '2024-11-25'
245+
const npDate = NepaliDate.parseEnglishDate(enDate, 'YYYY-MM-DD').format('YYYY-MM-DD')
246+
// 2081-08-10
247+
```
248+
249+
**Nepali Date to English Date**
250+
251+
```javascript
252+
const npDate = '2081-08-10'
253+
const enDate = new NepaliDate(npDate).formatEnglishDate('YYYY-MM-DD')
254+
// 2024-11-25
255+
```
256+
232257
## Acknowledgements
233258

234259
This project was inspired by [nepali-date](https://github.com/sharingapples/nepali-date). We would like to express our gratitude to their team for their excellent work and ideas, which served as a motivation for this project.

src/NepaliDate.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
nepaliDateToString,
88
} from './format'
99

10-
import { parse, parseFormat } from './parse'
10+
import { parse, parseFormat, parseEnglishDateFormat } from './parse'
1111
import { getDate, getNepalDateAndTime } from './utils'
1212
import { validateTime } from './validators'
1313

@@ -623,6 +623,25 @@ class NepaliDate {
623623
const englishDate = getDate(year, month0, date, hour, minute, second, ms)
624624
return new NepaliDate(englishDate)
625625
}
626+
627+
/**
628+
* Creates a NepaliDate instance by parsing a provided English Date and Time string
629+
* with the given format.
630+
*
631+
* @param dateString - The English Date and time string.
632+
* @param format - The format of the provided date-time string.
633+
* @example
634+
* const dateTimeString = '2024/11/23 14-05-23.789'
635+
* const format = 'YYYY/MM/DD HH-mm-ss.SSS'
636+
* const nepaliDate = NepaliDate.parseEnglishDate(dateTimeString, format)
637+
*/
638+
static parseEnglishDate(dateString: string, format: string): NepaliDate {
639+
const [year, month0, day, hour, minute, second, ms] = parseEnglishDateFormat(
640+
dateString,
641+
format
642+
)
643+
return NepaliDate.fromEnglishDate(year, month0, day, hour, minute, second, ms)
644+
}
626645
}
627646

628647
NepaliDate.minimum = () =>

src/parse/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { parseFormat, parse } from './parse'
2+
export { parseEnglishDateFormat } from './parseEnglishDate'

src/parse.ts renamed to src/parse/parse.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ import {
1717
NEPALI_MONTHS_SHORT_EN,
1818
WEEKDAYS_LONG_EN,
1919
WEEKDAYS_SHORT_EN,
20-
} from './constants'
21-
import { parseFormatTokens, seqToRE } from './utils'
20+
} from '../constants'
21+
import { parseFormatTokens, seqToRE } from '../utils'
2222

2323
/**
2424
* Parses date from the given string.
@@ -200,7 +200,7 @@ function getDateParams(
200200
break
201201
case 'A':
202202
case 'a':
203-
isPM = (match[i + 1] as string).toLowerCase() === 'pm'
203+
isPM = match[i + 1].toLowerCase() === 'pm'
204204
}
205205
}
206206

@@ -222,7 +222,7 @@ function getDateParams(
222222
export function parseFormat(dateString: string, format: string): number[] {
223223
const formatTokens = parseFormatTokens(format)
224224
const { dateTokens, regex: formatRegex } = tokensToRegex(formatTokens)
225-
const match = dateString.match(formatRegex)
225+
const match = RegExp(formatRegex).exec(dateString)
226226
if (!match) {
227227
throw new Error('Invalid date format')
228228
}

src/parse/parseEnglishDate.ts

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import {
2+
ENGLISH_MONTHS_EN,
3+
ENGLISH_MONTHS_SHORT_EN,
4+
WEEKDAYS_LONG_EN,
5+
WEEKDAYS_SHORT_EN,
6+
} from '../constants'
7+
import { parseFormatTokens, seqToRE } from '../utils'
8+
9+
const TOKEN_TO_REGEX: { [key: string]: RegExp } = {
10+
YY: /(\d\d)/,
11+
YYYY: /(\d\d\d\d)/,
12+
M: /(1[0-2]|0[1-9]|[1-9])/,
13+
MM: /(1[0-2]|0[1-9]|[1-9])/,
14+
D: /(3[0-2]|[1-2]\d|0[1-9]|[1-9]| [1-9])/,
15+
DD: /(3[0-2]|[1-2]\d|0[1-9]|[1-9]| [1-9])/,
16+
H: /(2[0-3]|[0-1]\d|\d)/,
17+
HH: /(2[0-3]|[0-1]\d|\d)/,
18+
hh: /(1[0-2]|0[1-9]|[1-9])/,
19+
mm: /([0-5]\d|\d)/,
20+
ss: /([0-5]\d|\d)/,
21+
SSS: /(\d\d\d)/,
22+
A: /(AM|PM)/,
23+
a: /(am|pm)/,
24+
MMMM: seqToRE(ENGLISH_MONTHS_EN),
25+
MMM: seqToRE(ENGLISH_MONTHS_SHORT_EN),
26+
dddd: seqToRE(WEEKDAYS_LONG_EN),
27+
ddd: seqToRE(WEEKDAYS_SHORT_EN),
28+
dd: seqToRE(WEEKDAYS_SHORT_EN),
29+
d: /([0-6])/,
30+
}
31+
32+
function tokensToRegex(arr: string[]): { dateTokens: string[]; regex: RegExp } {
33+
const dateTokens: string[] = []
34+
const regexParts: string[] = []
35+
36+
for (const token of arr) {
37+
if (token in TOKEN_TO_REGEX) {
38+
dateTokens.push(token)
39+
regexParts.push(TOKEN_TO_REGEX[token].source)
40+
} else {
41+
regexParts.push(token.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'))
42+
}
43+
}
44+
45+
const regexString = regexParts.join('')
46+
47+
return {
48+
dateTokens,
49+
regex: new RegExp(`^${regexString}$`),
50+
}
51+
}
52+
53+
function getDateParams(
54+
dateTokens: string[],
55+
match: RegExpMatchArray
56+
): { [key: string]: number } {
57+
// month and day are set to 1 in default
58+
let [year, month, day, hour, hour12, minute, second, ms] = [0, 1, 1, 0, 0, 0, 0, 0]
59+
let isPM = false
60+
let is12hourFormat = false
61+
62+
for (let i = 0; i < dateTokens.length; i++) {
63+
const token = dateTokens[i]
64+
const matchData = parseInt(match[i + 1])
65+
switch (token) {
66+
case 'YYYY':
67+
year = matchData
68+
break
69+
case 'YY':
70+
year = 2000 + parseInt(match[i])
71+
break
72+
case 'MM':
73+
case 'M':
74+
month = matchData
75+
break
76+
case 'MMMM':
77+
month = ENGLISH_MONTHS_EN.indexOf(match[i + 1]) + 1
78+
break
79+
case 'MMM':
80+
month = ENGLISH_MONTHS_SHORT_EN.indexOf(match[i + 1]) + 1
81+
break
82+
case 'DD':
83+
case 'D':
84+
day = matchData
85+
break
86+
case 'HH':
87+
case 'H':
88+
hour = matchData
89+
break
90+
case 'hh':
91+
case 'h':
92+
hour12 = matchData
93+
is12hourFormat = true
94+
break
95+
case 'mm':
96+
case 'm':
97+
minute = matchData
98+
break
99+
case 'ss':
100+
case 's':
101+
second = matchData
102+
break
103+
case 'SSS':
104+
ms = matchData
105+
break
106+
case 'A':
107+
case 'a':
108+
isPM = match[i + 1].toLowerCase() === 'pm'
109+
}
110+
}
111+
112+
if (is12hourFormat) {
113+
hour = hour12 + (isPM ? 12 : 0)
114+
}
115+
116+
return {
117+
year,
118+
month0: month - 1,
119+
day,
120+
hour,
121+
minute,
122+
second,
123+
ms,
124+
}
125+
}
126+
127+
export function parseEnglishDateFormat(dateString: string, format: string): number[] {
128+
const formatTokens = parseFormatTokens(format)
129+
const { dateTokens, regex: formatRegex } = tokensToRegex(formatTokens)
130+
const match = RegExp(formatRegex).exec(dateString)
131+
if (!match) {
132+
throw new Error('Invalid date format')
133+
}
134+
135+
const { year, month0, day, hour, minute, second, ms } = getDateParams(
136+
dateTokens,
137+
match
138+
)
139+
return [year, month0, day, hour, minute, second, ms]
140+
}

tests/NepaliDate.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,28 @@ describe('NepaliDate', () => {
6666
}).toThrow('Date out of range')
6767
})
6868

69+
it('should initialize by parsing English Date string with given format', () => {
70+
const n1 = NepaliDate.parseEnglishDate(
71+
'2024 August 13 14-05-23.789',
72+
'YYYY MMMM DD HH-mm-ss.SSS'
73+
)
74+
expect(n1.toString()).toBe('2081-04-29 14:05:23.789')
75+
})
76+
77+
it("should initialize by parsing English Date's year string with default month, day and time params", () => {
78+
const n1 = NepaliDate.parseEnglishDate('2024', 'YYYY')
79+
expect(n1.toString()).toBe('2080-09-16 00:00:00')
80+
})
81+
82+
it("should throw error if English Date's year component is missed during parsing", () => {
83+
expect(() => {
84+
const _ = NepaliDate.parseEnglishDate(
85+
'08/13 14-05-23.789',
86+
'MM/DD HH-mm-ss.SSS'
87+
)
88+
}).toThrow('Date out of range')
89+
})
90+
6991
it('checks for nepali date validity', () => {
7092
// 373314600000
7193
// Fri Oct 30 1981 18:30:00 GMT+0000
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { parseFormat } from '../src/parse'
1+
import { parseFormat } from '../../src/parse'
22

33
describe('parseFormat', () => {
44
it('should parse date string in valid format correctly', () => {

0 commit comments

Comments
 (0)