Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: add 'valid' property and update nepali validation logic #78

Merged
merged 2 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 25 additions & 7 deletions src/custom-calendars/nepaliCalendar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,18 @@ class NepaliCalendar extends Temporal.Calendar {
*
* A custom implementation of these methods is used to convert the calendar-space arguments to the ISO calendar.
*/
dateFromFields(fields: CalendarYMD): Temporal.PlainDate {
const { year, day, month } = _nepaliToIso({
year: fields.year,
month: fields.month,
day: fields.day,
})
dateFromFields(
fields: CalendarYMD,
options: Temporal.AssignmentOptions
): Temporal.PlainDate {
const { year, day, month } = _nepaliToIso(
{
year: fields.year,
month: fields.month,
day: fields.day,
},
options
)
return new Temporal.PlainDate(year, month, day, this)
}
yearMonthFromFields(fields: CalendarYMD): Temporal.PlainYearMonth {
Expand All @@ -96,7 +102,10 @@ const lastSupportedNepaliYear = Number(
supportedNepaliYears[supportedNepaliYears.length - 1]
)

const _nepaliToIso = (fields: { day: number; year: number; month: number }) => {
const _nepaliToIso = (
fields: { day: number; year: number; month: number },
{ overflow }: Temporal.AssignmentOptions = {}
) => {
let { year: nepaliYear } = fields

if (
Expand All @@ -109,6 +118,15 @@ const _nepaliToIso = (fields: { day: number; year: number; month: number }) => {
}
const { month: nepaliMonth, day: nepaliDay = 1 } = fields

if (
overflow === 'reject' &&
(nepaliMonth < 1 ||
nepaliMonth > 12 ||
nepaliDay > NEPALI_CALENDAR_DATA[nepaliYear][nepaliMonth])
) {
throw new Error('Invalid date in Nepali calendar')
}

let gregorianDayOfYear = 0

let monthCounter = nepaliMonth
Expand Down
6 changes: 3 additions & 3 deletions src/utils/validate-date-string.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,20 +312,20 @@ describe('validateDateString (nepali)', () => {
expect(
validateDateString('2080.04.33', { calendar: 'nepali' })
.validationText
).toBe('Day 33 is out of range | 1 <= 33 <= 32.')
).toBe('Invalid date in specified calendar')
})

it('should return an error message when month is out of range', () => {
expect(
validateDateString('2080.13.33', { calendar: 'nepali' })
.validationText
).toBe('Month 13 is out of range | 1 <= 13 <= 12.')
).toBe('Invalid date in specified calendar')
})

it('should return an error message when year is out of supported range', () => {
expect(
validateDateString('2101.04.33', { calendar: 'nepali' })
.validationText
).toBe('Year 2101 is out of range.')
).toBe('Invalid date in specified calendar')
})
})
85 changes: 21 additions & 64 deletions src/utils/validate-date-string.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,9 @@
import i18n from '@dhis2/d2-i18n'
import { Temporal } from '@js-temporal/polyfill'
import { dhis2CalendarsMap } from '../constants/dhis2CalendarsMap'
import { NEPALI_CALENDAR_DATA } from '../custom-calendars/nepaliCalendarData'
import type { SupportedCalendar } from '../types'
import { extractDatePartsFromDateString } from './extract-date-parts-from-date-string'
import { getCustomCalendarIfExists } from './helpers'

type ValidateNepaliDateFn = (
year: number,
month: number,
day: number
) => ValidationResult

const validateNepaliDate: ValidateNepaliDateFn = (year, month, day) => {
const nepaliYearData = NEPALI_CALENDAR_DATA[year]
if (!nepaliYearData) {
return {
error: true,
validationCode: DateValidationResult.INVALID_DATE_IN_CALENDAR,
validationText: i18n.t(`Year {{year}} is out of range.`, { year }),
}
}

if (month < 1 || month > 12) {
return {
error: true,
validationCode: DateValidationResult.INVALID_DATE_IN_CALENDAR,
validationText: i18n.t(
`Month {{month}} is out of range | 1 <= {{month}} <= 12.`,
{ month }
),
}
}

const daysInMonth = nepaliYearData[month]

if (day < 1 || day > daysInMonth) {
return {
error: true,
validationCode: DateValidationResult.INVALID_DATE_IN_CALENDAR,
validationText: i18n.t(
`Day {{day}} is out of range | 1 <= {{day}} <= {{daysInMonth}}.`,
{ day, daysInMonth }
),
}
}

return {
error: false,
warning: false,
}
}
import { getCustomCalendarIfExists, isCustomCalendar } from './helpers'

type ValidationOptions = {
calendar?: SupportedCalendar
Expand All @@ -69,18 +22,21 @@

type ValidationResult =
| {
valid: boolean
error: boolean
warning?: never
validationText: string
validationCode: DateValidationResult
}
| {
valid: boolean
warning: boolean
error?: never
validationText: string
validationCode: DateValidationResult
}
| {
valid: true
error: false
warning: false
validationText: undefined
Expand All @@ -107,13 +63,10 @@
dhis2CalendarsMap[calendar] ?? calendar
)

const validationType = strictValidation
? { error: true }
: { warning: true }

// Will throw if the format of the date is incorrect
if (!dateString) {
return {
valid: false,
error: true,
validationCode: DateValidationResult.WRONG_FORMAT,
validationText: i18n.t(`Date is not given`),
Expand All @@ -129,38 +82,41 @@

try {
dateParts = extractDatePartsFromDateString(dateString, format)
} catch (e: any) {

Check warning on line 85 in src/utils/validate-date-string.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
return {
valid: false,
error: true,
validationCode: DateValidationResult.WRONG_FORMAT,
validationText: e?.message,
}
}
if (resolvedCalendar.toString() === 'nepali') {
// ToDo: double check why nepali can't just be handle with Temporal.PlainDate.from
return validateNepaliDate(
dateParts.year,
dateParts.month,
dateParts.day
)
}

let date: Temporal.PlainDate

// Will throw if the year, month or day is out of range
try {
date = Temporal.PlainDate.from(
{ ...dateParts, calendar: resolvedCalendar },
{ overflow: 'reject' }
)
date = isCustomCalendar(resolvedCalendar)
? Temporal.Calendar.from(resolvedCalendar).dateFromFields(
dateParts,
{ overflow: 'reject' }
) // need to be handled separately for custom calendars
: Temporal.PlainDate.from(
{ ...dateParts, calendar: resolvedCalendar },
{ overflow: 'reject' }
)
} catch (err) {
return {
valid: false,
error: true,
validationCode: DateValidationResult.INVALID_DATE_IN_CALENDAR,
validationText: i18n.t('Invalid date in specified calendar'),
}
}

const validationType = strictValidation
? { error: true, valid: false }
: { warning: true, valid: true }

if (minDateString) {
const minDateParts = extractDatePartsFromDateString(minDateString)
const minDate = Temporal.PlainDate.from({
Expand Down Expand Up @@ -203,6 +159,7 @@
}
}
return {
valid: true,
error: false,
warning: false,
}
Expand Down
Loading