-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Update Hijri date calculation from anchor
- Loading branch information
Showing
14 changed files
with
384 additions
and
686 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
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
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,2 +1,3 @@ | ||
export * from './api'; | ||
export * from './misc'; | ||
export * from './timing'; |
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 |
---|---|---|
@@ -0,0 +1 @@ | ||
export type Nullable<T> = T | null | undefined; |
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 |
---|---|---|
@@ -0,0 +1,136 @@ | ||
import type { HijriDateAnchor } from '$lib/types'; | ||
import { describe, expect, test } from 'bun:test'; | ||
import { getHijriDateFromAnchor, getHijriMonthFromNumber as getMonth } from './dates'; | ||
|
||
describe('Dates utils tests', () => { | ||
describe('getHijriDateFromAnchor', () => { | ||
function getAnchor({ month = 0, day = 15, gregorianDate = '2024-01-01' } = {}): HijriDateAnchor { | ||
return { hijriDate: { day, month: getMonth(month), year: 1446 }, gregorianDate }; | ||
} | ||
|
||
test('Null anchor', () => { | ||
expect(getHijriDateFromAnchor(null, '2024-01-01')).toBeNil(); | ||
}); | ||
|
||
test('End of gregorian year', () => { | ||
const anchor = getAnchor({ gregorianDate: '2024-12-31' }); | ||
const hijriDate = getHijriDateFromAnchor(anchor, '2025-01-05'); | ||
|
||
expect(hijriDate).not.toBeNil(); | ||
expect(hijriDate?.day).toEqual(anchor.hijriDate.day + 5); | ||
}); | ||
|
||
describe('Invalid dates', () => { | ||
test('Current day is in the past', () => { | ||
const anchor = getAnchor(); | ||
const hijriDate = getHijriDateFromAnchor(anchor, '1990-01-01'); | ||
|
||
expect(hijriDate).toBeNil(); | ||
}); | ||
|
||
const invalidDateValues = ['not-a-date', 'not a date', 123, null, undefined]; | ||
|
||
test('Invalid saved gregorian date', () => { | ||
const targetDate = '2024-01-10'; | ||
|
||
invalidDateValues.forEach((value) => { | ||
const anchor = getAnchor(); | ||
|
||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore | ||
anchor.gregorianDate = value; | ||
expect(getHijriDateFromAnchor(anchor, targetDate)).toBeNil(); | ||
}); | ||
}); | ||
|
||
test('Invalid current gregorian date', () => { | ||
const anchor = getAnchor(); | ||
|
||
invalidDateValues.forEach((value) => { | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore | ||
expect(getHijriDateFromAnchor(anchor, value)).toBeNil(); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('Middle of hijri month', () => { | ||
test('To same day', () => { | ||
const anchor = getAnchor(); | ||
const hijriDate = getHijriDateFromAnchor(anchor, anchor.gregorianDate); | ||
|
||
expect(hijriDate).toEqual(anchor.hijriDate); | ||
}); | ||
|
||
test('To middle of same month', () => { | ||
const anchor = getAnchor(); | ||
const hijriDate = getHijriDateFromAnchor(anchor, '2024-01-11'); | ||
|
||
expect(hijriDate).toEqual({ ...anchor.hijriDate, day: anchor.hijriDate.day + 10 }); | ||
}); | ||
|
||
test('To end of same month', () => { | ||
const anchor = getAnchor(); | ||
const hijriDate = getHijriDateFromAnchor(anchor, '2024-01-16'); | ||
|
||
expect(hijriDate).toBeNil(); | ||
}); | ||
|
||
test('To next month', () => { | ||
const anchor = getAnchor(); | ||
const hijriDate = getHijriDateFromAnchor(anchor, '2024-02-01'); | ||
|
||
expect(hijriDate).toBeNil(); | ||
}); | ||
|
||
test('months jump', () => { | ||
const anchor = getAnchor(); | ||
const hijriDate = getHijriDateFromAnchor(anchor, '2024-06-01'); | ||
|
||
expect(hijriDate).toBeNil(); | ||
}); | ||
}); | ||
|
||
describe('End of hijri month', () => { | ||
test('To same day', () => { | ||
const anchor = getAnchor({ day: 30 }); | ||
const hijriDate = getHijriDateFromAnchor(anchor, anchor.gregorianDate); | ||
|
||
expect(hijriDate).toEqual(anchor.hijriDate); | ||
}); | ||
|
||
test('To next month', () => { | ||
const anchor = getAnchor({ day: 30 }); | ||
const hijriDate = getHijriDateFromAnchor(anchor, '2024-01-15'); | ||
|
||
expect(hijriDate).toEqual({ ...anchor.hijriDate, month: getMonth(1), day: 14 }); | ||
}); | ||
|
||
test('To end of next month', () => { | ||
const anchor = getAnchor({ day: 30 }); | ||
|
||
// Up until 29th is known | ||
expect(getHijriDateFromAnchor(anchor, '2024-01-30')).toEqual({ | ||
...anchor.hijriDate, | ||
month: getMonth(1), | ||
day: 29, | ||
}); | ||
|
||
// 30 is unknown | ||
expect(getHijriDateFromAnchor(anchor, '2024-01-31')).toBeNil(); | ||
}); | ||
|
||
test('To months jump', () => { | ||
const anchor = getAnchor({ day: 30 }); | ||
expect(getHijriDateFromAnchor(anchor, '2024-06-01')).toBeNil(); | ||
}); | ||
|
||
test('On last day of year', () => { | ||
const anchor = getAnchor({ day: 30, month: 11 }); | ||
const hijriDate = getHijriDateFromAnchor(anchor, '2024-01-15'); | ||
|
||
expect(hijriDate).toEqual({ day: 14, month: getMonth(0), year: anchor.hijriDate.year + 1 }); | ||
}); | ||
}); | ||
}); | ||
}); |
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,11 +1,62 @@ | ||
import { HIJRI_MONTHS } from '$lib/constants'; | ||
import type { HijriDate, HijriDateAnchor, Nullable } from '$lib/types'; | ||
import { DateTime, Interval } from 'luxon'; | ||
import { assert } from './polyfills'; | ||
|
||
export const DAY_MS = 24 * 60 * 60 * 1000; | ||
|
||
export function getDaysDiff(d1: Date, d2: Date): number { | ||
const diffTime = Math.abs(d2.valueOf() - d1.valueOf()); | ||
return Math.ceil(diffTime / (24 * 60 * 60 * 1000)); | ||
} | ||
|
||
export function getDayOfYear(date: Date) { | ||
const diff = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()) - Date.UTC(date.getFullYear(), 0, 0); | ||
return diff / DAY_MS; | ||
export function getHijriMonthNumber(month: string): number { | ||
assert(HIJRI_MONTHS.includes(month), `Invalid Hijri month "${month}"`); | ||
return HIJRI_MONTHS.indexOf(month); | ||
} | ||
|
||
export const DAY_MS = 24 * 60 * 60 * 1000; | ||
export function getHijriMonthFromNumber(order: number): string { | ||
assert(Number.isInteger(order) && order < 12 && order >= 0, `Invalid month number "${order}"`); | ||
return HIJRI_MONTHS[order]; | ||
} | ||
|
||
export function getHijriDateFromAnchor( | ||
anchor: Nullable<HijriDateAnchor>, | ||
currentGregorianIsoDate: string, | ||
): Nullable<HijriDate> { | ||
if (anchor == null) return null; | ||
const { hijriDate, gregorianDate: savedGregorianIsoDate } = JSON.parse(JSON.stringify(anchor)) as HijriDateAnchor; | ||
|
||
// Assert valid hijri date | ||
assert(Number.isInteger(hijriDate.year), `Invalid hijri year ${hijriDate.year}`); | ||
assert(HIJRI_MONTHS.includes(hijriDate.month), `Invalid hijri month ${hijriDate.month}`); | ||
assert( | ||
Number.isInteger(hijriDate.day) && hijriDate.day <= 30 && hijriDate.day > 0, | ||
`Invalid hijri day ${hijriDate.day}`, | ||
); | ||
|
||
const savedGregorianDate = DateTime.fromISO(savedGregorianIsoDate).startOf('day'); | ||
const currentGregorianDate = DateTime.fromISO(currentGregorianIsoDate).startOf('day'); | ||
if (!savedGregorianDate.isValid || !currentGregorianDate.isValid) return null; | ||
|
||
const interval = Interval.fromDateTimes(savedGregorianDate, currentGregorianDate); | ||
if (!interval.isValid) return null; | ||
|
||
const daysDiff = interval.length('days'); | ||
|
||
if (hijriDate.day == 30 && daysDiff > 0) { | ||
hijriDate.day = daysDiff; | ||
|
||
let finalMonthOrder = getHijriMonthNumber(hijriDate.month) + 1; | ||
if (finalMonthOrder == 12) { | ||
finalMonthOrder = 0; | ||
hijriDate.year++; | ||
} | ||
hijriDate.month = getHijriMonthFromNumber(finalMonthOrder); | ||
} else { | ||
hijriDate.day += daysDiff; | ||
} | ||
|
||
if (daysDiff == 0 || hijriDate.day < 30) return hijriDate; | ||
return null; | ||
} |
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,3 +1,4 @@ | ||
export * from './dates'; | ||
export * from './objects'; | ||
export * from './polyfills'; | ||
export * from './timings'; |
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 |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import { browser } from '$app/environment'; | ||
import serverAssert from 'node:assert/strict'; | ||
|
||
export const assert = browser ? console.assert : serverAssert; |
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
Oops, something went wrong.