Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
Louis-Aime committed Jun 23, 2024
2 parents cb9e697 + 9a441c6 commit bc96537
Show file tree
Hide file tree
Showing 37 changed files with 2,565 additions and 2,141 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ may easily be specified and used.
|Customisable Gregorian calendar with switchover date|0|+|
|French revolutionary calendar|0|+|
|Milesian calendar|0|+|
|Persian calendar (33 years cycle)|+|+|

Tentative new week numbering systems are also possible.
### `timeUnits` object (time-units.js)
Expand Down Expand Up @@ -88,3 +89,4 @@ As defined in calendrical-demo.js, some calendars are instantiated from the clas
* **julian**: the julian calendar. You can display date with ExtDateTimeFormat, using CLDR's names for days, months, eras.
* **gregorian** : you instantiate the *GregorianCalendar* with a user-defined switching date to Gregorian reckoning. The *era* field is used to diffentiate "os" (Old Style, meaning Julian reckoning) from "ns" (New Style, Gregorian reckoning). pldr is required, in order to display these eras in a few languages.
* **frenchRev**: the calendar defined by the French Convention in 1793. pldr is required.
* **persian 33**: the official algorithmic calendar used in Iran and Afghanistan, same effect as Unicode's.
157 changes: 132 additions & 25 deletions calendars.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@
* Passing non numeric values where numbers are expected will yield NaN results.
* Passing non integer values will yield erroneous results. Please control that figures are integer in your application.
* @module
* @version M2022-11-10
* @version M2024-06-31
* @requires module:time-units.js
* @requires module:chronos.js
* @requires module:extdate.js
* @author Louis A. de Fouquières https://github.com/Louis-Aime
* @license MIT 2016-2022
* @license MIT 2016-2024
// Character set is UTF-8
*/
/* Versions: M2022-11-10 Era codes in small letters, Gregorian and proleptic Gregorian calendar renamed.
/* Versions: See Github
*/
/* Copyright Louis A. de Fouquières https://github.com/Louis-Aime 2016-2022
/* Copyright Louis A. de Fouquières https://github.com/Louis-Aime 2016-2024
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
Expand Down Expand Up @@ -92,13 +92,13 @@ export class MilesianCalendar {
milesianWeek = new WeekClock (
{
originWeekday: 4, // Use day part of Posix timestamp, week of day of 1970-01-01 is Thursday
daysInYear: (year) => (Cbcce.isGregorianLeapYear( year + 1 ) ? 366 : 365), // leap year rule for Milesian calendar
daysInYear: (year) => (this.inLeapYear( { year : year } ) ? 366 : 365), // leap year rule for Milesian calendar
characDayIndex: (year) => ( Math.floor(this.counterFromFields({year : year, month : 1, day : 7})/Milliseconds.DAY_UNIT) ),
startOfWeek : 0, // week start with 0
startOfWeek : 0, // week start with 0 (Sunday).
characWeekNumber : 0, // we have a week 0 and the characteristic day for this week is 7 1m.
dayBase : 0, // use 0..6 display for weekday
weekBase : 0, // number of week begins with 0
weekLength : 7 // the Milesian week is the 7-days well-known week
dayBase : 0, // use 0..6 display for weekdays.
weekBase : 0, // number of week begins with 0.
weekLength : 7 // the Milesian week is the 7-days well-known week.
}
)
/* Field control
Expand All @@ -113,7 +113,6 @@ export class MilesianCalendar {
/* Basic conversion methods
*/
fieldsFromCounter (timeStamp) { // year, month, day, from Posix timestamp, UTC
// let TZOffset = TZ == "UTC" ? 0 : new ExtDate("iso8601",timeStamp).getRealTZmsOffset(); // decide not to use TZ here
let fields = this.milesianClockwork.getObject (timeStamp);
fields.fullYear = fields.year;
return fields
Expand All @@ -124,7 +123,6 @@ export class MilesianCalendar {
return this.milesianClockwork.getNumber( myFields )
}
buildDateFromFields (fields, construct = ExtDate) { // Construct an ExtDate object from the date in this calendar (UTC)
// let timeStamp = this.counterFromFields (fields, TZ);
return new construct (this, this.counterFromFields(fields))
}
weekFieldsFromCounter (timeStamp) { // week coordinates : number of week, weekday, last/this/next year, weeks in weekyear
Expand All @@ -145,7 +143,7 @@ export class MilesianCalendar {
*/
eras = null // list of code values for eras. No era in Milesian calendar.
inLeapYear (fields) { // is the Milesian year of this date a Milesian leap year.
return Cbcce.isGregorianLeapYear ( fields.year + 1 )
return Cbcce.isGregorianLeapYear ( this.solveAskedFields(fields).fullYear + 1 )
}
}

Expand All @@ -169,10 +167,10 @@ export class ProlepticGregorianCalendar {
gregorianWeek = new WeekClock (
{
originWeekday : 4, // 1 Jan. 1970 ISO is Thursday
daysInYear : (year) => (Cbcce.isGregorianLeapYear ( year ) ? 366 : 365),
daysInYear : (year) => (this.inLeapYear( { year : year } ) ? 366 : 365),
characDayIndex: (year) => ( Math.floor(this.counterFromFields({fullYear : year, month : 1, day : 4})/Milliseconds.DAY_UNIT) ),
startOfWeek : 1
// the rest of by default
startOfWeek : 1 // ISO week starts with Monday
// the rest by default
}
) // set for gregorian week elements.
solveAskedFields (askedFields) {
Expand Down Expand Up @@ -206,7 +204,7 @@ export class ProlepticGregorianCalendar {
buildDateFromFields (fields, construct = ExtDate) {
let myFields = { fullYear : 0, month : 1, day : 1, hours : 0, minutes : 0, seconds : 0, milliseconds : 0 };
myFields = Object.assign (myFields, this.solveAskedFields(fields));
return new construct (this, ExtDate.fullUTC(fields.fullYear, fields.month, fields.day, fields.hours, fields.minutes, fields.seconds, fields.milliseconds))
return new construct (this, this.counterFromFields(fields))
}
weekFieldsFromCounter (timeStamp) {
let myDate = new ExtDate ("iso8601", timeStamp),
Expand All @@ -222,7 +220,7 @@ export class ProlepticGregorianCalendar {
+ myFields.seconds * Milliseconds.SECOND_UNIT + myFields.milliseconds;
}
inLeapYear (fields) {
return Cbcce.isGregorianLeapYear ( fields.fullYear )
return Cbcce.isGregorianLeapYear ( this.solveAskedFields(fields).fullYear )
}
}

Expand Down Expand Up @@ -266,7 +264,7 @@ export class JulianCalendar {
julianWeek = new WeekClock (
{ // ISO 8601 rule applied to Julian calendar
originWeekday: 4, // Use day part of Posix timestamp, week of day of ISO 1970-01-01 is Thursday
daysInYear: (year) => (Cbcce.isJulianLeapYear( year ) ? 366 : 365), // leap year rule for this calendar
daysInYear: (year) => (this.inLeapYear( { fullYear : year } ) ? 366 : 365), // leap year rule for this calendar
characDayIndex: (year) => ( Math.floor(this.counterFromFields({fullYear : year, month : 1, day : 4})/Milliseconds.DAY_UNIT) ),
startOfWeek : 1, // week start with 1 (Monday)
characWeekNumber : 1, // we have a week 1 and the characteristic day for this week is 4 January.
Expand Down Expand Up @@ -332,8 +330,7 @@ export class JulianCalendar {
return this.julianClockwork.getNumber(this.shiftYearStart(myFields,2,1));
}
buildDateFromFields (fields, construct = ExtDate) { // Construct an ExtDate object from the date in this calendar (deemed UTC)
let timeStamp = this.setCounterFromFields (fields);
return new construct (this, timeStamp)
return new construct (this, this.counterFromFields(fields))
}
weekFieldsFromCounter (timeStamp) { // week fields, from a timestamp deemed UTC
let year = this.fieldsFromCounter (timeStamp).fullYear,
Expand All @@ -350,7 +347,7 @@ export class JulianCalendar {
/* properties and other methods
*/
inLeapYear (fields) { //
return Cbcce.isJulianLeapYear(fields.fullYear)
return Cbcce.isJulianLeapYear(this.solveAskedFields(fields).fullYear)
}
} // end of calendar class

Expand Down Expand Up @@ -434,8 +431,7 @@ export class GregorianCalendar {

}
buildDateFromFields (fields, construct = ExtDate) { // Construct an ExtDate object from the date in this calendar (deemed UTC)
let number = this.setCounterFromFields (fields);
return new construct (this, number)
return new construct (this, this.counterFromFields(fields))
}
weekFieldsFromCounter (timeStamp) {
if (timeStamp < this.switchingDate.valueOf())
Expand Down Expand Up @@ -565,13 +561,124 @@ export class FrenchRevCalendar {
+ myFields.hours * Milliseconds.HOUR_UNIT + myFields.minutes * Milliseconds.MINUTE_UNIT
+ myFields.seconds * Milliseconds.SECOND_UNIT + myFields.milliseconds;
}
buildDateFromFields (fields, construct = ExtDate) { // Construct an ExtDate object from the date in this calendar (UTC)
return new construct (this, this.counterFromFields(fields))
}
inLeapYear (fields) {
let myFields = {...fields};
if (myFields.inSextileYear == undefined) myFields = this.frenchClockWork.getObject (this.frenchClockWork.getNumber (fields));
let myFields = this.solveAskedFields(fields);
if (myFields.inSextileYear == undefined) myFields = this.fieldsFromCounter (this.counterFromFields (myFields));
return myFields.inSextileYear
}
valid (fields) { // enforced at date expressed by those fields
let counter = this.counterFromFields (fields);
return counter >= -5594227200000 && counter < -5175360000000
}
}

/** Generic algorithmic Persian calendar, as recommended by Mohammad Heydari-Malayeri and used by Unicode
* The main point: the intercalation rule is based on a 33 years cycle where 8 years are long.
* This implementation uses the integral postfix intercalation principle,
* hence the technical epoch of the calendar is set to the beginning of year -10 A.P.
* the computation of the day and month figures are done separately.
* There is only one era, coded 'ap'.
* Month names of CLDR are used.
* @class
* @param {string} id - the calendar identifier.
*/
export class Persian33Calendar {
constructor (id) {
this.id = id;
}
/* Basic references
*/
canvas = "persian"
eras = ["ap"]
stringFormat = "built-in" // This calendar is exactly the same as "persian" of Unicode.

persian33Clockwork = new Cbcce (
{ //calendarRule object, used with Posix epoch
timeepoch : -42879024000000, // Unix timestamp of 1 Farvardin -10 (1 4m 611) 00h00 UTC in ms
coeff : [
{cyclelength : 1041379200000, ceiling : Infinity, subCycleShift : 0, multiplier : 33, target : "year"}, // The main 33-years cycle.
{cyclelength : 126230400000, ceiling : 7, subCycleShift : +1, multiplier : 4, target : "year"}, // The cycle. If last of upper cycle, add one year.
{cyclelength : 31536000000, ceiling : 3, subCycleShift : 0, multiplier : 1, target : "year", notify : "inLongYear"}, // The year, grouped by 4 (3+1).
{cyclelength : 16070400000, ceiling : Infinity, subCycleShift : 0, multiplier : 1, target : "semester"}, // First semester of 6 x 31 month.
{cyclelength : 86400000, ceiling : Infinity, subCycleShift : 0, multiplier : 1, target : "semday"},
{cyclelength : 3600000, ceiling : Infinity, subCycleShift : 0, multiplier : 1, target : "hours"},
{cyclelength : 60000, ceiling : Infinity, subCycleShift : 0, multiplier : 1, target : "minutes"},
{cyclelength : 1000, ceiling : Infinity, subCycleShift : 0, multiplier : 1, target : "seconds"},
{cyclelength : 1, ceiling : Infinity, subCycleShift : 0, multiplier : 1, target : "milliseconds"}
],
canvas : [
{name : "year", init : -10},
{name : "semester", init : 0},
{name : "semday", init : 0},
{name : "hours", init : 0},
{name : "minutes", init : 0},
{name : "seconds", init : 0},
{name : "milliseconds", init : 0},
]
}) // end of calendarRule
persianWeek = new WeekClock (
{
originWeekday: 4, // Use day part of Posix timestamp, week of day of 1970-01-01 is Thursday
daysInYear: (year) => (this.inLeapYear({ year : year, month : 1, day : 1, hours : 0, minutes : 0, seconds : 0, milliseconds : 0 }) ? 366 : 365),
// leap year rule for this calendar
characDayIndex: (year) => ( Math.floor(this.counterFromFields({year : year, month : 1, day : 4})/Milliseconds.DAY_UNIT) ),
startOfWeek : 7 // week start with Sunday
// The rest by default
}
)
/* Field control
*/
solveAskedFields (askedFields) {
var fields = {...askedFields};
if (fields.year != undefined && fields.fullYear != undefined)
{ if (fields.year != fields.fullYear) throw new TypeError ('Unconsistent year and fullYear fields: ' + fields.year + ', ' + fields.fullYear) }
else { if (fields.year != undefined) { fields.fullYear = fields.year } else if (fields.fullYear != undefined) fields.year = fields.fullYear };
if (fields.month != undefined) {
fields.semester = Math.floor((fields.month - 1)/6);
if (fields.day != undefined) fields.semday = (fields.month < 7 ? (fields.month-1) * 31 : (fields.month-7) * 30) + fields.day - 1;
} else [fields.semester, fields.semday] = [undefined, undefined];
return fields
}
/* Basic conversion methods
*/
fieldsFromCounter (timeStamp) { // year, month, day, from Posix timestamp, UTC
let fields = this.persian33Clockwork.getObject (timeStamp);
fields.fullYear = fields.year;
// Computation of month and day in month
[fields.month, fields.day] = Cbcce.divmod (fields.semday, 31 - fields.semester);
[fields.month, fields.day] = [fields.month + 1 + 6*fields.semester, fields.day+1];
return fields
}
counterFromFields (fields) { // Posix timestamp at UTC, from year, month, day and possibly time
let myFields = { hours : 0, minutes : 0, seconds : 0, milliseconds : 0 };
myFields = Object.assign (myFields, this.solveAskedFields(fields)); // Here semester and day in semester are computed
return this.persian33Clockwork.getNumber( myFields )
}
buildDateFromFields (fields, construct = ExtDate) { // Construct an ExtDate object from the date in this calendar (UTC)
return new construct (this, this.counterFromFields(fields))
}
weekFieldsFromCounter (timeStamp) { // week coordinates : number of week, weekday, last/this/next year, weeks in weekyear
//let characDayFields = this.fieldsFromCounter (timeStamp); characDayFields.month = 1; characDayFields.day = 7;
let fields = this.persian33Clockwork.getObject (timeStamp),
myFigures = this.persianWeek.getWeekFigures(Math.floor(timeStamp/Milliseconds.DAY_UNIT), fields.year);
return {weekYearOffset : myFigures[2], weekYear : fields.year + myFigures[2], weekNumber : myFigures[0], weekday : myFigures[1], weeksInYear : myFigures[3],
hours : fields.hours, minutes : fields.minutes, seconds : fields.seconds, milliseconds : fields.milliseconds}
}
counterFromWeekFields (fields) { // Posix timestamp at UTC, from weekYear, weekNumber, dayOfWeek and time
let myFields = { weekYear : 0, weekNumber : 0, weekday : 0, hours : 0, minutes : 0, seconds : 0, milliseconds : 0 };
myFields = Object.assign (myFields, fields);
return this.persianWeek.getNumberFromWeek (myFields.weekYear, myFields.weekNumber, myFields.weekday) * Milliseconds.DAY_UNIT
+ myFields.hours * Milliseconds.HOUR_UNIT + myFields.minutes * Milliseconds.MINUTE_UNIT
+ myFields.seconds * Milliseconds.SECOND_UNIT + myFields.milliseconds;
}
/* Simple properties and method as inspired by Temporal
*/
inLeapYear (fields) { // is this year a long year.
let myFields = this.solveAskedFields(fields);
if (myFields.inLongYear == undefined) myFields = this.fieldsFromCounter (this.counterFromFields (myFields));
return myFields.inLongYear
}
}
Loading

0 comments on commit bc96537

Please sign in to comment.