docs: public FluentInitializer init(Date minDate, Date maxDate, TimeZone timeZone, Locale locale) {
      if (minDate == null || maxDate == null) {
        throw new IllegalArgumentException(
            "minDate and maxDate must be non-null.  " + dbg(minDate, maxDate));
      }
      if (minDate.after(maxDate)) {
        throw new IllegalArgumentException(
            "minDate must be before maxDate.  " + dbg(minDate, maxDate));
      }
      if (locale == null) {
        throw new IllegalArgumentException("Locale is null.");
      }
      if (timeZone == null) {
        throw new IllegalArgumentException("Time zone is null.");
      }
  
      // Make sure that all calendar instances use the same time zone and locale.
      this.timeZone = timeZone;
      this.locale = locale;
      today = Calendar.getInstance(timeZone, locale);
      minCal = Calendar.getInstance(timeZone, locale);
      maxCal = Calendar.getInstance(timeZone, locale);
      monthCounter = Calendar.getInstance(timeZone, locale);
      for (MonthDescriptor month : months) {
        month.setLabel(formatMonthDate(month.getDate()));
      }
      weekdayNameFormat =
          new SimpleDateFormat(getContext().getString(R.string.day_name_format), locale);
      weekdayNameFormat.setTimeZone(timeZone);
      fullDateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM, locale);
      fullDateFormat.setTimeZone(timeZone);
      monthFormatter = new Formatter(monthBuilder, locale);
  
      this.selectionMode = SelectionMode.SINGLE;
      // Clear out any previously-selected dates/cells.
      selectedCals.clear();
      selectedCells.clear();
      highlightedCals.clear();
      highlightedCells.clear();
  
      // Clear previous state.
      cells.clear();
      months.clear();
      minCal.setTime(minDate);
      maxCal.setTime(maxDate);
      setMidnight(minCal);
      setMidnight(maxCal);
      displayOnly = false;
  
      // maxDate is exclusive: bump back to the previous day so if maxDate is the first of a month,
      // we don't accidentally include that month in the view.
      maxCal.add(MINUTE, -1);
  
      // Now iterate between minCal and maxCal and build up our list of months to show.
      monthCounter.setTime(minCal.getTime());
      final int maxMonth = maxCal.get(MONTH);
      final int maxYear = maxCal.get(YEAR);
      while ((monthCounter.get(MONTH) <= maxMonth // Up to, including the month.
          || monthCounter.get(YEAR) < maxYear) // Up to the year.
          && monthCounter.get(YEAR) < maxYear + 1) { // But not > next yr.
        Date date = monthCounter.getTime();
        MonthDescriptor month =
            new MonthDescriptor(monthCounter.get(MONTH), monthCounter.get(YEAR),
                    date, formatMonthDate(date));
        cells.put(monthKey(month), getMonthCells(month, monthCounter));
        Logr.d("Adding month %s", month);
        months.add(month);
        monthCounter.add(MONTH, 1);
      }
  
      validateAndUpdate();
      return new FluentInitializer();
    }