# French Republican calendar

French Republican calendar is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

Write a program to convert dates between the Gregorian calendar and the French Republican calendar.

The year 1 of the Republican calendar began on 22 September 1792. There were twelve months (Vendémiaire, Brumaire, Frimaire, Nivôse, Pluviôse, Ventôse, Germinal, Floréal, Prairial, Messidor, Thermidor, and Fructidor) of 30 days each, followed by five intercalary days or Sansculottides (Fête de la vertu / Virtue Day, Fête du génie / Talent Day, Fête du travail / Labour Day, Fête de l'opinion / Opinion Day, and Fête des récompenses / Honours Day). In leap years (the years 3, 7, and 11) a sixth Sansculottide was added: Fête de la Révolution / Revolution Day.

As a minimum, your program should give correct results for dates in the range from 1 Vendémiaire 1 = 22 September 1792 to 10 Nivôse 14 = 31 December 1805 (the last day when the Republican calendar was officially in use). If you choose to accept later dates, be aware that there are several different methods (described on the Wikipedia page) about how to determine leap years after the year 14. You should indicate which method you are using. (Because of these different methods, correct programs may sometimes give different results for dates after 1805.)

Test your program by converting the following dates both from Gregorian to Republican and from Republican to Gregorian:

• 1 Vendémiaire 1 = 22 September 1792

• 1 Prairial 3 = 20 May 1795

• 27 Messidor 7 = 15 July 1799 (Rosetta Stone discovered)

• Fête de la Révolution 11 = 23 September 1803

• 10 Nivôse 14 = 31 December 1805

## BBC BASIC

Computes leap years using the "continuous" method: a year in the Republican calendar is a leap year if and only if the number of the following year is divisible by 4 but not by 100 unless also by 400. No attempt is made to deal with ill-formed or invalid input dates.

`REM >frrepcal:DIM gregorian\$(11)DIM gregorian%(11)DIM republican\$(11)DIM sansculottides\$(5)gregorian\$() = "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"gregorian%() = 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31REM 7-bit ASCII encoding, so no accents on French wordsrepublican\$() = "Vendemiaire", "Brumaire", "Frimaire", "Nivose", "Pluviose", "Ventose", "Germinal", "Floreal", "Prairial", "Messidor", "Thermidor", "Fructidor"sansculottides\$() = "Fete de la vertu", "Fete du genie", "Fete du travail", "Fete de l'opinion", "Fete des recompenses", "Fete de la Revolution":PRINT "*** French  Republican ***"PRINT "*** calendar converter ***"PRINT "Enter a date to convert, in the format 'day month year'"PRINT "e.g.: 1 Prairial 3,"PRINT "      20 May 1795."PRINT "For Sansculottides, use 'day year'"PRINT "e.g.: Fete de l'opinion 9."PRINT "Or just press 'RETURN' to exit the program."PRINTREPEAT  INPUT LINE "> " src\$  IF src\$ <> "" THEN    PROC_split(src\$, day%, month%, year%)    REM for simplicity, we assume that years up to 1791 are Republican    REM and years from 1792 onwards are Gregorian    IF year% < 1792 THEN      REM convert Republican date to number of days elapsed      REM since 21 September 1792, then convert that number      REM to the Gregorian date      PROC_day_to_gre(FN_rep_to_day(day%, month%, year%), day%, month%, year%)      PRINT; day%; " "; gregorian\$(month% - 1); " " year%    ELSE      REM convert Gregorian date to Republican, via      REM number of days elapsed since 21 September 1792      PROC_day_to_rep(FN_gre_to_day(day%, month%, year%), day%, month%, year%)      IF month% = 13 THEN        PRINT sansculottides\$(day% - 1); " "; year%      ELSE        PRINT; day%; " "; republican\$(month% - 1); " "; year%      ENDIF    ENDIF  ENDIFUNTIL src\$ = ""END:DEF PROC_split(s\$, RETURN d%, RETURN m%, RETURN y%)LOCAL month_and_year\$, month\$, months\$(), i%DIM months\$(11)IF LEFT\$(s\$, 4) = "Fete" THEN  m% = 13  FOR i% = 0 TO 5    IF LEFT\$(s\$, LEN sansculottides\$(i%)) = sansculottides\$(i%) THEN      d% = i% + 1      y% = VAL(RIGHT\$(s\$, LEN s\$ - LEN sansculottides\$(i%) - 1))    ENDIF  NEXTELSE  d% = VAL(LEFT\$(s\$, INSTR(s\$, " ") - 1))  month_and_year\$ = MID\$(s\$, INSTR(s\$, " ") + 1)  month\$ = LEFT\$(month_and_year\$, INSTR(month_and_year\$, " ") - 1)  y% = VAL(MID\$(month_and_year\$, INSTR(month_and_year\$, " ") + 1))  IF y% < 1792 THEN months\$() = republican\$() ELSE months\$() = gregorian\$()  FOR i% = 0 TO 11    IF months\$(i%) = month\$ THEN m% = i% + 1  NEXTENDIFENDPROC:DEF FN_gre_to_day(d%, m%, y%)REM modified & repurposed from code given atREM https://www.staff.science.uu.nl/~gent0113/calendar/isocalendar_text5.htmIF m% < 3 THEN  y% -= 1  m% += 12ENDIF= INT(365.25 * y%) - INT(y% / 100) + INT(y% / 400) + INT(30.6 * (m% + 1)) + d% - 654842:DEF FN_rep_to_day(d%, m%, y%)REM assume that a year is a leap year iff the _following_ year isREM divisible by 4, but not by 100 unless also by 400REMREM other methods for computing Republican leap years existIF m% = 13 THEN  m% -= 1  d% += 30ENDIFIF FN_rep_leap(y%) THEN d% -= 1= 365 * y% + INT((y% + 1) / 4) - INT((y% + 1) / 100) + INT((y% + 1) / 400) + 30 * m% + d% - 395:DEF PROC_day_to_gre(day%, RETURN d%, RETURN m%, RETURN y%)y% = INT(day% / 365.25)d% = day% - INT(365.25 * y%) + 21y% += 1792d% += INT(y% / 100) - INT(y% / 400) - 13m% = 8WHILE d% > gregorian%(m%)  d% -= gregorian%(m%)  m% += 1  IF m% = 12 THEN    m% = 0    y% += 1    IF FN_gre_leap(y%) THEN gregorian%(1) = 29 ELSE gregorian%(1) = 28  ENDIFENDWHILEm% += 1ENDPROC:DEF PROC_day_to_rep(day%, RETURN d%, RETURN m%, RETURN y%)LOCAL sansculottides%y% = INT(day% / 365.25)IF FN_rep_leap(y%) THEN y% -= 1d% = day% - INT(365.25 * y%) + INT((y% + 1) / 100) - INT((y% + 1) / 400)y% += 1m% = 1IF FN_rep_leap(y%) THEN sansculottides% = 6 ELSE sansculottides% = 5WHILE d% > 30  d% -= 30  m% += 1  IF m% = 13 THEN    IF d% > sansculottides% THEN      d% -= sansculottides%      m% = 1      y% += 1    ENDIF  ENDIFENDWHILEENDPROC:DEF FN_rep_leap(year%)REM see comment at the beginning of FN_rep_to_day= ((year% + 1) MOD 4 = 0 AND ((year% + 1) MOD 100 <> 0 OR (year% + 1) MOD 400 = 0)):DEF FN_gre_leap(year%)= (year% MOD 4 = 0 AND (year% MOD 100 <> 0 OR year% MOD 400 = 0))`

Output for the test dates:

```*** French  Republican ***
*** calendar converter ***
Enter a date to convert, in the format 'day month year'
e.g.: 1 Prairial 3,
20 May 1795.
For Sansculottides, use 'day year'
e.g.: Fete de l'opinion 9.
Or just press 'RETURN' to exit the program.

> 1 Vendemiaire 1
22 September 1792
> 22 September 1792
1 Vendemiaire 1
> 1 Prairial 3
20 May 1795
> 20 May 1795
1 Prairial 3
> 27 Messidor 7
15 July 1799
> 15 July 1799
27 Messidor 7
> Fete de la Revolution 11
23 September 1803
> 23 September 1803
Fete de la Revolution 11
> 10 Nivose 14
31 December 1805
> 31 December 1805
10 Nivose 14```

Output for a few subsequent dates:

```> 18 March 1871
27 Ventose 79
> 25 August 1944
7 Fructidor 152
> 19 September 2016
Fete du travail 224```

## Go

Translation of: BBC Basic

A rather literal port, just for reference. Far from idiomatic Go.

`package main import (    "bufio"    "fmt"    "os"    "strconv"    "strings") var (    gregorianStr = []string{"January", "February", "March",        "April", "May", "June",        "July", "August", "September",        "October", "November", "December"}    gregorian     = []int{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}    republicanStr = []string{"Vendemiaire", "Brumaire", "Frimaire",        "Nivose", "Pluviose", "Ventose",        "Germinal", "Floreal", "Prairial",        "Messidor", "Thermidor", "Fructidor"}    sansculottidesStr = []string{"Fete de la vertu", "Fete du genie",        "Fete du travail", "Fete de l'opinion",        "Fete des recompenses", "Fete de la Revolution"}) func main() {    fmt.Println("*** French  Republican ***")    fmt.Println("*** calendar converter ***")    fmt.Println("Enter a date to convert, in the format 'day month year'")    fmt.Println("e.g.: 1 Prairial 3,")    fmt.Println("      20 May 1795.")    fmt.Println("For Sansculottides, use 'day year'")    fmt.Println("e.g.: Fete de l'opinion 9.")    fmt.Println("Or just press 'RETURN' to exit the program.")    fmt.Println()    for sc := bufio.NewScanner(os.Stdin); ; {        fmt.Print("> ")        sc.Scan()        src := sc.Text()        if src == "" {            return        }        day, month, year := split(src)        if year < 1792 {            day, month, year = dayToGre(repToDay(day, month, year))            fmt.Println(day, gregorianStr[month-1], year)        } else {            day, month, year := dayToRep(greToDay(day, month, year))            if month == 13 {                fmt.Println(sansculottidesStr[day-1], year)            } else {                fmt.Println(day, republicanStr[month-1], year)            }        }    }} func split(s string) (d, m, y int) {    if strings.HasPrefix(s, "Fete") {        m = 13        for i, sc := range sansculottidesStr {            if strings.HasPrefix(s, sc) {                d = i + 1                y, _ = strconv.Atoi(s[len(sc)+1:])            }        }    } else {        d, _ = strconv.Atoi(s[:strings.Index(s, " ")])        my := s[strings.Index(s, " ")+1:]        mStr := my[:strings.Index(my, " ")]        y, _ = strconv.Atoi(my[strings.Index(my, " ")+1:])        months := gregorianStr        if y < 1792 {            months = republicanStr        }        for i, mn := range months {            if mn == mStr {                m = i + 1            }        }    }    return} func greToDay(d, m, y int) int {    if m < 3 {        y--        m += 12    }    return y*36525/100 - y/100 + y/400 + 306*(m+1)/10 + d - 654842} func repToDay(d, m, y int) int {    if m == 13 {        m--        d += 30    }    if repLeap(y) {        d--    }    return 365*y + (y+1)/4 - (y+1)/100 + (y+1)/400 + 30*m + d - 395} func dayToGre(day int) (d, m, y int) {    y = day * 100 / 36525    d = day - y*36525/100 + 21    y += 1792    d += y/100 - y/400 - 13    m = 8    for d > gregorian[m] {        d -= gregorian[m]        m++        if m == 12 {            m = 0            y++            if greLeap(y) {                gregorian[1] = 29            } else {                gregorian[1] = 28            }        }    }    m++    return} func dayToRep(day int) (d, m, y int) {    y = day * 100 / 36525    if repLeap(y) {        y--    }    d = day - y*36525/100 + (y+1)/100 - (y+1)/400    y++    m = 1    sansculottides := 5    if repLeap(y) {        sansculottides = 6    }    for d > 30 {        d -= 30        m += 1        if m == 13 {            if d > sansculottides {                d -= sansculottides                m = 1                y++            }        }    }    return} func repLeap(year int) bool {    return (year+1)%4 == 0 && ((year+1)%100 != 0 || (year+1)%400 == 0)} func greLeap(year int) bool {    return year%4 == 0 && (year%100 != 0 || year%400 == 0)}`
Output:
```\$ go run frc.go
*** French  Republican ***
*** calendar converter ***
Enter a date to convert, in the format 'day month year'
e.g.: 1 Prairial 3,
20 May 1795.
For Sansculottides, use 'day year'
e.g.: Fete de l'opinion 9.
Or just press 'RETURN' to exit the program.

> 1 Vendemiaire 1
22 September 1792
> 22 September 1792
1 Vendemiaire 1
> 1 Prairial 3
20 May 1795
> 20 May 1795
1 Prairial 3
> 27 Messidor 7
15 July 1799
> 15 July 1799
27 Messidor 7
> Fete de la Revolution 11
23 September 1803
> 23 September 1803
Fete de la Revolution 11
> 10 Nivose 14
31 December 1805
> 31 December 1805
10 Nivose 14
>
\$
```

More idiomatic

A start anyway. Computations extracted to a package, a type defined for French Republican Dates, time package from standard library used. Ignores invalid input rather than panicking.

`package frc import (    "fmt"    "strconv"    "strings"    "time") type Date struct {    Year  int    Month int    Day   int} func (d Date) String() string {    if d.Month == 13 {        return fmt.Sprintf("%s %d", sansculotidesStr[d.Day-1], d.Year)    }    return fmt.Sprintf("%d %s %d", d.Day, republicanStr[d.Month-1], d.Year)} func Parse(s string) (dt Date, ok bool) {    f := strings.Fields(s)    var err error    switch {    case len(f) == 3:        if dt.Day, err = strconv.Atoi(f[0]); err != nil {            return        }        i := 0        for republicanStr[i] != f[1] {            i++            if i == len(republicanStr) {                return            }        }        dt.Month = i + 1        if dt.Year, err = strconv.Atoi(f[2]); err != nil {            return        }        ok = true        return    case len(f) > 3:        for i, sc := range sansculotidesStr {            if strings.HasPrefix(s, sc) {                dt.Month = 13                dt.Day = i + 1                dt.Year, err = strconv.Atoi(s[len(sc)+1:])                if err == nil {                    ok = true                }                return            }        }    }    return} func (dt Date) ToGregorian() (y int, m time.Month, d int) {    yr, mn, dy := dayToGre(repToDay(dt.Day, dt.Month, dt.Year))    return yr, time.Month(mn), dy} func FromGregorian(y int, m time.Month, d int) Date {    day, month, year := dayToRep(greToDay(d, int(m), y))    return Date{Year: year, Month: month, Day: day}}var (    gregorian     = []int{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}    republicanStr = []string{"Vendemiaire", "Brumaire", "Frimaire",        "Nivose", "Pluviose", "Ventose",        "Germinal", "Floreal", "Prairial",        "Messidor", "Thermidor", "Fructidor"}    sansculotidesStr = []string{"Fete de la vertu", "Fete du genie",        "Fete du travail", "Fete de l'opinion",        "Fete des recompenses", "Fete de la Revolution"}) func greToDay(d, m, y int) int {    if m < 3 {        y--        m += 12    }    return y*36525/100 - y/100 + y/400 + 306*(m+1)/10 + d - 654842} func repToDay(d, m, y int) int {    if m == 13 {        m--        d += 30    }    if repLeap(y) {        d--    }    return 365*y + (y+1)/4 - (y+1)/100 + (y+1)/400 + 30*m + d - 395} func dayToGre(day int) (d, m, y int) {    y = day * 100 / 36525    d = day - y*36525/100 + 21    y += 1792    d += y/100 - y/400 - 13    m = 8    for d > gregorian[m] {        d -= gregorian[m]        m++        if m == 12 {            m = 0            y++            if greLeap(y) {                gregorian[1] = 29            } else {                gregorian[1] = 28            }        }    }    m++    return} func dayToRep(day int) (d, m, y int) {    y = day * 100 / 36525    if repLeap(y) {        y--    }    d = day - y*36525/100 + (y+1)/100 - (y+1)/400    y++    m = 1    sansculottides := 5    if repLeap(y) {        sansculottides = 6    }    for d > 30 {        d -= 30        m += 1        if m == 13 {            if d > sansculottides {                d -= sansculottides                m = 1                y++            }        }    }    return} func repLeap(year int) bool {    return (year+1)%4 == 0 && ((year+1)%100 != 0 || (year+1)%400 == 0)} func greLeap(year int) bool {    return year%4 == 0 && (year%100 != 0 || year%400 == 0)}`
`package main import (    "bufio"    "fmt"    "os"    "time"     "frc") func main() {    fmt.Println("*** French  Republican ***")    fmt.Println("*** calendar converter ***")    fmt.Println("Enter a date to convert, in the format 'day month year'")    fmt.Println("e.g.: 1 Prairial 3,")    fmt.Println("      20 May 1795.")    fmt.Println("For Sansculottides, use 'day year'")    fmt.Println("e.g.: Fete de l'opinion 9.")    fmt.Println("Or just press 'RETURN' to exit the program.")    fmt.Println()    for sc := bufio.NewScanner(os.Stdin); ; {        fmt.Print("> ")        sc.Scan()        src := sc.Text()        if src == "" {            return        }        f, ok := frc.Parse(src)        if ok {            fmt.Println(f.ToGregorian())            continue        }        t, err := time.Parse(`2 January 2006`, src)        if err == nil {            fmt.Println(frc.FromGregorian(t.Date()))        }    }}`

## Kotlin

`// version 1.1.4-3 import java.time.format.DateTimeFormatterimport java.time.LocalDateimport java.time.temporal.ChronoUnit.DAYS /* year = 1..  month = 1..13  day = 1..30 */ class FrenchRCDate(val year: Int, val month: Int, val day: Int) {     init {        require (year > 0 && month in 1..13)        if (month < 13) require (day in 1..30)        else {            val leap = isLeapYear(year)            require (day in (if (leap) 1..6 else 1..5))        }    }     override fun toString() =        if (month < 13) "\$day \${months[month - 1]} \$year"        else "\${intercal[day - 1]} \$year"     fun toLocalDate(): LocalDate {        var sumDays = 0L        for (i in 1 until year) sumDays += if (isLeapYear(i)) 366 else 365        val dayInYear = (month - 1) * 30 + day - 1        return introductionDate.plusDays(sumDays + dayInYear)    }     companion object {        /* uses the 'continuous method' for years after 1805 */        fun isLeapYear(y: Int): Boolean {            val yy = y + 1            return (yy % 4 == 0) && (yy % 100 != 0 || yy % 400 == 0)        }         fun parse(frcDate: String): FrenchRCDate {            val splits = frcDate.trim().split(' ')            if (splits.size == 3) {                val month = months.indexOf(splits[1]) + 1                require(month in 1..13)                val year = splits[2].toIntOrNull() ?: 0                require(year > 0)                val monthLength = if (month < 13) 30 else if (isLeapYear(year)) 6 else 5                val day = splits[0].toIntOrNull() ?: 0                require(day in 1..monthLength)                return FrenchRCDate(year, month, day)            }            else if (splits.size in 4..5) {                val yearStr = splits[splits.lastIndex]                val year = yearStr.toIntOrNull() ?: 0                require(year > 0)                val scDay = frcDate.trim().dropLast(yearStr.length + 1)                val day = intercal.indexOf(scDay) + 1                val maxDay = if (isLeapYear(year)) 6 else 5                require (day in 1..maxDay)                return FrenchRCDate(year, 13, day)            }            else throw IllegalArgumentException("Invalid French Republican date")        }         /* for convenience we treat 'Sansculottide' as an extra month with 5 or 6 days */        val months = arrayOf(            "Vendémiaire", "Brumaire", "Frimaire", "Nivôse", "Pluviôse", "Ventôse", "Germinal",            "Floréal", "Prairial", "Messidor", "Thermidor", "Fructidor", "Sansculottide"        )         val intercal = arrayOf(            "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"        )         val introductionDate = LocalDate.of(1792, 9, 22)    }} fun LocalDate.toFrenchRCDate(): FrenchRCDate {    val daysDiff  = DAYS.between(FrenchRCDate.introductionDate, this).toInt() + 1    if (daysDiff <= 0) throw IllegalArgumentException("Date can't be before 22 September 1792")    var year = 1    var startDay = 1    while (true) {        val endDay = startDay + if (FrenchRCDate.isLeapYear(year)) 365 else 364        if (daysDiff in startDay..endDay) break        year++        startDay = endDay + 1    }    val remDays = daysDiff - startDay    val month  = remDays / 30    val day = remDays - month * 30    return FrenchRCDate(year, month + 1, day + 1)} fun main(args: Array<String>) {    val formatter = DateTimeFormatter.ofPattern("d MMMM yyyy")    val dates = arrayOf("22 September 1792", "20 May 1795", "15 July 1799", "23 September 1803",                        "31 December 1805", "18 March 1871", "25 August 1944", "19 September 2016",                        "22 September 2017", "28 September 2017")    val frcDates = Array<String>(dates.size) { "" }    for ((i, date) in dates.withIndex()) {        val thisDate = LocalDate.parse(date, formatter)        val frcd = thisDate.toFrenchRCDate()        frcDates[i] = frcd.toString()        println("\${date.padEnd(25)} => \$frcd")    }     // now process the other way around    println()    for (frcDate in frcDates) {        val thisDate = FrenchRCDate.parse(frcDate)        val lds = formatter.format(thisDate.toLocalDate())        println("\${frcDate.padEnd(25)} => \$lds")    }}`
Output:
```22 September 1792         => 1 Vendémiaire 1
20 May 1795               => 1 Prairial 3
15 July 1799              => 27 Messidor 7
23 September 1803         => Fête de la Révolution 11
31 December 1805          => 10 Nivôse 14
18 March 1871             => 27 Ventôse 79
25 August 1944            => 7 Fructidor 152
19 September 2016         => Fête du travail 224
22 September 2017         => 1 Vendémiaire 226
28 September 2017         => 7 Vendémiaire 226

1 Vendémiaire 1           => 22 September 1792
1 Prairial 3              => 20 May 1795
27 Messidor 7             => 15 July 1799
Fête de la Révolution 11  => 23 September 1803
10 Nivôse 14              => 31 December 1805
27 Ventôse 79             => 18 March 1871
7 Fructidor 152           => 25 August 1944
Fête du travail 224       => 19 September 2016
1 Vendémiaire 226         => 22 September 2017
7 Vendémiaire 226         => 28 September 2017
```

## Perl

`use feature 'state';use DateTime;my @month_names = qw{    Vendémiaire Brumaire Frimaire  Nivôse   Pluviôse  Ventôse    Germinal    Floréal  Prairial  Messidor Thermidor Fructidor};my @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',);my %month_nums  = map { \$month_names[\$_] => \$_+1 } 0 .. \$#month_names;my %i_cal_nums  = map { \$intercalary[\$_] => \$_+1 } 0 .. \$#intercalary;my \$i_cal_month = 13;my \$epoch       = DateTime->new( year => 1792, month => 9, day => 22 ); sub is_republican_leap_year {    my \$y = \$_[0] + 1;    return !!( (\$y % 4)==0 and ((\$y % 100)!=0 or (\$y % 400)==0) );} sub Republican_to_Gregorian {    my (\$rep_date) = @_;    state \$months   = join '|', map { quotemeta } @month_names;    state \$intercal = join '|', map { quotemeta } @intercalary;    state \$re = qr{      \A        \s* (?:                (?<ic> \$intercal)              | (?<day> \d+) \s+ (?<month> \$months)            )        \s+ (?<year> \d+)        \s*      \z    }msx;     \$rep_date =~ /\$re/        or 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 \$year1  = \$+{year};     my \$days_since_epoch = (\$year1-1) * 365 + (\$month1-1) * 30 + (\$day1-1);     my \$leap_days = grep { is_republican_leap_year(\$_) } 1 .. \$year1-1;    return \$epoch->clone->add( days => (\$days_since_epoch + \$leap_days) );} sub Gregorian_to_Republican {    my (\$greg_date) = @_;     my \$days_since_epoch = \$epoch->delta_days(\$greg_date)->in_units('days');    die if \$days_since_epoch < 0;    my ( \$year, \$days ) = ( 1, \$days_since_epoch );    while (1) {        my \$year_length = 365 + ( is_republican_leap_year(\$year) ? 1 : 0 );        last if \$days < \$year_length;        \$days -= \$year_length;        \$year += 1;    }    my \$day0   = \$days % 30;    my \$month0 = (\$days - \$day0) / 30;     my ( \$day1, \$month1 ) = ( \$day0 + 1, \$month0 + 1 );     return \$month1 == \$i_cal_month        ?       "\$intercalary[\$day0  ] \$year"        : "\$day1 \$month_names[\$month0] \$year";} while (<DATA>) {    s{\s*\#.+\n?\z}{};    /^(\d{4})-(\d{2})-(\d{2})\s+(\S.+?\S)\s*\$/ or die;     my \$g = DateTime->new( year => \$1, month => \$2, day => \$3 );    my \$r = \$4;     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.'; __DATA__1792-09-22  1 Vendémiaire 11795-05-20  1 Prairial 31799-07-15  27 Messidor 71803-09-23  Fête de la Révolution 111805-12-31  10 Nivôse 141871-03-18  27 Ventôse 791944-08-25  7 Fructidor 1522016-09-19  Fête du travail 2241871-05-06  16 Floréal 79   # Paris Commune begins1871-05-23  3 Prairial 79   # Paris Commune ends1799-11-09  18 Brumaire 8   # Revolution ends by Napoléon coup1804-12-02  11 Frimaire 13  # Republic   ends by Napoléon coronation1794-10-30  9 Brumaire 3    # École Normale Supérieure established1794-07-27  9 Thermidor 2   # Robespierre falls1799-05-27  8 Prairial 7    # Fromental Halévy born1792-09-22  1 Vendémiaire 11793-09-22  1 Vendémiaire 21794-09-22  1 Vendémiaire 31795-09-23  1 Vendémiaire 41796-09-22  1 Vendémiaire 51797-09-22  1 Vendémiaire 61798-09-22  1 Vendémiaire 71799-09-23  1 Vendémiaire 81800-09-23  1 Vendémiaire 91801-09-23  1 Vendémiaire 101802-09-23  1 Vendémiaire 111803-09-24  1 Vendémiaire 121804-09-23  1 Vendémiaire 131805-09-23  1 Vendémiaire 141806-09-23  1 Vendémiaire 151807-09-24  1 Vendémiaire 161808-09-23  1 Vendémiaire 171809-09-23  1 Vendémiaire 181810-09-23  1 Vendémiaire 191811-09-24  1 Vendémiaire 202015-09-23  1 Vendémiaire 2242016-09-22  1 Vendémiaire 2252017-09-22  1 Vendémiaire 226 `
Output:
`All tests successful.`

## Perl 6

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

## Sidef

Translation of: Perl
`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 = 13var 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)     #say "#{r} -> #{r2g}"    #say "#{g} -> #{g2r}"     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 11795-05-20  1 Prairial 31799-07-15  27 Messidor 71803-09-23  Fête de la Révolution 111805-12-31  10 Nivôse 141871-03-18  27 Ventôse 791944-08-25  7 Fructidor 1522016-09-19  Fête du travail 2241871-05-06  16 Floréal 79   # Paris Commune begins1871-05-23  3 Prairial 79   # Paris Commune ends1799-11-09  18 Brumaire 8   # Revolution ends by Napoléon coup1804-12-02  11 Frimaire 13  # Republic   ends by Napoléon coronation1794-10-30  9 Brumaire 3    # École Normale Supérieure established1794-07-27  9 Thermidor 2   # Robespierre falls1799-05-27  8 Prairial 7    # Fromental Halévy born1792-09-22  1 Vendémiaire 11793-09-22  1 Vendémiaire 21794-09-22  1 Vendémiaire 31795-09-23  1 Vendémiaire 41796-09-22  1 Vendémiaire 51797-09-22  1 Vendémiaire 61798-09-22  1 Vendémiaire 71799-09-23  1 Vendémiaire 81800-09-23  1 Vendémiaire 91801-09-23  1 Vendémiaire 101802-09-23  1 Vendémiaire 111803-09-24  1 Vendémiaire 121804-09-23  1 Vendémiaire 131805-09-23  1 Vendémiaire 141806-09-23  1 Vendémiaire 151807-09-24  1 Vendémiaire 161808-09-23  1 Vendémiaire 171809-09-23  1 Vendémiaire 181810-09-23  1 Vendémiaire 191811-09-24  1 Vendémiaire 202015-09-23  1 Vendémiaire 2242016-09-22  1 Vendémiaire 2252017-09-22  1 Vendémiaire 226`
Output:
```All tests successful.
```