Passed
Push — master ( 790382...9b34f8 )
by Mark
09:58
created

Difference::datedifYD()   B

Complexity

Conditions 7
Paths 3

Size

Total Lines 26
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 7

Importance

Changes 0
Metric Value
cc 7
eloc 14
nc 3
nop 5
dl 0
loc 26
ccs 15
cts 15
cp 1
crap 7
rs 8.8333
c 0
b 0
f 0
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
4
5
use DateInterval;
6
use DateTime;
7
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
8
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
9
use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper;
10
11
class Difference
12
{
13
    /**
14
     * DATEDIF.
15
     *
16
     * @param mixed $startDate Excel date serial value, PHP date/time stamp, PHP DateTime object
17
     *                                    or a standard date string
18
     * @param mixed $endDate Excel date serial value, PHP date/time stamp, PHP DateTime object
19
     *                                    or a standard date string
20
     * @param string $unit
21
     *
22
     * @return int|string Interval between the dates
23
     */
24 254
    public static function interval($startDate, $endDate, $unit = 'D')
25
    {
26
        try {
27 254
            $startDate = Helpers::getDateValue($startDate);
28 253
            $endDate = Helpers::getDateValue($endDate);
29 252
            $difference = self::initialDiff($startDate, $endDate);
30 251
            $unit = strtoupper(Functions::flattenSingleValue($unit));
31 3
        } catch (Exception $e) {
32 3
            return $e->getMessage();
33
        }
34
35
        // Execute function
36 251
        $PHPStartDateObject = SharedDateHelper::excelToDateTimeObject($startDate);
37 251
        $startDays = (int) $PHPStartDateObject->format('j');
38 251
        $startMonths = (int) $PHPStartDateObject->format('n');
0 ignored issues
show
Unused Code introduced by
The assignment to $startMonths is dead and can be removed.
Loading history...
39 251
        $startYears = (int) $PHPStartDateObject->format('Y');
40
41 251
        $PHPEndDateObject = SharedDateHelper::excelToDateTimeObject($endDate);
42 251
        $endDays = (int) $PHPEndDateObject->format('j');
43 251
        $endMonths = (int) $PHPEndDateObject->format('n');
0 ignored issues
show
Unused Code introduced by
The assignment to $endMonths is dead and can be removed.
Loading history...
44 251
        $endYears = (int) $PHPEndDateObject->format('Y');
45
46 251
        $PHPDiffDateObject = $PHPEndDateObject->diff($PHPStartDateObject);
47
48 251
        $retVal = false;
49 251
        $retVal = self::replaceRetValue($retVal, $unit, 'D') ?? self::datedifD($difference);
50 251
        $retVal = self::replaceRetValue($retVal, $unit, 'M') ?? self::datedifM($PHPDiffDateObject);
51 251
        $retVal = self::replaceRetValue($retVal, $unit, 'MD') ?? self::datedifMD($startDays, $endDays, $PHPEndDateObject, $PHPDiffDateObject);
52 251
        $retVal = self::replaceRetValue($retVal, $unit, 'Y') ?? self::datedifY($PHPDiffDateObject);
53 251
        $retVal = self::replaceRetValue($retVal, $unit, 'YD') ?? self::datedifYD($difference, $startYears, $endYears, $PHPStartDateObject, $PHPEndDateObject);
54 251
        $retVal = self::replaceRetValue($retVal, $unit, 'YM') ?? self::datedifYM($PHPDiffDateObject);
55
56 251
        return is_bool($retVal) ? Functions::VALUE() : $retVal;
57
    }
58
59 252
    private static function initialDiff(float $startDate, float $endDate): float
60
    {
61
        // Validate parameters
62 252
        if ($startDate > $endDate) {
63 1
            throw new Exception(Functions::NAN());
64
        }
65
66 251
        return $endDate - $startDate;
67
    }
68
69
    /**
70
     * Decide whether it's time to set retVal.
71
     *
72
     * @param bool|int $retVal
73
     *
74
     * @return null|bool|int
75
     */
76 251
    private static function replaceRetValue($retVal, string $unit, string $compare)
77
    {
78 251
        if ($retVal !== false || $unit !== $compare) {
79 251
            return $retVal;
80
        }
81
82 250
        return null;
83
    }
84
85 165
    private static function datedifD(float $difference): int
86
    {
87 165
        return (int) $difference;
88
    }
89
90 15
    private static function datedifM(DateInterval $PHPDiffDateObject): int
91
    {
92 15
        return 12 * (int) $PHPDiffDateObject->format('%y') + (int) $PHPDiffDateObject->format('%m');
93
    }
94
95 15
    private static function datedifMD(int $startDays, int $endDays, DateTime $PHPEndDateObject, DateInterval $PHPDiffDateObject): int
96
    {
97 15
        if ($endDays < $startDays) {
98 3
            $retVal = $endDays;
99 3
            $PHPEndDateObject->modify('-' . $endDays . ' days');
100 3
            $adjustDays = (int) $PHPEndDateObject->format('j');
101 3
            $retVal += ($adjustDays - $startDays);
102
        } else {
103 12
            $retVal = (int) $PHPDiffDateObject->format('%d');
104
        }
105
106 15
        return $retVal;
107
    }
108
109 19
    private static function datedifY(DateInterval $PHPDiffDateObject): int
110
    {
111 19
        return (int) $PHPDiffDateObject->format('%y');
112
    }
113
114 20
    private static function datedifYD(float $difference, int $startYears, int $endYears, DateTime $PHPStartDateObject, DateTime $PHPEndDateObject): int
115
    {
116 20
        $retVal = (int) $difference;
117 20
        if ($endYears > $startYears) {
118 9
            $isLeapStartYear = $PHPStartDateObject->format('L');
119 9
            $wasLeapEndYear = $PHPEndDateObject->format('L');
120
121
            // Adjust end year to be as close as possible as start year
122 9
            while ($PHPEndDateObject >= $PHPStartDateObject) {
123 9
                $PHPEndDateObject->modify('-1 year');
124 9
                $endYears = $PHPEndDateObject->format('Y');
0 ignored issues
show
Unused Code introduced by
The assignment to $endYears is dead and can be removed.
Loading history...
125
            }
126 9
            $PHPEndDateObject->modify('+1 year');
127
128
            // Get the result
129 9
            $retVal = $PHPEndDateObject->diff($PHPStartDateObject)->days;
0 ignored issues
show
Documentation Bug introduced by
It seems like $PHPEndDateObject->diff(...PStartDateObject)->days can also be of type boolean. However, the property $days is declared as type false|integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
130
131
            // Adjust for leap years cases
132 9
            $isLeapEndYear = $PHPEndDateObject->format('L');
133 9
            $limit = new DateTime($PHPEndDateObject->format('Y-02-29'));
134 9
            if (!$isLeapStartYear && !$wasLeapEndYear && $isLeapEndYear && $PHPEndDateObject >= $limit) {
135 1
                --$retVal;
136
            }
137
        }
138
139 20
        return (int) $retVal;
140
    }
141
142 16
    private static function datedifYM(DateInterval $PHPDiffDateObject): int
143
    {
144 16
        return (int) $PHPDiffDateObject->format('%m');
145
    }
146
}
147