Dennis Schubert

The (leap) day, the profiles died

2016-03-05 diaspora, software

On Monday, something weird happened. Without any changes, our CI status turned into a very dark red. Some contributors got scared because their Pull Requests were red and even the untouched stable release code suddenly failed. So… what happened?

Like all profile fields, the birthday field in a users profile is entirely optional. Not only can the users decide to not fill in the information, they are also free to enter only a day and a month, but not a year. Internally, the birthday field is a date field. Obviously, you cannot store a time stamp without a year so we needed a distinctive dummy value. Using 1970 is pretty dangerous since people could actually be born on that year, so we used:

params["year"] = "1000" if params["year"].blank?

Later, when showing the birth date in the users profile, we simply check for that value

def birthday_format(bday)
  if bday.year == 1_000
    I18n.l bday, format: I18n.t("date.formats.birthday")
  else
    I18n.l bday, format: I18n.t("date.formats.birthday_with_year")
  end
end

Simple, right?

Well, no. In our test suite, we do have a user which has a birthday and month, but no year. So the year gets replaced with the default year, 1000. Since our tests ran on Tuesday and we use the current month and day, the birthday was 1000-02-29. So, as it turned out, databases do not like that:

PG::DatetimeFieldOverflow: ERROR: date/time field value out of range: "1000-02-29"

Bummer. If we do the maths, year 1000 was not a leap year and apparently, databases check for that. Fun, eh? The solution is simple: use 1004 as a dummy year - or never run test suites on leap days. You decide.