# -*- encoding: utf-8; indent-tabs-mode: nil -*-

=begin pod

=head1 Designer's Notes

=head2 Preliminary

It is better to work with actual examples rather than generic values.
So in the text below, I will use the following dates (Bastille day this
year):

  Sunday 14 July 2019
  11 Tammuz 5779
  11 Dhu al-Qada 1440
  Sextdi 26 Messidor 227
  Abib 7, 1735
  Hamle 7, 2011
  13.0.6.11.16, 8 Cib, 4 Xul

=head2 Purposes

The various calendar module should obey the following contradictory
purposes:

=item A user can use as many or as few calendar as he wants. He can
use none or all of them.

=item Limit code duplication. Prefer DRY (Don't Repeat Yourself) over WET
(Write Everything Twice).

=item No alteration to the core modules, including C<Date>.

=head2 First Idea: Roles Everywhere

My first idea was to make intensive  use of the novelty in Raku Object
Oriented   Programming,  roles.   Only  the   Gregorian  calendar   is
implemented  as a  class C<Date>,  all  others are  roles to  C<Date>.
Example:

  my Date $bastille-day does Date::Calendar::Hebrew
                        does Date::Calendar::Hijri
                        does Date::Calendar::FrenchRevolutionary
                        does Date::Calendar::Maya
            .= new(year => 2019, month => 7, day => 14);

The first problem is the conflict between the methods of the various roles.
If we use

  say $bastille-day.month-name

should we get C<Tammuz>, C<Dhu al-Qada> or C<Messidor>?
Granted, after reading pages 226 and following of I<Learning Perl 6>,
you can write;

  say $bastille-day.month-name( Date::Calendar::FrenchRevolutionary );
  # → Messidor
  say $bastille-day.month-name( Date::Calendar::Hebrew );
  # → Tammuz

but it is cumbersome.

The second problem is that the concept of date is not the same across different
civilizations. The Gregorian and French Revolutionary calendars use midnight-to-midnight
dates, while the Hijri and Hebrew calendars (and others) use sunset-to-sunset dates.
The Julian Day Number calendar (not to be confused with the Julian calendar) uses
noon-to-noon dates.
And it is possible that some other calendars use sunrise-to-sunrise dates. So, while
you can equate 14 July 2019 to 26 Messidor 227, you cannot equate it to 11 Tammuz
5779. Usually, date conversion programs take the lazy path and mention in their documentation that
the correspondance 14 July 2019 → 11 Tammuz 5779 is valid only until sunset,
but we can imagine a hubristic programmer who would use two conversion routines
or add a C<time-of-day> parameter to the conversion routine to get either

  14 July 2019 → 11 Tammuz 5779

or 

  14 July 2019 → 12 Tammuz 5779

With the Hebrew calendar as a role, that would not be possible, with the Hebrew
calendar as a class, that would be possible. There are still a few problems,
but at least we are not painted out in a corner.

I found another reason when writing the French Revolutionary module.
While there are reasons to allow negative years (or BC years) for the
Julian and Gregorian calendars (even when considering that the Gregorian
calendar took effect on 15 October 1582), there is no reason to take into
account dates before the epochs of the other calendars. For example, 
the French Revolutionary calendar's epoch is 22 September 1792, so
the 21 September 1792 object should not be allowed to use the
FrenchRevolutionary role's methods.

So the solution is: one calendar = one class.

=head2 Taking a look at C<Date>

I did not bother to deal with times, so I will look at the 
C<Date> core module instead of C<DateTime>.

=head3 Day-Only Counting

The first difference with Perl  5's C<DateTime> is that Raku's C<Date>
uses the  MJD or Modified  Julian Day  Number instead of  I<Rata Die>.
Fine with me.

=head3 Immutability

A second difference with Perl 5's C<DateTime>
is that dates are immutable. Why not? It does not bother me.
Therefore dates in a C<Date::Calendar::>R<Whatever> module will be
immutable too (but see below for multilingual distributions).

=head3 Localization

Another difference with Perl 5 is that there is no provision for
localized string values: month names and the like. Maybe it was
thought that the main purpose of this module would be to manage
schedules, timetables and the like, for which we need the numerical
values of the days and months but not their localized string values.

For those interested in localized values, there is the CPAN6 module
C<Date::Names>.

=head2 Simple Calendars

As a simple calendar example, I will consider the Hebrew Calendar.
In this discussion, simplicity is about the module architecture,
not the conversion algorithms. So, with this point of view, the
Hebrew calendar, with no variants, is a simple calendar.

=head3 Calendar with one Locale

In my opinion, calendar modules will be used to display dates, not to
build schedules with them. I will keep the names in a separate module,
but this module will be within the distribution. For example, the
C<Date::Calendar::Hebrew> distribution will include both the
C<Date::Calendar::Hebrew> module and the
C<Date::Calendar::Hebrew::Names> module.

So we will have:

  class    Date::Calendar::Hebrew
  routines Date::Calendar::Hebrew::Names

=head3 Calendar with Several Locales

If there  are different  languages and  different localizations  for a
given calendar, I may put the mechanisms  and all the data in the same
C<Date::Calendar::>R<Whatever>C<::Names>  module,  or I  may  separate
them with  the mechanisms  in C<Date::Calendar::>R<Whatever>C<::Names>
and    the   data    in   C<Date::Calendar::>R<Whatever>C<::Names::en>
C<Date::Calendar::>R<Whatever>C<::Names::fr>,
C<Date::Calendar::>R<Whatever>C<::Names::it>   and  others.   It  will
depend on the size.

In the case of the Hebrew calendar and the Hebrew, Yiddish and Aramaic
languages, we would have:

  class    Date::Calendar::Hebrew
  routines Date::Calendar::Hebrew::Names
  routines Date::Calendar::Hebrew::Names::he
  routines Date::Calendar::Hebrew::Names::yi
  routines Date::Calendar::Hebrew::Names::arc

(Sorry, there is no 2-char ISO 639 code for Aramaic, so I have to fall
back to the 3-char code.)

Another     point    with     multi-localization    is     that    the
C<Date::Calendar::>R<Whatever> objects will have a C<locale> attribute
and  this  attribute will  be  read-write.  The  year, month  and  day
attributes will still be readonly,  but the locale will be read-write.
So the object  is not fully immutable. Using  the French Revolutionary
calendar as an example, changing "Sextidi  26 Messidor 227, jour de la
sauge" into "Sixday 26 Reapidor 227,  day of sage" is just a skin-deep
cosmetic modification,  while changing "Sextidi 26  Messidor 227, jour
de la sauge" into  "Septidi 27 Messidor 227, jour de  l'ail" is a deep
modification of the date's inner self.

=head3 Another Multilingual Calendar, The Julian Calendar

The structure for  the Julian calendar is very simple.  A first module
for the C<Date::Calendar::Julian> class and a second one for the month
names and  the day names.  Of course, for  this second module,  I have
chosen an  already existing  one, Tom Browder's  C<Date::Names> rather
than rewriting everything. The only  glitch, when compared with what I
wrote above, is that C<Date::Names> has, partially, an object-oriented
interface. For  abbreviations, it is  a procedural interface,  but for
the  month  names  and  the  day   names  we  need  to  instantiate  a
C<Date::Names> object.

I could have  created a C<month-name> method and  a C<day-name> method
which would have created on-the-fly  an instance of C<Date::Names> and
which would discard it just  after, letting the garbage collector reap
it. But usually, the module user asks  both the month name and the day
name,  so  this  solution  is  not  optimal,  because  there  are  two
instantiated C<Date::Names> objects where one would be enough.

I  could  have instantiated  a  C<Date::Names>  object each  time  the
C<locale> attribute is  updated. I have not done this  for two reasons
of dubious value. Firstly, I do not know how to trigger some code when
an object attribute  is updated. Secondly, if the  module user updates
the C<locale>  attribute several times in  a row without asking  for a
month name or  a day name, several C<Date::Names>  instances will have
been created and all of them except  the last one are useless. But why
would a programmer do this?

So  I  have decided  to  do  lazy  instantiation. When  the  C<locale>
attribute  is  updated,   the  C<Date::Calendar::Julian>  object  does
nothing special. Each time the program asks  for a month name or a day
name, the  C<Date::Calendar::Julian> object  first checks  whether the
C<Date::Names> class was instantiated  and whether it was instantiated
with  the   proper  value.   If  no,   the  object   instantiates  the
C<Date::Names> class and caches the new object instance into a private
attribute. It also stores the  current value of C<locale> into another
private attribute, C<$!instantiated-locale>. Example

  code               $.locale  $!inst-l  $!date-names
  $.locale = 'en'    en        empty     empty
  $.locale = 'fr'    fr        empty     empty
  $.locale = 'de'    de        empty     empty
  say $.month-name   de        empty→de  Date::Names::de.new
  say $.day-name     de        de        Date::Names::de
  $.locale = 'it'    it        de        Date::Names::de
  $.locale = 'en'    en        de        Date::Names::de
  say $.day-name     en        de→en     Date::Names::en.new
  say $.month-name   en        en        Date::Names::en

Since this  lazy instantiation method is  well in the Raku's  style, I
have not tried  to come back to the previous  solution of intercepting
updates of C<$.locale> and instantiating C<Date::Names>.

=head2 Calendars with variants

=head3 French Revolutionary Calendar

There are two ways of computing the change of a year to the
next in the  French Revolutionary Calendar: either the first day
of the year is the autumn equinox, or the length of each year is 365
or 366 and computed with an algorithm similar to the Gregorian
algorithm. Historically, the  French Revolutionary Calendar began
with the astronomical rule and a reform to adopt the arithmetic rule
was scheduled for year XX (Gregorian year 1811).

So we can define three variants.

=item the astronomical variant, where the first day is always on
the autumn equinox, even after year XX,

=item the arithmetic variant, where the year length is computed
with Gregorian-like moduloes, event before year XX,

=item and the historical variant, which takes into account
the year XX reform and the change of rule.

How does this translate in Raku?

A first idea is to define three different functions or methods to
translate an other date into French Revolutionary Calendar
and also three  different functions or methods to
translate an French Revolutionary date into another calendar.

This is a bad idea. What happens  if the other calendar has also three
variants,  like  the Maya  calendar?  Should  we define  9  functions/
methods for each direction?

A  second  idea is  defining  only  one  function  or method  in  each
direction, using a parameter giving which  variant to use. In the case
of  the French  Revolutionary →  Maya  conversion, we  would have  two
parameters,  the  C<from_variant>   parameter  and  the  C<to_variant>
parameter.  These parameters  would be  optional  and, in  the cas  of
variantless calendars, ignored.

This does not  work either. Suppose we want to  translate 22 September
1795 to French  Revolutionary using the astronomical  variant and then
back to Gregorian  using the arithmetic variant. The  first step would
result in "sextidi 6 sans-culottide  III" (sixth additional day, which
means  that this  is a  leap  year), and  the second  step would  fail
because according to  the arithmetic rule, year III is  a normal year,
with only 5 additional days.

A third possibility is adding a  C<variant> attribute to the  French Revolutionary Calendar
class to keep track of the variant used when creating the date object.
In this cas, the "here and back again" conversion will no longer be able
to use two different variants. It will just add some code in the module
to dispatch between variants of the "French Revolutionary Calendar to
other" conversion functions. Not a problem to the module user, just some
more code to write for the module author / maintainer (me).

A fourth  possibility consists  in storing this  information not  in a
attribute,  but directly  in the  object's  metadata, I  mean, in  the
class. In other words, I define three different classes for the French
Revolutionary Calendar, one  per variant. With that, the  burden is no
longer  on  the  module  writer  / maintainer,  but  on  the  dispatch
mechanism  coded in  the Perl  6 interpreter.  Yet, the  three classes
would contain much duplicated code.  To prevent code duplication, this
code will  be written  in a role  used by the  three classes.  Thus we
have:

=item1 Three classes

=item2  Date::Calendar::FrenchRevolutionary
=item2  Date::Calendar::FrenchRevolutionary::Astronomical
=item2  Date::Calendar::FrenchRevolutionary::Arithmetic

=item1 One role

=item2  Date::Calendar::FrenchRevolutionary::Common

=item1 One procedural module

=item2  Date::Calendar::FrenchRevolutionary::Names

This is the possibility I have adopted.

Note that the shortest name is given to the main variant, which will
certainly be the most often used variant.

Actually, there is even a fifth possibility, which merges possibilities 3 and
4. Each one of the three classes has a C<algorithm> method which returns
a constant result, which will be  C<"historic">, C<"astronomical">
or C<"arithmetic"> depending on the class. If I think it can be useful,
I will switch to this possibility.

=head3 Coptic and Ethiopic Calendars

The Coptic and Ethiopic calendars are very simple calendars, sharing a
common structure:

=item1 12 months of 30 days each
=item1 followed by 5 or 6 additional days, called I<epagomenal days>.

Leap years are years preceding a multiple of 4, or equal to 4 × n + 3,
no matter if they are near the end of a century or not.

There are two differences between these calendars:

=item1 Obviously, months and days have different names.

=item1 The conversion  with other calendars is not the  same. In other
words, the Gregorian date for Coptic 0001-01-01 is not the same as the
Gregorian date for Ethiopic 0001-01-01.

But there  are still  many common points,  which suggest  code sharing
between the two modules. So we will have:

=item1 Two classes

=item2  Date::Calendar::Coptic
=item2  Date::Calendar::Ethiopic

=item1 One role

=item2  Date::Calendar::CopticEthiopic (no need to add C<"::Common">)

=item1 Two procedural modules

=item2  Date::Calendar::Coptic::Names
=item2  Date::Calendar::Ethiopic::Names

The only remaining point is the  name of the distribution. There is no
reason  to call  it after  the Coptic  calendar while  belittleing the
Ethiopic calendar.  And vice-versa. So  the distribution name  will be
C<Date::Calendar::CopticEthiopic>.

=head3 Maya Calendar and Aztec Calendar

The case of  the Maya Calendar is similar to  the French Revolutionary
calendar, because there  are several ways to convert  dates between it
and the Gregorian calendar. It is also similar to the Coptic calendar,
because it shares  many mechanisms with the Aztec  calendar, just like
the Coptic calendar shares many mechanisms with the Ethiopic calendar.

Pedants  will  argue that  there  is  not  I<one> Maya  calendar,  but
I<three>:

=item the  long count, using  embedded cycles  of length 20  (with one
exception)  and   usually  displayed   in  a  dotted   notation,  e.g.
13.0.6.11.16

=item the clerical calendar, or Tzolkin,  which combines a cycle of 13
numbers with a cycle of 20 names, e.g. 8 Cib

=item the  common calendar,  or Haab, consisting  of 18  named months,
each with 20 numbered days, plus 5 additional days. E.g. 4 Xul

So the various date converters  deal with the "Maya calendars system",
with an  "s" to  "calendars", but to  streamline the  explanations, we
will talk about the Maya calendar (with no "s").

It  is the  same, only  simpler, with  the Aztec  calendar. This  is a
two-calendar system,  with a  clerical calendar,  or I<tonalpohualli>,
similar to Tzolkin, and a  common calendar, or I<xiupohualli>, similar
to Haab. Aztecs had nothing ressembling the Mayas' long count.

For the conversion from Gregorian (or other) to Aztec, there are three
opinions  from  the specialists:  the  Alfonso  Caso method,  with  or
without  H.B. Nicholson's  adjustment, or  Francisco Cortes's  method.
Here is a table showing how these methods work and differ.

  Gregorian        Caso               Caso          Francisco
             without Nicholson    with Nicholson      Cortes
  2019-09-26   17 Tititl          17 Tititl         20 Tititl
  2019-09-27   18 Tititl          18 Tititl          1 Nemontemi
  2019-09-28   19 Tititl          19 Tititl          2 Nemontemi
  2019-09-29   20 Tititl          20 Tititl          3 Nemontemi
  2019-09-30    1 Nemontemi        1 Izcalli         4 Nemontemi
  2019-10-01    2 Nemontemi        2 Izcalli         5 Nemontemi
  2019-10-02    3 Nemontemi        3 Izcalli         1 Izcalli
  2019-10-03    4 Nemontemi        4 Izcalli         2 Izcalli
  2019-10-04    5 Nemontemi        5 Izcalli         3 Izcalli
  2019-10-05    1 Izcalli          6 Izcalli         4 Izcalli
  ...
  2019-10-19   15 Izcalli         20 Izcalli        18 Izcalli
  2019-10-20   16 Izcalli          1 Nemontemi      19 Izcalli
  2019-10-21   17 Izcalli          2 Nemontemi      20 Izcalli
  2019-10-22   18 Izcalli          3 Nemontemi       1 Atlcahualo
  2019-10-23   19 Izcalli          4 Nemontemi       2 Atlcahualo
  2019-10-24   20 Izcalli          5 Nemontemi       3 Atlcahualo
  2019-10-25    1 Atlcahualo       1 Atlcahualo      4 Atlcahualo

A small reminder  about the French Revolutionary,  Coptic and Ethiopic
calendars:  the year  is  incremented after  the  last additional  day
(sans-culottide and epagomenal). So the month following the additional
days is  the first month of  the year and  is given the number  1. The
month just before these additional days  is the last month of the year
and receive the number 12.

Now,  in the  Aztec calendar,  which is  the first  month of  the year
Izcalli or Atlcahualo? And the last month, is it Tititl or Izcalli? So
I have decided to ignore  the "Caso with Nicholson adjustment" method,
Izcalli will always be month number  1 and Tititl will always be month
number 18. By  the way, Reingold and Dershowitz too  ignore the method
with  Nicholson's adjustment.  They  also ignore  Cortes' method,  but
there I differ from them.

How do  we correlate  the Maya  calendar to  the others?  Reingold and
Dershowitz  describe  two  rules, the  Goodman-Martinez-Thompson  rule
(which  they  implement) and  the  Spinden  rule  (which they  do  not
implement). Abigail's module C<Date::Maya>  gives three unnamed rules,
but we  recognize the  Goodman-Martinez-Thompson rule and  the Spinden
rule. We  find the same three  unnamed rules in Claus  Tøndering's FAQ
(L<https://www.tondering.dk/claus/cal/maya.php>).  The  FAMSI  website
(L<http://research.famsi.org/date_mayaLC.php>) suggest eight different
rules. The default rule is the GMT  rule, which has nothing to do with
the     Greenwich      Mean     Time,     but      which     represent
Goodman-Martinez-Thompson.

At first,  I will implement the  three rules suggested by  Abigail and
Claus Tøndering.  Later, it  will still  be possible  to add  the five
remaining rules mentionned by the FAMSI. So we will have

=item1 Five classes

=item2  Date::Calendar::Maya
implementing the Goodman-Martinez-Thompson rule

=item2  Date::Calendar::Maya::Spinden
implementing the Spinden rule

=item2  Date::Calendar::Maya::Astronomical
implementing the rule with the same name

=item2  Date::Calendar::Aztec
implementing the Alfonso Caso rule

=item2  Date::Calendar::Aztec::Cortes
implementing the Francisco Cortes rule

=item1 Three roles

=item2  Date::Calendar::MayaAztec
(no need to add "::Common"), implementing the civil and clerical calendars

=item2  Date::Calendar::Maya::Common
implementing the long count and giving the aliases I<Haab> and I<Tzolkin> to the civil and clerical calendars

=item2  Date::Calendar::Aztec::Common
giving the aliases I<xiupohualli> and I<tonalpohualli>  to the civil and clerical calendars

=item1 Two procedural modules

=item2  Date::Calendar::Maya::Names
=item2  Date::Calendar::Aztec::Names

=head1 In Hindsight

After having  implemented the calendars  mentioned above, I  find that
the architecture I have designed is a correct one. Not necessarily the
best one,  but good enough.  I am still  not sure about  splitting the
C<Calendar::>R<xxx>C<::Names>  modules  from  the  corresponding  main
class C<Calendar::>R<xxx>, but it works.

But there  are things  I had  not foreseen, or  for which  my original
intention would have led to a bad design.

The first point is the C<strftime> method. At first, I thought I would
have to write different, yet  similar, methods for each calendar, thus
trampling the  DRY principle. Then  I realised  that I could  write an
independent  role  with  just  this  method,  released  and  installed
separately from  the calendar classes,  and the various  classes would
just invoke  this role. All  I needed was to  define some kind  of API
with  common  names for  the  calendar  class  methods and  a  precise
structure for a dispatch table.

The other point I discovered while writing my modules is that it would
be a good idea to write a  Gregorian calendar module, even if there is
already  the  core class  C<Date>.  Since  the  beginning, I  had  the
intention  to make  my  conversions methods  compatible with  C<Date>,
hence the "push  style" and the "pull style". On  the other hand, even
after     defining    C<strftime>     as    an     independant    role
C<Date::Calendar::Strftime>,  I did  not  bother  to make  C<strftime>
compatible with  C<Date>. I  still made some  checks and  I discovered
that  by  writing a  few  additional  lines,  a programmer  could  use
C<strftime> with standard dates and get at least the numerical values.
The alphabetic values (month names and day names) were still lacking.

If we want a complete support of C<strftime> in C<Date>, we must write
some  additional code,  especially a  C<month-name> and  a C<day-name>
methods, plus the management of  a new C<locale> attribute. Instead of
requesting  every user  of  my  C<Date::Calendar::Strftime> module  to
write  this additional  code, I  realised that  I could  write it  and
release it as  a new class C<Date::Calendar::Gregorian>.  Of course, I
would follow  the DRY  principle and  my class  would inherit  all the
stuff of C<Date>. I had only to add conversion methods, and getters to
give the month names and the day  names. Oh, and also tweak the C<new>
methods  to allow  a C<locale>  parameter which  would initialize  the
homonymous attribute. Well,  no, it was not  necessary. Fortunately, I
wrote the  tests before changing  the module.  I wrote the  tests, ran
them, and  I was  astonished (and  glad) to  see absolutely  no errors
where I expected a whole truckload of them. So calling a C<new> method
from  the class  C<Date>  with  a C<locale>  parameter  would have  no
problems  at  all with  storing  this  parameter into  the  homonymous
atribute, without having to change a  line in the parent C<Date> class
nor in the child C<Date::Calendar::Gregorian> class.

=head1 License

This  text is  licensed  under  the terms  of  Creative Commons,  with
attribution and share-alike (CC-BY-SA).

=end pod
