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

=begin pod

=head1 Notes du concepteur

=head2 Préliminaire

Il est préférable de faire référence à des exemples plutôt
que d'utiliser des données génériques. Pour ce faire, j'utiliserai
les dates suivantes (le 14 juillet de cette année) :

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

=head2 Buts

Les modules de calendrier doivent tendre vers ces buts contradictoires :

=item Un utilisateur peut décider de charger autant de modules qu'il
souhaite. Il peut tous les utiliser ou n'utiliser aucun module outre
le module central C<Date>.

=item Limiter autant que possible la duplication de code. Plutôt DRY
(I<Don't Repeat Yourself>, Ne vous répétez pas) que WET
(I<Write Everything Twice>, Tout écrire deux fois).

=item Ne pas toucher aux modules standards, y compris C<Date>.

=head2 Première idée : des rôles partout

Ma première idée était d'utiliser de manière intensive la nouveauté
de Perl 6 en programmation objet, les rôles. Seul le calendrier grégorien
serait implémenté en tant que classe, les autres calendriers seraient
des rôles accolés à la classe C<Date>. Par exemple :

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

Le premier problème est le conflit de nom entre les méthodes des
divers rôles. Si nous codons :

  say $quatorze-juillet.month-name

obtiendrons-nous C<Tammuz>, C<Dhu al-Qada> ou C<Messidor> ?
D'accord, il suffit de lire la page 226 et les suivantes de 
I<Learning Perl 6> et l'on apprend que l'on peut écrire :

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

Mais c'est laborieux.

Le second problème est que le concept de date n'est pas le même
d'une civilisation à l'autre. Le calendrier grégorien et le calendrier
républicain utilisent des dates de minuit à minuit, alors que le calendrier
hébraïque, le calendrier islamique et plusieurs autres utilisent des
dates du coucher du soleil au coucher suivant. Le I<Julian Day Number>
(à ne pas confondre avec le calendrier julien)
se base sur des dates de midi à midi. Et il n'est pas exclu que d'autres
calendriers définissent les dates comme s'étendant du lever du soleil
au lever suivant du soleil. On peut donc assimiler
le 14 juillet 2019 au 26 Messidor 227, mais pas au 11 Tammuz 5579.
Habituellement, les programmes de conversion s'en tirent en écrivant
dans la documentation utilisateur que la conversion 14 juillet 2019
→ 11 Tammuz 5779 est valide avant le coucher du soleil. Mais il pourrait
y avoir un programmeur un peu plus aventureux qui coderait deux routines
de conversion ou qui ajouterait un paramètre « moment de la journée »
à sa routine de conversion pour obtenir soit :

  14 juillet 2019 → 11 Tammuz 5779

soit :

  14 juillet 2019 → 12 Tammuz 5779

En implémentant le calendrier hébraïque en tant que rôle affecté au
calendrier grégorien, cette possibilité n'existerait plus. En implémentant
le calendrier hébraïque en tant que classe séparée, la possibilité
reste ouverte. Il reste des problèmes à résoudre dans ce cas, mais au
moins nous ne sommes pas bloqués.

Pendant que j'écrivais le module de calendrier républicain, j'ai
trouvé un argument supplémentaire. L'utilisation d'années négatives
(ou av J.C.) est justifiée pour le calendrier julien et le calendrier
grégorien (même en considérant que le calendrier grégorien a pris 
effet le 15 octobre 1582), ce n'est pas le cas pour les autres 
calendriers. Ainsi, le calendrier républicain a pour origine des
temps le 22 septembre 1792 et il n'y a alors aucune raison d'autoriser
l'objet 21 septembre 1792 à utiliser les méthodes du rôle
« calendrier républicain ».

La solution est donc : un calendrier = une classe.

=head2 Examen du module standard C<Date>

Je n'avais pas l'intention de traiter les heures (décimales ou 24×60×60)
donc je n'ai pas  regardé C<DateTime>, seulement C<Date>.

=head3 Échelle de temps en jours

La première différence entre le module C<Date> de Perl 6 et le module
C<DateTime> de Perl 5 est que Perl 6 utilise le MJD (I<Modified Julian
Day Number>) au lieu d'utiliser le I<Rata Die>. Cela me va très bien.

=head3 Immutabilité

Une deuxième différence avec le module C<DateTime> de Perl 5, c'est que les dates
ne sont pas modifiables. Et pourquoi pas ? Cela ne me dérange pas.
Donc les dates dans C<Date::Calendar::>R<xxx> ne seront pas modifiables.
Voir cependant ci-dessous le cas d'un calendrier avec plusieurs langues.

=head3 Affichage en langage naturel

Une autre différence avec Perl 5, c'est qu'il n'y a rien de prévu en
standard pour afficher du langage naturel, essentiellement les noms
des jours et les noms des mois. Peut-être a-t-on estimé que
l'utilisation principale du module C<Date> serait la constitution
d'horaires ou la planification de tâches, ce qui ne nécessite que les
valeurs numériques des jours et des mois, pas les noms en clair.

Et si vous avez quand même besoin des noms en clair, vous pouvez installer
et utiliser C<Date::Names> disponible sur CPAN6.

=head2 Calendrier simple

Comme exemple de calendrier simple, je prendrai le calendrier hébraïque. 
La simplicité concerne l'architecture du module, pas les algorithmes
de conversion. Il n'y a pas de variante pour le calendrier hébraïque,
donc de ce point de vue c'est un calendrier simple.

=head3 Calendrier avec une seule langue

À mon avis, si quelqu'un utilise un module de calendrier autre que
grégorien, c'est pour afficher la date convertie, pas pour faire des
calculs de délai ou planifier des tâches. Les noms en clair seront
dans un module séparé, mais ce module séparé fera partie intégrante de la
distribution. Ainsi, la distribution C<Date::Calendar::Hebrew>
incluera à la fois le module C<Date::Calendar::Hebrew> et le module
C<Date::Calendar::Hebrew::Names>.

En résumé, nous avons dans ce cas :

  classe   Date::Calendar::Hebrew
  routines Date::Calendar::Hebrew::Names

=head3  Calendrier avec plusieurs langues

Si les noms sont disponibles dans plusieurs langages pour un même
calendrier, je peux dans certains cas tout mettre dans un même module
C<Date::Calendar::>R<xxx>C<::Names>, mécanisme et données, ou bien je peux
scinder cela en mettant le mécanisme dans
C<Date::Calendar::>R<xxx>C<::Names> et les données dans
C<Date::Calendar::>R<xxx>C<::Names::en>, C<Date::Calendar::>R<xxx>C<::Names::fr>,
C<Date::Calendar::>R<xxx>C<::Names::it> et ainsi de suite. Tout dépendra
de la taille.

Pour le calendrier hébraïque, avec l'hébreu, le yiddish et l'araméen, cela donnerait :

  classe   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

(Désolé, l'araméen n'a pas de code  ISO 639 à deux caractères, j'ai dû
me rabattre sur le code à trois caractères.)

Un  autre  point est  que  dans  le  cas  où plusieurs  langages  sont
disponibles,  l'objet   C<Date::Calendar::>R<xxx>  aura   un  attribut
C<locale> et l'utilisateur pourra alors faire varier cet attribut pour
afficher une même  date dans plusieurs langues. Même  si les attributs
année,  mois  et  jour  sont  invariants,  l'attribut  C<locale>  sera
modifiable.  Et l'objet  ne sera  plus  totalement «  immutable ».  En
prenant un exemple  tiré du calendrier républicain,  changer « Sextidi
26 Messidor 227, jour  de la sauge » en « Sixday  26 Reapidor 227, day
of  sage  »  est  une  modification  cosmétique  et  superficielle  de
l'instance de  date, changer «  Sextidi 26 Messidor  227 , jour  de la
sauge»  en  «  Septidi 27  Messidor  227,  jour  de  l'ail »  est  une
modification profonde et essentielle de l'instance.


=head2 Calendriers avec variantes

=head3 Calendrier révolutionnaire

Pour le calendrier révolutionnaire, il y a deux façons de déterminer le passage d'une année à la
suivante. Ou bien le premier jour de l'année correspond à l'équinoxe
d'automne, ou bien on calcule la durée de chaque année, 365 ou 366 jours,
avec un algorithme similaire à celui du calendrier grégorien.
Historiquement, nous avons commencé avec la règle astronomique
et une réforme prévoyait de passer à la règle arithmétique en
l'an XX.

Nous avons donc trois variantes.

=item la variante astronomique, basée uniquement
sur la détermination de l'équinoxe d'automne, même
après l'an XX,

=item la variante arithmétique, qui s'étend sur toute
la période de validité, y compris avant l'an XX,

=item et la variante historique, qui prend en compte
le changement de règle en l'an XX.

Comment cela se traduit-il en Perl 6 ?

Une première idée consiste à prévoir trois fonctions différentes
de conversion vers le calendrier républicain et trois fonctions
différentes de conversion à partir du calendrier républicain.

Cela ne tient pas la route. Que se passe-t-il si le calendrier d'en face
est le calendrier maya, qui a lui aussi 3 variantes ?
Faut-il définir 9 fonctions de conversion ?

Une deuxième possibilité consiste à ne faire qu'une fonction de conversion
dans chaque sens, laquelle admet un paramètre précisant la variante.
Ou, pour prévoir le cas de la conversion républicain → maya, un
paramètre donnant la variante du calendrier de départ et un deuxième
paramètre pour la variante du calendrier d'arrivée. Ces paramètres
sont facultatifs et les calendriers sans variante les ignorent.

Cela ne tient pas la route non plus. Imaginons que l'on veuille convertir
le 22 septembre 1795 vers le calendrier républicain avec la variante
astronomique, puis en sens inverse avec la règle arithmétique.
La première étape donne le « sextidi 6 sans-culottide III » (pour une année
à 366 jours) et la deuxième étape plante, car avec la variante arithmétique,
l'an III a 365 jours et le sixième jour complémentaire est invalide.

Une troisième possibilité consiste à ajouter un nouvel attribut
à la date pour mémoriser la variante utilisée à la création.
De la sorte, l'aller-retour mentionné ci-dessus ne pourra pas se faire.
Si une date est créée en tant que date du calendrier astronomique, au moment
de la conversion républicain → grégorien (ou autre), elle utilisera la
méthode astronomique. Le seul problème est que cela ajoute du code dans le module,
pour tester à chaque conversion la méthode à utiliser. Pas très gênant pour l'utilisateur
du module, juste un peu gonflant pour le créateur et le mainteneur du module (moi).

Une quatrième  possibilité consiste  à mémoriser  la variante  non pas
dans un  attribut, mais  dans les  méta-informations de  l'objet. Plus
clairement,  créer  trois  classes   spécifiques  pour  le  calendrier
républicain, une classe par variante. Ainsi, ce n'est plus le créateur
/ mainteneur du  module qui doit tester la variante  à utiliser, c'est
le mécanisme de dispatch inclus dans l'interpréteur Perl 6. Toutefois,
il y a beaucoup de code similaire entre ces trois classes. Pour éviter
la duplication de ce code, il sera  écrit dans un rôle utilisé par les
trois classes. On a donc ainsi :

=item1 Trois classes

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

=item1 Un rôle

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

=item1 Un module procédural

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

C'est la possibilité que j'ai adoptée.

Notons que j'ai attribué le nom court à la variante principale,
ce qui sera vraisemblablement la plus utilisée.

Il existe une cinquième possibilité, fusionnant la troisième et la quatrième.
Chacune des trois classes contient une méthode C<algorithm> donnant un résultat
constant, C<"historic">, C<"astronomical"> ou C<"arithmetic"> selon le cas.
Si j'en trouve l'utilité, j'adopterai cette cinquième possibilité.

=head3 Calendrier copte et calendrier éthiopien

Le calendrier copte  et le calendrier éthiopien  sont deux calendriers
très simples, avec la même structure :

=item1 12 mois de trente jours
=item1 suivis de 5 ou 6 jours complémentaires, dits « épagomènes ».

Les années bisextiles sont les années divisibles par 4, qu'il s'agisse
d'années séculaires ou non.

Il y a deux différences entre ces deux calendriers :

=item1 de façon évidente, les noms des jours et des mois,

=item et  la correspondance avec les  autres calendriers, c'est-â-dire
la date (grégorienne par exemple) du jour 0001-01-01.

Les  nombreuses  similitudes entre  les  deux  calendriers incitent  à
partager le code entre les deux modules.  Nous aurons ainsi :

=item1 Deux classes

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

=item1 Un rôle

=item2  Date::Calendar::CopticEthiopic (à la réflexion, pas besoin d'ajouter « ::Common »)

=item1 Deux modules procéduraux

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

Le seul point qui pose problème,  c'est le nom de la distribution.  Je
pense que le  meilleur nom, ou plutôt le moins  mauvais, serait un nom
qui ne  passe sous silence  ni le  calendrier copte, ni  le calendrier
éthiopien, c'est-à-dire C<Date::Calendar::CopticEthiopic>.

=head3 Calendrier maya et calendrier aztèque

Le  cas  du  calendrier  maya  est  semblable  au  cas  du  calendrier
républicain,  en  ce  qu'il  existe  plusieurs  façons  de  faire  les
conversions avec  le calendrier grégorien. Il  est également semblable
au cas du calendrier copte  parce qu'il partage de nombreux mécanismes
avec  le  calendrier  aztèque,  comme  le  calendrier  copte  avec  le
calendrier éthiopien.

Les  coupeurs de  cheveux  en  quatre diront  qu'il  n'y  a pas  I<un>
calendrier maya, mais I<trois> :

=item  le compte  long, constitué  de cycles  emboîtés de  longueur 20
(avec  une  exception) et  habituellement  affiché  avec une  notation
pointée, par exemple 13.0.6.11.16,

=item le calendrier  clérical, ou Tzolkin, qui combine un  cycle de 13
nombres avec un cycle de 20 noms, par exemple 8 Cib,

=item le calendrier profane, ou Haab, constitué de 18 mois (nommés) de
20 jours (numérotés) plus 5 jours complémentaires, par exemple 4 Xul.

En  fait,  les programmes  de  conversion  traitent  le «  système  de
calendriers mayas » (avec un « s » à « calendrier », mais pour alléger
et  fluidifier  les  phrases,  nous nous  permettons  d'évoquer  I<le>
calendrier maya.

C'est la même chose, en plus simple, avec le calendrier aztèque. C'est
un   système  de   deux   calendriers,  le   calendrier  clérical   ou
I<tonalpohualli>, semblable  au Tzolkin, et le  calendrier profane, ou
I<xiupohualli>,  semblable  au  Haab.   Les  Aztèques  n'avaient  rien
d'analogue au compte long des Mayas.

Pour  la  conversion  du  calendrier  grégorien  (ou  autre)  vers  le
calendrier maya ou  aztèque, il existe plusieurs avis  différents : la
méthode d'Alfonso Cortes, avec ou  sans l'ajustement de H.B. Nicholson
et la méthode de Francisco Cortes.

À suivre.

=head1 Licence

Texte  diffusé  sous  la  licence CC-BY-SA  :  Creative  Commons  avec
attribution, partage dans les mêmes conditions.

=end pod
