docs: def validate_analysis_period(self):
          """Get a collection where the header analysis_period aligns with datetimes.
  
          This means that checks for four criteria will be performed:
          1) All days in the data collection are chronological starting from the
              analysis_period start day to the end day.
          2) No duplicate days exist in the data collection.
          3) There are no days that lie outside of the analysis_period time range.
          4) February 29th is excluded if is_leap_year is False on the analysis_period.
  
          Note that there is no need to run this check any time that a discontinous
          data collection has been derived from a continuous one or when the
          validated_a_period attribute of the collection is True.
          """
          a_per = self.header.analysis_period
          n_ap = [a_per.st_month, a_per.st_day, a_per.st_hour, a_per.end_month,
                  a_per.end_day, a_per.end_hour, a_per.timestep, a_per.is_leap_year]
  
          # check that the analysis_period leap_year is correct.
          if a_per.is_leap_year is False:
              for date_t in self.datetimes:
                  if date_t == 366:
                      n_ap[7] = True
  
          # make sure that datetimes are all in chronological order.
          sort_datetimes, sort_values = zip(*sorted(zip(self.datetimes, self.values)))
          if not a_per.is_reversed and not a_per.is_annual:
              if sort_datetimes[0] < a_per.st_time.doy:
                  new_start = DateTime.from_hoy((sort_datetimes[0] - 1) * 24, n_ap[7])
                  n_ap[0] = new_start.month
                  n_ap[1] = new_start.day
              if sort_datetimes[-1] > a_per.end_time.doy:
                  new_end = DateTime.from_hoy((sort_datetimes[-1] - 1) * 24, n_ap[7])
                  n_ap[3] = new_end.month
                  n_ap[4] = new_end.day
          elif a_per.is_reversed:
              last_ind = None
              for i, date_t in enumerate(sort_datetimes):
                  last_ind = i if date_t <= a_per.end_time.doy else last_ind
              if last_ind is not None:
                  last_ind = last_ind + 1
                  sort_datetimes = sort_datetimes[last_ind:] + sort_datetimes[:last_ind]
                  sort_values = sort_values[last_ind:] + sort_values[:last_ind]
              # If datetimes are outside the a_period range, just make it annual.
              # There's no way to know what side of the analysis_period should be etended.
              if sort_datetimes[0] > a_per.end_time.doy and \
                      sort_datetimes[0] < a_per.st_time.doy:
                  n_ap[0], n_ap[1], n_ap[3], n_ap[4] = 1, 1, 12, 31
                  sort_datetimes, sort_values = zip(*sorted(zip(
                      self.datetimes, self.values)))
  
          # check that there are no duplicate days.
          for i in xrange(len(sort_datetimes)):
              assert sort_datetimes[i] != sort_datetimes[i - 1], 'Duplicate day of year ' \
                  'was found in the collection: {}'.format(sort_datetimes[i])
  
          # build a validated collection.
          new_ap = AnalysisPeriod(*n_ap)
          new_header = self.header.duplicate()
          new_header._analysis_period = new_ap
          new_coll = DailyCollection(new_header, sort_values, sort_datetimes)
          new_coll._validated_a_period = True
          return new_coll