meysampg/intldate

View on GitHub
IntlDateTrait.php

Summary

Maintainability
B
6 hrs
Test Coverage
<?php

/**
 * IntlDate is a trait for easy conversion date and time between multiple calendars.
 *
 * @category Traits
 *
 * @author   meysampg <p.g.meysam@gmail.com>
 * @license  https://github.com/meysampg/intldate/blob/master/LICENSE MIT
 *
 * @version  1.1
 *
 * @link     https://github.com/meysampg/intldate
 */

namespace meysampg\intldate;

use Exception;
use IntlCalendar;
use IntlDateFormatter;

trait IntlDateTrait
{
    private $_fromLocale;
    private $_toLocale;
    private $_fromCalendar;
    private $_toCalendar;

    private $_intlDateFormatter;
    private $_intlCalendar;

    public static $CAL_PERSIAN = 'persian';
    public static $CAL_JAPANESE = 'japanese';
    public static $CAL_BUDDHIST = 'buddhist';
    public static $CAL_CHINESE = 'chinese';
    public static $CAL_INDIAN = 'indian';
    public static $CAL_ISLAMIC = 'islamic';
    public static $CAL_HEBREW = 'hebrew';
    public static $CAL_COPTIC = 'coptic';
    public static $CAL_ETHIOPIC = 'ethiopic';
    public static $CAL_GREGORIAN = '';

    /**
     * Return final date and time on supplied format.
     *
     * @param string $pattern pattern of datetime based on ICU standards
     *
     * @return string
     *
     * @since 1.0.0
     */
    public function asDateTime($pattern = 'yyyy/MM/dd, HH:mm:ss')
    {
        $this->setFinalPattern($this->parsePattern($pattern));

        return $this->getIntlDateFormatter()->format($this->getIntlCalendar());
    }

    /**
     * Return final time as a timestamp.
     *
     * @return int
     *
     * @since 1.0.0
     */
    public function asTimestamp()
    {
        return $this->getIntlCalendar()->toDateTime()->format('U');
    }

    /**
     * Get datetime as a timestamp.
     *
     * @param int $timestamp timestamp on origin
     *
     * @return static
     *
     * @since 1.0.3
     */
    public function fromTimestamp($timestamp, $timezone = 'UTC')
    {
        // [TODO] use DateTime object for parse timestamp
        $oldTz = date_default_timezone_get();
        date_default_timezone_set($timezone);
        $timestamp = mktime(date('H', $timestamp), date('i', $timestamp), date('s', $timestamp), date('n', $timestamp), date('j', $timestamp), date('Y', $timestamp));
        date_default_timezone_set('UTC');
        $utcDT = gmdate('c', $timestamp);
        $dateArray = getdate(strtotime(gmdate('c', $timestamp)));
        date_default_timezone_set($oldTz);
        unset($dateArray[0]);

        $this->from($dateArray, 'en_US', null, $timezone);

        return $this;
    }

    /**
     * Get information of datetime on origin.
     *
     * @param array  $datetime Array contains datetime information. Its elements
     *                         are [year, month, day, hour, minute, second].
     * @param string $locale   locale for showing datetime on it (e.g. `en_US` or
     *                         `fa_IR`, 'es_US', ...)
     * @param string $calendar Calendar on origin
     * @param string $timezone Timezone on origin
     *
     * @return static
     *
     * @since 1.0.0
     */
    public function from(
        $datetime = [],
        $locale = 'en_US',
        $calendar = null,
        $timezone = 'UTC'
    ) {
        $datetime = $this->parseDateTime($datetime);
        $calendar = $calendar ?: self::$CAL_GREGORIAN;

        $this->setIntlCalendar()
            ->setFromCalendar($calendar)->setFromLocale($locale)
            ->setOriginCalendar($this->getFromLocaleAndCalendar())
            ->setOriginTimeZone($timezone)
            ->setOriginDate($datetime);

        return $this;
    }

    /**
     * Convert datetime to a desired calendar.
     *
     * @param string $locale   locale for showing new datetime
     * @param mixed  $calendar calendar system for showing new datetime
     * @param string $timezone timezone of destination
     *
     * @return static
     */
    public function to(
        $locale = 'en_US',
        $calendar = null,
        $timezone = 'UTC'
    ) {
        $calendar = $calendar !== null ? $calendar : self::$CAL_PERSIAN;

        $this->setIntlDateFormatter()
            ->setToCalendar($calendar)->setToLocale($locale)
            ->setFinalTimeZone($timezone)
            ->setFinalCalendar($this->getToLocaleAndCalendar());

        if ($calendar == self::$CAL_GREGORIAN) {
            $this->setFinalCalendarType(IntlDateFormatter::GREGORIAN);
        } else {
            $this->setFinalCalendarType(IntlDateFormatter::TRADITIONAL);
        }

        return $this;
    }

    public function fromPersian($datetime, $locale = 'en_US', $timezone = 'UTC')
    {
        $this->from($datetime, $locale, self::$CAL_PERSIAN, $timezone);

        return $this;
    }

    public function toPersian($locale = 'fa', $timezone = 'UTC')
    {
        $this->to($locale, self::$CAL_PERSIAN, $timezone);

        return $this;
    }

    public function fromJapanese($datetime, $locale = 'en_US', $timezone = 'UTC')
    {
        $this->from($datetime, $locale, self::$CAL_JAPANESE, $timezone);

        return $this;
    }

    public function toJapanese($locale = 'jp', $timezone = 'UTC')
    {
        $this->to($locale, self::$CAL_JAPANESE, $timezone);

        return $this;
    }

    public function fromBuddhist($datetime, $locale = 'en_US', $timezone = 'UTC')
    {
        $this->from($datetime, $locale, self::$CAL_BUDDHIST, $timezone);

        return $this;
    }

    public function toBuddhist($locale = 'th', $timezone = 'UTC')
    {
        $this->to($locale, self::$CAL_BUDDHIST, $timezone);

        return $this;
    }

    public function fromChinese($datetime, $locale = 'en_US', $timezone = 'UTC')
    {
        $this->from($datetime, $locale, self::$CAL_CHINESE, $timezone);

        return $this;
    }

    public function toChinese($locale = 'ch', $timezone = 'UTC')
    {
        $this->to($locale, self::$CAL_CHINESE, $timezone);

        return $this;
    }

    public function fromIndian($datetime, $locale = 'en_US', $timezone = 'UTC')
    {
        $this->from($datetime, $locale, self::$CAL_INDIAN, $timezone);

        return $this;
    }

    public function toIndian($locale = 'hi', $timezone = 'UTC')
    {
        $this->to($locale, self::$CAL_INDIAN, $timezone = 'UTC', $timezone);

        return $this;
    }

    public function fromIslamic($datetime, $locale = 'en_US', $timezone = 'UTC')
    {
        $this->from($datetime, $locale, self::$CAL_ISLAMIC, $timezone);

        return $this;
    }

    public function toIslamic($locale = 'ar', $timezone = 'UTC')
    {
        $this->to($locale, self::$CAL_ISLAMIC, $timezone);

        return $this;
    }

    public function fromHebrew($datetime, $locale = 'en_US', $timezone = 'UTC')
    {
        $this->from($datetime, $locale, self::$CAL_HEBREW, $timezone);

        return $this;
    }

    public function toHebrew($locale = 'he', $timezone = 'UTC')
    {
        $this->to($locale, self::$CAL_HEBREW, $timezone);

        return $this;
    }

    public function fromCoptic($datetime, $locale = 'en_US', $timezone = 'UTC')
    {
        $this->from($datetime, $locale, self::$CAL_COPTIC, $timezone);

        return $this;
    }

    public function toCoptic($locale = 'en_US', $timezone = 'UTC')
    {
        $this->to($locale, self::$CAL_COPTIC, $timezone);

        return $this;
    }

    public function fromEthiopic($datetime, $locale = 'en_US', $timezone = 'UTC')
    {
        $this->from($datetime, $locale, self::$CAL_ETHIOPIC, $timezone);

        return $this;
    }

    public function toEthiopic($locale = 'am', $timezone = 'UTC')
    {
        $this->to($locale, self::$CAL_ETHIOPIC, $timezone);

        return $this;
    }

    public function fromGregorian($datetime, $locale = 'en_US', $timezone = 'UTC')
    {
        $this->from($datetime, $locale, self::$CAL_GREGORIAN, $timezone);

        return $this;
    }

    public function toGregorian($locale = 'en_US', $timezone = 'UTC')
    {
        $this->to($locale, self::$CAL_GREGORIAN, $timezone);

        return $this;
    }

    public function setOriginDate($datetimeArray)
    {
        $this->getIntlCalendar()->set($datetimeArray[0], $datetimeArray[1], $datetimeArray[2], $datetimeArray[3], $datetimeArray[4], $datetimeArray[5]);

        return $this;
    }

    public function getFinalDate()
    {
        return $this->getIntlDateFormatter()->formatter($this->getIntlCalendar());
    }

    public function setFromLocale($locale)
    {
        $this->_fromLocale = trim($locale);

        return $this;
    }

    public function getFromLocale()
    {
        return $this->_fromLocale;
    }

    public function setFromCalendar($calendar)
    {
        $this->_fromCalendar = '@calendar='.trim($calendar);

        return $this;
    }

    public function getFromCalendar()
    {
        return $this->_fromCalendar;
    }

    public function setToLocale($locale)
    {
        $this->_toLocale = trim($locale);

        return $this;
    }

    public function getToLocale()
    {
        return $this->_toLocale;
    }

    public function setToCalendar($calendar)
    {
        $this->_toCalendar = '@calendar='.trim($calendar);

        return $this;
    }

    public function getToCalendar()
    {
        return $this->_toCalendar;
    }

    public function getFromLocaleAndCalendar()
    {
        return $this->getFromLocale().$this->getFromCalendar();
    }

    public function getToLocaleAndCalendar()
    {
        return $this->getToLocale().$this->getToCalendar();
    }

    public function setOriginTimeZone($timezone)
    {
        $this->getIntlCalendar()->setTimeZone($timezone);

        return $this;
    }

    public function getOriginTimeZone()
    {
        return $this->getIntlCalendar()->getTimeZone();
    }

    public function setFinalTimeZone($timezone)
    {
        $this->setIntlDateFormatter(
            $this->getToLocaleAndCalendar(),
            $this->getFinalDateType(),
            $this->getFinalTimeType(),
            $timezone,
            $this->getFinalCalendarType(),
            $this->getFinalPattern()
        );

        return $this;
    }

    public function getFinalTimeZone()
    {
        return $this->getIntlDateFormatter()->getTimeZone();
    }

    public function setOriginCalendar($locale)
    {
        $this->setIntlCalendar(
            $this->getOriginTimeZone(),
            $locale
        );

        return $this;
    }

    public function getOriginCalendar()
    {
        return $this->getIntlCalendar()->getLocale();
    }

    public function setFinalCalendar($locale)
    {
        $this->setIntlDateFormatter(
            $locale,
            $this->getFinalDateType(),
            $this->getFinalTimeType(),
            $this->getFinalTimeZone(),
            $this->getFinalCalendarType(),
            $this->getFinalPattern(),
            true
        );

        return $this;
    }

    public function getFinalCalendar()
    {
        return $this->getIntlDateFormatter()->getLocale();
    }

    public function setFinalDateType($datetype)
    {
        $this->setIntlDateFormatter(
            $this->getToLocaleAndCalendar(),
            $datetype,
            $this->getFinalTimeType(),
            $this->getFinalTimeZone(),
            $this->getFinalCalendarType(),
            $this->getFinalPattern(),
            true
        );

        return $this;
    }

    public function getFinalDateType()
    {
        $this->getIntlDateFormatter()->getDateType();
    }

    public function setFinalTimeType($timetype)
    {
        $this->setIntlDateFormatter(
            $this->getToLocaleAndCalendar(),
            $this->getFinalDateType(),
            $timetype,
            $this->getFinalTimeZone(),
            $this->getFinalCalendarType(),
            $this->getFinalPattern(),
            true
        );

        return $this;
    }

    public function getFinalTimeType()
    {
        return $this->getIntlDateFormatter()->getTimeType();
    }

    public function setFinalCalendarType($calendarType)
    {
        $this->setIntlDateFormatter(
            $this->getToLocaleAndCalendar(),
            $this->getFinalDateType(),
            $this->getFinalTimeType(),
            $this->getFinalTimeZone(),
            $calendarType,
            $this->getFinalPattern(),
            true
        );

        return $this;
    }

    public function getFinalCalendarType()
    {
        return $this->getIntlDateFormatter()->getCalendar();
    }

    public function setFinalPattern($pattern)
    {
        $this->getIntlDateFormatter()->setPattern($pattern);

        return $this;
    }

    public function getFinalPattern()
    {
        return $this->getIntlDateFormatter()->getPattern();
    }

    // [TODO]
    public function parsePattern($pattern)
    {
        /*
         * Implement a function to parse both ICU patterns and php date
         * function patterns and return a pattern that is compatible on
         * ICU format. The php pattern must start with php keyword, for
         * example `php:Y-F-d, H:i:s` is a php pattern.
         */
        return $pattern;
    }

    /**
     * This method gets a datetime string as the input and converts it
     * into a standard array.
     * Example: '2016/01/22 11:43:24'
     * to an array like:
     * [
     *      0 => 2016, // Year
     *      1 => 0, // Month. IntlCalendar use 0 for first month and so on.
     *      2 => 22, // Day
     *      3 => 11, // Hour
     *      4 => 43, // Minute
     *      5 => 24, // Seconds
     *]
     * Returns False if invalid month, day, hour, minute, or second is given.
     */
    public function guessDateTime($timestring)
    {
        $date_time = explode(' ', $timestring);

        $date = explode('/', $date_time[0]);
        $time = explode(':', $date_time[1]);

        $year = (int) $date[0];
        $month = $date[1];
        $day = (int) $date[2];
        $hour = (int) $time[0];
        $minute = (int) $time[1];
        $second = (int) $time[2];

        if ($day > 31 || $day < 1) {
            return false;
        } elseif ($hour > 24 || $hour < 0) {
            return false;
        } elseif ($minute > 60 || $minute < 0) {
            return false;
        } elseif ($second > 60 || $second < 0) {
            return false;
        }

        switch ($month) {
            case '01':
                $month = 0;
                break;
            case '02':
                $month = 1;
                break;
            case '03':
                $month = 2;
                break;
            case '04':
                $month = 3;
                break;
            case '05':
                $month = 4;
                break;
            case '06':
                $month = 5;
                break;
            case '07':
                $month = 6;
                break;
            case '08':
                $month = 7;
                break;
            case '09':
                $month = 8;
                break;
            case '10':
                $month = 9;
                break;
            case '11':
                $month = 10;
                break;
            case '12':
                $month = 11;
                break;
            default:
                return false;
                break;
        }

        return [$year, $month, $day, $hour, $minute, $second];
    }

    /**
     * Parse DateTime information array to be in correct format.
     *
     * @param array $datetimeArray array contains information of DateTime in
     *                             `year, month, day, hour, minute, day` order. This parameter can be a
     *                             either associative or non-associative array. For the former, keys must
     *                             be compitiable with http://php.net/manual/en/function.getdate.php. For
     *                             missing pieces of information, a corresponded part from 1970/1/Jan., 00:00:00
     *                             will be replaced.
     *
     * @throws Exception
     *
     * @return array An `IntlDateFormatter` compatible array.
     */
    private function parseDateTime($datetimeArray)
    {
        $finalDatetimeArray = [];

        if (!is_array($datetimeArray)) {
            throw new Exception('DateTime information must be an array in [year, month, day, hours, minutes, seconds] format.');
        }

        $finalDatetimeArray[0] = isset($datetimeArray[0]) ? (int) $datetimeArray[0] : (isset($datetimeArray['year']) ? (int) $datetimeArray['year'] : 1970);
        $finalDatetimeArray[1] = isset($datetimeArray[1]) ? (int) $datetimeArray[1] - 1 : (isset($datetimeArray['mon']) ? (int) $datetimeArray['mon'] - 1 : 0);
        $finalDatetimeArray[2] = isset($datetimeArray[2]) ? (int) $datetimeArray[2] : (isset($datetimeArray['mday']) ? (int) $datetimeArray['mday'] : 1);
        $finalDatetimeArray[3] = isset($datetimeArray[3]) ? (int) $datetimeArray[3] : (isset($datetimeArray['hours']) ? (int) $datetimeArray['hours'] : 0);
        $finalDatetimeArray[4] = isset($datetimeArray[4]) ? (int) $datetimeArray[4] : (isset($datetimeArray['minutes']) ? (int) $datetimeArray['minutes'] : 0);
        $finalDatetimeArray[5] = isset($datetimeArray[5]) ? (int) $datetimeArray[5] : (isset($datetimeArray['seconds']) ? (int) $datetimeArray['seconds'] : 0);

        return $finalDatetimeArray;
    }

    public function setIntlDateFormatter(
        $locale = 'en_US',
        $datetype = IntlDateFormatter::FULL,
        $timetype = IntlDateFormatter::FULL,
        $timezone = 'UTC',
        $calendar = IntlDateFormatter::GREGORIAN,
        $pattern = 'yyyy/MM/dd HH:mm:ss'
    ) {
        $this->_intlDateFormatter = new IntlDateFormatter(
            $locale, // string $locale
            $datetype, // int $datetype
            $timetype, // int $timetype
            $timezone, // mixed $timezone
            $calendar, // mixed $calendar
            $pattern // string $pattern
        );

        return $this;
    }

    public function getIntlDateFormatter()
    {
        return $this->_intlDateFormatter;
    }

    public function setIntlCalendar(
        $timezone = 'UTC',
        $locale = 'en_US@calendar=gregorian'
    ) {
        $this->_intlCalendar = IntlCalendar::createInstance(
            $timezone,
            $locale
        );

        return $this;
    }

    public function getIntlCalendar()
    {
        return $this->_intlCalendar;
    }
}