You are here

calendar_jalali.module in Calendar Systems 6.2

Implements necessary hooks & helpers to support Jalali Calendar.

File

calendars/calendar_jalali/calendar_jalali.module
View source
<?php

/**
 * @file
 * Implements necessary hooks & helpers to support Jalali Calendar.
 */

/**
 * Implements hook_calendar_info().
 *
 * @ingroup hook_implementation
 */
function calendar_jalali_calendar_info() {
  $calendar = array();

  // An array of calendar information,
  // keyed by the calendar identifier.
  $calendar['jalali'] = array(
    // Calendar name:
    'name' => t('Jalali (Solar Hijri)'),
    // Administration form callback:
    'config callback' => 'calendar_jalali_admin_form',
    // Date formatter callback:
    'format callback' => 'calendar_jalali_formatter',
    // Calendar convert callbacks:
    'convert callbacks' => array(
      'from gregorian' => 'calendar_jalali_convert',
      'to gregorian' => 'calendar_jalali_convert_reverse',
    ),
    // Callback calendar month days:
    'month days callback' => 'calendar_jalali_month_days',
  );
  return $calendar;
}

/**
 * Form callback for Jalali calendar administration.
 *
 * @param $config
 *   Calendar specific config options.
 *
 * @ingroup forms
 */
function calendar_jalali_admin_form($config) {
  $form = array();
  $locale = function_exists('locale') ? TRUE : FALSE;
  $form['calendar_jalali_translate_numbers'] = array(
    '#type' => 'checkbox',
    '#title' => t('Use the Persian representaion of numbers in date strings.'),
    '#description' => t('e.g. Use !number-fa instead of <strong>!number-en</strong>.', array(
      '!number-fa' => '۹',
      '!number-en' => '9',
    )),
    '#default_value' => $config['calendar_jalali_translate_numbers'],
    '#return_value' => 1,
  );
  $form['translate_options'] = array(
    '#type' => 'fieldset',
    '#title' => t('Calendar Translation Settings'),
    '#description' => t('You need to have the <em>Locale</em> module enabled to make use of these options.'),
    '#collapsible' => TRUE,
    '#collapsed' => FALSE,
  );
  $form['translate_options']['calendar_jalali_translate_meridiem'] = array(
    '#type' => 'checkbox',
    '#title' => t('Translate Ante/Post meridiems regardless of site language.'),
    '#description' => t('If checked, you also need to translate Ante/Post meridiem to Persian using the <a href="!link">translate interface</a>.', array(
      '!link' => url('admin/build/translate'),
    )),
    '#default_value' => !$locale ? 0 : $config['calendar_jalali_translate_meridiem'],
    '#return_value' => 1,
    '#disabled' => !$locale,
  );
  $form['translate_options']['calendar_jalali_translate_weekdays'] = array(
    '#type' => 'checkbox',
    '#title' => t('Translate weekdays and show Persian representations regardless of site language.'),
    '#description' => t('If checked, you also need to translate weekday names to Persian using the <a href="!link">translate interface</a>.', array(
      '!link' => url('admin/build/translate'),
    )),
    '#default_value' => !$locale ? 0 : $config['calendar_jalali_translate_weekdays'],
    '#return_value' => 1,
    '#disabled' => !$locale,
  );
  $form['translate_options']['calendar_jalali_translate_months'] = array(
    '#type' => 'checkbox',
    '#title' => t('Translate month names and show Persian representations regardless of site language.'),
    '#description' => t('If checked, you also need to translate month names to Persian using the <a href="!link">translate interface</a>.', array(
      '!link' => url('admin/build/translate'),
    )),
    '#default_value' => !$locale ? 0 : $config['calendar_jalali_translate_months'],
    '#return_value' => 1,
    '#disabled' => !$locale,
  );
  return $form;
}

/**
 * Jalali calendar date converter callback.
 *
 * Converts Gregorian to Jalali.
 *
 * @param $year
 *   Gregorian year.
 * @param $month
 *   Gregorian month.
 * @param $day
 *   Gregorian day.
 *
 * @return
 *   An array of Jalali date elements.
 *   - year: Jalali year.
 *   - month: Jalali month.
 *   - day: Jalali day.
 *
 * @ingroup calendar_convert_callback
 */
function calendar_jalali_convert($year, $month, $day) {
  $day = $day - 1;
  $month = $month - 1;
  $year = $year - 1600;
  $jmonth_days = calendar_jalali_month_days();
  $gmonth_days = calendar_gregorian_month_days();
  $gday_no = 365 * $year + (int) (($year + 3) / 4) - (int) (($year + 99) / 100) + (int) (($year + 399) / 400);
  for ($i = 0; $i < $month; ++$i) {
    $gday_no += $gmonth_days[$i];
  }

  // Leap year / After february.
  if ($month > 1 && ($year % 4 == 0 && $year % 100 != 0 || $year % 400 == 0)) {
    ++$gday_no;
  }
  $gday_no += $day;
  $jday_no = $gday_no - 79;
  $j_np = (int) ($jday_no / 12053);
  $jday_no %= 12053;
  $jyear = 979 + 33 * $j_np + 4 * (int) ($jday_no / 1461);
  $jday_no %= 1461;
  if ($jday_no >= 366) {
    $jyear += (int) (($jday_no - 1) / 365);
    $jday_no = ($jday_no - 1) % 365;
  }
  for ($i = 0; $i < 11 && $jday_no >= $jmonth_days[$i]; ++$i) {
    $jday_no -= $jmonth_days[$i];
  }
  return array(
    $jyear,
    $i + 1,
    $jday_no + 1,
  );
}

/**
 * Jalali calendar reverse date converter callback.
 *
 * Converts Jalali to Gregorian.
 *
 * @param $year
 *   Jalali year.
 * @param $month
 *   Jalali month.
 * @param $day
 *   Jalali day.
 *
 * @return
 *   An array of Gregorian date elements.
 *   - year: Gregorian year.
 *   - month: Gregorian month.
 *   - day: Gregorian day.
 *
 * @ingroup calendar_convert_callback
 */
function calendar_jalali_convert_reverse($year, $month, $day) {
  $day = $day - 1;
  $month = $month - 1;
  $year = $year - 979;
  $jmonth_days = calendar_jalali_month_days();
  $gmonth_days = calendar_gregorian_month_days();
  $jday_no = 365 * $year + (int) ($year / 33) * 8 + (int) (($year % 33 + 3) / 4);
  for ($i = 0; $i < $month; ++$i) {
    $jday_no += $jmonth_days[$i];
  }
  $jday_no += $day;
  $gday_no = $jday_no + 79;
  $gyear = 1600 + 400 * (int) ($gday_no / 146097);
  $gday_no = $gday_no % 146097;
  $leap = TRUE;
  if ($gday_no >= 36525) {
    $gday_no--;
    $gyear += 100 * (int) ($gday_no / 36524);
    $gday_no = $gday_no % 36524;
    if ($gday_no >= 365) {
      $gday_no++;
    }
    else {
      $leap = FALSE;
    }
  }
  $gyear += 4 * (int) ($gday_no / 1461);
  $gday_no %= 1461;
  if ($gday_no >= 366) {
    $leap = FALSE;
    $gday_no--;
    $gyear += (int) ($gday_no / 365);
    $gday_no = $gday_no % 365;
  }
  for ($i = 0; $gday_no >= $gmonth_days[$i] + ($i == 1 && $leap); $i++) {
    $gday_no -= $gmonth_days[$i] + ($i == 1 && $leap);
  }
  return array(
    $gyear,
    $i + 1,
    $gday_no + 1,
  );
}

/**
 * Jalali calendar date formatter callback.
 *
 * {Calendar conversion is a just a pain!}
 *
 * @param $timestamp
 *   Unix timestamp to be formatted.
 *   The proper $timezone value has been added by Drupal format_date().
 * @param $format
 *   PHP date() function format string.
 * @param $timezone
 *   Time zone offset in seconds.
 * @param $langcode
 *   Optional language code to translate to a language other than the default.
 * @param $calendar
 *   A copy of calendar information.
 *
 * @return
 *   The formatted date or FALSE otherwise.
 *
 * @see format_date()
 *
 * @ingroup calendar_format_callback
 */
function calendar_jalali_formatter($timestamp, $format, $timezone = 0, $langcode = NULL, $calendar = NULL) {
  $date = '';
  list($year, $month, $day) = explode('~', calendar_systems_formatter($timestamp, 'Y~n~j'));
  list($jyear, $jmonth, $jday) = calendar_jalali_convert($year, $month, $day);
  $max = strlen($format);
  for ($i = 0; $i < $max; $i++) {
    switch ($format[$i]) {
      case 'y':
        $date .= substr($jyear, 2, 4);
        break;
      case 'Y':
        $date .= $jyear;
        break;
      case 'M':
      case 'F':

        // Check configs for month representation.
        $function = $calendar['config']['calendar_jalali_translate_months'] ? 'array_values' : 'array_keys';
        $jmonths = $function(_calendar_jalali_t('months'));
        $date .= $jmonths[$jmonth - 1];
        break;
      case 'D':
      case 'l':
        $function = $calendar['config']['calendar_jalali_translate_weekdays'] ? 'array_values' : 'array_keys';
        $weekdays = $function(_calendar_jalali_t('weekdays'));
        $date .= $weekdays[calendar_jalali_formatter($timestamp, 'w')];
        break;
      case 'a':
      case 'A':
        $default = calendar_systems_formatter($timestamp, $format[$i]);
        if ($calendar['config']['calendar_jalali_translate_meridiem']) {
          $case = $format[$i] == 'a' ? 'lower' : 'upper';
          $meridiems = _calendar_jalali_t('meridiem_' . $case);
          $date .= $meridiems[$default];
        }
        else {
          $date .= $default;
        }
        break;
      case 'm':
        $date .= sprintf('%02d', $jmonth);
        break;
      case 'n':
        $date .= $jmonth;
        break;
      case 'd':
        $date .= sprintf('%02d', $jday);
        break;
      case 'j':
        $date .= $jday;
        break;
      case 'w':
        $date .= (calendar_systems_formatter($timestamp, 'w') + 1) % 7;
        break;
      case 'r':
        $date .= calendar_jalali_formatter($timestamp, 'D, d M Y H:i:s O', $timezone, NULL, $calendar);
        break;
      case 'N':
        $date .= calendar_jalali_formatter($timestamp, 'w') + 1;
        break;
      case 't':
        $jmonth_days = calendar_jalali_month_days();
        if ($jmonth < 12) {
          $date .= $jmonth_days[$jmonth - 1];
        }
        elseif (_calendar_jalali_check($jyear, $jmonth, 30)) {
          $date .= '30';
        }
        else {
          $date .= '29';
        }
        break;
      case 'z':
        $day_of_year = 0;
        $jmonth_days = calendar_jalali_month_days();
        for ($n = 0; $n < $jmonth - 1; $n++) {
          $day_of_year += $jmonth_days[$n];
        }
        $day_of_year += $jday - 1;
        $date .= $day_of_year;
        break;
      case 'L':
        $date .= _calendar_jalali_check($jyear, 12, 30) ? '1' : '0';
        break;
      case 'W':
        $zone = calendar_jalali_formatter($timestamp, 'z');

        // First saturday.
        $saturday = ($zone - calendar_jalali_formatter($timestamp, 'w') + 7) % 7;
        $days = $zone - $saturday;
        if ($days < 0) {
          $zone += _calendar_jalali_check($jyear - 1, 12, 30) ? 366 : 365;
          $saturday = ($zone - calendar_jalali_formatter($timestamp, 'w') + 7) % 7;
          $days = $zone - $saturday;
        }
        $date .= floor($days / 7) + 1;
        break;
      case '\\':
        if ($i + 1 < strlen($format)) {
          $date .= $format[++$i];
        }
        else {
          $date .= $format[$i];
        }
        break;
      default:
        $date .= calendar_systems_formatter($timestamp, $format[$i], $timezone);
    }
  }

  // Check configs for number representation.
  if ($calendar['config']['calendar_jalali_translate_numbers']) {
    return _calendar_jalali_convert_number($date);
  }
  return $date;
}

/**
 * Helper function to check whether the combination
 * of passed values are in Jalali calendar range or not.
 *
 * @param $year
 *   Jalali year.
 * @param $month
 *   Jalali month.
 * @param $day
 *   Jalali day.
 *
 * @return
 *   Boolean value.
 */
function _calendar_jalali_check($year, $month, $day) {
  $jmonth_days = calendar_jalali_month_days();
  if ($year < 0 || $year > 32767 || $month < 1 || $month > 12 || $day < 1) {
    return FALSE;
  }
  if ($day > $jmonth_days[$month - 1] + ($month == 12 && !(($year - 979) % 33 % 4))) {
    return FALSE;
  }
  return TRUE;
}

/**
 * Helper function to convert a
 * Latin number to its Persian representaion.
 *
 * @param $string
 *   The haystack string to be googled!
 *
 * @return
 *   Number-Persianized string!
 */
function _calendar_jalali_convert_number($string) {
  $output = '';
  $max = strlen(trim($string));
  for ($i = 0; $i < $max; ++$i) {
    $output .= ctype_digit($string[$i]) ? pack('C*', 0xdb, 0xb0 + $string[$i]) : $string[$i];
  }
  return $output;
}

/**
 * Calendar month days helper.
 *
 * @return
 *   An array of ordered numbers corresponding
 *   to a Jalali year months length of days.
 *
 * @ingroup calendar_helper
 */
function calendar_jalali_month_days() {
  return array(
    31,
    31,
    31,
    31,
    31,
    31,
    30,
    30,
    30,
    30,
    30,
    29,
  );
}

/**
 * Calendar translate helper.
 *
 * @param $type
 *   Type of needed translation.
 *   - weekdays
 *   - months
 *   - meridiem_lower
 *   - meridiem_upper
 * @param $destination
 *   Imply the destination string:
 *   - source
 *   - translation
 *
 * @return
 *   An associative array of translations keyed by their sources.
 *
 * @ingroup calendar_helper
 */
function _calendar_jalali_t($type, $destination = 'translation') {
  if ($destination == 'translation' && !function_exists('locale')) {
    $destination = 'source';
  }
  $source = array(
    'weekdays' => array(
      'Shanbeh',
      'Yekshanbeh',
      'Doshanbeh',
      'Sehshanbeh',
      'Chaharshanbeh',
      'Panjshanbeh',
      'Jomeh',
    ),
    'months' => array(
      'Farvardin',
      'Ordibehesht',
      'Khordad',
      'Tir',
      'Mordad',
      'Shahrivar',
      'Mehr',
      'Aban',
      'Azar',
      'Dey',
      'Bahman',
      'Esfand',
    ),
    'meridiem_lower' => array(
      'am',
      'pm',
    ),
    'meridiem_upper' => array(
      'AM',
      'PM',
    ),
  );
  return $destination == 'source' ? $source[$type] : array_combine($source[$type], array_map('locale', $source[$type], array_fill(0, count($source[$type]), 'fa')));
}

/**
 * Calendar translate dummy.
 *
 * @ingroup calendar_helper
 * @see http://api.drupal.org/api/drupal/includes--common.inc/function/t/6
 */
function _calendar_jalali_potx($type = 'all', $destination = 'translation') {
  $dummy = array(
    t('am'),
    t('pm'),
    t('AM'),
    t('PM'),
    t('Shanbeh'),
    t('Yekshanbeh'),
    t('Doshanbeh'),
    t('Sehshanbeh'),
    t('Chaharshanbeh'),
    t('Panjshanbeh'),
    t('Jomeh'),
    t('Farvardin'),
    t('Ordibehesht'),
    t('Khordad'),
    t('Tir'),
    t('Mordad'),
    t('Shahrivar'),
    t('Mehr'),
    t('Aban'),
    t('Azar'),
    t('Dey'),
    t('Bahman'),
    t('Esfand'),
  );
}

Functions

Namesort descending Description
calendar_jalali_admin_form Form callback for Jalali calendar administration.
calendar_jalali_calendar_info Implements hook_calendar_info().
calendar_jalali_convert Jalali calendar date converter callback.
calendar_jalali_convert_reverse Jalali calendar reverse date converter callback.
calendar_jalali_formatter Jalali calendar date formatter callback.
calendar_jalali_month_days Calendar month days helper.
_calendar_jalali_check Helper function to check whether the combination of passed values are in Jalali calendar range or not.
_calendar_jalali_convert_number Helper function to convert a Latin number to its Persian representaion.
_calendar_jalali_potx Calendar translate dummy.
_calendar_jalali_t Calendar translate helper.