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

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-1) * 100 / 36525
    if repLeap(y) {
        y--
    }
    d = day - (y+1)*36525/100 + 365 + (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++
                sansculottides = 5
                if repLeap(y) {
                    sansculottides = 6
                }
            }
        }
    }
    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)
}