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 )