You are here

class FacetsDateHandler in Facets 8

Dates Handler service.

Hierarchy

Expanded class hierarchy of FacetsDateHandler

1 file declares its use of FacetsDateHandler
FacetsDateHandlerTest.php in tests/src/Unit/Utility/FacetsDateHandlerTest.php
1 string reference to 'FacetsDateHandler'
facets.services.yml in ./facets.services.yml
facets.services.yml
1 service uses FacetsDateHandler
facets.utility.date_handler in ./facets.services.yml
Drupal\facets\Utility\FacetsDateHandler

File

src/Utility/FacetsDateHandler.php, line 10

Namespace

Drupal\facets\Utility
View source
class FacetsDateHandler {

  /**
   * String that represents a time gap of a day between two dates.
   */
  const FACETS_DATE_DAY = 'DAY';

  /**
   * String that represents a time gap of a year between two dates.
   */
  const FACETS_DATE_YEAR = 'YEAR';

  /**
   * String that represents a time gap of a month between two dates.
   */
  const FACETS_DATE_MONTH = 'MONTH';

  /**
   * String that represents a time gap of an hour between two dates.
   */
  const FACETS_DATE_HOUR = 'HOUR';

  /**
   * String that represents a time gap of a minute between two dates.
   */
  const FACETS_DATE_MINUTE = 'MINUTE';

  /**
   * String that represents a time gap of a second between two dates.
   */
  const FACETS_DATE_SECOND = 'SECOND';

  /**
   * Date string for ISO 8601 date formats.
   */
  const FACETS_DATE_ISO8601 = 'Y-m-d\\TH:i:s\\Z';

  /**
   * Regex pattern for range queries.
   */
  const FACETS_REGEX_RANGE = '/^[\\[\\{](\\S+) TO (\\S+)[\\]\\}]$/';

  /**
   * Regex pattern for date queries.
   */
  const FACETS_REGEX_DATE = '/^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})Z$/';

  /**
   * Regex pattern for date ranges.
   */
  const FACETS_REGEX_DATE_RANGE = '/^\\[((\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})Z) TO ((\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})Z)\\]$/';

  /**
   * The date formatting service.
   *
   * @var \Drupal\Core\Datetime\DateFormatterInterface
   */
  protected $dateFormatter;

  /**
   * FacetsDateHandler constructor.
   *
   * @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
   *   The date formatting service.
   */
  public function __construct(DateFormatterInterface $date_formatter) {
    $this->dateFormatter = $date_formatter;
  }

  /**
   * Converts dates from Unix timestamps into ISO 8601 format.
   *
   * @param int $timestamp
   *   An integer containing the Unix timestamp being converted.
   * @param string $gap
   *   A string containing the gap, see FACETS_DATE_* constants for valid
   *   values. Defaults to FACETS_DATE_SECOND.
   *
   * @return string
   *   A string containing the date in ISO 8601 format.
   */
  public function isoDate($timestamp, $gap = 'SECOND') {
    switch ($gap) {
      case static::FACETS_DATE_SECOND:
        $format = static::FACETS_DATE_ISO8601;
        break;
      case static::FACETS_DATE_MINUTE:
        $format = 'Y-m-d\\TH:i:00\\Z';
        break;
      case static::FACETS_DATE_HOUR:
        $format = 'Y-m-d\\TH:00:00\\Z';
        break;
      case static::FACETS_DATE_DAY:
        $format = 'Y-m-d\\T00:00:00\\Z';
        break;
      case static::FACETS_DATE_MONTH:
        $format = 'Y-m-01\\T00:00:00\\Z';
        break;
      case static::FACETS_DATE_YEAR:
        $format = 'Y-01-01\\T00:00:00\\Z';
        break;
      default:
        $format = static::FACETS_DATE_ISO8601;
        break;
    }
    return gmdate($format, $timestamp);
  }

  /**
   * Return a date gap one increment smaller than the one passed.
   *
   * @param string $gap
   *   A string containing the gap, see FACETS_DATE_* constants for valid
   *   values.
   * @param string $min_gap
   *   A string containing the minimum gap that can be returned, defaults to
   *   FACETS_DATE_SECOND. This is useful for defining the smallest increment
   *   that can be used in a date drilldown.
   *
   * @return string
   *   A string containing the smaller date gap, NULL if there is no smaller
   *   gap. See FACETS_DATE_* constants for valid values.
   */
  public function getNextDateGap($gap, $min_gap = self::FACETS_DATE_SECOND) {

    // Array of numbers used to determine whether the next gap is smaller than
    // the minimum gap allowed in the drilldown.
    $gap_numbers = [
      static::FACETS_DATE_YEAR => 6,
      static::FACETS_DATE_MONTH => 5,
      static::FACETS_DATE_DAY => 4,
      static::FACETS_DATE_HOUR => 3,
      static::FACETS_DATE_MINUTE => 2,
      static::FACETS_DATE_SECOND => 1,
    ];

    // Gets gap numbers for both the gap and minimum gap, checks if the next gap
    // is within the limit set by the $min_gap parameter.
    $gap_num = isset($gap_numbers[$gap]) ? $gap_numbers[$gap] : 6;
    $min_num = isset($gap_numbers[$min_gap]) ? $gap_numbers[$min_gap] : 1;
    return $gap_num > $min_num ? array_search($gap_num - 1, $gap_numbers) : $min_gap;
  }

  /**
   * Determines the best search gap to use for an arbitrary date range.
   *
   * Generally, we use the maximum gap that fits between the start and end date.
   * If they are more than a year apart, 1 year; if they are more than a month
   * apart, 1 month; etc.
   *
   * This function uses Unix timestamps for its computation and so is not useful
   * for dates outside that range.
   *
   * @param int $start_time
   *   A string containing the start date as an ISO date string.
   * @param int $end_time
   *   A string containing the end date as an ISO date string.
   * @param string|null $min_gap
   *   (Optional) The minimum gap that should be returned.
   *
   * @return string
   *   A string containing the gap, see FACETS_DATE_* constants for valid
   *   values. Returns FALSE of either of the dates cannot be converted to a
   *   timestamp.
   */
  public function getTimestampGap($start_time, $end_time, $min_gap = NULL) {
    $time_diff = $end_time - $start_time;
    switch (TRUE) {
      case $time_diff >= 31536000:
        $gap = static::FACETS_DATE_YEAR;
        break;
      case $time_diff >= 86400 * gmdate('t', $start_time):
        $gap = static::FACETS_DATE_MONTH;
        break;
      case $time_diff >= 86400:
        $gap = static::FACETS_DATE_DAY;
        break;
      case $time_diff >= 3600:
        $gap = static::FACETS_DATE_HOUR;
        break;
      case $time_diff >= 60:
        $gap = static::FACETS_DATE_MINUTE;
        break;
      default:
        $gap = static::FACETS_DATE_SECOND;
        break;
    }

    // Return the calculated gap if a minimum gap was not passed of the
    // calculated gap is a larger interval than the minimum gap.
    if (is_null($min_gap) || $this
      ->gapCompare($gap, $min_gap) >= 0) {
      return $gap;
    }
    else {
      return $min_gap;
    }
  }

  /**
   * Converts ISO date strings to Unix timestamps.
   *
   * Passes values to the FACETS_get_timestamp_gap() function to calculate the
   * gap.
   *
   * @param string $start_date
   *   A string containing the start date as an ISO date string.
   * @param string $end_date
   *   A string containing the end date as an ISO date string.
   * @param string|null $min_gap
   *   (Optional) The minimum gap that should be returned.
   *
   * @return string
   *   A string containing the gap, see FACETS_DATE_* constants for valid
   *   values. Returns FALSE of either of the dates cannot be converted to a
   *   timestamp.
   *
   * @see FACETS_get_timestamp_gap()
   */
  public function getDateGap($start_date, $end_date, $min_gap = NULL) {
    $range = [
      strtotime($start_date),
      strtotime($end_date),
    ];
    if (!in_array(FALSE, $range, TRUE)) {
      return $this
        ->getTimestampGap($range[0], $range[1], $min_gap);
    }
    return FALSE;
  }

  /**
   * Returns a formatted date based on the passed timestamp and gap.
   *
   * This function assumes that gaps less than one day will be displayed in a
   * search context in which a larger containing gap including a day is already
   * displayed. So, HOUR, MINUTE, and SECOND gaps only display time information,
   * without date.
   *
   * @param int $timestamp
   *   An integer containing the Unix timestamp.
   * @param string $gap
   *   A string containing the gap, see FACETS_DATE_* constants for valid
   *   values, defaults to YEAR.
   *
   * @return string
   *   A gap-appropriate display date used in the facet link.
   */
  public function formatTimestamp($timestamp, $gap = self::FACETS_DATE_YEAR) {
    switch ($gap) {
      case static::FACETS_DATE_MONTH:
        return $this->dateFormatter
          ->format($timestamp, 'custom', 'F Y', 'UTC');
      case static::FACETS_DATE_DAY:
        return $this->dateFormatter
          ->format($timestamp, 'custom', 'F j, Y', 'UTC');
      case static::FACETS_DATE_HOUR:
        return $this->dateFormatter
          ->format($timestamp, 'custom', 'g A', 'UTC');
      case static::FACETS_DATE_MINUTE:
        return $this->dateFormatter
          ->format($timestamp, 'custom', 'g:i A', 'UTC');
      case static::FACETS_DATE_SECOND:
        return $this->dateFormatter
          ->format($timestamp, 'custom', 'g:i:s A', 'UTC');
      default:
        return $this->dateFormatter
          ->format($timestamp, 'custom', 'Y', 'UTC');
    }
  }

  /**
   * Returns a formatted date based on the passed ISO date string and gap.
   *
   * @param string $date
   *   A string containing the date as an ISO date string.
   * @param int $gap
   *   An integer containing the gap, see FACETS_DATE_* constants for valid
   *   values, defaults to YEAR.
   * @param string $callback
   *   The formatting callback, defaults to "FACETS_format_timestamp". This is
   *   a string that can be called as a valid callback.
   *
   * @return string
   *   A gap-appropriate display date used in the facet link.
   *
   * @see FACETS_format_timestamp()
   */
  public function formatDate($date, $gap = self::FACETS_DATE_YEAR, $callback = 'facets_format_timestamp') {
    $timestamp = strtotime($date);
    return $callback($timestamp, $gap);
  }

  /**
   * Returns the next increment from the given ISO date and gap.
   *
   * This function is useful for getting the upper limit of a date range from
   * the given start date.
   *
   * @param string $date
   *   A string containing the date as an ISO date string.
   * @param string $gap
   *   A string containing the gap, see FACETS_DATE_* constants for valid
   *   values, defaults to YEAR.
   *
   * @return string
   *   A string containing the date, FALSE if the passed date could not be
   *   parsed.
   */
  public function getNextDateIncrement($date, $gap) {
    if (preg_match(static::FACETS_REGEX_DATE, $date, $match)) {

      // Increments the timestamp.
      switch ($gap) {
        case static::FACETS_DATE_MONTH:
          $match[2] += 1;
          break;
        case static::FACETS_DATE_DAY:
          $match[3] += 1;
          break;
        case static::FACETS_DATE_HOUR:
          $match[4] += 1;
          break;
        case static::FACETS_DATE_MINUTE:
          $match[5] += 1;
          break;
        case static::FACETS_DATE_SECOND:
          $match[6] += 1;
          break;
        default:
          $match[1] += 1;
          break;
      }

      // Gets the next increment.
      return $this
        ->isoDate(gmmktime($match[4], $match[5], $match[6], $match[2], $match[3], $match[1]));
    }
    return FALSE;
  }

  /**
   * Compares two timestamp gaps.
   *
   * @param int $gap1
   *   An integer containing the gap, see FACETS_DATE_* constants for valid
   *   values.
   * @param int $gap2
   *   An integer containing the gap, see FACETS_DATE_* constants for valid
   *   values.
   *
   * @return int
   *   Returns -1 if gap1 is less than gap2, 1 if gap1 is greater than gap2, and
   *   0 if they are equal.
   */
  public function gapCompare($gap1, $gap2) {
    $gap_numbers = [
      static::FACETS_DATE_YEAR => 6,
      static::FACETS_DATE_MONTH => 5,
      static::FACETS_DATE_DAY => 4,
      static::FACETS_DATE_HOUR => 3,
      static::FACETS_DATE_MINUTE => 2,
      static::FACETS_DATE_SECOND => 1,
    ];
    $gap1_num = isset($gap_numbers[$gap1]) ? $gap_numbers[$gap1] : 6;
    $gap2_num = isset($gap_numbers[$gap2]) ? $gap_numbers[$gap2] : 6;
    if ($gap1_num == $gap2_num) {
      return 0;
    }
    else {
      return $gap1_num < $gap2_num ? -1 : 1;
    }
  }

  /**
   * Extracts "start" and "end" dates from an active item.
   *
   * @param string $item
   *   The active item to extract the dates.
   *
   * @return mixed
   *   Returns FALSE if no item found and an array with the dates if the dates
   *    were extracted as expected.
   */
  public function extractActiveItems($item) {
    $active_item = [];
    if (preg_match(static::FACETS_REGEX_DATE_RANGE, $item, $matches)) {
      $active_item['start'] = [
        'timestamp' => strtotime($matches[1]),
        'iso' => $matches[1],
      ];
      $active_item['end'] = [
        'timestamp' => strtotime($matches[8]),
        'iso' => $matches[8],
      ];
      return $active_item;
    }
    return FALSE;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
FacetsDateHandler::$dateFormatter protected property The date formatting service.
FacetsDateHandler::extractActiveItems public function Extracts "start" and "end" dates from an active item.
FacetsDateHandler::FACETS_DATE_DAY constant String that represents a time gap of a day between two dates.
FacetsDateHandler::FACETS_DATE_HOUR constant String that represents a time gap of an hour between two dates.
FacetsDateHandler::FACETS_DATE_ISO8601 constant Date string for ISO 8601 date formats.
FacetsDateHandler::FACETS_DATE_MINUTE constant String that represents a time gap of a minute between two dates.
FacetsDateHandler::FACETS_DATE_MONTH constant String that represents a time gap of a month between two dates.
FacetsDateHandler::FACETS_DATE_SECOND constant String that represents a time gap of a second between two dates.
FacetsDateHandler::FACETS_DATE_YEAR constant String that represents a time gap of a year between two dates.
FacetsDateHandler::FACETS_REGEX_DATE constant Regex pattern for date queries.
FacetsDateHandler::FACETS_REGEX_DATE_RANGE constant Regex pattern for date ranges.
FacetsDateHandler::FACETS_REGEX_RANGE constant Regex pattern for range queries.
FacetsDateHandler::formatDate public function Returns a formatted date based on the passed ISO date string and gap.
FacetsDateHandler::formatTimestamp public function Returns a formatted date based on the passed timestamp and gap.
FacetsDateHandler::gapCompare public function Compares two timestamp gaps.
FacetsDateHandler::getDateGap public function Converts ISO date strings to Unix timestamps.
FacetsDateHandler::getNextDateGap public function Return a date gap one increment smaller than the one passed.
FacetsDateHandler::getNextDateIncrement public function Returns the next increment from the given ISO date and gap.
FacetsDateHandler::getTimestampGap public function Determines the best search gap to use for an arbitrary date range.
FacetsDateHandler::isoDate public function Converts dates from Unix timestamps into ISO 8601 format.
FacetsDateHandler::__construct public function FacetsDateHandler constructor.