Date::Convert::French_Rev - Convert from / to French Revolutionary Calendar


Date-Convert-French_Rev documentation Contained in the Date-Convert-French_Rev distribution.

Index


Code Index:

NAME

Top

Date::Convert::French_Rev - Convert from / to French Revolutionary Calendar

SYNOPSIS

Top

  use Date::Convert::French_Rev;

Converting from Gregorian (or other) to Revolutionary

    $date=new Date::Convert::Gregorian(1799, 11, 9);
    convert Date::Convert::French_Rev $date;
    print $date->date_string, "\n";
    print $date->date_string($format), "\n";

Converting from Revolutionary to Gregorian (or other)

    $date=new Date::Convert::French_Rev(8, 2, 18);
    convert Date::Convert::Gregorian $date;
    print $date->date_string, "\n";

REQUIRES

Top

Date::Convert, Roman

EXPORTS

Top

Nothing.

DESCRIPTION

Top

This module is obsolete. It is replaced by a module compatible with DateTime.pm: DateTime::Calendar::FrenchRevolutionary

The following methods are available:

new

Create a new Revolutinary date object with the specified start parameters, ie. $date = new Date::Convert::French_Rev(8, 2, 18) for 18 Brumaire VIII.

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.

year

Return just the year element of date.

month

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

day

Just like year and month.

is_leap

Boolean.

convert

Change the date to a new format.

date_string

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

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

The following field descriptors are recognized:

%y

year - 00 to 99

%Y, %G, %L

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.

%EY, %Ey

year as a Roman number - I to MMM

%m

month of year - 01 to 12, or 13 for the end-of-year additional days.

%f

month of year - " 1" to "12", or "13" for the end-of-year additional days.

%b, %h

month abbreviation - Ven to Fru, or S-C for the end-of-year additional days (called Sans-Culottides).

%B

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

%d

day of month - 01 to 30

%e

day of month - " 1" to "30"

%A

day of décade - "Primidi" to "Décadi".

%a

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

%w

day of décade - " 1" to "10" (" 1" for Primidi, " 2" for Duodi, etc)

%j

day of the year - "001" to "366".

%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.

%EJ

same as %Ej, but significant words are capitalized.

%Oj

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

%n, %t, %%, %+

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

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

DIAGNOSTICS

Top

These are the diagnostics which can be produced by Date::Convert::French_Rev. The explanations can be displayed when the error is produced if you have installed diagnostics.pm, version 1.2. Just insert one of the following lines in your script:

    use diagnostics qw(-m=Date::Convert::French_Rev);
    use diagnostics qw(-m=Date::Convert::French_Rev -m=perl);

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

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).

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

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

If you ask a negative (Revolutionary) year, zero included, the module may produce various errors, such as off-by-one conditions.

KNOWN BUGS AND CAVEATS

Top

Not many bugs, but many caveats.

Some day names correspond to little known plants. Therefore, I am not sure if they use the masculine gender or the feminine gender. I have found some in various dictionaries, but there still remain a few of them. Note: they are tagged with a 4, 5 or 6 code.

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 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 date(1) in two flavors of Unix: Linux and AIX. In the best case, the extended field descriptors %Ex and %Oy are poorly documented, but usually they are not documented.

HISTORICAL NOTES

Top

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 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

AUTHOR

Top

Jean Forget <J-FORGET@wanadoo.fr>

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

The development of this module is hosted by Les Mongueurs de Perl, http://www.mongueurs.net.

SEE ALSO

Top

Software

date(1), perl(1), Date::DateCalc(3), Date::Convert(3)

calendar/cal-french.el in emacs-21.2 or xemacs 21.1.8

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

Internet

http://www.faqs.org/faqs/calendars/faq/part3/

LICENSE STUFF

Top

Copyright (c) 2001, 2002 Jean Forget. All rights reserved. This program is free software. You can distribute, modify, and otherwise mangle Date::Convert::French_Rev under the same terms as perl.


Date-Convert-French_Rev documentation Contained in the Date-Convert-French_Rev distribution.

package Date::Convert::French_Rev;

use strict;
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.05';

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 @ADD_DAYS_SHORT= qw ( Vertu Génie Trav Raison Récomp Révol);
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";

# 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          2amarante         0panais           1cuve
       1pomme_de_terre   2immortelle       0potiron          0réséda           2âne
       1belle_de_nuit    1citrouille       0sarrasin         0tournesol        0pressoir
       0chanvre          1pèche            0navet            2amaryllis        0boeuf
       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
       4chervis          0cresson          1dentelaire       1grenade          1herse
       5bacchante        2azerole          1garance          2orange           0faisan
       1pistache         4macjon           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(
       5lauréole         1mousse           0fragon           0perce-neige      0taureau
       0laurier-thym     2amadouvier       4mézéréon         0peuplier         1cognée
       2ellébore         0brocoli          0laurier          2avelinier        1vache
       0buis             0lichen           2if               1pulmonaire       1serpette
       0thlaspi          4thymelé          0chiendent        5trainasse        0lièvre
       1guède            0noisetier        0cyclamen         1chélidoine       0traîneau
	),
# Ventôse
	qw(
       0tussilage        0cornouiller      0violier          0troène           0bouc
       2asaret           2alaterne         1violette         0marsault         1bêche
       0narcisse         2orme             1fumeterre        0vélar            1chèvre
       2épinard          0doronic          0mouron           0cerfeuil         0cordeau
       1mandragore       0persil           0cochléaria       1pâquerette       0thon
       0pissenlit        1sylvie           0capillaire       0frêne            0plantoir
	),
# Germinal
	qw(
       1primevère        0platane          2asperge          1tulipe           1poule
       1blette           0bouleau          1jonquille        2aulne            0couvoir
       1pervenche        0charme           1morille          0hêtre            2abeille
       1laitue           0mélèze           1ciguë            0radis            1ruche
       0gainier          1romaine          0marronnier       1roquette         0pigeon
       0lilas            2anémone          1pensée           1myrtille         0greffoir
	),
# Floréal
	qw(
       1rose             0chêne            1fougère          2aubépine         0rossignol
       2ancolie          0muguet           0champignon       1jacinthe         0rateau
       1rhubarbe         0sainfoin         0bâton-d'or       4chamérisier      0ver_à_soie
       1consoude         1pimprenelle      1corbeille-d'or   2arroche          0sarcloir
       0statice          1fritillaire      1bourrache        1valériane        1carpe
       0fusain           1civette          1buglosse         0sénevé           1houlette
	),
# Prairial
	qw(
       1luzerne          6hémérocalle      0trèfle           2angélique        0canard
       1mélisse          0fromental        0martagon         0serpolet         1faux
       1fraise           1bétoine          0pois             2acacia           1caille
       2oeillet          0sureau           0pavot            0tilleul          1fourche
       0barbeau          1camomille        0chèvrefeuille    0caille-lait      1tanche
       0jasmin           1verveine         0thym             1pivoine          0chariot
	),
# Messidor
	qw(
       0seigle           2avoine           2oignon           1véronique        0mulet
       0romarin          0concombre        2échalotte        2absinthe         1faucille
       0coriandre        2artichaut        1giroflée         1lavande          0chamois
       0tabac            1groseille        1gesse            1cerise           0parc
       1menthe           0cumin            0haricot          2orcanète         1pintade
       1sauge            2ail              1vesce            0blé              5chalémie
	),
# Thermidor
	qw(
       2épautre          0bouillon-blanc   0melon            2ivraie           0bélier
       1prèle            2armoise          0carthame         1mûre             2arrosoir
       4panis            4salicor          2abricot          0basilic          1brebis
       1guimauve         0lin              2amande           1gentiane         2écluse
       1carline          0câprier          1lentille         2aunée            1loutre
       1myrte            0colza            0lupin            0coton            0moulin
	),
# Fructidor
	qw(
       1prune            0millet           0lycoperdon       2escourgeon       0saumon
       1tubéreuse        4sucrion          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
       5bagarade         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);

sub initialize {
    my $self = shift;
    my ($year, $month, $day) = @_;
    if (not defined($year) and not defined($month) and not defined($day))
      { return Date::Convert::initialize }
    if (not defined($year) or  not defined($month) or  not defined($day))
      { croak "Date::Convert::French_Rev::initialize needs more args" }
    confess "These routines don't work well for French_Rev before year 1"
	if $year < 1;
    my $absol = REV_BEGINNING;
    $$self{'year'}  = $year;
    $$self{'month'} = $month;
    $$self{'day'}   = $day;

    my $is_leap = is_leap Date::Convert::French_Rev $year;
    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) and $is_leap;

    $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++;
      }
    else
      {
	#$days --;
	my $x;
	$x     = int ($days / FOUR_MILLENIA);
        $year += $x * 4000;
	$days -= $x * FOUR_MILLENIA;

	$x     = int ($days / FOUR_CENTURIES);
        $year += $x * 400;
	$days -= $x * FOUR_CENTURIES;

	$x     = int ($days / CENTURY);
        $x     = 3 if $x == 4; # last day of the 400-year period
        $year += $x * 100;
	$days -= $x * CENTURY;

	$x     = int ($days / FOUR_YEARS);
        $year += $x * 4;
	$days -= $x * FOUR_YEARS;

	$x     = int ($days / NORMAL_YEAR);
        $x     = 3 if $x == 4; # last day of the 4-year period
        $year += $x;
	$days -= $x * NORMAL_YEAR;

        ++$year; # because of 0-based mathematics vs 1-based chronology
        ++$days;
      }
    $$self{year}           = $year;
    $$self{days_into_year} = $days;
    return $year;
}

sub month {
    my $self = shift;
    return $$self{month} if exists $$self{month};
    my $year = $self -> year;
    my $days = $$self{days_into_year} - 1;
    my $day  = $days % 30;
    $days   -= $day;
    my $month = $days / 30 + 1;
    $$self{month} = $month;
    $$self{day}   = $day + 1;
    return $month;
}

sub day {
    my $self = shift;
    return $$self{day} if exists $$self{day};
    $self->month; # calculates day as a side-effect
    return $$self{day};
}

sub date {
    my $self = shift;
    return ($self->year, $self->month, $self->day);
}

sub is_leap {
    my $self = shift;
    my $year = shift || $self->year; # so is_leap can be static or method

    # Autumn equinox from I to XIX
    return 1 if ($year == 3) or ($year == 7) or ($year == 11) or ($year == 15);
    return 0 if ($year < 20);

    # Romme rule from XX on
    return 0 if $year %    4; # not a multiple of 4 -> normal year
    return 1 if $year %  100; # a multiple of 4 but not of 100 -> leap year
    return 0 if $year %  400; # a multiple of 100 but not of 400 -> normal year
    return 1 if $year % 4000; # a multiple of 400 but not of 4000 -> leap
    return 0; # multiple of 4000 -> normal year
}

sub field {
  my ($self, $spec) = @_;
  my $decade_day = $self->day % 10;
  # below, a switch statement, more or less, as described in perlfaq7

  $spec eq '%d'		&& do { return sprintf "%02d", $self->day };
  $spec eq '%j'		&& do { return sprintf "%03d", 30 * $self->month + $self->day - 30 };
  $spec eq '%e'		&& do { return sprintf "%2d",  $self->day };
  $spec eq '%m'		&& do { return sprintf "%02d", $self->month };
  $spec eq '%f'		&& do { return sprintf "%2d",  $self->month };
  $spec =~ /\%[YGL]/	&& do { return sprintf "%04d", $self->year };
  $spec =~ /\%B/	&& do { return $MONTHS[$self->month - 1] };
  $spec =~ /\%[bh]/	&& do { return $MONTHS_SHORT[$self->month - 1] };
  $spec eq '%y'		&& do { return sprintf "%02d", $self->year % 100 };
  $spec eq '%n'		&& do { return "\n" };
  $spec eq '%t'		&& do { return "\t" };
  $spec eq '%+'		&& do { return '+' };
  $spec eq '%%'		&& do { return '%' };
  $spec eq '%a'         && do { return $DECADE_DAYS_SHORT[$decade_day] };
  $spec eq '%A'         && do { return $DECADE_DAYS[$decade_day] };
  $spec eq '%w'         && do { return sprintf("%2d", $decade_day || 10) };
  $spec eq '%EY'        && do { return Roman $self->year };
  $spec eq '%Ey'        && do { return roman $self->year };
  $spec eq '%Ej'        && do
    {
      my $jj = 30 * $self->month + $self->day - 31; # %j is 1..366, but $jj is 0..365
      my $lb = $DAYS[$jj];
      $lb =~ s/_/ /g;
      $lb =~ s/^(\d)/$PREFIXES[$1 % 4]/;
      return $lb;
    };
  $spec eq '%EJ'  && do
    {
      my $jj = 30 * $self->month + $self->day - 31; # %j is 1..366, but $jj is 0..365
      my $lb = $DAYS[$jj];
      $lb =~ s/_/ /g;
      # Using a capitalized prefix, and capitalizing the first letter
      $lb =~ s/^(\d)(.)/\u$PREFIXES[$1 % 4]\u$2/;
      return $lb;
    };
  $spec eq '%Oj'  && do
    {
      my $jj = 30 * $self->month + $self->day - 31; # %j is 1..366, but $jj is 0..365
      my $lb = substr $DAYS[$jj], 1;
      $lb =~ s/_/ /g;
      return $lb;
    };
  return $spec;
}

sub date_string {
  my ($self, $format) = @_;

  # Default value when not provided. I do not test true / false, because
  # some adventurous mind could think that "0" is a valid format, even if false.
  $format = "%e %B %EY" if (! defined $format or $format eq '');

  my $year = roman($self->year); # possibly to trigger the side effect
  my $month = $self->month;
  $format =~ s/(          # start of $1
                              \%         # percent sign
                              (?:        # start of alternative
                                (?:O.)    # extended field specifier: O with a second char
                              |          # or
                                (?:E.)    # other extended field specifier: E with a second char
                              |          # or
                                .         # basic field specifier: single char
                              ))         # end of alternative and end of $1
                            /'$self->field($1)'/eegx; # is there a simpler way to do it?

  return $format;
}

# A module must return a true value. Traditionally, a module returns 1.
# But this module is a revolutionary one, so it discards all old traditions.
"Liberté, égalité, fraternité
ou la mort !";

__END__