Date-Convert-French_Rev

 view release on metacpan or  search on metacpan

lib/Date/Convert/French_Rev.pm  view on Meta::CPAN

# -*- encoding: utf-8; indent-tabs-mode: nil -*-
#
#     Perl Date::Convert extension to convert dates from/to the French Revolutionary calendar
#     Copyright (C) 2001-2003, 2013, 2015, 2020 Jean Forget
#
#     See the license in the embedded documentation below.
#
package Date::Convert::French_Rev;

use utf8;
use strict;
use warnings;
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
use Date::Convert;
use Carp;
use Roman;

require Exporter;

@ISA = qw(Date::Convert Exporter);
# Do not export methods, therefore export nothing
@EXPORT = qw(

);
$VERSION = '0.09';

use constant REV_BEGINNING => 2375840; # 1 Vendémiaire I in the Revolutionary calendar
my @MONTHS_SHORT  = qw ( Vnd Bru Fri Niv Plu Vnt Ger Flo Pra Mes The Fru S-C);
my @MONTHS = qw(Vendémiaire Brumaire  Frimaire
                Nivôse      Pluviôse  Ventôse
                Germinal    Floréal   Prairial
                Messidor    Thermidor Fructidor);
push @MONTHS, "jour complémentaire"; # Incompatible with qw(), because of embedded space

# The day numer 10 is counterintuitively placed in the 0-th element
# because the modulus operator and the Perl arrays are 0-based.
# It works. Do not report a bug.
my @DECADE_DAYS = qw ( Décadi Primidi Duodi Tridi Quartidi Quintidi Sextidi Septidi Octidi Nonidi);
my @DECADE_DAYS_SHORT = qw ( Déc Pri Duo Tri Qua Qui Sex Sep Oct Non);

# When initializing an array with lists within lists, it means one of two things:
# Either it is a newbie who does not know how to make multi-dimensional arrays,
# Or it is a (at least mildly) experienced Perl-coder who, for some reason,
# wants to initialize a flat array with the concatenation of lists.
# I am a (at least mildly) experienced programmer who wants to use qw() and yet insert
# comments in some places.
my @DAYS = (
# Vendémiaire
        qw(
       0raisin           0safran           1châtaigne        1colchique        0cheval
       1balsamine        1carotte          2amaranthe        0panais           1cuve
       1pomme_de_terre   2immortelle       0potiron          0réséda           2âne
       1belle-de-nuit    1citrouille       0sarrasin         0tournesol        0pressoir
       0chanvre          1pêche            0navet            2amarillis        0bœuf
       2aubergine        0piment           1tomate           2orge             0tonneau
        ),
# Brumaire
        qw(
       1pomme            0céleri           1poire            1betterave        2oie
       2héliotrope       1figue            1scorsonère       2alisier          1charrue
       0salsifis         1macre            0topinambour      2endive           0dindon
       0chervis          0cresson          1dentelaire       1grenade          1herse
       1bacchante        2azerole          1garance          2orange           0faisan
       1pistache         0macjonc          0coing            0cormier          0rouleau
        ),
# Frimaire
        qw(
       1raiponce         0turneps          1chicorée         1nèfle            0cochon
       1mâche            0chou-fleur       0miel             0genièvre         1pioche
       1cire             0raifort          0cèdre            0sapin            0chevreuil
       2ajonc            0cyprès           0lierre           1sabine           0hoyau
       2érable-sucre     1bruyère          0roseau           2oseille          0grillon
       0pignon           0liège            1truffe           2olive            1pelle
        ),
# Nivôse
        qw(
       1tourbe           1houille          0bitume           0soufre           0chien
       1lave             1terre_végétale   0fumier           0salpêtre         0fléau
       0granit           2argile           2ardoise          0grès             0lapin
       0silex            1marne            1pierre_à_chaux   0marbre           0van
       1pierre_à_plâtre  0sel              0fer              0cuivre           0chat
       2étain            0plomb            0zinc             0mercure          0crible
        ),
# Pluviôse
        qw(
       1lauréole         1mousse           0fragon           0perce-neige      0taureau
       0laurier-thym     2amadouvier       0mézéréon         0peuplier         1coignée

lib/Date/Convert/French_Rev.pm  view on Meta::CPAN

# Floréal
        qw(
       1rose             0chêne            1fougère          2aubépine         0rossignol
       2ancolie          0muguet           0champignon       1hyacinthe        0râteau
       1rhubarbe         0sainfoin         0bâton-d'or       0chamérisier      0ver_à_soie
       1consoude         1pimprenelle      1corbeille-d'or   2arroche          0sarcloir
       0staticé          1fritillaire      1bourrache        1valériane        1carpe
       0fusain           1civette          1buglosse         0sénevé           1houlette
        ),
# Prairial
        qw(
       1luzerne          2hémérocale       0trèfle           2angélique        0canard
       1mélisse          0fromental        0martagon         0serpolet         1faulx
       1fraise           1bétoine          0pois             2acacia           1caille
       2œillet           0sureau           0pavot            0tilleul          1fourche
       0barbeau          1camomille        0chèvre-feuille   0caille-lait      1tanche
       0jasmin           1verveine         0thym             1pivoine          0chariot
        ),
# Messidor
        qw(
       0seigle           2avoine           2oignon           1véronique        0mulet
       0romarin          0concombre        2échalotte        2absinthe         1faucille
       1coriandre        2artichaut        1giroflée         1lavande          0chamois
       0tabac            1groseille        1gesse            1cerise           0parc
       1menthe           0cumin            3haricots         2orcanète         1pintade
       1sauge            2ail              1vesce            0blé              1chalémie
        ),
# Thermidor
        qw(
       2épeautre         0bouillon-blanc   0melon            2ivraie           0bélier
       1prêle            2armoise          0carthame         1mûre             2arrosoir
       0panis            0salicor          2abricot          0basilic          1brebis
       1guimauve         0lin              2amande           1gentiane         2écluse
       1carline          0caprier          1lentille         2aunée            1loutre
       1myrte            0colza            0lupin            0coton            0moulin
        ),
# Fructidor
        qw(
       1prune            0millet           0lycoperde        2escourgeon       0saumon
       1tubéreuse        0sucrion          2apocyn           1réglisse         2échelle
       1pastèque         0fenouil          2épine-vinette    1noix             1truite
       0citron           1cardère          0nerprun          0tagette          1hotte
       2églantier        1noisette         0houblon          0sorgho           2écrevisse
       1bigarade         1verge-d'or       0maïs             0marron           0panier
        ),
# Jours complémentaires
        qw(
       1vertu            0génie            0travail          2opinion          3récompenses
       1révolution
         ));

my @PREFIXES = ('jour du ', 'jour de la ', "jour de l'", 'jour des ');

use constant NORMAL_YEAR    => 365;
use constant LEAP_YEAR      => 366;
use constant FOUR_YEARS     => 4 * NORMAL_YEAR + 1; # one leap year every four years
use constant CENTURY        => 25 * FOUR_YEARS - 1; # centuries aren't leap years...
use constant FOUR_CENTURIES => 4 * CENTURY + 1;     # ...except every four centuries that are.
use constant FOUR_MILLENIA  => 10 * FOUR_CENTURIES - 1; # ...except every four millenia that are not.

# number of days between the start of the revolutionary calendar, and the
# beginning of year n - 1
my @YEARS_BEGINS=    (0, 365, 730, 1096, 1461, 1826, 2191, 2557, 2922, 3287, 3652,
                   4018, 4383, 4748, 5113, 5479, 5844);

# This method shoudl be in the master class, but for the moment, it is only available here
sub change_to {
  croak "Need to specify the new calendar"
    if @_ <= 1;
  my ($self, $new_cal) = @_;
  $new_cal->convert($self);
}

sub initialize {
    my $self = shift;
    my ($year, $month, $day) = @_;
    unless (defined($year) and defined($month) and defined($day))
      { croak "Date::Convert::French_Rev::initialize needs more args" }
    my $absol = REV_BEGINNING;
    $$self{'year'}  = $year;
    $$self{'month'} = $month;
    $$self{'day'}   = $day;

    my $is_leap = Date::Convert::French_Rev->is_leap($year);
    croak "year $year out of range" if $year <= 0;
    croak "month $month out of range" if $month > 13 or $month <= 0;
    croak "standard day number $day out of range" if $day <= 0 and $month <= 12;
    croak "standard day number $day out of range" if $day > 30 and $month <= 12;
    croak "additional day $day out of range" if ($month == 13) and ($day <= 0);
    croak "additional day $day out of range" if ($month == 13) and ($day > 5) and !$is_leap;
    croak "additional day $day out of range" if ($month == 13) and ($day > 6); # implying "and $is_leap" other cases already discarded

    $year --;  #get years *before* this year.  Makes math easier.  :)
    # first, convert year into days. . .
    if ($year >= 16) # Romme rule in effect, or nearly so
      {
        $absol += int($year/4000) * FOUR_MILLENIA;
        $year  %= 4000;
        $absol += int($year/400) * FOUR_CENTURIES;
        $year  %= 400;
        $absol += int($year/100) * CENTURY;
        $year  %= 100;
        $absol += int($year/4)* FOUR_YEARS;
        $year  %= 4;
        $absol += $year * NORMAL_YEAR;
      }
    else # table look-up for the programmer-hostile equinox rule
      { $absol += $YEARS_BEGINS[$year] }

    # now, month into days.
    $absol += 30 * ($month - 1) + $day - 1;

    $$self{absol} = $absol;
}

sub year {
    my $self = shift;
    return $$self{year} if exists $$self{year}; # no point recalculating.
    my $days;
    my $year;
    # note:  years and days are initially days *before* today, rather than
    # today's date.  This is because of fenceposts.  :)
    $days =  $$self{absol} - REV_BEGINNING;
    if ($days < $YEARS_BEGINS[16]) {
      $year = scalar grep { $_ <= $days } @YEARS_BEGINS;
      $days -= $YEARS_BEGINS[$year - 1];
      $days++;
    }

lib/Date/Convert/French_Rev.pm  view on Meta::CPAN

    print $date->date_string, "\n";                     # ... is "1799 Nov 9"

Alternate way of converting from Revolutionary to Gregorian (or other)

    $date = Date::Convert::French_Rev->new(8, 2, 18);   # 18 Brumaire VIII...
    $date->change_to("Date::Convert::Gregorian");
    print $date->date_string, "\n";                     # ... is "1799 Nov 9"

=head1 REQUIRES

Date::Convert, Roman

A Unicode-friendly version of Perl,  that is, more or less, Perl 5.8.8
or greater.  Yet,  if you do not use  the C<date_string> accessor, you
can use a lower version of Perl.

=head1 EXPORTS

Nothing.

=head1 DESCRIPTION

The following methods are available:

=over 4

=item new

Create a new Revolutionary date object with the specified year, month,
day parameters, e.g.

  $date = Date::Convert::French_Rev->new(8, 2, 18)

for 18 Brumaire VIII.

=item date

Extract a  list consisting  of the  year, the month  and the  day. The
end-of-year additional days are assigned to the virtual 13th month.

=item year

Return just the year element of date.

=item month

Return the month  element of date, or 13 if the  date is an additional
day at the end of the year.

=item day

Return just the day number element of date.

=item is_leap

Boolean.

=item convert

Change the date to a new format. The invocant is the class name of the
destination calendar, the parameter is the C<Date::Convert::>I<whatever>
object to convert.

=item change_to

Change the date to a new format. The invocant is the
C<Date::Convert::>I<whatever> object to convert, the parameter is the
class name of the destination calendar. For the moment, this method is
available only for C<Date::Convert::French_Rev> invocant objects.

=item date_string

Return the date  in a pretty format. You can  give an string parameter
to adjust the date format to your preferences.

=back

The format parameter  to C<date_string> is a string  consisting of any
characters (letters, digits,  whitespace, other) with %-prefixed field
descriptors, inspired from the Unix standard C<date(1)> command.

The following field descriptors are recognized:

=over 4

=item %y

2-digit year - 00 to 99

=item %Y, %G

year  - 0001  to  9999. There  is  no difference  between these  three
variants. This is because in the Revolutionary calendar, the beginning
of a year  is always aligned with the beginning of  a décade, while in
the Gregorian calendar, the beginning of a year is usually not aligned
with the beginning of a week.

=item %L

This is  a third specifier  for the year. The  problem is that  I have
forgotten why I  have implemented this specifier. There  are plenty of
strftime-like libraries  visible on the  Internet, none of them  has a
C<%L> specifier for the year. So where does it come from?

I will deprecate  this speficier in the next module  releases. For the
next two years,  it will function as previously. In  the release after
this 2-year  delay, the  C<%L> specifier will  produce a  warning. And
after another 2-year delay it will be removed.

=item %EY, %Ey

year as a Roman number - I to MMM

=item %m

month of year  - 01 to 12, or 13 for  the end-of-year additional days.
The number  is formatted as  a 2-char string,  with a leading  zero if
necessary.

=item %f

month of year  - " 1" to "12", or "13"  for the end-of-year additional
days.   The number is  formatted as  a 2-char  string, with  a leading
space if necessary.

=item %b, %h

month abbreviation - Ven to Fru, or S-C for the end-of-year additional
days (called I<Sans-Culottides>). A 3-char string.

=item %B

month full name - Vendémiaire to Fructidor, or "jour complémentaire"
for the end-of-year additional days. A variable length string.

=item %d

day of month - 01 to 30.   The number is formatted as a 2-char string,
with a leading zero if necessary.

=item %e

day of  month - "  1" to  "30".  The number  is formatted as  a 2-char
string, with a leading space if necessary.

=item %A

day of décade - "Primidi" to "Décadi". A variable length string.

=item %a

abbreviated day of décade -  "Pri" to "Déc".  A 3-char string. Beware:
do not confuse Sep, Oct and Déc with Gregorian calendar months

=item %w

day  of décade -  " 1"  to "10"  (" 1"  for Primidi,  " 2"  for Duodi,
etc). The number is formatted as a 2-char string, with a leading space
if necessary.

=item %j

day of the year - "001" to "366". A 3-char string, with leading zeroes
if necessary.

=item %Ej

full name of the day of the year. Instead of assigning a saint to each
day, the creators of the calendar decided to assign a plant, an animal
or a tool. A variable-length string.

=item %EJ

same as %Ej, but significant words are capitalized.

=item %*

same as %Ej.

=item %Oj

simple name of the day of the year. Same as %Ej, without the prefix.

=item %n, %t, %%, %+

replaced by a newline, tab, percent and plus character respectively.

=back

The time-related field specifiers  are irrelevant. Therefore, they are
copied "as is" into the result string. These fields are:

  %H, %k, %i, %I, %p, %M, %S, %s, %o, %Z, %z

Neither are the composite field specifiers supported:

  %c, %C, %u, %g, %D, %x, %l, %r, %R, %T, %X, %V, %Q, %q, %P, %F, %J, %K

If a percent-sequence  is not a valid specifier, it  is copied "as is"
into the  result string. This  is true especially  for C<%E>-sequences
and C<%O>-sequences other than those listed above.

=head1 DIAGNOSTICS

=over 4

=item year %s out of range

The module does  not deal with year prior to the  epoch. The year must
be "1" or greater.

=item month %s out of range

The  French  Revolutionary  calendar  has  12  months,  plus  5  or  6
additional days  that do not belong  to a month.  So  the month number
must be in the 1-12 range for normal days, or 13 for additional days

=item standard day number %s out of range

The day number for any normal month is in the 1-30 range.

=item additional day %s out of range

The day number for the end-of-year  additional days is a number in the
1-5 range (or the 1-6 range for leap years).

=item Date::Convert::French_Rev::initialize needs more args

You  must provide  a year,  a month  number and  a day  number to
C<Date::Convert::French_Rev::initialize>.

=back

=head1 KNOWN BUGS AND CAVEATS

Not many bugs, but many caveats.

My sources  disagree about the 4th  additional day. One  says "jour de
l'opinion", the other says "jour de la raison".

Another disagreement is  that some sources ignore the  Romme rule, and
use only the equinox rule. So, a 1- or 2-day difference can happen.

This  module  inherits  its  user  interface  from  Mordechai  Abzug's
C<Date::Convert>,  which is,  according to  its author,  "in pre-alpha
state".  Therefore, my  module's  user interface  is  also subject  to
changes.

I  have checked the  manpage for  C<date(1)> in  two flavors  of Unix:
Linux and AIX. In the best case, the extended field descriptors C<%Ex>
and C<%Oy> are poorly documented, but usually they are not documented.

The C<Test::Exception>  and C<Test::Warning> modules are  required for
the    build    process,    not     for    the    regular    use    of
C<Date::Convert::French_Rev>.

You should  not use the  C<%L> strftime  specifier. For the  moment it
prints the  year, but in some  next release, about in  Vendémiaire 231
(that is, September 2022) it will produce a warning. And after another
two years it will be removed.

=head1 HISTORICAL NOTES

The Revolutionary calendar was in  use in France from 24 November 1793
(4 Frimaire II) to 31 December 1805 (10 Nivôse XIV). An attempt to use
the  decimal   rule  (the   basis  of  the   metric  system)   to  the
calendar. Therefore, the week  disappeared, replaced by the décade (10
days, totally different from the  English word "decade", 10 years). In
addition, all months have exactly 3 decades, no more, no less.

At first,  the year was  beginning on the  equinox of autumn,  for two
reasons.  First, the  republic had  been established  on  22 September
1792, which  happened to be the  equinox, and second,  the equinox was
the symbol of equality, the day and the night lasting exactly 12 hours
each. It  was therefore  in tune with  the republic's  motto "Liberty,
Equality, Fraternity". But  it was not practical, so  Romme proposed a
leap year rule similar to the Gregorian calendar rule.

In his book  I<The French Revolution>, the 19th  century writer Thomas
Carlyle proposes these translations for the month names:

  Vendémiaire -> Vintagearious
  Brumaire    -> Fogarious
  Frimaire    -> Frostarious
  Nivôse      -> Snowous
  Pluviôse    -> Rainous
  Ventôse     -> Windous
  Germinal    -> Buddal
  Floréal     -> Floweral
  Prairial    -> Meadowal
  Messidor    -> Reapidor
  Thermidor   -> Heatidor
  Fructidor   -> Fruitidor

=head1 AUTHOR

Jean Forget <JFORGET@cpan.org>

based on Mordechai T. Abzug's work <morty@umbc.edu>

with thanks to  Gérald Sédrati-Dinet (GIBUS at cpan dot  org), for his
thorough documentation research.

=head1 SEE ALSO

=head2 Perl Software

perl(1), L<Date::Convert>

L<DateTime>

L<DateTime::Calendar::FrenchRevolutionary>
or L<https://github.com/jforget/DateTime-Calendar-FrenchRevolutionary>

L<Date::Converter>

=head2 Other Software

date(1)

CALENDRICA 4.0 -- Common Lisp, which can be download in the "Resources" section of
L<https://www.cambridge.org/us/academic/subjects/computer-science/computing-general-interest/calendrical-calculations-ultimate-edition-4th-edition?format=PB&isbn=9781107683167>

F<calendar/cal-french.el>  in emacs-21.2  or later  or xemacs  21.1.8,
forked in L<https://github.com/jforget/emacs-lisp-cal-french>

C<Date::Calendar::FrenchRevolutionary> for Raku at L<https://modules.raku.org/dist/Date::Calendar::FrenchRevolutionary:cpan:JFORGET>
or L<https://github.com/jforget/raku-Date-Calendar-FrenchRevolutionary>

L<https://www.gnu.org/software/apl/Bits_and_Pieces/calfr.apl.html> or L<https://github.com/jforget/apl-calendar-french>

L<https://www.hpcalc.org/details/7309> or L<https://github.com/jforget/hp48-hp50-French-Revolutionary-calendar>

L<https://github.com/jforget/hp41-calfr>

French Calendar for Android at
L<https://f-droid.org/packages/ca.rmen.android.frenchcalendar/>
or L<https://github.com/caarmen/FRCAndroidWidget>
and L<https://github.com/caarmen/french-revolutionary-calendar>

Thermidor for Android at L<https://github.com/jhbadger/Thermidor-Android>

A Ruby program at L<https://github.com/jhbadger/FrenchRevCal-ruby>

=head2 books

Quid 2001, M and D Frémy, publ. Robert Laffont

Agenda Républicain 197 (1988/89), publ. Syros Alternatives

Any French schoolbook about the French Revolution

The French Revolution, Thomas Carlyle, Oxford University Press

=head2 Internet

L<http://www.faqs.org/faqs/calendars/faq/part3/>

L<https://h2g2.com/approved_entry/A2903636>

L<https://www.allhotelscalifornia.com/kokogiakcom/frc/default.asp>

L<https://en.wikipedia.org/wiki/French_Republican_Calendar>

L<https://fr.wikipedia.org/wiki/Calendrier_républicain>

L<http://prairial.free.fr/index.php?lien=cal_sommaireFR>
(in French)

L<https://archive.org/details/decretdelaconven00fran_40>

"Décret  du  4 frimaire,  an  II  (24  novembre  1793) sur  l'ère,  le
commencement et l'organisation de l'année et sur les noms des jours et
des mois"

L<https://archive.org/details/decretdelaconven00fran_41>

Same text, with a slightly different typography.

L<https://purl.stanford.edu/dx068ky1531>

"Archives parlementaires  de 1789 à  1860: recueil complet  des débats
législatifs & politiques  des Chambres françaises", J.  Madival and E.
Laurent, et. al.,  eds, Librairie administrative de  P. Dupont, Paris,
1912.

Starting with  page 6,  this document  includes the  same text  as the
previous links, with  a much improved typography.  Especially, all the
"long s"  letters have been replaced  by short s. Also  interesting is
the text  following the  decree, page 21  and following:  "Annuaire ou
calendrier pour la seconde année de la République française, annexe du
décret  du  4  frimaire,  an  II (24  novembre  1793)  sur  l'ère,  le
commencement et l'organisation de l'année et sur les noms des jours et
des mois". In the remarks above, it is refered as [Annexe].

L<https://gallica.bnf.fr/ark:/12148/bpt6k48746z>

[Fabre] "Rapport fait à la Convention nationale dans la séance du 3 du
second mois de la seconde année  de la République française, au nom de
la   Commission    chargée   de   la   confection    du   calendrier",
Philippe-François-Nazaire  Fabre  d'Églantine,  Imprimerie  nationale,
Paris, 1793

L<https://gallica.bnf.fr/ark:/12148/bpt6k49016b>

[Annuaire] "Annuaire  du cultivateur,  pour la  troisième année  de la
République  : présenté  le  30 pluviôse  de l'an  II  à la  Convention
nationale, qui en  a décrété l'impression et l'envoi,  pour servir aux
écoles  de la  République",  Gilbert Romme,  Imprimerie nationale  des
lois, Paris, 1794-1795

L<https://gallica.bnf.fr/ark:/12148/bpt6k43978x>

"Calendrier militaire,  ou tableau  sommaire des  victoires remportées
par les  Armées de  la République française,  depuis sa  fondation (22
septembre 1792),  jusqu'au 9  floréal an  7, époque  de la  rupture du
Congrès de Rastadt et de la reprise des hostilités" Moutardier, Paris,



( run in 0.544 second using v1.01-cache-2.11-cpan-39bf76dae61 )