French Republican calendar
use v6;
constant @month_names = <
Vendémiaire Brumaire Frimaire Nivôse Pluviôse Ventôse
Germinal Floréal Prairial Messidor Thermidor Fructidor
>;
constant @intercalary =
'Fête de la vertu', 'Fête du génie', 'Fête du travail',
"Fête de l'opinion", 'Fête des récompenses', 'Fête de la Révolution',
;
constant %month_nums = %( @month_names Z=> 1..12 );
constant %i_cal_nums = %( @intercalary Z=> 1.. 6 );
constant $i_cal_month = 13;
constant $epoch = Date.new: '1792-09-22';
sub is_republican_leap_year ( Int:D $year --> Bool ) {
my $y := $year + 1;
return ?( $y %% 4 and ($y !%% 100 or $y %% 400) );
}
sub Republican_to_Gregorian ( Str:D $rep_date --> Date ) {
grammar Republican_date_text {
token day { \d+ }
token year { \d+ }
token ic { @intercalary }
token month { @month_names }
rule TOP { ^ [ <ic> | <day> <month> ] <year> $ }
}
Republican_date_text.parse($rep_date)
orelse die "Republican date not recognized: '$rep_date'";
my $day1 := $/<ic> ?? %i_cal_nums{~$/<ic>} !! +$/<day>;
my $month1 := $/<month> ?? %month_nums{~$/<month>} !! $i_cal_month;
my @ymd0 := ($/<year>, $month1, $day1) »-» 1;
my $days_since_epoch := [+] @ymd0 Z* (365, 30, 1);
my $leap_days := +grep &is_republican_leap_year, 1 ..^ $/<year>;
return $epoch + $days_since_epoch + $leap_days;
}
sub Gregorian_to_Republican ( Date:D $greg_date --> Str ) {
my $days_since_epoch := $greg_date - $epoch;
die if $days_since_epoch < 0;
my ( $year, $days ) = 1, $days_since_epoch;
loop {
my $year_length = 365 + (1 if $year.&is_republican_leap_year);
last if $days < $year_length;
$days -= $year_length;
$year += 1;
}
my ( $day0, $month0 ) = $days.polymod( 30 );
my ( $day1, $month1 ) = ($day0, $month0) X+ 1;
return $month1 == $i_cal_month
?? "@intercalary[$day0 ] $year"
!! "$day1 @month_names[$month0] $year";
}
my @test_data =
( '1792-09-22', '1 Vendémiaire 1' ),
( '1795-05-20', '1 Prairial 3' ),
( '1799-07-15', '27 Messidor 7' ),
( '1803-09-23', 'Fête de la Révolution 11' ),
( '1805-12-31', '10 Nivôse 14' ),
( '1871-03-18', '27 Ventôse 79' ),
( '1944-08-25', '7 Fructidor 152' ),
( '2016-09-19', 'Fête du travail 224' ),
( '1871-05-06', '16 Floréal 79' ), # Paris Commune begins
( '1871-05-23', '3 Prairial 79' ), # Paris Commune ends
( '1799-11-09', '18 Brumaire 8' ), # Revolution ends by Napoléon coup
( '1804-12-02', '11 Frimaire 13' ), # Republic ends by Napoléon coronation
( '1794-10-30', '9 Brumaire 3' ), # École Normale Supérieure established
( '1794-07-27', '9 Thermidor 2' ), # Robespierre falls
( '1799-05-27', '8 Prairial 7' ), # Fromental Halévy born
( '1792-09-22', '1 Vendémiaire 1' ),
( '1793-09-22', '1 Vendémiaire 2' ),
( '1794-09-22', '1 Vendémiaire 3' ),
( '1795-09-23', '1 Vendémiaire 4' ),
( '1796-09-22', '1 Vendémiaire 5' ),
( '1797-09-22', '1 Vendémiaire 6' ),
( '1798-09-22', '1 Vendémiaire 7' ),
( '1799-09-23', '1 Vendémiaire 8' ),
( '1800-09-23', '1 Vendémiaire 9' ),
( '1801-09-23', '1 Vendémiaire 10' ),
( '1802-09-23', '1 Vendémiaire 11' ),
( '1803-09-24', '1 Vendémiaire 12' ),
( '1804-09-23', '1 Vendémiaire 13' ),
( '1805-09-23', '1 Vendémiaire 14' ),
( '1806-09-23', '1 Vendémiaire 15' ),
( '1807-09-24', '1 Vendémiaire 16' ),
( '1808-09-23', '1 Vendémiaire 17' ),
( '1809-09-23', '1 Vendémiaire 18' ),
( '1810-09-23', '1 Vendémiaire 19' ),
( '1811-09-24', '1 Vendémiaire 20' ),
( '2015-09-23', '1 Vendémiaire 224' ),
( '2016-09-22', '1 Vendémiaire 225' ),
( '2017-09-22', '1 Vendémiaire 226' ),
;
for @test_data -> ( $g_text, $r ) {
my $g = Date.new: $g_text;
die if Republican_to_Gregorian($r) != $g
or Gregorian_to_Republican($g) ne $r;
die if Gregorian_to_Republican(Republican_to_Gregorian($r)) ne $r
or Republican_to_Gregorian(Gregorian_to_Republican($g)) != $g;
}
say 'All tests successful.';
Output:
All tests successful.
Last updated