.
Registered on JuliaHub, read the Docs, get the source from the repository or install with Pkg.add("Calendars").
The package Calendars.jl provides a Julia implementation of six calendars and two day numbers:
Enum | Acronym | Calendar |
---|---|---|
(1) | EC | European |
(2) | CE | Common |
(3) | JD | Julian |
(4) | AM | Hebrew |
(5) | AH | Islamic |
(6) | ID | IsoDate |
~ | ~~ | ~~~~~~~ |
(7) | EN | EuroNum |
(8) | JN | JulianNum |
The Common Era CE
dates are computed according to the proleptic Gregorian rules, Julian dates JD
according to the rules of the reformed Roman calendar proposed by Julius Caesar. The European calendar EC
uses the Gregorian calendar for dates on and after CE
1582-10-15 and otherwise the Julian calendar.
We recommend using the European calendar to avoid errors and confusion by extending the Gregorian calendar rules backward to the dates preceding its official introduction in 1582, as the common calendar CE
does. However, if you are not working with dates older than 400 years, this distinction is irrelevant, and the use of CE
brings no disadvantages.
We do not use the acronym AD
("Anno Domini nostri Jesu Christi") but use JD
instead, which stands for the Julian dates. AD is an acronym invented by a 6th-century monk, member of the Roman Curia. Nowadays, the AD
terminology is exclusive to non-Christian people. However, we keep the start of the epoch and allow the use of AD
as an option.
Dates can be converted from one to another. We follow ISO 8601 for calendar date representations: Year, followed by the month, then the day, YYYY-MM-DD
. This order is also used in the signature of the functions. For example, 2022-07-12 represents the 12th of July 2022. We extend this notation by prefixing it with two letters, the acronyms of the names of calendars given in the table above: CE-YYYY-MM-DD
, JD-YYYY-MM-DD
, AM-YYYY-MM-DD
and so on.
Roughly speaking a calendar date CDate
is a pair (CC, YMD) where CC
is a calendar specifier and YMD
is 3-tuple of integers which, as a string, is written YYYY-MM-DD
. However, internally we represent a calendar specifier also as an integer. Therefore a CDate
is implemented as 4-tuple of integers extending the calendar date representation of ISO 8601.
The primary function provided is:
ConvertDate(date::CDate, calendar::DPart, string=false::Bool, show=false::Bool)
It converts a calendar date to the representation of the date in the 'calendar.'
-
The calendar date
CDate
is a tuple (calendar, year, month, day). The parts of the date can be given as a tuple or individually. -
'calendar' is one of "Common", "European", "Julian", "Hebrew", "Islamic", or "IsoDate". Alternatively, you can use the acronyms CE, EC, JD, AM, AH, or ID explained in the table above. "Gregorian" is used synonymously with "Common," and AD can be used synonymously with JD.
-
If the optional parameter 'string' is set to 'true,' the date is returned as a string in the format
CC-YYYY-MM-DD
. Otherwise an integer 4-tuple is returned, where the first item is the calendar specifier as defined in the enumeration in the table at the top of this page. By default the integer representation is returned. -
If the optional parameter 'show' is set to 'true,' both dates are printed. 'show' is 'false' by default.
For example:
julia> ConvertDate("Common", 1756, 1, 27, "Hebrew")
which can also be written
julia> ConvertDate(CE, 1756, 1, 27, AM)
computes from the Common date (1756, 1, 27) the corresponding Hebrew date (5516, 11, 25). If 'show' is 'true' the line
"CE-1756-01-27 -> AM-5516-11-25"
is printed.
Another key function returns a table of the dates of all supported calendars.
CalendarDates(date::CDate, show=false::Bool)
The parameters follow the same conventions as those of ConvertDate
. The date can be given as a tuple, or the components of the date can be given separately. For example:
julia> CalendarDates("Common", 1756, 1, 27, true)
computes a table, which is a tuple of six dates plus the European and the Julian day number. If 'show' is 'true' the date table below will be printed.
The table returned by the last command shows the birthday of Wolfgang Amadeus Mozart. The EC
-date and the CE
-date are the same, as was to be expected.
European EC-1756-01-27
Common CE-1756-01-27
Julian JD-1756-01-16
Hebrew AM-5516-11-25
Islamic AH-1169-04-24
IsoDate ID-1756-05-02
EuroNum EN#0641029
JulianNum JN#2362452
But if you look up the birthday of Leonardo da Vinci, you better use the European calendar EC
and not the Common calendar CE.
This time, the EC
-date and the JD
-date are the same, and this is correct. But the Gregorian calendar misses Leonardo's birthday by nine days! This error occurs because the Gregorian calendar extrapolates the date backward from 1582, ignoring the historical course. (Or because you forgot that Gregory lived 100 years after Leonardo.)
julia> CalendarDates("European", 1452, 4, 15, true)
European EC-1452-04-15
Common CE-1452-04-24
Julian JD-1452-04-15
Hebrew AM-5212-01-26
Islamic AH-0856-03-25
IsoDate ID-1452-17-06
EuroNum EN#0530083
JulianNum JN#2251506
Extrapolating backwards is why the Gregorian calendar is called 'proleptic.' But even that is not correct: proleptic
refers to the future; it would be better to call it preleptic.
In any case, the message is: use the European calendar, not the Common/Gregorian one.
With many functions you can choose which format the return value has: an integer tuple or a string. For example the signature of ConvertDate
is:
ConvertDate(date, calendar, string=false, show=false)
The calls
ConvertDate("Gregorian", 1756, 1, 27, "Hebrew")
ConvertDate("Gregorian", 1756, 1, 27, "Hebrew", true)
return, respectively,
(4, 5516, 11, 25)
"AM-5516-11-25"
Here we see that the specifier for the Hebrew calendar, AM
, is internally encoded as the integer 4. This integer is conventionally defined in the enumeration given in the table at the top of this page.
One can use the functions Calendar
, Year
, Month
, Day
, and Date
to extract the parts of a calendar date.
A broader overview give the utility functions PrintEuropeanMonth(year, month)
and SaveEuropeanMonth(year, month, directory)
. The latter writes the EC-year-month
month for all calendar representations to the given directory in a markdown file. The next example is particularly instructive.
julia> PrintEuropeanMonth(1582, 10)
It shows the month when Pope Gregory severely interfered with Chronos turning the Zodiac wheel: at his behest, Thursday 4 October 1582 was followed by Friday 15 October 1582. To say that the confusion this caused was minor would be the understatement of the millennium. But this break can only be seen in the European calendar; both the Julian and the Gregorian calendars smoothly cross this break as if nothing had happened. (What Gregory's contemporaries saw very differently: they felt cheated of 1/3 of their monthly rent.)
Common | European | Julian | Hebrew | Islamic | IsoDate |
---|---|---|---|---|---|
CE-1582-10-11 | EC-1582-10-01 | JD-1582-10-01 | AM-5343-07-15 | AH-0990-09-13 | ID-1582-41-01 |
CE-1582-10-12 | EC-1582-10-02 | JD-1582-10-02 | AM-5343-07-16 | AH-0990-09-14 | ID-1582-41-02 |
CE-1582-10-13 | EC-1582-10-03 | JD-1582-10-03 | AM-5343-07-17 | AH-0990-09-15 | ID-1582-41-03 |
CE-1582-10-14 | EC-1582-10-04 | JD-1582-10-04 | AM-5343-07-18 | AH-0990-09-16 | ID-1582-41-04 |
CE-1582-10-15 | EC-1582-10-15 | JD-1582-10-05 | AM-5343-07-19 | AH-0990-09-17 | ID-1582-41-05 |
CE-1582-10-16 | EC-1582-10-16 | JD-1582-10-06 | AM-5343-07-20 | AH-0990-09-18 | ID-1582-41-06 |
CE-1582-10-17 | EC-1582-10-17 | JD-1582-10-07 | AM-5343-07-21 | AH-0990-09-19 | ID-1582-41-07 |
The range of validity of the calendars is limited to the range from JD-0001-01-01 to CE-9999-12-31. This choice is by convention but has practical advantages: the four-digit year format of the ISO representation can be met (exception is the Hebrew case, which overflows this format); also, handling dates before the beginning of the Julian epoch is avoided in this way. This means that no year zero appears in our setup apart from the two dates CE-0000-12-30 and 31 (and their ID
equivalents). We include them only for the sake of completeness at the beginning of the calendar.
The first four days of the calendar are
Common | European | Julian | Hebrew | Islamic | IsoDate |
---|---|---|---|---|---|
CE-0000-12-30 | EC-0001-01-01 | JD-0001-01-01 | AM-3761-10-16 | 00-0000-00-00 | ID-0000-52-06 |
CE-0000-12-31 | EC-0001-01-02 | JD-0001-01-02 | AM-3761-10-17 | 00-0000-00-00 | ID-0000-52-07 |
CE-0001-01-01 | EC-0001-01-03 | JD-0001-01-03 | AM-3761-10-18 | 00-0000-00-00 | ID-0001-01-01 |
CE-0001-01-02 | EC-0001-01-04 | JD-0001-01-04 | AM-3761-10-19 | 00-0000-00-00 | ID-0001-01-02 |
We take advantage of the fact that there is no date (0000-00-00) and use this for uniform error handling: the tuple (0, 0, 0) represents the invalid date and is written as the string "00-0000-00-00". In case of invalid input or other errors, this representation is returned. The Islamic dates in the table above show this format because we do not backward extrapolate Islamic dates before JD-0622-07-16 (the start of the Islamic calendar).
The last four days of the calendar are
Common | European | Julian | Hebrew | Islamic | IsoDate |
---|---|---|---|---|---|
CE-9999-12-28 | EC-9999-12-28 | JD-9999-10-16 | AM-13760-08-25 | AH-9666-03-29 | ID-9999-52-02 |
CE-9999-12-29 | EC-9999-12-29 | JD-9999-10-17 | AM-13760-08-26 | AH-9666-03-30 | ID-9999-52-03 |
CE-9999-12-30 | EC-9999-12-30 | JD-9999-10-18 | AM-13760-08-27 | AH-9666-04-01 | ID-9999-52-04 |
CE-9999-12-31 | EC-9999-12-31 | JD-9999-10-19 | AM-13760-08-28 | AH-9666-04-02 | ID-9999-52-05 |
In some cases it is convenient to reduce the size of this table (from which only a section was shown above). That is why we also offer a shorter week-based format.
julia> PrintIsoWeek(1582, 41)
Days of week 41 of the year 1582.
Weekday | European | Hebrew | Islamic |
---|---|---|---|
Monday | 1582-10-01 | 5343-07-15 | 0990-09-13 |
Tuesday | 1582-10-02 | 5343-07-16 | 0990-09-14 |
Wednesday | 1582-10-03 | 5343-07-17 | 0990-09-15 |
Thursday | 1582-10-04 | 5343-07-18 | 0990-09-16 |
Friday | 1582-10-15 | 5343-07-19 | 0990-09-17 |
Saturday | 1582-10-16 | 5343-07-20 | 0990-09-18 |
Sunday | 1582-10-17 | 5343-07-21 | 0990-09-19 |
The package also implements two ordinal dates:
Acronym | Ordinal |
---|---|
EN | European day number |
JN | Julian day number |
An ordinal date is an integer counting the elapsed days since the epoch of some calendar. For example, the first day of the European calendar (EC-0001-01-01) has the European date number 1 (denoted by EN#1) and the Julian date number 1721424 (prefixed by JN#
).
European | EuroNum | JulianNum |
---|---|---|
EC-0001-01-01 | EN# 1 | JN#1721424 |
EC-0001-01-02 | EN# 2 | JN#1721425 |
EC-0001-01-03 | EN# 3 | JN#1721426 |
... | ... | ... |
EC-9999-12-29 | EN#3652059 | JN#5373482 |
EC-9999-12-30 | EN#3652060 | JN#5373483 |
EC-9999-12-31 | EN#3652061 | JN#5373484 |
According to our convention, the last day represented by the calendar is EC-9999-12-31, has the Julian date number 5373484 and the European date number 3652061. Thus the European calendar gives 3652061 days a name. Both counters differ only by an additive constant. The European date number is formally defined as
EN(date) = JN(date) − 1721423.
The European date number is similar to the day number Rata Die defined by R
eingold and D
ershowitz. In fact, RD
differs only by two days from the European date number since RD
is based on the proleptic Gregorian calendar and CE-0001-01-01 = EC-0001-01-03. But we think it is much more natural to start at the beginning of the Julian calendar, JD-0001-01-01 = EC-0001-01-01, and not with a 'proleptic' relation.
The Julian day number
JN
is the count of days since the beginning of the Julian Period in 4713 BCE. There is also a continuous
form of the Julian day number, JC
, containing a fraction that represents a daytime
. This form we do not use. Our counts are pure integers, likewise our algorithms do not use real numbers.
However, we remark that if a day indicated by a common (midnight based) date starts in the continuous form with the JC
N.5 and ends with JC
(N+1).4999..., then the corresponding day number JN
is (N+1), which could be seen as (N+1).0 in terms of JC
, indicating the noon of that day. Conceptually, of course, a counter is a different thing than a point in time.
ConvertOrdinalDate(dnum::DPart, from::String, to::String)
dnum
is an ordinal day number. from
and to
are ordinal date names. Admissible ordinal date names are "EuroNum", "JulianNum", or their acronyms EN and JN.
For instance, to convert a Julian day number to a European day number, write
julia> ConvertOrdinalDate(2440422, JN, EN)
It returns 719000, the European day number of the first crewed moon landing.
A common question is how a calendar fits into another calendar. This question is partially answered by the function ProfileYearAsEuropean
.
ProfileYearAsEuropean(calendar::String, year::DPart, show=false)
This function returns a 4-tuple (YearStart, YearEnd, MonthsInYear, DaysInYear) which show how these items are represented in the European calendar. Note that the Jewish New Year (Rosh HaShanah) begins the evening before the date returned. For the ISO-calendar read 'WeeksInYear' instead of 'MonthsInYear'.
Examples:
julia> ProfileYearAsEuropean(EC, 2022, true)
julia> ProfileYearAsEuropean(CE, 2022, true)
julia> ProfileYearAsEuropean(JD, 2022, true)
julia> ProfileYearAsEuropean(AM, 5783, true)
julia> ProfileYearAsEuropean(AH, 1444, true)
julia> ProfileYearAsEuropean(ID, 2022, true)
These commands return:
EC-2022 -> [EC-2022-01-01, EC-2022-12-31], 12, 365
CE-2022 -> [EC-2022-01-01, EC-2022-12-31], 12, 365
JD-2022 -> [EC-2022-01-14, EC-2023-01-13], 12, 365
AM-5783 -> [EC-2022-09-26, EC-2023-09-15], 12, 355
AH-1444 -> [EC-2022-07-30, EC-2023-07-18], 12, 354
ID-2022 -> [EC-2022-01-03, EC-2023-01-01], 52, 364
The package provides additional functions; read the documentation for this. You might start exploring with a Jupyter notebook. For a quick interactive date conversion you can also use the function IDate()
in the REPL.
Calendars are more tricky to implement than it first appears. It is therefore essential to compare the results with reference implementations. There are many implementations online, we trust few, but we trust this one: IMCCE. If you find any deviation from the data displayed there, please notify us immediately via the issues page.
We use the algorithms by Nachum Dershowitz and Edward M. Reingold, described in 'Calendrical Calculations', Software--Practice & Experience, vol. 20, no. 9 (September, 1990), pp. 899--928.
The picture shows the 'Calendar calculator', owned by Anton Ignaz Joseph Graf von Fugger-Glött, Prince-Provost of Ellwangen, Ostalbkreis, 1765 - Landesmuseum Württemberg, Stuttgart, Germany. The picture is in the public domain.