Skip to content
Open
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
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,11 @@
"date-chinese": "^2.1.4",
"date-easter": "^1.0.2",
"deepmerge": "^4.2.2",
"jalaali-js": "^1.2.6",
"moment-timezone": "^0.5.39"
"jalaali-js": "^1.2.6"
},
"devDependencies": {
"c8": "^7.12.0",
"date-fns-tz": "^1.3.7",
"dtslint": "^4.2.1",
"eslint": "^8.28.0",
"eslint-config-standard": "^17.0.0",
Expand Down
47 changes: 47 additions & 0 deletions scripts/hijri-calendar/hijri-to-js-date.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// taken from https://stackoverflow.com/questions/71222556/how-to-convert-any-of-the-5-islamic-hijri-calendars-dates-to-any-of-18-world
// watch the temporal ECMAscript proposal which will make much of this obsolete - https://github.com/tc39/proposal-temporal

const formatters = {}
function getFormatter (calendar) {
const locale = 'en-u-ca-' + calendar
let returnFormatter = formatters[locale]
if (returnFormatter) return returnFormatter
const support = ['islamic-umalqura', 'islamic-civil', 'islamic-tbla', 'islamic-rgsa', 'islamic']
if (!support.includes(calendar)) throw new Error(`calendar must be one of '${support.join("', '")}'`)
if (!Intl || typeof Intl.DateTimeFormat !== 'function') throw new Error('Intl.DateTimeFormat is not available in this environment')
try {
returnFormatter = new Intl.DateTimeFormat(locale, { dateStyle: 'short', timeZone: 'UTC' })
} catch (err) {
throw new Error(`Intl.DateTimeFormat threw an error, usually because locale '${locale}' is unsupported`, { cause: err })
}
return (formatters[locale] = returnFormatter)
}
/**********************************************************************************
* @purpose : Converts Islamic (Hijri) Date to a Javascript Date.
* Handles all 5 Islamic Calendar Types.
* Uses the 'JS Calendar Conversion by Target Approximation' Method.
* @warning Uses Intl.DateTimeFormat which is not supported on android. Most polyfills only work with gregorian calendars, in which case this script will not work.
* @author : Mohsen Alyafei (Feb 2022)
* @licence : MIT
* @param {number} year Hijri year
* @param {number} month Hijri month (1 to 12) note: months is standard 1 based
* @param {number} day Hijri day (1 to 29/30)
* @param {('islamic-umalqura'|'islamic-civil'|'islamic-tbla'|'islamic-rgsa'|'islamic')} [fromCalendar] Specifies the the type of input Islamic Calendar. default 'islamic-umalqura'
* @returns A new JavaScript Date at UTC midnight corresponding to the provided Hijri year, month and day
*/
module.exports = function hijriToJSDate (year, month, day, fromCalendar) {
'use strict'
const dFormat = getFormatter(fromCalendar)
let gD = new Date(Date.UTC(2000, 0, 1))
gD.setUTCDate(gD.getUTCDate() + Math.trunc(227022 + (year + (month - 1) / 12 + day / 354) * 354.367))
const gY = gD.getUTCFullYear() - 2000
gD = new Date(Date.UTC(gY, gD.getUTCMonth(), gD.getUTCDate()))
let [iM, iD, iY] = dFormat.format(gD).split('/').map(n => parseInt(n, 10))
gD.setUTCDate(gD.getUTCDate() + Math.trunc(year * 354 + month * 29.53 + day - (iY * 354 + iM * 29.53 + iD * 1) - 2))
for (let i = 0; i < 4; ++i) {
[iM, iD, iY] = dFormat.format(gD).split('/').map(n => parseInt(n, 10))
if (iD === day && iM === month && iY === year) return gD
gD.setUTCDate(gD.getUTCDate() + 1)
}
throw new Error('Invalid ' + fromCalendar + ' date!')
}
42 changes: 19 additions & 23 deletions scripts/hijri-calendar/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/**
* Follw https://github.com/tc39/proposal-temporal/issues/1450 for when this is superseded
* provides a mapping of islamic calendar dates per gregorian year
* the islamic calender year being shorter than the gregorian year the
* start of an islamic month may show up twice with a gregorian date
Expand All @@ -8,49 +9,44 @@
* // month `M` and date `D` and the islamic year `iY`
* { year: {number}, <year>: [[M,D,iY], ... [M,D,iY,M,D,iY]], <year + 1>: {} ... }
* ```
* > Note: The library moment-hijri currently only provides dates between
* > 1356/1/1 (1937-03-14) and 1500/12/30 (2077-11-16) which should be
* > sufficient for our needs
*/

const hijriToJSDate = require('./hijri-to-js-date')
const fs = require('fs')
const path = require('path')
const moment = require('moment-hijri')

// 1 Muharram 1389 is at 1969-03-19 in gregorian year
// to double check start dates for each month of 1389, go to https://hijri.habibur.com/1389/
const START_YEAR = 1389

const filename = path.resolve(__dirname, '../../src/internal/hijri-calendar.js')
const newYear = year => moment(`${year}-01-01 00:00:00`)

const out = {
year: START_YEAR
}
const newYear = year => new Date(`${year}-01-01 00:00:00Z`)

const endYear = newYear(2077).iYear()
const out = new Map([['year', START_YEAR]])
const calendar = 'islamic-umalqura'
const dFormat = new Intl.DateTimeFormat('en-u-ca-' + calendar, { dateStyle: 'short', timeZone: 'UTC' })
const endYear = parseInt(dFormat.format(newYear(2077)).split('/')[2])

for (let iy = START_YEAR; iy <= endYear; iy++) {
for (let im = 1; im <= 12; im++) {
const m = moment(iy + '/' + im + '/1', 'iYYYY/iM/iD')

const iyy = iy - out.year
const d = hijriToJSDate(iy, im, 1, calendar)
const iyy = iy - START_YEAR

const gy = m.year()
const iim = m.iMonth()
const gy = d.getUTCFullYear()
const iim = im - 1

if (!out[gy]) {
out[gy] = []
if (!out.has(gy)) {
out.set(gy, [])
}

const monthDateDiffYear = [m.month(), m.date(), iyy]
const monthDateDiffYear = [d.getUTCMonth(), d.getUTCDate(), iyy]

if (out[gy][iim]) {
out[gy][iim] = out[gy][iim].concat(monthDateDiffYear)
if (out.get(gy)[iim]) {
out.get(gy)[iim] = out.get(gy)[iim].concat(monthDateDiffYear)
} else {
out[gy][iim] = monthDateDiffYear
out.get(gy)[iim] = monthDateDiffYear
}
}
}

const final = '/*eslint-disable*/\nexport const calendar =' + JSON.stringify(out).replace(/"/g, '')
const final = '/*eslint-disable*/\nexport const calendar =' + JSON.stringify(Object.fromEntries(out.entries())).replace(/"/g, '')
fs.writeFileSync(filename, final, 'utf8')
1 change: 0 additions & 1 deletion scripts/hijri-calendar/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,5 @@
"test": "echo \"Error: no test specified\" && exit 1"
},
"devDependencies": {
"moment-hijri": "^2.1.2"
}
}
14 changes: 4 additions & 10 deletions src/Equinox.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { solstice, julian, planetposition } from 'astronomia'
import { vsop87Bearth } from './vsop87Bearth.js'

import moment from 'moment-timezone'
import utcToZonedTime from 'date-fns-tz/utcToZonedTime'
import CalDate from 'caldate'
import CalEvent from './CalEvent.js'

Expand Down Expand Up @@ -43,17 +42,12 @@ export default class Equinox extends CalEvent {
}

const str = new julian.Calendar().fromJDE(jde).toDate().toISOString()
let date
if (/^[+-]\d{2}:\d{2}?$/.test(this._timezone)) { // for '+08:00' formats
date = moment(str).utcOffset(this._timezone)
} else { // for 'Asia/Shanghai' formats
date = moment(str).tz(this._timezone) // move to timezone
}
const date = utcToZonedTime(str, this._timezone)

const floorDate = {
year,
month: date.month() + 1,
day: date.date()
month: date.getMonth() + 1,
day: date.getDate()
}

const d = new CalDate(floorDate).setOffset(this.offset)
Expand Down
Loading