You are here

datex_api_classes.inc in Datex 7

API and helper functions used by other datex modules.

File

datex_api/datex_api_classes.inc
View source
<?php

/**
 * @file
 * API and helper functions used by other datex modules.
 */

/**
 * whether to use PHP-Intl for date conversion or not. It's a default value and
 * can be set in module's settings form.
 */
define('DATEX_USE_INTL', FALSE);

/**
 * State of a given date.
 */
define('DATEX_GREGORIAN', TRUE);

/**
 * State of a given date.
 */
define('DATEX_JALALI', FALSE);

/**
 * Date tools for Jalali Dates.
 */
class DatexFormatter {
  protected static $daysInGregorianMonth = array(
    31,
    28,
    31,
    30,
    31,
    30,
    31,
    31,
    30,
    31,
    30,
    31,
  );
  protected static $daysInJalaliMonth = array(
    31,
    31,
    31,
    31,
    31,
    31,
    30,
    30,
    30,
    30,
    30,
    29,
  );

  /**
   * Similar to php date_format.
   *
   * @param mixed $date
   *   Date to format. For a list of accepted dates see ObjectFromDate.
   * @param DateTimeZone $tz
   *   DateTimeZone object or name (string) of timezone area.
   * @param bool $use_intl
   *   Wheter to use PHP-Intl or not, recomended: TRUE.
   * @param array $intl_args
   *   Arguments passed to PHP-Intl formatter.
   *
   * @return string
   *   formatted date.
   */
  public static function format($date, $format, $tz = NULL, $use_intl = DATEX_USE_INTL, $intl_args = array()) {
    if (self::hasINTL() && $use_intl) {
      return self::formatINTL($date, $format, $tz, $intl_args);
    }
    else {
      return self::formatPHP($date, $format, $tz);
    }
  }

  /**
   * Returns array containing names of days and monthes in persian.
   *
   * @todo replace with a translator function like Drupal's t().
   */
  public static function persianDateNames() {
    static $names = NULL;
    if (!$names) {
      $names = array(
        'months' => array(
          1 => 'فروردین',
          2 => 'اردیبهشت',
          3 => 'خرداد',
          4 => 'تیر',
          5 => 'مرداد',
          6 => 'شهریور',
          7 => 'مهر',
          8 => 'آبان',
          9 => 'آذر',
          10 => 'دی',
          11 => 'بهمن',
          12 => 'اسفند',
        ),
        'ampm' => array(
          'am' => 'ق.ظ',
          'pm' => 'ب.ظ',
        ),
        'day_abbr' => array(
          6 => 'ش.',
          7 => 'ی.',
          1 => 'د.',
          2 => 'س.',
          3 => 'چ.',
          4 => 'پ.',
          5 => 'ج.',
        ),
        'day' => array(
          6 => 'شنبه',
          7 => 'یک‌شنبه',
          1 => 'دوشنبه',
          2 => 'سه‌شنبه',
          3 => 'چهارشنبه',
          4 => 'پنج‌شنبه',
          5 => 'جمعه',
        ),
        'tz' => 'تهران',
      );
    }
    return $names;
  }

  /**
   * Converts a Gregorian date to Jalali.
   */
  public static function toJalali($gregorian_year = 0, $gregorian_month = 0, $gregorian_day = 0) {
    $now = getdate();
    $gregorian_year = ($gregorian_year ? $gregorian_year : $now['year']) - 1600;
    $gregorian_month = ($gregorian_month ? $gregorian_month : $now['mon']) - 1;
    $gregorian_day = ($gregorian_day ? $gregorian_day : $now['mday']) - 1;
    $gregorian_day_no = 365 * $gregorian_year + intval(($gregorian_year + 3) / 4) - intval(($gregorian_year + 99) / 100) + intval(($gregorian_year + 399) / 400);
    for ($i = 0; $i < $gregorian_month; ++$i) {
      $gregorian_day_no += self::$daysInGregorianMonth[$i];
    }
    if ($gregorian_month > 1 && ($gregorian_year % 4 == 0 && $gregorian_year % 100 != 0) || $gregorian_year % 400 == 0) {

      // Leap and after Feb.
      $gregorian_day_no++;
    }
    $gregorian_day_no += $gregorian_day;
    $jalali_day_no = $gregorian_day_no - 79;
    $j_np = intval($jalali_day_no / 12053);
    $jalali_day_no = $jalali_day_no % 12053;
    $j_year = 979 + 33 * $j_np + 4 * intval($jalali_day_no / 1461);
    $jalali_day_no %= 1461;
    if ($jalali_day_no >= 366) {
      $j_year += intval(($jalali_day_no - 1) / 365);
      $jalali_day_no = ($jalali_day_no - 1) % 365;
    }
    for ($i = 0; $i < 11 && $jalali_day_no >= self::$daysInJalaliMonth[$i]; ++$i) {
      $jalali_day_no -= self::$daysInJalaliMonth[$i];
    }
    $j_month = $i + 1;
    $j_day = $jalali_day_no + 1;
    return array(
      'day' => $j_day,
      'month' => $j_month,
      'year' => $j_year,
    );
  }

  /**
   * Converts a Jalali date to Gregorian.
   */
  public static function toGregorian($jalali_year = 0, $jalali_month = 0, $jalali_day = 0) {
    $now = self::toJalali();
    $jalali_year = ($jalali_year ? $jalali_year : $now['year']) - 979;
    $jalali_month = ($jalali_month ? $jalali_month : $now['month']) - 1;
    $jalali_day = ($jalali_day ? $jalali_day : $now['day']) - 1;
    $jalali_day_no = 365 * $jalali_year + intval($jalali_year / 33) * 8 + intval(($jalali_year % 33 + 3) / 4);
    for ($i = 0; $i < $jalali_month; ++$i) {
      $jalali_day_no += self::$daysInJalaliMonth[$i];
    }
    $jalali_day_no += $jalali_day;
    $gregorian_day_no = $jalali_day_no + 79;
    $g_year = 1600 + 400 * intval($gregorian_day_no / 146097);
    $gregorian_day_no = $gregorian_day_no % 146097;
    $leap = TRUE;
    if ($gregorian_day_no >= 36525) {
      $gregorian_day_no--;
      $g_year += 100 * intval($gregorian_day_no / 36524);
      $gregorian_day_no = $gregorian_day_no % 36524;
      if ($gregorian_day_no >= 365) {
        $gregorian_day_no++;
      }
      else {
        $leap = FALSE;
      }
    }
    $g_year += 4 * intval($gregorian_day_no / 1461);
    $gregorian_day_no %= 1461;
    if ($gregorian_day_no >= 366) {
      $leap = FALSE;
      $gregorian_day_no--;
      $g_year += intval($gregorian_day_no / 365);
      $gregorian_day_no = $gregorian_day_no % 365;
    }
    for ($i = 0; $gregorian_day_no >= self::$daysInGregorianMonth[$i] + ($i == 1 && $leap); $i++) {
      $gregorian_day_no -= self::$daysInGregorianMonth[$i] + ($i == 1 && $leap);
    }
    $g_month = $i + 1;
    $g_day = $gregorian_day_no + 1;
    return array(
      'year' => $g_year,
      'month' => $g_month,
      'day' => $g_day,
    );
  }

  /**
   * Converts date format string (like 'Y-m-d') to it's PHP-Intl equivilant.
   *
   * @param string $format
   *   Format accepted by php date_format.
   *
   * @return string
   *   Format accepted by PHP-Intl date formatter (ICU).
   */
  public static function phpToIntl($format) {
    static $format_map = NULL;
    if (!$format_map) {
      $format_map = array(
        'd' => 'dd',
        'D' => 'EEE',
        'j' => 'd',
        'l' => 'EEEE',
        'N' => 'e',
        'S' => 'LLLL',
        'w' => '',
        'z' => 'D',
        'W' => 'w',
        'm' => 'MM',
        'M' => 'MMM',
        'F' => 'MMMM',
        'n' => 'M',
        't' => '',
        'L' => '',
        'o' => 'yyyy',
        'y' => 'yy',
        'Y' => 'YYYY',
        'a' => 'a',
        'A' => 'a',
        'B' => '',
        'g' => 'h',
        'G' => 'H',
        'h' => 'hh',
        'H' => 'HH',
        'i' => 'mm',
        's' => 'ss',
        'u' => 'SSSSSS',
        'e' => 'z',
        'I' => '',
        'O' => 'Z',
        'P' => 'ZZZZ',
        'T' => 'v',
        'Z' => '',
        'c' => '',
        'r' => '',
        'U' => '',
        ' ' => ' ',
        '-' => '-',
        '.' => '.',
        '-' => '-',
        ':' => ':',
      );
    }
    $replace_pattern = '/[^ \\:\\-\\/\\.\\\\dDjlNSwzWmMFntLoyYaABgGhHisueIOPTZcrU]/';
    return strtr(preg_replace($replace_pattern, '', $format), $format_map);
  }

  /**
   * Formats a date according to format given.
   *
   * This function uses internal
   * methods for converting, See DatexFormatter::formatINTL is suggested
   * instead.
   */
  public static function formatPHP($date, $format, $tz) {
    $persian_date_names = self::persianDateNames();

    // For anyone reading this comment: Passing a DateTimeZone to datetime
    // constructor has no effect on it! You MUST use setTimezone to set a
    // tz on the stupid object.
    // Tested on PHP 5.4.15 (built: May 12 2013 13:11:23) Archlinux.
    $date = self::ObjectFromDate($date);
    if ($tz) {
      $tz = self::getTzObject($tz);
      $date
        ->setTimezone($tz);
    }
    $gregorian_date = array(
      'd' => intval($date
        ->format('j')),
      'm' => intval($date
        ->format('n')),
      'Y' => intval($date
        ->format('Y')),
    );
    $jalali_date = self::toJalali($gregorian_date['Y'], $gregorian_date['m'], $gregorian_date['d']);
    $z = $jalali_date['month'] <= 6 ? $jalali_date['month'] * 31 + $jalali_date['day'] : 186 + (($jalali_date['month'] - 6) * 30 + $jalali_date['day']);
    $format = preg_replace('/[\\\\][a-z]/', '', $format);
    $is_leap = self::isLeap($jalali_date['year']) ? 1 : 0;
    $replacements = array(
      'd' => sprintf('%02d', $jalali_date['day']),
      'D' => $persian_date_names['day_abbr'][$date
        ->format('N')],
      'j' => $jalali_date['day'],
      'l' => $persian_date_names['day'][$date
        ->format('N')],
      'S' => $persian_date_names['day_abbr'][$date
        ->format('N')],
      'F' => $persian_date_names['months'][$jalali_date['month']],
      'm' => sprintf('%02d', $jalali_date['month']),
      'n' => $jalali_date['month'],
      'L' => str_replace('L', $is_leap, $format),
      'Y' => $jalali_date['year'],
      'y' => $jalali_date['year'],
      'o' => $jalali_date['year'],
      'a' => $persian_date_names['ampm'][$date
        ->format('a')],
      'A' => $persian_date_names['ampm'][$date
        ->format('a')],
      'B' => $persian_date_names['ampm'][$date
        ->format('a')],
      'c' => $jalali_date['year'] . '-' . $jalali_date['month'] . '-' . $jalali_date['day'] . 'T' . $date
        ->format('H:i:sP'),
      'g' => $date
        ->format('g'),
      'G' => $date
        ->format('G'),
      'h' => $date
        ->format('h'),
      'H' => $date
        ->format('H'),
      'i' => $date
        ->format('i'),
      's' => $date
        ->format('s'),
      'u' => $date
        ->format('u'),
      'I' => $date
        ->format('I'),
      'O' => $date
        ->format('O'),
      'P' => $date
        ->format('P'),
      'T' => $date
        ->format('T'),
      'Z' => $date
        ->format('Z'),
      'U' => $date
        ->format('U'),
      'w' => $date
        ->format('w'),
      'N' => $date
        ->format('N'),
      'e' => $date
        ->format('e'),
      'z' => $z,
      'W' => ceil($z / 7),
      't' => self::$daysInJalaliMonth[$jalali_date['month'] - 1],
    );
    $replacements['r'] = $persian_date_names['day_abbr'][$date
      ->format('N')] . ', ' . $jalali_date['day'] . ' ' . $persian_date_names['months'][$jalali_date['month']] . ' ' . $jalali_date['year'] . $date
      ->format('H:i:s P');
    if ($is_leap && $jalali_date['month'] == 12) {
      $replacements['t'] = 30;
    }
    else {
      $replacements['t'] = self::$daysInJalaliMonth[$jalali_date['month'] - 1];
    }
    return strtr($format, $replacements);
  }

  /**
   * Formats a date according to format given.
   *
   * This function uses php-intl methods for converting. PECL package php-intl
   * must be enabled.
   */
  public static function formatINTL($date, $format, $tz, $formatter_args) {
    $intl_formatter = new IntlDateFormatter("fa_IR@calendar=persian", IntlDateFormatter::FULL, IntlDateFormatter::FULL, self::getTzString($tz), IntlDateFormatter::TRADITIONAL, self::phpToIntl($format));
    $date = $intl_formatter
      ->format(self::getTimestamp($date));

    // By default undecorates decorated date by intl.
    return isset($formatter_args['decor']) ? $date : self::decor($date, FALSE);
  }

  /**
   * Created a DateTime object containing date from $date.
   *
   * Accepted date formats are an integer (as timestamp), A DatexObject object,
   * A DateTime or DateObject object or an array.
   *
   * If an array is given and $gregorian is set to FALSE, Then Object is
   * extracted from a Jalali date. This is the only way of getting date from
   * jalali date.
   */
  public static function ObjectFromDate($date = NULL, $tz = NULL, $gregorian = TRUE) {

    // If is a number or a timestamp string: like 432432 or '432432', it
    // will become like '@432432'. This case covers both integer and string.
    if (is_numeric($date)) {
      $date = '@' . $date;
    }
    elseif (is_object($date)) {
      $date = '@' . $date
        ->format('U');
    }
    elseif (is_array($date)) {
      if (!$gregorian) {
        $greg_date = self::toGregorian(@$date['year'], @$date['month'], @$date['day']);
        $year = $greg_date['year'];
        $month = $greg_date['month'];
        $day = $greg_date['day'];
      }
      else {
        $year = isset($date['year']) ? intval($date['year']) : intval(date('Y'));
        $month = isset($date['month']) ? intval($date['month']) : intval(date('n'));
        $day = isset($date['day']) ? intval($date['day']) : intval(date('j'));
      }
      $hour = isset($date['hour']) ? intval($date['hour']) : intval(date('G'));
      $minute = isset($date['minute']) ? intval($date['minute']) : intval(date('i'));
      $second = isset($date['second']) ? intval($date['second']) : intval(date('s'));
      $date = '@' . mktime($hour, $minute, $second, $month, $day, $year);
    }
    elseif ($date == NULL) {
      $date = 'now';
    }

    // Why doesn't the freaking DateTime accept NULL az tz?!!
    return isset($tz) ? new DateTime($date, $tz) : new DateTime($date);
  }

  /**
   * Generates timestamp from given date.
   */
  public static function getTimestamp($date) {
    $date = self::ObjectFromDate($date);
    return intval($date
      ->format('U'));
  }

  /**
   * Determines wether PECL package php-intl is available or not.
   */
  public static function hasINTL() {
    return class_exists('IntlDateFormatter');
  }

  /**
   * Returns non zero if given year is a leap year.
   *
   * Algorithm author Amin Saeedi <amin.w3dev@gmail.com>,
   * Find him on github! @link http://github.org/amsa
   */
  public static function isLeap($year_value) {
    return array_search(($year_value + 2346) % 2820 % 128, array(
      5,
      9,
      13,
      17,
      21,
      25,
      29,
      34,
      38,
      42,
      46,
      50,
      54,
      58,
      62,
      67,
      71,
      75,
      79,
      83,
      87,
      91,
      95,
      100,
      104,
      108,
      112,
      116,
      120,
      124,
      0,
    ));
  }

  /**
   * Returns a valid timezone object from given timezone.
   *
   * Returns $tz itself if it is already a DateTimeZone object.
   */
  public static function getTzObject($tz = NULL) {
    if ($tz) {
      if (is_string($tz)) {
        $tz = new DateTimeZone($tz);
      }
      elseif (!$tz instanceof DateTimeZone) {
        $tz = new DateTimeZone(date_default_timezone_get());
      }
    }
    return $tz;
  }

  /**
   * Returns a valid timezone string from given timezone.
   */
  public static function getTzString($tz) {
    $tz = self::getTzObject($tz);
    return method_exists($tz, 'getName') ? $tz
      ->getName() : NULL;
  }

  /**
   * Translates English numbers to Persian numbers and vice versa.
   *
   * @param bool $decorate
   *   If true, Returned string contains Persian numbers and if false, will
   *   contain English numbers.
   */
  public static function decor($value, $decorate = FALSE) {
    static $to_en = array(
      '۱' => 1,
      '۲' => 2,
      '۳' => 3,
      '۴' => 4,
      '۵' => 5,
      '۶' => 6,
      '۷' => 7,
      '۸' => 8,
      '۹' => 9,
      '۰' => 0,
    );
    static $to_fa = array(
      1 => '۱',
      2 => '۲',
      3 => '۳',
      4 => '۴',
      5 => '۵',
      6 => '۶',
      7 => '۷',
      8 => '۸',
      9 => '۹',
      0 => '۰',
    );
    return strtr($value, $decorate ? $to_fa : $to_en);
  }

  /**
   * For extracting date from formatted date strings.
   */
  public static function parser($date, $format) {
    if (!(is_string($date) && is_string($format))) {
      throw new Exception('Datex parser: date or format not valid.');
    }
    $granularities = array();
    $j = $i = 0;
    $format_len = strlen($format);
    do {
      while (!ctype_alpha($format[$i]) && $i < $format_len) {
        $i++;
      }
      while (!ctype_digit($date[$j])) {
        $j++;
      }
      switch ($format[$i]) {
        case 'd':
          $granularities['day'] = $date[$j] . $date[++$j];
          break;
        case 'j':
          if (ctype_digit($date[$j + 1])) {
            $granularities['day'] = $date[$j] . $date[++$j];
          }
          else {
            $granularities['day'] = $date[$j];
          }
          break;
        case 'm':
          $granularities['month'] = $date[$j] . $date[++$j];
          break;
        case 'n':
          if (ctype_digit($date[$j + 1])) {
            $granularities['month'] = $date[$j] . $date[++$j];
          }
          else {
            $granularities['month'] = $date[$j];
          }
          break;
        case 'y':
        case 'Y':
          $granularities['year'] = $date[$j] . $date[++$j] . $date[++$j] . $date[++$j];
        case 'h':
          $granularities['hour'] = $date[$j] . $date[++$j];
          break;
        case 'g':
          if (ctype_digit($date[$j + 1])) {
            $granularities['hour'] = $date[$j] . $date[++$j];
          }
          else {
            $granularities['hour'] = $date[$j];
          }
          break;
        case 'H':
          $granularities['hour'] = $date[$j] . $date[++$j];
          break;
        case 'G':
          if (ctype_digit($date[$j + 1])) {
            $granularities['hour'] = $date[$j] . $date[++$j];
          }
          else {
            $granularities['hour'] = $date[$j];
          }
          break;
        case 'i':
          $granularities['minute'] = $date[$j] . $date[++$j];
          break;
        case 's':
          $granularities['second'] = $date[$j] . $date[++$j];
          break;
      }
      $j++;
    } while (++$i < $format_len);
    return $granularities;
  }

}

/**
 * This class is Jalali equivilant of php DateTime. It also has some
 * functionallity from object defiend in Drupal's date module DateObject.
 */
class DatexObject {
  protected $dateobj;
  public $error;
  public $errorMessage;
  public $hasError;
  public $tz;
  protected $formatString;

  /**
   * Constructor for DatexObject.
   *
   * @param mixed $datetime
   *   Given date/time
   * @param bool $is_gregorian
   *   Indicates wheter given date to constructor is Gregorian or not, default
   *   is set by a constant in module file.
   * @param DateTimezone $tz
   *   DateTimeZone to use.
   * @param string $format
   *   format used for formatting date.
   */
  public function __construct($datetime = NULL, $is_gregorian = DATEX_GREGORIAN, $tz = NULL, $format = NULL) {
    $this->hasError = FALSE;
    $this->error = '';
    $this->errorMessage = '';
    $this
      ->setDatetime($datetime, $is_gregorian, $tz);
    $format = $format ? $format : 'Y-m-d';
    $this
      ->setFormat($format);
    $tz = DatexFormatter::getTzObject($tz);
    if ($tz instanceof DateTimeZone) {
      $this->dateobj
        ->setTimezone($tz);
    }
    return $this;
  }

  /**
   * Magic Function toString.
   *
   * Returns date stored in this function, Formatted according to internal
   * format string, As an string.
   */
  public function __toString() {
    return $this
      ->format();
  }

  /**
   * Clones internal DateTime object when cloning instance of this class.
   */
  public function __clone() {
    $this->dateobj = clone $this->dateobj;
  }

  /**
   * Reset Date/Time to now.
   */
  public function reset() {
    $this->dateobj = new DateTime(NULL, $this->dateobj
      ->getTimezone());
    return $this;
  }

  /**
   * Similar to DateTime::format().
   *
   * @param string $format
   *   Format string.
   * @param bool $use_intl
   *   Whether to use php-intl or not.
   */
  public function format($format = NULL, $use_intl = DATEX_USE_INTL) {
    $format = $format ? $format : $this->formatString;
    $tz = $this->dateobj
      ->getTimezone();
    return DatexFormatter::format($this->dateobj, $format, $tz, $use_intl);
  }

  /**
   * Returns a clone of internal DateTime object.
   *
   * Cloned object always contains Gregorian date converted from jalali date
   * given to DatexObject.
   */
  public function getDateobjClone() {
    return clone $this->dateobj;
  }

  /**
   * Set's date from given date.
   *
   * For accepted list of accepted date formats,
   * See DatexFormatter::ObjectFromDate.
   */
  public function setDatetime($datetime = NULL, $gregorian = DATEX_GREGORIAN, $tz = NULL) {
    $tz = DatexFormatter::getTzObject($tz);
    $this->dateobj = DatexFormatter::ObjectFromDate($datetime, $tz, $gregorian);
    return $this;
  }

  /**
   * Sets date and time zone.
   */
  public function setDate($year = NULL, $month = NULL, $day = NULL, $tz = NULL) {
    if (!$year) {
      $year = $this
        ->format('Y');
    }
    if (!$mont) {
      $month = $this
        ->format('n');
    }
    if (!$day) {
      $day = $this
        ->format('j');
    }
    $date = DatexFormatter::toGregorian($year, $month, $day);
    $this
      ->xsetDate($date['year'], $date['month'], $date['day']);
    if ($tz = DatexFormatter::getTzObject($tz)) {
      $this
        ->setTimezone($tz);
    }
    return $this;
  }

  /**
   * Set format string used for formating date.
   */
  public function setFormat($format) {
    $this->formatString = $format;
    return $this;
  }

  /**
   * Returns format string set by setFormat.
   */
  public function getFormat() {
    return $this->formatString;
  }

  /**
   * Same as DateTime::getTimezone().
   */
  public function getTimezone() {
    return $this->dateobj
      ->getTimezone();
  }

  /**
   * Sets Time Zone of internal date object.
   *
   * Accepts a DateTimeZone Object or an string representing a timezone.
   */
  public function setTimezone($timezone) {
    $timezone = DatexFormatter::getTzObject($timezone);
    $this->dateobj
      ->setTimezone($timezone);
    return $this;
  }

  /**
   * Same as DateTime::setTimestamp().
   */
  public function setTimestamp($timestamp) {
    $this->dateobj
      ->setTimestamp($timestamp);
    return $this;
  }

  /**
   * Same as DateTime::setTime().
   */
  public function setTime($hour, $minute, $second = 0) {
    $this->dateobj
      ->setTime($hour, $minute, $second);
    return $this;
  }

  /**
   * Same as DateTime::getOffset().
   */
  public function getOffset() {
    return $this->dateobj
      ->getOffset();
  }

  /**
   * Same as DateTime::diff().
   */
  public function xdiff(DateTime $datetime2, $absolute = FALSE) {
    return $this->dateobj
      ->diff($datetime2, $absolute);
  }

  /**
   * Same as DateTime::format().
   */
  public function xformat($format) {
    return $this->dateobj
      ->format($format);
  }

  /**
   * Same as DateTime::getLastErrors().
   */
  public function xgetLastErrors() {
    return $this
      ->getLastErrors();
  }

  /**
   * Same as DateTime::setDate().
   */
  public function xsetDate($year, $month, $day) {
    return $this->dateobj
      ->setDate($year, $month, $day);
  }

  /**
   * Same as DateTime::getTimestamp().
   */
  public function getTimestamp() {
    return $this->dateobj
      ->format('U');
  }

  /**
   * Same as DateTime::modify().
   */
  public function modify($modify) {
    $this->dateobj
      ->modify($modify);
    return $this;
  }

  /**
   * Returns an object containing first day of Jalali month.
   */
  public function monthFirstDay() {
    $date = clone $this;
    $date
      ->setDate(NULL, NULL, 1);
    return $date;
  }

  /**
   * Returns an object containing last day of Jalali month.
   */
  public function monthLastDay() {
    $date = clone $this;
    $date
      ->setDate(NULL, NULL, DatexFormatter::format($date, 't'));
    return $date;
  }

  /**
   * Returns date granularities put in an array.
   *
   * @return array
   *   Date granularities put in an array.
   */
  public function toArray() {
    return array(
      'year' => $this
        ->format('Y'),
      'month' => $this
        ->format('n'),
      'day' => $this
        ->format('j'),
      'hour' => $this
        ->format('H'),
      'minute' => $this
        ->format('i'),
      'second' => $this
        ->format('s'),
      'timezone' => $this
        ->format('e'),
    );
  }

  /**
   * Returns amount of time difference to another date object.
   *
   * @throws Exception if given measure is week, it will throw an exception, it
   * is not implemented yet.
   */
  public function difference(DatexObject $date2_in, $measure = 'seconds', $absolute = TRUE) {

    // Create cloned objects or original dates will be impacted by the
    // date_modify() operations done in this code.
    $date1 = $this
      ->getDateobjClone();
    $date2 = $date2_in
      ->getDateobjClone();
    $diff = date_format($date2, 'U') - date_format($date1, 'U');
    if ($diff == 0) {
      return 0;
    }
    elseif ($diff < 0 && $absolute) {

      // Make sure $date1 is the smaller date.
      $temp = $date2;
      $date2 = $date1;
      $date1 = $temp;
      $diff = date_format($date2, 'U') - date_format($date1, 'U');
    }
    $year_diff = intval(date_format($date2, 'Y') - date_format($date1, 'Y'));
    switch ($measure) {
      case 'seconds':
        return $diff;
      case 'minutes':
        return $diff / 60;
      case 'hours':
        return $diff / 3600;
      case 'years':
        return $year_diff;
      case 'months':
        $format = 'n';
        $item1 = $this
          ->format_php($format, $date1);
        $item2 = $this
          ->format_php($format, $date2);
        if ($year_diff == 0) {
          return intval($item2 - $item1);
        }
        else {
          $item_diff = 12 - $item1;
          $item_diff += intval(($year_diff - 1) * 12);
          return $item_diff + $item2;
        }
        break;
      case 'days':
        $format = 'z';
        $item1 = $this
          ->format_php($format, $date1);
        $item2 = $this
          ->format_php($format, $date2);
        if ($year_diff == 0) {
          return intval($item2 - $item1);
        }
        else {
          $item_diff = date_days_in_year($date1) - $item1;
          for ($i = 1; $i < $year_diff; $i++) {
            date_modify($date1, '+1 year');
            $item_diff += date_days_in_year($date1);
          }
          return $item_diff + $item2;
        }
        break;
      case 'weeks':
      default:
        break;
    }
    return NULL;
  }

  /**
   * Same as DatexObject toArray but in Gregorian format.
   *
   * @return array
   *   An array of date granuls.
   */
  public function xtoArray() {
    return array(
      'year' => $this->dateobj
        ->format('Y'),
      'month' => $this->dateobj
        ->format('n'),
      'day' => $this->dateobj
        ->format('j'),
      'hour' => intval($this->dateobj
        ->format('H')),
      'minute' => intval($this->dateobj
        ->format('i')),
      'second' => intval($this->dateobj
        ->format('s')),
      'timezone' => $this->dateobj
        ->format('e'),
    );
  }

}

/**
 * Utitilities to work with a DatexObject
 */
class DatexObjectUtils {

  /**
   * Returns first day of a month.
   */
  public static function monthFirstDay($date = NULL) {
    $date = new DatexObject($date, FALSE);
    return $date
      ->monthFirstDay();
  }

  /**
   * Returns last day of a month.
   */
  public static function monthLastDay($date = NULL) {
    $date = new DatexObject($date, FALSE);
    return $date
      ->monthLastDay();
  }

  /**
   * Returns granularity parts of a given date in an array.
   */
  public static function toArray($date = NULL) {
    if (!is_a($date, 'DatexObject')) {
      $date = new DatexObject($date, FALSE);
    }
    return $date
      ->toArray();
  }

  /**
   * Returns a Jalali Object from a given date.
   */
  public static function getJalaliObject($date = NULL) {
    return new DatexObject($date, FALSE);
  }

  /**
   * Returns current Jalali Year.
   */
  public static function getYear() {
    return self::getByName('year');
  }

  /**
   * Returns current Jalali month.
   */
  public static function getMonth() {
    return self::getByName('month');
  }

  /**
   * Returns current Jalali day.
   */
  public static function getDay() {
    return self::getByName('day');
  }

  /**
   * Get current part of date as specified in name. Any of day, month, year.
   */
  public static function getByName($name) {
    $date = DatexFormatter::toJalali();
    return $date[$name];
  }

  /**
   * See php date().
   */
  public static function date($format, $timestamp = NULL) {
    return DatexFormatter::format($timestamp === NULL ? time() : $timestamp, $format);
  }

  /**
   * See php mktime().
   */
  public static function mktime($hour = NULL, $minute = NULL, $second = NULL, $month = NULL, $day = NULL, $year = NULL) {
    $date = DatexFormatter::toGregorian($year, $month, $day);
    $year = $year === NULL ? NULL : $date['year'];
    $month = $month === NULL ? NULL : $date['month'];
    $day = $day === NULL ? NULL : $date['day'];
    return mktime($hour, $minute, $second, $month, $day, $year);
  }

  /**
   * See php getdate().
   */
  public static function getdate($timestamp = NULL) {
    if ($timestamp === NULL) {
      $timestamp = time();
    }
    $date = new DateTime($timestamp);
    $ret['seconds'] = intval($date
      ->format('s'));
    $ret['minutes'] = intval($date
      ->format('i'));
    $ret['hours'] = intval($date
      ->format('G'));
    $jalali = DatexFormatter::toJalali();
    $ret['mday'] = intval($jalali['day']);
    $ret['mon'] = intval($jalali['month']);
    $ret['year'] = intval($jalali['year']);
    $ret['wday'] = DatexFormatter::formatPHP($date, 'w');
    $ret['yday'] = DatexFormatter::formatPHP($date, 'z');
    $ret['weekday'] = DatexFormatter::formatPHP($date, 'l');
    $ret['month'] = DatexFormatter::formatPHP($date, 'F');
    $ret[0] = $timestamp;
    return $ret;
  }

}

Constants

Namesort descending Description
DATEX_GREGORIAN State of a given date.
DATEX_JALALI State of a given date.
DATEX_USE_INTL whether to use PHP-Intl for date conversion or not. It's a default value and can be set in module's settings form.

Classes

Namesort descending Description
DatexFormatter Date tools for Jalali Dates.
DatexObject This class is Jalali equivilant of php DateTime. It also has some functionallity from object defiend in Drupal's date module DateObject.
DatexObjectUtils Utitilities to work with a DatexObject