• Как написать конвертер с григорианского в иранский календари и обратно?

    @RUvik_od Автор вопроса
    нашел где-то на восточных сайтах кусок на JS. Переписал на PHP, жители Ирана оценили и сказали что работает правильно ;-)
    class IranCalendarParser
    {
        const GREGORIAN_EPOCH = 1721425.5;
        const PERSIAN_EPOCH = 1948320.5;
        const ISLAMIC_EPOCH = 1948439.5;
        static public $MONTH_NAME = array(
            1 => 'Farvardin',
            2 => 'Ordibehesht',
            3 => 'Khordad',
            4 => 'Tir',
            5 => 'Mordad',
            6 => 'Shahrivar',
            7 => 'Mehr',
            8 => 'Aban',
            9 => 'Azar',
            10 => 'Dey',
            11 => 'Bahman',
            12 => 'Esfand',
        );
    
        static function GregorianToIran($year, $month, $day)
        {
            return self::jd_to_persian(self::gregorian_to_jd($year, $month, $day));
        }
    
        static function IranToGregorian($year, $month, $day)
        {
            return self::jd_to_gregorian(self::persian_to_jd($year, $month, $day));
        }
    
        static public function GetMonthName($month)
        {
            return self::$MONTH_NAME[$month];
        }
    
        public function jd_to_gregorian($jd)
        {
           // self::GREGORIAN_EPOCH = 1721425.5;
            $wjd = floor($jd - 0.5) + 0.5;
    
            $depoch = $wjd - self::GREGORIAN_EPOCH;
            $quadricent = floor($depoch / 146097);
            $dqc = self::mod($depoch, 146097);
            $cent = floor($dqc / 36524);
            $dcent = self::mod($dqc, 36524);
            $quad = floor($dcent / 1461);
            $dquad = self::mod($dcent, 1461);
            $yindex = floor($dquad / 365);
            $year = ($quadricent * 400) + ($cent * 100) + ($quad * 4) + $yindex;
            if (!(($cent == 4) || ($yindex == 4))) {
                $year++;
            }
            $yearday = $wjd - self::gregorian_to_jd($year, 1, 1);
            $leapadj = (($wjd < self::gregorian_to_jd($year, 3, 1)) ? 0
                :
                (self::leap_gregorian($year) ? 1 : 2)
            );
            $month = floor(((($yearday + $leapadj) * 12) + 373) / 367);
            $day = ($wjd - self::gregorian_to_jd($year, $month, 1)) + 1;
            return array( 'day'=>$day,'month'=>$month,'year'=>$year );
        }
    
        public function mod($a, $b)
        {
            return $a - ($b * floor($a / $b));
        }
    
        public function gregorian_to_jd($year, $month, $day)
        {
            return (self::GREGORIAN_EPOCH  - 1) +
                (365 * ($year - 1)) +
                floor(($year - 1) / 4) +
                (-floor(($year - 1) / 100)) +
                floor(($year - 1) / 400)+
                floor((((367 * $month) - 362) / 12) +
                    (($month <= 2) ? 0 :
                        (self::leap_gregorian($year) ? -1 : -2)
                    ) +
                    $day)
                ;
        }
    
        public function leap_gregorian($year)
        {
            return (($year % 4) == 0) &&
                (!((($year % 100) == 0) && (($year % 400) != 0)));
        }
    
        public function persian_to_jd($year, $month, $day)
        {
            $epbase = $year - (($year >= 0) ? 474 : 473);
            $epyear = 474 + self::mod($epbase, 2820);
            return $day +
                (($month <= 7) ?
                    (($month - 1) * 31) :
                    ((($month - 1) * 30) + 6)
                ) +
                floor((($epyear * 682) - 110) / 2816) +
                ($epyear - 1) * 365 +
                floor($epbase / 2820) * 1029983 +
                (self::PERSIAN_EPOCH - 1);
        }
    
        public function jd_to_persian($jd)
        {
            $jd = floor($jd) + 0.5;
    
            $depoch = $jd - self::persian_to_jd(475, 1, 1);
            $cycle = floor($depoch / 1029983);
            $cyear = self::mod($depoch, 1029983);
            if ($cyear == 1029982) {
                $ycycle = 2820;
            } else {
                $aux1 = floor($cyear / 366);
                $aux2 = self::mod($cyear, 366);
                $ycycle = floor(((2134 * $aux1) + (2816 * $aux2) + 2815) / 1028522) +
                    $aux1 + 1;
            }
            $year = $ycycle + (2820 * $cycle) + 474;
            if ($year <= 0) {
                $year--;
            }
            $yday = ($jd - self::persian_to_jd($year, 1, 1)) + 1;
            $month = ($yday <= 186) ? ceil($yday / 31) : ceil(($yday - 6) / 30);
            $day = ($jd - self::persian_to_jd($year, $month, 1)) + 1;
            return array( 'day'=>$day,'month'=>$month,'year'=>$year );
        }
    
        public function jd_to_islamic($jd)
        {
            $jd = floor($jd) + 0.5;
            $year = floor(((30 * ($jd - self::ISLAMIC_EPOCH)) + 10646) / 10631);
            $month = min(12,
                    ceil(($jd - (29 + self::islamic_to_jd($year, 1, 1))) / 29.5) + 1);
            $day = ($jd - self::islamic_to_jd($year, $month, 1)) + 1;
            return array( 'day'=>$day,'month'=>$month,'year'=>$year );
        }
    
        public function islamic_to_jd($year, $month, $day)
        {
                return ($day +
                        ceil(29.5 * ($month - 1)) +
                        ($year - 1) * 354 +
                        floor((3 + (11 * $year)) / 30) +
                        self::ISLAMIC_EPOCH) - 1;
        }
    
        public function leap_islamic($year)
        {
                return ((($year * 11) + 14) % 30) < 11;
        }
    }
    
    /**
     * Example
     */
    IranCalendarParser::GregorianToIran(2017,2,10);
    IranCalendarParser::IranToGregorian(1395,11,22);
    Ответ написан
    Комментировать