Palindrome Dates

Date: 2019-09-18 · Word Count: 1203 · Reading Time: 6 minutes

Recently I saw a meme going around about how 9-19-19 was the last day where we’d have palindrome dates in our lifetimes. Of course, my immediate thought was, “Well, maybe if you restrict yourself to short form US style dates and you’re already pretty old. I wonder how many palindrome dates there really are to experience over the next human lifetime?”.

So, what date formats are available to us and in common use?

  • ISO: YYYY-MM-DD
  • European: DD-MM-YYYY
  • USA: MM-DD-YYYY

Now, being human, we also often see short versions:

  • European: D-M-YY
  • USA: M-D-YY

Or versions with just the year shortened:

  • European: DD-MM-YY
  • USA: MM-DD-YY

And some people got burned in the year 2000 mess, so we see short day and month, with a long year:

  • European: D-M-YYYY
  • USA: M-D-YYYY

Then there’s the question of what is a lifetime. A quick look at Wikipedia, gives us quite a range, with the ~83 years of Japan given as a high national average. Rounding to 80 seems like it’ll give us a reasonable view of the average human perspective on palindrome dates, especially given that almost no one is going to remember such an event if they’re under 3.

Of course, calculating this by hand would be annoying, so I got my computer to help out…

package main

import (
  "fmt"
  "sort"
  "strings"
  "time"
)

func Reverse(s string) string {
  runes := []rune(s)
  for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
    runes[i], runes[j] = runes[j], runes[i]
  }
  return string(runes)
}

func check_date(d time.Time, format string, format_name string, debug bool) bool {
  date_str := d.Format(format)
  rev_str := Reverse(date_str)
  if strings.Compare(date_str, rev_str) == 0 {
    if debug {
      fmt.Println(format_name + " matches: " + d.Format(format))
    }
    return true
  }
  return false
}

func main() {
  date, _ := time.Parse("2006-01-02", "2019-01-01")
  palindates := 0
  palindates_per_decade := make(map[int]int)
  palindates_per_decade_per_style := make(map[int]map[string]int)
  palindate_styles := map[string]string{
    "ISO (YYYY-MM-DD)":                                  "20060102",
    "European (DD-MM-YYYY)":                             "02012006",
    "European (short; D-M-YY)":                          "2106",
    "European (short year, padded day/month; DD-MM-YY)": "020106",
    "European (short day/month, full year; D-M-YYYY)":   "212006",
    "USA (MM-DD-YYYY)":                                  "01022006",
    "USA (short; M-D-YY)":                               "1206",
    "USA (short year, padded day/month; MM-DD-YY)":      "010206",
    "USA (short day/month, full year; M-D-YYYY)":        "122006",
  }
  palindates_by_type := map[string]int{
    "ISO (YYYY-MM-DD)":                                  0,
    "European (DD-MM-YYYY)":                             0,
    "European (short; D-M-YY)":                          0,
    "European (short year, padded day/month; DD-MM-YY)": 0,
    "European (short day/month, full year; D-M-YYYY)":   0,
    "USA (MM-DD-YYYY)":                                  0,
    "USA (short; M-D-YY)":                               0,
    "USA (short year, padded day/month; MM-DD-YY)":      0,
    "USA (short day/month, full year; M-D-YYYY)":        0,
  }
  var palindates_per_decade_keys []int
  var palindates_style_keys []string
  for k := range palindate_styles {
    palindates_style_keys = append(palindates_style_keys, k)
  }
  sort.Strings(palindates_style_keys)

  // Look forward over the next 80(ish) years, which is on the higher side of
  // average life expectancy
  for i := 0; i <= 365*80; i++ {
    debug := false
    date = date.Add(time.Hour * 24)
    day_is_palindrome := false

    for k, v := range palindate_styles {
      p := check_date(date, v, k, debug)
      if p {
        palindates_by_type[k]++
        day_is_palindrome = true
      }
    }
    if day_is_palindrome {
      palindates++
    }

    if (i%365)/10 == 0 {
      palindates_per_decade[i/365/10] = palindates
      palindates_per_decade_per_style[i/365/10] = make(map[string]int)
      for _, p := range palindates_style_keys {
        palindates_per_decade_per_style[i/365/10][p] = palindates_by_type[p]
      }
    }
  }

  for k := range palindates_per_decade {
    palindates_per_decade_keys = append(palindates_per_decade_keys, k)
  }
  sort.Ints(palindates_per_decade_keys)
  for _, k := range palindates_per_decade_keys {
    fmt.Println("If you lived", k, "decades from 2019-01-01, you would see", palindates_per_decade[k], "palindates broken down by:")
    for _, p := range palindates_style_keys {
      fmt.Println("*", p+":", palindates_per_decade_per_style[k][p])
    }
    fmt.Println()
  }
}

Which provided us with the following breakdown:

If you lived 0 decades from 2019-01-01, you would see 131 palindates broken down by:

  • European (DD-MM-YYYY): 3
  • European (short day/month, full year; D-M-YYYY): 1
  • European (short year, padded day/month; DD-MM-YY): 3
  • European (short; D-M-YY): 31
  • ISO (YYYY-MM-DD): 2
  • USA (MM-DD-YYYY): 2
  • USA (short day/month, full year; M-D-YYYY): 8
  • USA (short year, padded day/month; MM-DD-YY): 4
  • USA (short; M-D-YY): 98

If you lived 1 decades from 2019-01-01, you would see 203 palindates broken down by:

  • European (DD-MM-YYYY): 6
  • European (short day/month, full year; D-M-YYYY): 1
  • European (short year, padded day/month; DD-MM-YY): 6
  • European (short; D-M-YY): 60
  • ISO (YYYY-MM-DD): 3
  • USA (MM-DD-YYYY): 3
  • USA (short day/month, full year; M-D-YYYY): 16
  • USA (short year, padded day/month; MM-DD-YY): 6
  • USA (short; M-D-YY): 137

If you lived 2 decades from 2019-01-01, you would see 248 palindates broken down by:

  • European (DD-MM-YYYY): 9
  • European (short day/month, full year; D-M-YYYY): 1
  • European (short year, padded day/month; DD-MM-YY): 9
  • European (short; D-M-YY): 89
  • ISO (YYYY-MM-DD): 4
  • USA (MM-DD-YYYY): 4
  • USA (short day/month, full year; M-D-YYYY): 18
  • USA (short year, padded day/month; MM-DD-YY): 8
  • USA (short; M-D-YY): 149

If you lived 3 decades from 2019-01-01, you would see 290 palindates broken down by:

  • European (DD-MM-YYYY): 12
  • European (short day/month, full year; D-M-YYYY): 1
  • European (short year, padded day/month; DD-MM-YY): 12
  • European (short; D-M-YY): 118
  • ISO (YYYY-MM-DD): 5
  • USA (MM-DD-YYYY): 5
  • USA (short day/month, full year; M-D-YYYY): 18
  • USA (short year, padded day/month; MM-DD-YY): 10
  • USA (short; M-D-YY): 158

If you lived 4 decades from 2019-01-01, you would see 332 palindates broken down by:

  • European (DD-MM-YYYY): 15
  • European (short day/month, full year; D-M-YYYY): 1
  • European (short year, padded day/month; DD-MM-YY): 15
  • European (short; D-M-YY): 147
  • ISO (YYYY-MM-DD): 6
  • USA (MM-DD-YYYY): 6
  • USA (short day/month, full year; M-D-YYYY): 18
  • USA (short year, padded day/month; MM-DD-YY): 12
  • USA (short; M-D-YY): 167

If you lived 5 decades from 2019-01-01, you would see 374 palindates broken down by:

  • European (DD-MM-YYYY): 18
  • European (short day/month, full year; D-M-YYYY): 1
  • European (short year, padded day/month; DD-MM-YY): 18
  • European (short; D-M-YY): 176
  • ISO (YYYY-MM-DD): 7
  • USA (MM-DD-YYYY): 7
  • USA (short day/month, full year; M-D-YYYY): 18
  • USA (short year, padded day/month; MM-DD-YY): 14
  • USA (short; M-D-YY): 176

If you lived 6 decades from 2019-01-01, you would see 417 palindates broken down by:

  • European (DD-MM-YYYY): 21
  • European (short day/month, full year; D-M-YYYY): 1
  • European (short year, padded day/month; DD-MM-YY): 21
  • European (short; D-M-YY): 205
  • ISO (YYYY-MM-DD): 8
  • USA (MM-DD-YYYY): 8
  • USA (short day/month, full year; M-D-YYYY): 18
  • USA (short year, padded day/month; MM-DD-YY): 16
  • USA (short; M-D-YY): 185

If you lived 7 decades from 2019-01-01, you would see 459 palindates broken down by:

  • European (DD-MM-YYYY): 24
  • European (short day/month, full year; D-M-YYYY): 1
  • European (short year, padded day/month; DD-MM-YY): 24
  • European (short; D-M-YY): 234
  • ISO (YYYY-MM-DD): 9
  • USA (MM-DD-YYYY): 9
  • USA (short day/month, full year; M-D-YYYY): 18
  • USA (short year, padded day/month; MM-DD-YY): 18
  • USA (short; M-D-YY): 194

If you lived 8 decades from 2019-01-01, you would see 461 palindates broken down by:

  • European (DD-MM-YYYY): 24
  • European (short day/month, full year; D-M-YYYY): 1
  • European (short year, padded day/month; DD-MM-YY): 24
  • European (short; D-M-YY): 235
  • ISO (YYYY-MM-DD): 9
  • USA (MM-DD-YYYY): 9
  • USA (short day/month, full year; M-D-YYYY): 18
  • USA (short year, padded day/month; MM-DD-YY): 18
  • USA (short; M-D-YY): 195

If you’re confused by the “0 decades” output, that’s the initial year: 2019. The numbers also don’t add up as there’s some overlap.

While ISO is the best date system for general use (it’s sortable and there’s no d/m vs m/d confusion), it lags in volume for this use case at this point in time. Of course, if you’re really looking to get the most bang for your buck, taking advantage of all the possible approaches to writing dates is going to give you a lot more opportunities to celebrate palindromic dates.