-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: expose function to convert calendar dates to iso8601
- Loading branch information
Showing
5 changed files
with
204 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,4 +5,5 @@ export { | |
getNowInCalendar, | ||
validateDateString, | ||
convertFromIso8601, | ||
convertToIso8601, | ||
} from './utils' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,48 +1,122 @@ | ||
import { convertFromIso8601 } from './convert-date' | ||
import { convertFromIso8601, convertToIso8601 } from '../' | ||
|
||
describe('date conversion from gregorian', () => { | ||
describe('to ethiopic', () => { | ||
it('should convert a date', () => { | ||
const result = convertFromIso8601('2024-05-23', 'ethiopic') | ||
expect(result.eraYear).toEqual(2016) | ||
expect(result.year).toEqual(7516) | ||
expect(result.month).toEqual(9) | ||
expect(result.day).toEqual(15) | ||
expect(result).toMatchObject({ | ||
year: 7516, | ||
eraYear: 2016, | ||
month: 9, | ||
day: 15, | ||
}) | ||
}) | ||
it('should convert a date object', () => { | ||
const result = convertFromIso8601( | ||
{ | ||
year: 2024, | ||
month: 5, | ||
day: 23, | ||
}, | ||
'ethiopic' | ||
) | ||
expect(result).toMatchObject({ | ||
year: 7516, | ||
eraYear: 2016, | ||
month: 9, | ||
day: 15, | ||
}) | ||
}) | ||
it('should convert a date if "ethiopian" is passed instad of "ethiopic"', () => { | ||
const result = convertFromIso8601('2024-05-23', 'ethiopian' as any) | ||
expect(result.eraYear).toEqual(2016) | ||
expect(result.year).toEqual(7516) | ||
expect(result.month).toEqual(9) | ||
expect(result.day).toEqual(15) | ||
expect(result).toMatchObject({ | ||
year: 7516, | ||
eraYear: 2016, | ||
month: 9, | ||
day: 15, | ||
}) | ||
}) | ||
}) | ||
describe('to nepali', () => { | ||
it('should convert a date', () => { | ||
const result = convertFromIso8601('2024-05-23', 'nepali') | ||
expect(result.eraYear).toEqual(2081) | ||
expect(result.year).toEqual(2081) | ||
expect(result.month).toEqual(2) | ||
expect(result.day).toEqual(10) | ||
expect(result).toMatchObject({ | ||
eraYear: 2081, | ||
year: 2081, | ||
month: 2, | ||
day: 10, | ||
}) | ||
}) | ||
|
||
it('should convert a date object', () => { | ||
const result = convertFromIso8601( | ||
{ | ||
year: 2024, | ||
month: 5, | ||
day: 23, | ||
}, | ||
'nepali' | ||
) | ||
expect(result).toMatchObject({ | ||
eraYear: 2081, | ||
year: 2081, | ||
month: 2, | ||
day: 10, | ||
}) | ||
}) | ||
}) | ||
it('should convert to islamic date', () => { | ||
const result = convertFromIso8601('2024-05-23', 'islamic') | ||
expect(result.eraYear).toEqual(1445) | ||
expect(result.year).toEqual(1445) | ||
expect(result.month).toEqual(11) | ||
expect(result.day).toEqual(15) | ||
expect(result).toMatchObject({ | ||
eraYear: 1445, | ||
year: 1445, | ||
month: 11, | ||
day: 15, | ||
}) | ||
}) | ||
}) | ||
|
||
// gregorian, ethiopic and nepali | ||
|
||
describe('date conversion from', () => { | ||
describe('date conversion to gregorian', () => { | ||
describe('ethiopic to gregorian', () => { | ||
it('should convert a date', () => {}) | ||
it('should convert a date if "ethiopian" is passed instad of "ethiopic"', () => {}) | ||
it('should convert a date taking care of setting the correct era for ethiopic calendar', () => { | ||
const result = convertToIso8601('2016-09-15', 'ethiopic') | ||
expect(result).toMatchObject({ year: 2024, month: 5, day: 23 }) | ||
}) | ||
it('should convert a date if "ethiopian" is passed instad of "ethiopic"', () => { | ||
const result = convertToIso8601('2016-09-15', 'ethiopian' as any) | ||
expect(result).toMatchObject({ year: 2024, month: 5, day: 23 }) | ||
}) | ||
it('should convert a date object', () => { | ||
const result = convertToIso8601( | ||
{ | ||
year: 2016, | ||
month: 9, | ||
day: 15, | ||
}, | ||
'ethiopic' | ||
) | ||
expect(result).toMatchObject({ year: 2024, month: 5, day: 23 }) | ||
}) | ||
}) | ||
describe('nepali to gregorian', () => { | ||
it('should convert a date', () => {}) | ||
it('should convert a date', () => { | ||
const result = convertToIso8601('2081-02-10', 'nepali') | ||
expect(result).toMatchObject({ year: 2024, month: 5, day: 23 }) | ||
}) | ||
it('should convert a date object', () => { | ||
const result = convertToIso8601( | ||
{ | ||
year: 2081, | ||
month: 2, | ||
day: 10, | ||
}, | ||
'nepali' | ||
) | ||
expect(result).toMatchObject({ year: 2024, month: 5, day: 23 }) | ||
}) | ||
}) | ||
it('islamic to gregorian', () => { | ||
const result = convertToIso8601('1445-11-15', 'islamic') | ||
expect(result).toMatchObject({ year: 2024, month: 5, day: 23 }) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,81 @@ | ||
import { Temporal } from '@js-temporal/polyfill' | ||
import { dhis2CalendarsMap } from '../constants/dhis2CalendarsMap' | ||
import { SupportedCalendar } from '../types' | ||
import { extractDatePartsFromDateString } from './extract-date-parts-from-date-string' | ||
import { getCustomCalendarIfExists } from './helpers' | ||
|
||
type PlainDate = { | ||
year: number | ||
month: number | ||
day: number | ||
// keeping eraYear to be consistent with the default behaviour of Temporal (check method documentation for more info) | ||
eraYear?: number | ||
} | ||
|
||
type ConvertDateFn = ( | ||
date: string | Temporal.PlainDate, | ||
date: string | Temporal.PlainDateLike, | ||
calendar: SupportedCalendar | ||
) => Temporal.PlainDate | ||
) => PlainDate | ||
|
||
/** | ||
* converts from an iso8601 (gregorian) date to a specific calendar | ||
* | ||
* @param date string in the format yyyy-MM-dd | ||
* @param userCalendar the calendar to covert to | ||
* @returns an object representing the date | ||
* | ||
* NOTE: the returned object contains two properties year and eraYear | ||
* to be consistent with the default behaviour of Temporal. This only affects | ||
* ethiopic calendar in practice. When accessing year, consumers should be defensive | ||
* and do: `const yearToUse = result.eraYear ?? result.year` for example. | ||
* | ||
* @see https://github.com/tc39/ecma402/issues/534 for more details | ||
*/ | ||
export const convertFromIso8601: ConvertDateFn = (date, userCalendar) => { | ||
const calendar = getCustomCalendarIfExists( | ||
dhis2CalendarsMap[userCalendar] ?? userCalendar | ||
) as SupportedCalendar | ||
|
||
return Temporal.PlainDate.from(date).withCalendar(calendar) | ||
const { eraYear, year, month, day } = | ||
Temporal.PlainDate.from(date).withCalendar(calendar) | ||
|
||
return { | ||
eraYear, | ||
year, | ||
month, | ||
day, | ||
} | ||
} | ||
|
||
/** | ||
* converts from a specific calendar (i.e. ethiopic or nepali) to iso8601 (gregorian) | ||
* | ||
* @param date calendar date in the format yyyy-MM-dd | ||
* @param userCalendar the calendar to convert from | ||
* @returns an object representing the iso8601 date | ||
*/ | ||
export const convertToIso8601: ConvertDateFn = (date, userCalendar) => { | ||
const calendar = getCustomCalendarIfExists( | ||
dhis2CalendarsMap[userCalendar] ?? userCalendar | ||
) as SupportedCalendar | ||
|
||
const dateParts: Temporal.PlainDateLike = | ||
typeof date === 'string' ? extractDatePartsFromDateString(date) : date | ||
|
||
// this is a workaround for the ethiopic calendar being in a different | ||
// era by default. There is a discussion on Temporal on which should be | ||
// considered the default era. For us, we need to manually set it to era1 | ||
// https://github.com/js-temporal/temporal-polyfill/blob/8fd0dead40de7c31398f4d2d41e145466ca57a16/lib/calendar.ts#L2010 | ||
if (calendar === 'ethiopic') { | ||
dateParts.eraYear = dateParts.year | ||
dateParts.era = 'era1' | ||
delete dateParts.year | ||
} | ||
|
||
dateParts.calendar = calendar | ||
|
||
const { year, month, day } = | ||
Temporal.PlainDate.from(dateParts).withCalendar('iso8601') | ||
|
||
return { year, month, day } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters