code: def _normalize_day(year, month, day):
      
      if year < MIN_YEAR or year > MAX_YEAR:
          raise ValueError("Year out of range (%d..%d)" % (MIN_YEAR, MAX_YEAR))
      if month < 1 or month > 12:
          raise ValueError("Month out of range (1..12)")
      days_in_month = DAYS_IN_MONTH[(year, month)]
      if day in (days_in_month, -1):
          return year, month, -1
      if day in (days_in_month - 1, -2):
          return year, month, -2
      if day in (days_in_month - 2, -3):
          return year, month, -3
      if 1 <= day <= days_in_month - 3:
          return year, month, int(day)
      # TODO improve this error message
      raise ValueError("Day %d out of range (1..%d, -1, -2 ,-3)" % (day, days_in_month))
docs: Coerce the day of the month to an internal value that may or
      may not match the "public" value.
  
      With the exception of the last three days of every month, all
      days are stored as-is. The last three days are instead stored
      as -1 (the last), -2 (first from last) and -3 (second from last).
  
      Therefore, for a 28-day month, the last week is as follows:
  
          Day   | 22 23 24 25 26 27 28
          Value | 22 23 24 25 -3 -2 -1
  
      For a 29-day month, the last week is as follows:
  
          Day   | 23 24 25 26 27 28 29
          Value | 23 24 25 26 -3 -2 -1
  
      For a 30-day month, the last week is as follows:
  
          Day   | 24 25 26 27 28 29 30
          Value | 24 25 26 27 -3 -2 -1
  
      For a 31-day month, the last week is as follows:
  
          Day   | 25 26 27 28 29 30 31
          Value | 25 26 27 28 -3 -2 -1
  
      This slightly unintuitive system makes some temporal arithmetic
      produce a more desirable outcome.
  
      :param year:
      :param month:
      :param day:
      :return: