require('DateTime')
var month_names = %w(
Vendémiaire Brumaire Frimaire Nivôse Pluviôse Ventôse
Germinal Floréal Prairial Messidor Thermidor Fructidor
)
var 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',
]
var i_cal_month = 13
var epoch = %O<DateTime>.new(year => 1792, month => 9, day => 22)
var month_nums = Hash(month_names.kv.map {|p| [p[1], p[0]+1] }.flat...)
var i_cal_nums = Hash(intercalary.kv.map {|p| [p[1], p[0]+1] }.flat...)
func is_republican_leap_year(Number year) -> Bool {
var y = (year + 1)
!!(4.divides(y) && (!100.divides(y) || 400.divides(y)))
}
func Republican_to_Gregorian(String rep_date) -> String {
static months = month_names.map { .escape }.join('|')
static intercal = intercalary.map { .escape }.join('|')
static re = Regex("^
\\s* (?:
(?<ic> #{intercal})
| (?<day> \\d+) \\s+ (?<month> #{months})
)
\\s+ (?<year> \\d+)
\\s*
\\z", 'x')
var m = (rep_date =~ re)
m || die "Republican date not recognized: '#{rep_date}'"
var ncap = m.named_captures
var day1 = Number(ncap{:ic} ? i_cal_nums{ncap{:ic}} : ncap{:day})
var month1 = Number(ncap{:month} ? month_nums{ncap{:month}} : i_cal_month)
var year1 = Number(ncap{:year})
var days_since_epoch = (365*(year1 - 1) + 30*(month1 - 1) + (day1 - 1))
var leap_days = (1 ..^ year1 -> grep { is_republican_leap_year(_) })
epoch.clone.add(days => (days_since_epoch + leap_days)).strftime("%Y-%m-%d")
}
func Gregorian_to_Republican(String greg_date) -> String {
var m = (greg_date =~ /^(\d{4})-(\d{2})-(\d{2})\z/)
m || die "Gregorian date not recognized: '#{greg_date}'"
var g = %O<DateTime>.new(year => m[0], month => m[1], day => m[2])
var days_since_epoch = epoch.delta_days(g).in_units('days')
days_since_epoch < 0 && die "unexpected error"
var (year, days) = (1, days_since_epoch)
loop {
var year_length = (365 + (is_republican_leap_year(year) ? 1 : 0))
days < year_length && break
days -= year_length
year += 1;
}
var day0 = (days % 30)
var month0 = (days - day0)/30
var (day1, month1) = (day0 + 1, month0 + 1)
(month1 == i_cal_month
? "#{intercalary[day0]} #{year}"
: "#{day1} #{month_names[month0]} #{year}")
}
for line in DATA {
line.sub!(/\s*\#.+\R?\z/, '')
var m = (line =~ /^(\d{4})-(\d{2})-(\d{2})\s+(\S.+?\S)\s*$/)
m || die "error for: #{line.dump}"
var g = "#{m[0]}-#{m[1]}-#{m[2]}"
var r = m[3]
var r2g = Republican_to_Gregorian(r)
var g2r = Gregorian_to_Republican(g)
if ((g2r != r) || (r2g != g)) {
die "1-way error"
}
if ((Gregorian_to_Republican(r2g) != r) ||
(Republican_to_Gregorian(g2r) != g)
) {
die "2-way error"
}
}
say 'All tests successful.'
__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
1871-05-23 3 Prairial 79
1799-11-09 18 Brumaire 8
1804-12-02 11 Frimaire 13
1794-10-30 9 Brumaire 3
1794-07-27 9 Thermidor 2
1799-05-27 8 Prairial 7
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
Output:
All tests successful.