Skip to content
  • P
    Projects
  • G
    Groups
  • S
    Snippets
  • Help

semour / semour_admin

  • This project
    • Loading...
  • Sign in
Go to a project
  • Project
  • Repository
  • Issues 0
  • Merge Requests 0
  • Pipelines
  • Wiki
  • Snippets
  • Settings
  • Activity
  • Graph
  • Charts
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
  • Files
  • Commits
  • Branches
  • Tags
  • Contributors
  • Graph
  • Compare
  • Charts
Find file
BlameHistoryPermalink
Switch branch/tag
  • semour_admin
  • ..
  • DateTimeExcel
  • Difference.php
  • 孙龙's avatar
    订单 · f785d072
    孙龙 committed 2 years ago
    f785d072
Difference.php 5.95 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
<?php

namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;

use DateInterval;
use DateTime;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper;

class Difference
{
    use ArrayEnabled;

    /**
     * DATEDIF.
     *
     * @param mixed $startDate Excel date serial value, PHP date/time stamp, PHP DateTime object
     *                                    or a standard date string
     *                         Or can be an array of date values
     * @param mixed $endDate Excel date serial value, PHP date/time stamp, PHP DateTime object
     *                                    or a standard date string
     *                         Or can be an array of date values
     * @param array|string $unit
     *                         Or can be an array of unit values
     *
     * @return array|int|string Interval between the dates
     *         If an array of values is passed for the $startDate or $endDays,arguments, then the returned result
     *            will also be an array with matching dimensions
     */
    public static function interval($startDate, $endDate, $unit = 'D')
    {
        if (is_array($startDate) || is_array($endDate) || is_array($unit)) {
            return self::evaluateArrayArguments([self::class, __FUNCTION__], $startDate, $endDate, $unit);
        }

        try {
            $startDate = Helpers::getDateValue($startDate);
            $endDate = Helpers::getDateValue($endDate);
            $difference = self::initialDiff($startDate, $endDate);
            $unit = strtoupper($unit);
        } catch (Exception $e) {
            return $e->getMessage();
        }

        // Execute function
        $PHPStartDateObject = SharedDateHelper::excelToDateTimeObject($startDate);
        $startDays = (int) $PHPStartDateObject->format('j');
        $startMonths = (int) $PHPStartDateObject->format('n');
        $startYears = (int) $PHPStartDateObject->format('Y');

        $PHPEndDateObject = SharedDateHelper::excelToDateTimeObject($endDate);
        $endDays = (int) $PHPEndDateObject->format('j');
        $endMonths = (int) $PHPEndDateObject->format('n');
        $endYears = (int) $PHPEndDateObject->format('Y');

        $PHPDiffDateObject = $PHPEndDateObject->diff($PHPStartDateObject);

        $retVal = false;
        $retVal = self::replaceRetValue($retVal, $unit, 'D') ?? self::datedifD($difference);
        $retVal = self::replaceRetValue($retVal, $unit, 'M') ?? self::datedifM($PHPDiffDateObject);
        $retVal = self::replaceRetValue($retVal, $unit, 'MD') ?? self::datedifMD($startDays, $endDays, $PHPEndDateObject, $PHPDiffDateObject);
        $retVal = self::replaceRetValue($retVal, $unit, 'Y') ?? self::datedifY($PHPDiffDateObject);
        $retVal = self::replaceRetValue($retVal, $unit, 'YD') ?? self::datedifYD($difference, $startYears, $endYears, $PHPStartDateObject, $PHPEndDateObject);
        $retVal = self::replaceRetValue($retVal, $unit, 'YM') ?? self::datedifYM($PHPDiffDateObject);

        return is_bool($retVal) ? ExcelError::VALUE() : $retVal;
    }

    private static function initialDiff(float $startDate, float $endDate): float
    {
        // Validate parameters
        if ($startDate > $endDate) {
            throw new Exception(ExcelError::NAN());
        }

        return $endDate - $startDate;
    }

    /**
     * Decide whether it's time to set retVal.
     *
     * @param bool|int $retVal
     *
     * @return null|bool|int
     */
    private static function replaceRetValue($retVal, string $unit, string $compare)
    {
        if ($retVal !== false || $unit !== $compare) {
            return $retVal;
        }

        return null;
    }

    private static function datedifD(float $difference): int
    {
        return (int) $difference;
    }

    private static function datedifM(DateInterval $PHPDiffDateObject): int
    {
        return 12 * (int) $PHPDiffDateObject->format('%y') + (int) $PHPDiffDateObject->format('%m');
    }

    private static function datedifMD(int $startDays, int $endDays, DateTime $PHPEndDateObject, DateInterval $PHPDiffDateObject): int
    {
        if ($endDays < $startDays) {
            $retVal = $endDays;
            $PHPEndDateObject->modify('-' . $endDays . ' days');
            $adjustDays = (int) $PHPEndDateObject->format('j');
            $retVal += ($adjustDays - $startDays);
        } else {
            $retVal = (int) $PHPDiffDateObject->format('%d');
        }

        return $retVal;
    }

    private static function datedifY(DateInterval $PHPDiffDateObject): int
    {
        return (int) $PHPDiffDateObject->format('%y');
    }

    private static function datedifYD(float $difference, int $startYears, int $endYears, DateTime $PHPStartDateObject, DateTime $PHPEndDateObject): int
    {
        $retVal = (int) $difference;
        if ($endYears > $startYears) {
            $isLeapStartYear = $PHPStartDateObject->format('L');
            $wasLeapEndYear = $PHPEndDateObject->format('L');

            // Adjust end year to be as close as possible as start year
            while ($PHPEndDateObject >= $PHPStartDateObject) {
                $PHPEndDateObject->modify('-1 year');
                $endYears = $PHPEndDateObject->format('Y');
            }
            $PHPEndDateObject->modify('+1 year');

            // Get the result
            $retVal = $PHPEndDateObject->diff($PHPStartDateObject)->days;

            // Adjust for leap years cases
            $isLeapEndYear = $PHPEndDateObject->format('L');
            $limit = new DateTime($PHPEndDateObject->format('Y-02-29'));
            if (!$isLeapStartYear && !$wasLeapEndYear && $isLeapEndYear && $PHPEndDateObject >= $limit) {
                --$retVal;
            }
        }

        return (int) $retVal;
    }

    private static function datedifYM(DateInterval $PHPDiffDateObject): int
    {
        return (int) $PHPDiffDateObject->format('%m');
    }
}