Completed
Push — develop ( 682b1b...fe73b2 )
by Adrien
20:00
created

DateTime::getTimeValue()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 6
nc 1
nop 1
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
ccs 6
cts 6
cp 1
crap 1
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Calculation;
4
5
/**
6
 * Copyright (c) 2006 - 2016 PhpSpreadsheet.
7
 *
8
 * This library is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU Lesser General Public
10
 * License as published by the Free Software Foundation; either
11
 * version 2.1 of the License, or (at your option) any later version.
12
 *
13
 * This library is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
 * Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public
19
 * License along with this library; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
 *
22
 * @category    PhpSpreadsheet
23
 *
24
 * @copyright    Copyright (c) 2006 - 2016 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet)
25
 * @license        http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt    LGPL
26
 */
27
class DateTime
28
{
29
    /**
30
     * Identify if a year is a leap year or not.
31
     *
32
     * @param int $year The year to test
33
     *
34
     * @return bool TRUE if the year is a leap year, otherwise FALSE
35
     */
36 20
    public static function isLeapYear($year)
37
    {
38 20
        return (($year % 4) == 0) && (($year % 100) != 0) || (($year % 400) == 0);
39
    }
40
41
    /**
42
     * Return the number of days between two dates based on a 360 day calendar.
43
     *
44
     * @param int $startDay Day of month of the start date
45
     * @param int $startMonth Month of the start date
46
     * @param int $startYear Year of the start date
47
     * @param int $endDay Day of month of the start date
48
     * @param int $endMonth Month of the start date
49
     * @param int $endYear Year of the start date
50
     * @param bool $methodUS Whether to use the US method or the European method of calculation
51
     *
52
     * @return int Number of days between the start date and the end date
53
     */
54 68
    private static function dateDiff360($startDay, $startMonth, $startYear, $endDay, $endMonth, $endYear, $methodUS)
55
    {
56 68
        if ($startDay == 31) {
57 12
            --$startDay;
58 56
        } elseif ($methodUS && ($startMonth == 2 && ($startDay == 29 || ($startDay == 28 && !self::isLeapYear($startYear))))) {
59 1
            $startDay = 30;
60
        }
61 68
        if ($endDay == 31) {
62 20
            if ($methodUS && $startDay != 30) {
63 9
                $endDay = 1;
64 9
                if ($endMonth == 12) {
65 3
                    ++$endYear;
66 3
                    $endMonth = 1;
67
                } else {
68 9
                    ++$endMonth;
69
                }
70
            } else {
71 11
                $endDay = 30;
72
            }
73
        }
74
75 68
        return $endDay + $endMonth * 30 + $endYear * 360 - $startDay - $startMonth * 30 - $startYear * 360;
76
    }
77
78
    /**
79
     * getDateValue.
80
     *
81
     * @param string $dateValue
82
     *
83
     * @return mixed Excel date/time serial value, or string if error
84
     */
85 382
    public static function getDateValue($dateValue)
86
    {
87 382
        if (!is_numeric($dateValue)) {
88 349
            if ((is_string($dateValue)) &&
89 349
                (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC)) {
90
                return Functions::VALUE();
91
            }
92 349
            if ((is_object($dateValue)) && ($dateValue instanceof \DateTime)) {
93
                $dateValue = \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel($dateValue);
94
            } else {
95 349
                $saveReturnDateType = Functions::getReturnDateType();
96 349
                Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
97 349
                $dateValue = self::DATEVALUE($dateValue);
98 349
                Functions::setReturnDateType($saveReturnDateType);
99
            }
100
        }
101
102 382
        return $dateValue;
103
    }
104
105
    /**
106
     * getTimeValue.
107
     *
108
     * @param string $timeValue
109
     *
110
     * @return mixed Excel date/time serial value, or string if error
111
     */
112 12
    private static function getTimeValue($timeValue)
113
    {
114 12
        $saveReturnDateType = Functions::getReturnDateType();
115 12
        Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
116 12
        $timeValue = self::TIMEVALUE($timeValue);
117 12
        Functions::setReturnDateType($saveReturnDateType);
118
119 12
        return $timeValue;
120
    }
121
122 32
    private static function adjustDateByMonths($dateValue = 0, $adjustmentMonths = 0)
123
    {
124
        // Execute function
125 32
        $PHPDateObject = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($dateValue);
126 32
        $oMonth = (int) $PHPDateObject->format('m');
127 32
        $oYear = (int) $PHPDateObject->format('Y');
128
129 32
        $adjustmentMonthsString = (string) $adjustmentMonths;
130 32
        if ($adjustmentMonths > 0) {
131 15
            $adjustmentMonthsString = '+' . $adjustmentMonths;
132
        }
133 32
        if ($adjustmentMonths != 0) {
134 26
            $PHPDateObject->modify($adjustmentMonthsString . ' months');
135
        }
136 32
        $nMonth = (int) $PHPDateObject->format('m');
137 32
        $nYear = (int) $PHPDateObject->format('Y');
138
139 32
        $monthDiff = ($nMonth - $oMonth) + (($nYear - $oYear) * 12);
140 32
        if ($monthDiff != $adjustmentMonths) {
141 5
            $adjustDays = (int) $PHPDateObject->format('d');
142 5
            $adjustDaysString = '-' . $adjustDays . ' days';
143 5
            $PHPDateObject->modify($adjustDaysString);
144
        }
145
146 32
        return $PHPDateObject;
147
    }
148
149
    /**
150
     * DATETIMENOW.
151
     *
152
     * Returns the current date and time.
153
     * The NOW function is useful when you need to display the current date and time on a worksheet or
154
     * calculate a value based on the current date and time, and have that value updated each time you
155
     * open the worksheet.
156
     *
157
     * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
158
     * and time format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
159
     *
160
     * Excel Function:
161
     *        NOW()
162
     *
163
     * @category Date/Time Functions
164
     *
165
     * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
166
     *                        depending on the value of the ReturnDateType flag
167
     */
168
    public static function DATETIMENOW()
169
    {
170
        $saveTimeZone = date_default_timezone_get();
171
        date_default_timezone_set('UTC');
172
        $retValue = false;
173
        switch (Functions::getReturnDateType()) {
174
            case Functions::RETURNDATE_EXCEL:
175
                $retValue = (float) \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel(time());
176
                break;
177
            case Functions::RETURNDATE_PHP_NUMERIC:
178
                $retValue = (int) time();
179
                break;
180
            case Functions::RETURNDATE_PHP_OBJECT:
181
                $retValue = new \DateTime();
182
                break;
183
        }
184
        date_default_timezone_set($saveTimeZone);
185
186
        return $retValue;
187
    }
188
189
    /**
190
     * DATENOW.
191
     *
192
     * Returns the current date.
193
     * The NOW function is useful when you need to display the current date and time on a worksheet or
194
     * calculate a value based on the current date and time, and have that value updated each time you
195
     * open the worksheet.
196
     *
197
     * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
198
     * and time format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
199
     *
200
     * Excel Function:
201
     *        TODAY()
202
     *
203
     * @category Date/Time Functions
204
     *
205
     * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
206
     *                        depending on the value of the ReturnDateType flag
207
     */
208 1
    public static function DATENOW()
209
    {
210 1
        $saveTimeZone = date_default_timezone_get();
211 1
        date_default_timezone_set('UTC');
212 1
        $retValue = false;
213 1
        $excelDateTime = floor(\PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel(time()));
214 1
        switch (Functions::getReturnDateType()) {
215 1
            case Functions::RETURNDATE_EXCEL:
216
                $retValue = (float) $excelDateTime;
217
                break;
218 1
            case Functions::RETURNDATE_PHP_NUMERIC:
219 1
                $retValue = (int) \PhpOffice\PhpSpreadsheet\Shared\Date::excelToTimestamp($excelDateTime);
220 1
                break;
221
            case Functions::RETURNDATE_PHP_OBJECT:
222
                $retValue = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($excelDateTime);
223
                break;
224
        }
225 1
        date_default_timezone_set($saveTimeZone);
226
227 1
        return $retValue;
228
    }
229
230
    /**
231
     * DATE.
232
     *
233
     * The DATE function returns a value that represents a particular date.
234
     *
235
     * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
236
     * format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
237
     *
238
     * Excel Function:
239
     *        DATE(year,month,day)
240
     *
241
     * PhpSpreadsheet is a lot more forgiving than MS Excel when passing non numeric values to this function.
242
     * A Month name or abbreviation (English only at this point) such as 'January' or 'Jan' will still be accepted,
243
     *     as will a day value with a suffix (e.g. '21st' rather than simply 21); again only English language.
244
     *
245
     * @category Date/Time Functions
246
     *
247
     * @param int $year The value of the year argument can include one to four digits.
248
     *                                Excel interprets the year argument according to the configured
249
     *                                date system: 1900 or 1904.
250
     *                                If year is between 0 (zero) and 1899 (inclusive), Excel adds that
251
     *                                value to 1900 to calculate the year. For example, DATE(108,1,2)
252
     *                                returns January 2, 2008 (1900+108).
253
     *                                If year is between 1900 and 9999 (inclusive), Excel uses that
254
     *                                value as the year. For example, DATE(2008,1,2) returns January 2,
255
     *                                2008.
256
     *                                If year is less than 0 or is 10000 or greater, Excel returns the
257
     *                                #NUM! error value.
258
     * @param int $month A positive or negative integer representing the month of the year
259
     *                                from 1 to 12 (January to December).
260
     *                                If month is greater than 12, month adds that number of months to
261
     *                                the first month in the year specified. For example, DATE(2008,14,2)
262
     *                                returns the serial number representing February 2, 2009.
263
     *                                If month is less than 1, month subtracts the magnitude of that
264
     *                                number of months, plus 1, from the first month in the year
265
     *                                specified. For example, DATE(2008,-3,2) returns the serial number
266
     *                                representing September 2, 2007.
267
     * @param int $day A positive or negative integer representing the day of the month
268
     *                                from 1 to 31.
269
     *                                If day is greater than the number of days in the month specified,
270
     *                                day adds that number of days to the first day in the month. For
271
     *                                example, DATE(2008,1,35) returns the serial number representing
272
     *                                February 4, 2008.
273
     *                                If day is less than 1, day subtracts the magnitude that number of
274
     *                                days, plus one, from the first day of the month specified. For
275
     *                                example, DATE(2008,1,-15) returns the serial number representing
276
     *                                December 16, 2007.
277
     *
278
     * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
279
     *                        depending on the value of the ReturnDateType flag
280
     */
281 83
    public static function DATE($year = 0, $month = 1, $day = 1)
282
    {
283 83
        $year = Functions::flattenSingleValue($year);
284 83
        $month = Functions::flattenSingleValue($month);
285 83
        $day = Functions::flattenSingleValue($day);
286
287 83
        if (($month !== null) && (!is_numeric($month))) {
288 3
            $month = \PhpOffice\PhpSpreadsheet\Shared\Date::monthStringToNumber($month);
0 ignored issues
show
Bug introduced by
It seems like $month defined by \PhpOffice\PhpSpreadshee...hStringToNumber($month) on line 288 can also be of type boolean or object; however, PhpOffice\PhpSpreadsheet...::monthStringToNumber() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
289
        }
290
291 83
        if (($day !== null) && (!is_numeric($day))) {
292 3
            $day = \PhpOffice\PhpSpreadsheet\Shared\Date::dayStringToNumber($day);
0 ignored issues
show
Bug introduced by
It seems like $day defined by \PhpOffice\PhpSpreadshee...dayStringToNumber($day) on line 292 can also be of type boolean or object; however, PhpOffice\PhpSpreadsheet...te::dayStringToNumber() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
293
        }
294
295 83
        $year = ($year !== null) ? \PhpOffice\PhpSpreadsheet\Shared\StringHelper::testStringAsNumeric($year) : 0;
296 83
        $month = ($month !== null) ? \PhpOffice\PhpSpreadsheet\Shared\StringHelper::testStringAsNumeric($month) : 0;
297 83
        $day = ($day !== null) ? \PhpOffice\PhpSpreadsheet\Shared\StringHelper::testStringAsNumeric($day) : 0;
298 83
        if ((!is_numeric($year)) ||
299 82
            (!is_numeric($month)) ||
300 83
            (!is_numeric($day))) {
301 3
            return Functions::VALUE();
302
        }
303 80
        $year = (int) $year;
304 80
        $month = (int) $month;
305 80
        $day = (int) $day;
306
307 80
        $baseYear = \PhpOffice\PhpSpreadsheet\Shared\Date::getExcelCalendar();
308
        // Validate parameters
309 80
        if ($year < ($baseYear - 1900)) {
310 2
            return Functions::NAN();
311
        }
312 78
        if ((($baseYear - 1900) != 0) && ($year < $baseYear) && ($year >= 1900)) {
313 1
            return Functions::NAN();
314
        }
315
316 77
        if (($year < $baseYear) && ($year >= ($baseYear - 1900))) {
317 6
            $year += 1900;
318
        }
319
320 77
        if ($month < 1) {
321
            //    Handle year/month adjustment if month < 1
322 21
            --$month;
323 21
            $year += ceil($month / 12) - 1;
324 21
            $month = 13 - abs($month % 12);
325 56
        } elseif ($month > 12) {
326
            //    Handle year/month adjustment if month > 12
327 7
            $year += floor($month / 12);
328 7
            $month = ($month % 12);
329
        }
330
331
        // Re-validate the year parameter after adjustments
332 77
        if (($year < $baseYear) || ($year >= 10000)) {
333 2
            return Functions::NAN();
334
        }
335
336
        // Execute function
337 75
        $excelDateValue = \PhpOffice\PhpSpreadsheet\Shared\Date::formattedPHPToExcel($year, $month, $day);
338 75 View Code Duplication
        switch (Functions::getReturnDateType()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
339 75
            case Functions::RETURNDATE_EXCEL:
340 73
                return (float) $excelDateValue;
341 2
            case Functions::RETURNDATE_PHP_NUMERIC:
342 1
                return (int) \PhpOffice\PhpSpreadsheet\Shared\Date::excelToTimestamp($excelDateValue);
343 1
            case Functions::RETURNDATE_PHP_OBJECT:
344 1
                return \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($excelDateValue);
345
        }
346
    }
347
348
    /**
349
     * TIME.
350
     *
351
     * The TIME function returns a value that represents a particular time.
352
     *
353
     * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the time
354
     * format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
355
     *
356
     * Excel Function:
357
     *        TIME(hour,minute,second)
358
     *
359
     * @category Date/Time Functions
360
     *
361
     * @param int $hour A number from 0 (zero) to 32767 representing the hour.
362
     *                                    Any value greater than 23 will be divided by 24 and the remainder
363
     *                                    will be treated as the hour value. For example, TIME(27,0,0) =
364
     *                                    TIME(3,0,0) = .125 or 3:00 AM.
365
     * @param int $minute A number from 0 to 32767 representing the minute.
366
     *                                    Any value greater than 59 will be converted to hours and minutes.
367
     *                                    For example, TIME(0,750,0) = TIME(12,30,0) = .520833 or 12:30 PM.
368
     * @param int $second A number from 0 to 32767 representing the second.
369
     *                                    Any value greater than 59 will be converted to hours, minutes,
370
     *                                    and seconds. For example, TIME(0,0,2000) = TIME(0,33,22) = .023148
371
     *                                    or 12:33:20 AM
372
     *
373
     * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
374
     *                        depending on the value of the ReturnDateType flag
375
     */
376 24
    public static function TIME($hour = 0, $minute = 0, $second = 0)
377
    {
378 24
        $hour = Functions::flattenSingleValue($hour);
379 24
        $minute = Functions::flattenSingleValue($minute);
380 24
        $second = Functions::flattenSingleValue($second);
381
382 24
        if ($hour == '') {
383
            $hour = 0;
384
        }
385 24
        if ($minute == '') {
386 5
            $minute = 0;
387
        }
388 24
        if ($second == '') {
389 5
            $second = 0;
390
        }
391
392 24
        if ((!is_numeric($hour)) || (!is_numeric($minute)) || (!is_numeric($second))) {
393 1
            return Functions::VALUE();
394
        }
395 23
        $hour = (int) $hour;
396 23
        $minute = (int) $minute;
397 23
        $second = (int) $second;
398
399 23 View Code Duplication
        if ($second < 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
400 4
            $minute += floor($second / 60);
401 4
            $second = 60 - abs($second % 60);
402 4
            if ($second == 60) {
403 4
                $second = 0;
404
            }
405 19
        } elseif ($second >= 60) {
406 1
            $minute += floor($second / 60);
407 1
            $second = $second % 60;
408
        }
409 23 View Code Duplication
        if ($minute < 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
410 7
            $hour += floor($minute / 60);
411 7
            $minute = 60 - abs($minute % 60);
412 7
            if ($minute == 60) {
413 7
                $minute = 0;
414
            }
415 16
        } elseif ($minute >= 60) {
416 3
            $hour += floor($minute / 60);
417 3
            $minute = $minute % 60;
418
        }
419
420 23
        if ($hour > 23) {
421 1
            $hour = $hour % 24;
422 22
        } elseif ($hour < 0) {
423 2
            return Functions::NAN();
424
        }
425
426
        // Execute function
427 21
        switch (Functions::getReturnDateType()) {
428 21
            case Functions::RETURNDATE_EXCEL:
429 19
                $date = 0;
430 19
                $calendar = \PhpOffice\PhpSpreadsheet\Shared\Date::getExcelCalendar();
431 19
                if ($calendar != \PhpOffice\PhpSpreadsheet\Shared\Date::CALENDAR_WINDOWS_1900) {
432
                    $date = 1;
433
                }
434
435 19
                return (float) \PhpOffice\PhpSpreadsheet\Shared\Date::formattedPHPToExcel($calendar, 1, $date, $hour, $minute, $second);
436 2
            case Functions::RETURNDATE_PHP_NUMERIC:
437 1
                return (int) \PhpOffice\PhpSpreadsheet\Shared\Date::excelToTimestamp(\PhpOffice\PhpSpreadsheet\Shared\Date::formattedPHPToExcel(1970, 1, 1, $hour, $minute, $second)); // -2147468400; //    -2147472000 + 3600
438 1
            case Functions::RETURNDATE_PHP_OBJECT:
439 1
                $dayAdjust = 0;
440 1 View Code Duplication
                if ($hour < 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
441
                    $dayAdjust = floor($hour / 24);
442
                    $hour = 24 - abs($hour % 24);
443
                    if ($hour == 24) {
444
                        $hour = 0;
445
                    }
446 1
                } elseif ($hour >= 24) {
447
                    $dayAdjust = floor($hour / 24);
448
                    $hour = $hour % 24;
449
                }
450 1
                $phpDateObject = new \DateTime('1900-01-01 ' . $hour . ':' . $minute . ':' . $second);
451 1
                if ($dayAdjust != 0) {
452
                    $phpDateObject->modify($dayAdjust . ' days');
453
                }
454
455 1
                return $phpDateObject;
456
        }
457
    }
458
459
    /**
460
     * DATEVALUE.
461
     *
462
     * Returns a value that represents a particular date.
463
     * Use DATEVALUE to convert a date represented by a text string to an Excel or PHP date/time stamp
464
     * value.
465
     *
466
     * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
467
     * format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
468
     *
469
     * Excel Function:
470
     *        DATEVALUE(dateValue)
471
     *
472
     * @category Date/Time Functions
473
     *
474
     * @param string $dateValue Text that represents a date in a Microsoft Excel date format.
475
     *                                    For example, "1/30/2008" or "30-Jan-2008" are text strings within
476
     *                                    quotation marks that represent dates. Using the default date
477
     *                                    system in Excel for Windows, date_text must represent a date from
478
     *                                    January 1, 1900, to December 31, 9999. Using the default date
479
     *                                    system in Excel for the Macintosh, date_text must represent a date
480
     *                                    from January 1, 1904, to December 31, 9999. DATEVALUE returns the
481
     *                                    #VALUE! error value if date_text is out of this range.
482
     *
483
     * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
484
     *                        depending on the value of the ReturnDateType flag
485
     */
486 423
    public static function DATEVALUE($dateValue = 1)
487
    {
488 423
        $dateValueOrig = $dateValue;
0 ignored issues
show
Unused Code introduced by
$dateValueOrig is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
489 423
        $dateValue = trim(Functions::flattenSingleValue($dateValue), '"');
490
        //    Strip any ordinals because they're allowed in Excel (English only)
491 423
        $dateValue = preg_replace('/(\d)(st|nd|rd|th)([ -\/])/Ui', '$1$3', $dateValue);
492
        //    Convert separators (/ . or space) to hyphens (should also handle dot used for ordinals in some countries, e.g. Denmark, Germany)
493 423
        $dateValue = str_replace(['/', '.', '-', '  '], [' ', ' ', ' ', ' '], $dateValue);
494
495 423
        $yearFound = false;
496 423
        $t1 = explode(' ', $dateValue);
497 423
        foreach ($t1 as &$t) {
498 423
            if ((is_numeric($t)) && ($t > 31)) {
499 390
                if ($yearFound) {
500
                    return Functions::VALUE();
501
                }
502 390
                if ($t < 100) {
503 2
                    $t += 1900;
504
                }
505 423
                $yearFound = true;
506
            }
507
        }
508 423
        if ((count($t1) == 1) && (strpos($t, ':') != false)) {
0 ignored issues
show
Bug introduced by
The variable $t does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug Best Practice introduced by
It seems like you are loosely comparing strpos($t, ':') of type integer to the boolean false. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
509
            //    We've been fed a time value without any date
510 1
            return 0.0;
511 422
        } elseif (count($t1) == 2) {
512
            //    We only have two parts of the date: either day/month or month/year
513 27
            if ($yearFound) {
514 3
                array_unshift($t1, 1);
515
            } else {
516 24
                if ($t1[1] > 29) {
517 1
                    $t1[1] += 1900;
518 1
                    array_unshift($t1, 1);
519
                } else {
520 23
                    array_push($t1, date('Y'));
521
                }
522
            }
523
        }
524 422
        unset($t);
525 422
        $dateValue = implode(' ', $t1);
526
527 422
        $PHPDateArray = date_parse($dateValue);
528 422
        if (($PHPDateArray === false) || ($PHPDateArray['error_count'] > 0)) {
529 316
            $testVal1 = strtok($dateValue, '- ');
530 316
            if ($testVal1 !== false) {
531 315
                $testVal2 = strtok('- ');
532 315
                if ($testVal2 !== false) {
533 301
                    $testVal3 = strtok('- ');
534 301
                    if ($testVal3 === false) {
535 301
                        $testVal3 = strftime('%Y');
536
                    }
537
                } else {
538 315
                    return Functions::VALUE();
539
                }
540
            } else {
541 1
                return Functions::VALUE();
542
            }
543 301
            if ($testVal1 < 31 && $testVal2 < 12 && $testVal3 < 12 && strlen($testVal3) == 2) {
544 1
                $testVal3 += 2000;
545
            }
546 301
            $PHPDateArray = date_parse($testVal1 . '-' . $testVal2 . '-' . $testVal3);
547 301
            if (($PHPDateArray === false) || ($PHPDateArray['error_count'] > 0)) {
548 25
                $PHPDateArray = date_parse($testVal2 . '-' . $testVal1 . '-' . $testVal3);
549 25
                if (($PHPDateArray === false) || ($PHPDateArray['error_count'] > 0)) {
550 22
                    return Functions::VALUE();
551
                }
552
            }
553
        }
554
555 391
        if (($PHPDateArray !== false) && ($PHPDateArray['error_count'] == 0)) {
556
            // Execute function
557 391
            if ($PHPDateArray['year'] == '') {
558
                $PHPDateArray['year'] = strftime('%Y');
559
            }
560 391
            if ($PHPDateArray['year'] < 1900) {
561 2
                return Functions::VALUE();
562
            }
563 389
            if ($PHPDateArray['month'] == '') {
564
                $PHPDateArray['month'] = strftime('%m');
565
            }
566 389
            if ($PHPDateArray['day'] == '') {
567
                $PHPDateArray['day'] = strftime('%d');
568
            }
569 389
            if (!checkdate($PHPDateArray['month'], $PHPDateArray['day'], $PHPDateArray['year'])) {
570 3
                return Functions::VALUE();
571
            }
572 386
            $excelDateValue = floor(
573 386
                \PhpOffice\PhpSpreadsheet\Shared\Date::formattedPHPToExcel(
574 386
                    $PHPDateArray['year'],
575 386
                    $PHPDateArray['month'],
576 386
                    $PHPDateArray['day'],
577 386
                    $PHPDateArray['hour'],
578 386
                    $PHPDateArray['minute'],
579 386
                    $PHPDateArray['second']
580
                )
581
            );
582 386 View Code Duplication
            switch (Functions::getReturnDateType()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
583 386
                case Functions::RETURNDATE_EXCEL:
584 384
                    return (float) $excelDateValue;
585 2
                case Functions::RETURNDATE_PHP_NUMERIC:
586 1
                    return (int) \PhpOffice\PhpSpreadsheet\Shared\Date::excelToTimestamp($excelDateValue);
587 1
                case Functions::RETURNDATE_PHP_OBJECT:
588 1
                    return new \DateTime($PHPDateArray['year'] . '-' . $PHPDateArray['month'] . '-' . $PHPDateArray['day'] . ' 00:00:00');
589
            }
590
        }
591
592
        return Functions::VALUE();
593
    }
594
595
    /**
596
     * TIMEVALUE.
597
     *
598
     * Returns a value that represents a particular time.
599
     * Use TIMEVALUE to convert a time represented by a text string to an Excel or PHP date/time stamp
600
     * value.
601
     *
602
     * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the time
603
     * format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
604
     *
605
     * Excel Function:
606
     *        TIMEVALUE(timeValue)
607
     *
608
     * @category Date/Time Functions
609
     *
610
     * @param string $timeValue A text string that represents a time in any one of the Microsoft
611
     *                                    Excel time formats; for example, "6:45 PM" and "18:45" text strings
612
     *                                    within quotation marks that represent time.
613
     *                                    Date information in time_text is ignored.
614
     *
615
     * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
616
     *                        depending on the value of the ReturnDateType flag
617
     */
618 30
    public static function TIMEVALUE($timeValue)
619
    {
620 30
        $timeValue = trim(Functions::flattenSingleValue($timeValue), '"');
621 30
        $timeValue = str_replace(['/', '.'], ['-', '-'], $timeValue);
622
623 30
        $arraySplit = preg_split('/[\/:\-\s]/', $timeValue);
624 30
        if ((count($arraySplit) == 2 || count($arraySplit) == 3) && $arraySplit[0] > 24) {
625 1
            $arraySplit[0] = ($arraySplit[0] % 24);
626 1
            $timeValue = implode(':', $arraySplit);
627
        }
628
629 30
        $PHPDateArray = date_parse($timeValue);
630 30
        if (($PHPDateArray !== false) && ($PHPDateArray['error_count'] == 0)) {
631 25
            if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
632
                $excelDateValue = \PhpOffice\PhpSpreadsheet\Shared\Date::formattedPHPToExcel(
633
                    $PHPDateArray['year'],
634
                    $PHPDateArray['month'],
635
                    $PHPDateArray['day'],
636
                    $PHPDateArray['hour'],
637
                    $PHPDateArray['minute'],
638
                    $PHPDateArray['second']
639
                );
640
            } else {
641 25
                $excelDateValue = \PhpOffice\PhpSpreadsheet\Shared\Date::formattedPHPToExcel(1900, 1, 1, $PHPDateArray['hour'], $PHPDateArray['minute'], $PHPDateArray['second']) - 1;
642
            }
643
644 25 View Code Duplication
            switch (Functions::getReturnDateType()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
645 25
                case Functions::RETURNDATE_EXCEL:
646 23
                    return (float) $excelDateValue;
647 2
                case Functions::RETURNDATE_PHP_NUMERIC:
648 1
                    return (int) $phpDateValue = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToTimestamp($excelDateValue + 25569) - 3600;
649 1
                case Functions::RETURNDATE_PHP_OBJECT:
650 1
                    return new \DateTime('1900-01-01 ' . $PHPDateArray['hour'] . ':' . $PHPDateArray['minute'] . ':' . $PHPDateArray['second']);
651
            }
652
        }
653
654 5
        return Functions::VALUE();
655
    }
656
657
    /**
658
     * DATEDIF.
659
     *
660
     * @param mixed $startDate Excel date serial value, PHP date/time stamp, PHP DateTime object
661
     *                                    or a standard date string
662
     * @param mixed $endDate Excel date serial value, PHP date/time stamp, PHP DateTime object
663
     *                                    or a standard date string
664
     * @param string $unit
665
     *
666
     * @return int Interval between the dates
667
     */
668 150
    public static function DATEDIF($startDate = 0, $endDate = 0, $unit = 'D')
669
    {
670 150
        $startDate = Functions::flattenSingleValue($startDate);
671 150
        $endDate = Functions::flattenSingleValue($endDate);
672 150
        $unit = strtoupper(Functions::flattenSingleValue($unit));
673
674 150
        if (is_string($startDate = self::getDateValue($startDate))) {
675 1
            return Functions::VALUE();
676
        }
677 149
        if (is_string($endDate = self::getDateValue($endDate))) {
678 1
            return Functions::VALUE();
679
        }
680
681
        // Validate parameters
682 148
        if ($startDate > $endDate) {
683 1
            return Functions::NAN();
684
        }
685
686
        // Execute function
687 147
        $difference = $endDate - $startDate;
688
689 147
        $PHPStartDateObject = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($startDate);
690 147
        $startDays = $PHPStartDateObject->format('j');
691 147
        $startMonths = $PHPStartDateObject->format('n');
692 147
        $startYears = $PHPStartDateObject->format('Y');
693
694 147
        $PHPEndDateObject = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($endDate);
695 147
        $endDays = $PHPEndDateObject->format('j');
696 147
        $endMonths = $PHPEndDateObject->format('n');
697 147
        $endYears = $PHPEndDateObject->format('Y');
698
699 147
        $retVal = Functions::NAN();
0 ignored issues
show
Unused Code introduced by
$retVal is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
700
        switch ($unit) {
701 147
            case 'D':
702 64
                $retVal = (int) $difference;
703 64
                break;
704 83 View Code Duplication
            case 'M':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
705 15
                $retVal = (int) ($endMonths - $startMonths) + ((int) ($endYears - $startYears) * 12);
706
                //    We're only interested in full months
707 15
                if ($endDays < $startDays) {
708 3
                    --$retVal;
709
                }
710 15
                break;
711 68
            case 'Y':
712 17
                $retVal = (int) ($endYears - $startYears);
713
                //    We're only interested in full months
714 17
                if ($endMonths < $startMonths) {
715 5
                    --$retVal;
716 12
                } elseif (($endMonths == $startMonths) && ($endDays < $startDays)) {
717
                    // Remove start month
718 1
                    --$retVal;
719
                    // Remove end month
720 1
                    --$retVal;
721
                }
722 17
                break;
723 51
            case 'MD':
724 15
                if ($endDays < $startDays) {
725 3
                    $retVal = $endDays;
726 3
                    $PHPEndDateObject->modify('-' . $endDays . ' days');
727 3
                    $adjustDays = $PHPEndDateObject->format('j');
728 3
                    $retVal += ($adjustDays - $startDays);
729
                } else {
730 12
                    $retVal = $endDays - $startDays;
731
                }
732 15
                break;
733 36 View Code Duplication
            case 'YM':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
734 15
                $retVal = (int) ($endMonths - $startMonths);
735 15
                if ($retVal < 0) {
736 4
                    $retVal += 12;
737
                }
738
                //    We're only interested in full months
739 15
                if ($endDays < $startDays) {
740 3
                    --$retVal;
741
                }
742 15
                break;
743 21
            case 'YD':
744 20
                $retVal = (int) $difference;
745 20
                if ($endYears > $startYears) {
746 9
                    $isLeapStartYear = $PHPStartDateObject->format('L');
747 9
                    $wasLeapEndYear = $PHPEndDateObject->format('L');
748
749
                    // Adjust end year to be as close as possible as start year
750 9
                    while ($PHPEndDateObject >= $PHPStartDateObject) {
751 9
                        $PHPEndDateObject->modify('-1 year');
752 9
                        $endYears = $PHPEndDateObject->format('Y');
0 ignored issues
show
Unused Code introduced by
$endYears is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
753
                    }
754 9
                    $PHPEndDateObject->modify('+1 year');
755
756
                    // Get the result
757 9
                    $retVal = $PHPEndDateObject->diff($PHPStartDateObject)->days;
758
759
                    // Adjust for leap years cases
760 9
                    $isLeapEndYear = $PHPEndDateObject->format('L');
761 9
                    $limit = new \DateTime($PHPEndDateObject->format('Y-02-29'));
762 9
                    if (!$isLeapStartYear && !$wasLeapEndYear && $isLeapEndYear && $PHPEndDateObject >= $limit) {
763 1
                        --$retVal;
764
                    }
765
                }
766 20
                break;
767
            default:
768 1
                $retVal = Functions::VALUE();
769
        }
770
771 147
        return $retVal;
772
    }
773
774
    /**
775
     * DAYS360.
776
     *
777
     * Returns the number of days between two dates based on a 360-day year (twelve 30-day months),
778
     * which is used in some accounting calculations. Use this function to help compute payments if
779
     * your accounting system is based on twelve 30-day months.
780
     *
781
     * Excel Function:
782
     *        DAYS360(startDate,endDate[,method])
783
     *
784
     * @category Date/Time Functions
785
     *
786
     * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
787
     *                                        PHP DateTime object, or a standard date string
788
     * @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
789
     *                                        PHP DateTime object, or a standard date string
790
     * @param bool $method US or European Method
791
     *                                        FALSE or omitted: U.S. (NASD) method. If the starting date is
792
     *                                        the last day of a month, it becomes equal to the 30th of the
793
     *                                        same month. If the ending date is the last day of a month and
794
     *                                        the starting date is earlier than the 30th of a month, the
795
     *                                        ending date becomes equal to the 1st of the next month;
796
     *                                        otherwise the ending date becomes equal to the 30th of the
797
     *                                        same month.
798
     *                                        TRUE: European method. Starting dates and ending dates that
799
     *                                        occur on the 31st of a month become equal to the 30th of the
800
     *                                        same month.
801
     *
802
     * @return int Number of days between start date and end date
803
     */
804 72
    public static function DAYS360($startDate = 0, $endDate = 0, $method = false)
805
    {
806 72
        $startDate = Functions::flattenSingleValue($startDate);
807 72
        $endDate = Functions::flattenSingleValue($endDate);
808
809 72
        if (is_string($startDate = self::getDateValue($startDate))) {
810 1
            return Functions::VALUE();
811
        }
812 71
        if (is_string($endDate = self::getDateValue($endDate))) {
813 1
            return Functions::VALUE();
814
        }
815
816 70
        if (!is_bool($method)) {
817 2
            return Functions::VALUE();
818
        }
819
820
        // Execute function
821 68
        $PHPStartDateObject = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($startDate);
822 68
        $startDay = $PHPStartDateObject->format('j');
823 68
        $startMonth = $PHPStartDateObject->format('n');
824 68
        $startYear = $PHPStartDateObject->format('Y');
825
826 68
        $PHPEndDateObject = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($endDate);
827 68
        $endDay = $PHPEndDateObject->format('j');
828 68
        $endMonth = $PHPEndDateObject->format('n');
829 68
        $endYear = $PHPEndDateObject->format('Y');
830
831 68
        return self::dateDiff360($startDay, $startMonth, $startYear, $endDay, $endMonth, $endYear, !$method);
832
    }
833
834
    /**
835
     * YEARFRAC.
836
     *
837
     * Calculates the fraction of the year represented by the number of whole days between two dates
838
     * (the start_date and the end_date).
839
     * Use the YEARFRAC worksheet function to identify the proportion of a whole year's benefits or
840
     * obligations to assign to a specific term.
841
     *
842
     * Excel Function:
843
     *        YEARFRAC(startDate,endDate[,method])
844
     *
845
     * @category Date/Time Functions
846
     *
847
     * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
848
     *                                    PHP DateTime object, or a standard date string
849
     * @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
850
     *                                    PHP DateTime object, or a standard date string
851
     * @param int $method Method used for the calculation
852
     *                                        0 or omitted    US (NASD) 30/360
853
     *                                        1                Actual/actual
854
     *                                        2                Actual/360
855
     *                                        3                Actual/365
856
     *                                        4                European 30/360
857
     *
858
     * @return float fraction of the year
859
     */
860 89
    public static function YEARFRAC($startDate = 0, $endDate = 0, $method = 0)
861
    {
862 89
        $startDate = Functions::flattenSingleValue($startDate);
863 89
        $endDate = Functions::flattenSingleValue($endDate);
864 89
        $method = Functions::flattenSingleValue($method);
865
866 89
        if (is_string($startDate = self::getDateValue($startDate))) {
867 4
            return Functions::VALUE();
868
        }
869 85
        if (is_string($endDate = self::getDateValue($endDate))) {
870
            return Functions::VALUE();
871
        }
872
873 85
        if (((is_numeric($method)) && (!is_string($method))) || ($method == '')) {
874
            switch ($method) {
875 84
                case 0:
876 22
                    return self::DAYS360($startDate, $endDate) / 360;
877 62
                case 1:
878 19
                    $days = self::DATEDIF($startDate, $endDate);
879 19
                    $startYear = self::YEAR($startDate);
880 19
                    $endYear = self::YEAR($endDate);
881 19
                    $years = $endYear - $startYear + 1;
882 19
                    $leapDays = 0;
883 19
                    if ($years == 1) {
884 13
                        if (self::isLeapYear($endYear)) {
885 3
                            $startMonth = self::MONTHOFYEAR($startDate);
886 3
                            $endMonth = self::MONTHOFYEAR($endDate);
887 3
                            $endDay = self::DAYOFMONTH($endDate);
888 3
                            if (($startMonth < 3) ||
889 3
                                (($endMonth * 100 + $endDay) >= (2 * 100 + 29))) {
890 13
                                $leapDays += 1;
891
                            }
892
                        }
893
                    } else {
894 6
                        for ($year = $startYear; $year <= $endYear; ++$year) {
895 6
                            if ($year == $startYear) {
896 6
                                $startMonth = self::MONTHOFYEAR($startDate);
897 6
                                $startDay = self::DAYOFMONTH($startDate);
0 ignored issues
show
Unused Code introduced by
$startDay is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
898 6
                                if ($startMonth < 3) {
899 6
                                    $leapDays += (self::isLeapYear($year)) ? 1 : 0;
900
                                }
901 6
                            } elseif ($year == $endYear) {
902 6
                                $endMonth = self::MONTHOFYEAR($endDate);
903 6
                                $endDay = self::DAYOFMONTH($endDate);
904 6
                                if (($endMonth * 100 + $endDay) >= (2 * 100 + 29)) {
905 6
                                    $leapDays += (self::isLeapYear($year)) ? 1 : 0;
906
                                }
907
                            } else {
908 2
                                $leapDays += (self::isLeapYear($year)) ? 1 : 0;
909
                            }
910
                        }
911 6
                        if ($years == 2) {
912 4
                            if (($leapDays == 0) && (self::isLeapYear($startYear)) && ($days > 365)) {
913
                                $leapDays = 1;
914 4
                            } elseif ($days < 366) {
915 1
                                $years = 1;
916
                            }
917
                        }
918 6
                        $leapDays /= $years;
919
                    }
920
921 19
                    return $days / (365 + $leapDays);
922 43
                case 2:
923 14
                    return self::DATEDIF($startDate, $endDate) / 360;
924 29
                case 3:
925 14
                    return self::DATEDIF($startDate, $endDate) / 365;
926 15
                case 4:
927 15
                    return self::DAYS360($startDate, $endDate, true) / 360;
928
            }
929
        }
930
931 1
        return Functions::VALUE();
932
    }
933
934
    /**
935
     * NETWORKDAYS.
936
     *
937
     * Returns the number of whole working days between start_date and end_date. Working days
938
     * exclude weekends and any dates identified in holidays.
939
     * Use NETWORKDAYS to calculate employee benefits that accrue based on the number of days
940
     * worked during a specific term.
941
     *
942
     * Excel Function:
943
     *        NETWORKDAYS(startDate,endDate[,holidays[,holiday[,...]]])
944
     *
945
     * @category Date/Time Functions
946
     *
947
     * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
948
     *                                            PHP DateTime object, or a standard date string
949
     * @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
950
     *                                            PHP DateTime object, or a standard date string
951
     *
952
     * @return int Interval between the dates
953
     */
954 18
    public static function NETWORKDAYS($startDate, $endDate, ...$dateArgs)
955
    {
956
        //    Retrieve the mandatory start and end date that are referenced in the function definition
957 18
        $startDate = Functions::flattenSingleValue($startDate);
958 18
        $endDate = Functions::flattenSingleValue($endDate);
959
        //    Get the optional days
960 18
        $dateArgs = Functions::flattenArray($dateArgs);
961
962
        //    Validate the start and end dates
963 18
        if (is_string($startDate = $sDate = self::getDateValue($startDate))) {
964
            return Functions::VALUE();
965
        }
966 18
        $startDate = (float) floor($startDate);
967 18
        if (is_string($endDate = $eDate = self::getDateValue($endDate))) {
968
            return Functions::VALUE();
969
        }
970 18
        $endDate = (float) floor($endDate);
971
972 18
        if ($sDate > $eDate) {
973 2
            $startDate = $eDate;
974 2
            $endDate = $sDate;
975
        }
976
977
        // Execute function
978 18
        $startDoW = 6 - self::WEEKDAY($startDate, 2);
979 18
        if ($startDoW < 0) {
980
            $startDoW = 0;
981
        }
982 18
        $endDoW = self::WEEKDAY($endDate, 2);
983 18
        if ($endDoW >= 6) {
984 2
            $endDoW = 0;
985
        }
986
987 18
        $wholeWeekDays = floor(($endDate - $startDate) / 7) * 5;
988 18
        $partWeekDays = $endDoW + $startDoW;
989 18
        if ($partWeekDays > 5) {
990 14
            $partWeekDays -= 5;
991
        }
992
993
        //    Test any extra holiday parameters
994 18
        $holidayCountedArray = [];
995 18
        foreach ($dateArgs as $holidayDate) {
996 4
            if (is_string($holidayDate = self::getDateValue($holidayDate))) {
997
                return Functions::VALUE();
998
            }
999 4 View Code Duplication
            if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1000 4
                if ((self::WEEKDAY($holidayDate, 2) < 6) && (!in_array($holidayDate, $holidayCountedArray))) {
1001 4
                    --$partWeekDays;
1002 4
                    $holidayCountedArray[] = $holidayDate;
1003
                }
1004
            }
1005
        }
1006
1007 18
        if ($sDate > $eDate) {
1008 2
            return 0 - ($wholeWeekDays + $partWeekDays);
1009
        }
1010
1011 16
        return $wholeWeekDays + $partWeekDays;
1012
    }
1013
1014
    /**
1015
     * WORKDAY.
1016
     *
1017
     * Returns the date that is the indicated number of working days before or after a date (the
1018
     * starting date). Working days exclude weekends and any dates identified as holidays.
1019
     * Use WORKDAY to exclude weekends or holidays when you calculate invoice due dates, expected
1020
     * delivery times, or the number of days of work performed.
1021
     *
1022
     * Excel Function:
1023
     *        WORKDAY(startDate,endDays[,holidays[,holiday[,...]]])
1024
     *
1025
     * @category Date/Time Functions
1026
     *
1027
     * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
1028
     *                                        PHP DateTime object, or a standard date string
1029
     * @param int $endDays The number of nonweekend and nonholiday days before or after
1030
     *                                        startDate. A positive value for days yields a future date; a
1031
     *                                        negative value yields a past date.
1032
     *
1033
     * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
1034
     *                        depending on the value of the ReturnDateType flag
1035
     */
1036 13
    public static function WORKDAY($startDate, $endDays, ...$dateArgs)
1037
    {
1038
        //    Retrieve the mandatory start date and days that are referenced in the function definition
1039 13
        $startDate = Functions::flattenSingleValue($startDate);
1040 13
        $endDays = Functions::flattenSingleValue($endDays);
1041
        //    Get the optional days
1042 13
        $dateArgs = Functions::flattenArray($dateArgs);
1043
1044 13
        if ((is_string($startDate = self::getDateValue($startDate))) || (!is_numeric($endDays))) {
1045 1
            return Functions::VALUE();
1046
        }
1047 12
        $startDate = (float) floor($startDate);
1048 12
        $endDays = (int) floor($endDays);
1049
        //    If endDays is 0, we always return startDate
1050 12
        if ($endDays == 0) {
1051
            return $startDate;
1052
        }
1053
1054 12
        $decrementing = ($endDays < 0) ? true : false;
1055
1056
        //    Adjust the start date if it falls over a weekend
1057
1058 12
        $startDoW = self::WEEKDAY($startDate, 3);
1059 12
        if (self::WEEKDAY($startDate, 3) >= 5) {
1060 4
            $startDate += ($decrementing) ? -$startDoW + 4 : 7 - $startDoW;
1061 4
            ($decrementing) ? $endDays++ : $endDays--;
1062
        }
1063
1064
        //    Add endDays
1065 12
        $endDate = (float) $startDate + ((int) ($endDays / 5) * 7) + ($endDays % 5);
1066
1067
        //    Adjust the calculated end date if it falls over a weekend
1068 12
        $endDoW = self::WEEKDAY($endDate, 3);
1069 12
        if ($endDoW >= 5) {
1070 1
            $endDate += ($decrementing) ? -$endDoW + 4 : 7 - $endDoW;
1071
        }
1072
1073
        //    Test any extra holiday parameters
1074 12
        if (!empty($dateArgs)) {
1075 4
            $holidayCountedArray = $holidayDates = [];
1076 4
            foreach ($dateArgs as $holidayDate) {
1077 4
                if (($holidayDate !== null) && (trim($holidayDate) > '')) {
1078 4
                    if (is_string($holidayDate = self::getDateValue($holidayDate))) {
1079
                        return Functions::VALUE();
1080
                    }
1081 4
                    if (self::WEEKDAY($holidayDate, 3) < 5) {
1082 4
                        $holidayDates[] = $holidayDate;
1083
                    }
1084
                }
1085
            }
1086 4
            if ($decrementing) {
1087 1
                rsort($holidayDates, SORT_NUMERIC);
1088
            } else {
1089 3
                sort($holidayDates, SORT_NUMERIC);
1090
            }
1091 4
            foreach ($holidayDates as $holidayDate) {
1092 4
                if ($decrementing) {
1093 1 View Code Duplication
                    if (($holidayDate <= $startDate) && ($holidayDate >= $endDate)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1094 1
                        if (!in_array($holidayDate, $holidayCountedArray)) {
1095 1
                            --$endDate;
1096 1
                            $holidayCountedArray[] = $holidayDate;
1097
                        }
1098
                    }
1099 View Code Duplication
                } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1100 3
                    if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) {
1101 3
                        if (!in_array($holidayDate, $holidayCountedArray)) {
1102 3
                            ++$endDate;
1103 3
                            $holidayCountedArray[] = $holidayDate;
1104
                        }
1105
                    }
1106
                }
1107
                //    Adjust the calculated end date if it falls over a weekend
1108 4
                $endDoW = self::WEEKDAY($endDate, 3);
1109 4
                if ($endDoW >= 5) {
1110 4
                    $endDate += ($decrementing) ? -$endDoW + 4 : 7 - $endDoW;
1111
                }
1112
            }
1113
        }
1114
1115 12 View Code Duplication
        switch (Functions::getReturnDateType()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1116 12
            case Functions::RETURNDATE_EXCEL:
1117 12
                return (float) $endDate;
1118
            case Functions::RETURNDATE_PHP_NUMERIC:
1119
                return (int) \PhpOffice\PhpSpreadsheet\Shared\Date::excelToTimestamp($endDate);
1120
            case Functions::RETURNDATE_PHP_OBJECT:
1121
                return \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($endDate);
1122
        }
1123
    }
1124
1125
    /**
1126
     * DAYOFMONTH.
1127
     *
1128
     * Returns the day of the month, for a specified date. The day is given as an integer
1129
     * ranging from 1 to 31.
1130
     *
1131
     * Excel Function:
1132
     *        DAY(dateValue)
1133
     *
1134
     * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
1135
     *                                    PHP DateTime object, or a standard date string
1136
     *
1137
     * @return int Day of the month
1138
     */
1139 17
    public static function DAYOFMONTH($dateValue = 1)
1140
    {
1141 17
        $dateValue = Functions::flattenSingleValue($dateValue);
1142
1143 17
        if ($dateValue === null) {
1144
            $dateValue = 1;
1145 17
        } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
1146 1
            return Functions::VALUE();
1147 16
        } elseif ($dateValue == 0.0) {
1148
            return 0;
1149 16
        } elseif ($dateValue < 0.0) {
1150 1
            return Functions::NAN();
1151
        }
1152
1153
        // Execute function
1154 15
        $PHPDateObject = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($dateValue);
1155
1156 15
        return (int) $PHPDateObject->format('j');
1157
    }
1158
1159
    /**
1160
     * WEEKDAY.
1161
     *
1162
     * Returns the day of the week for a specified date. The day is given as an integer
1163
     * ranging from 0 to 7 (dependent on the requested style).
1164
     *
1165
     * Excel Function:
1166
     *        WEEKDAY(dateValue[,style])
1167
     *
1168
     * @param int $dateValue Excel date serial value (float), PHP date timestamp (integer),
1169
     *                                    PHP DateTime object, or a standard date string
1170
     * @param int $style A number that determines the type of return value
1171
     *                                        1 or omitted    Numbers 1 (Sunday) through 7 (Saturday).
1172
     *                                        2                Numbers 1 (Monday) through 7 (Sunday).
1173
     *                                        3                Numbers 0 (Monday) through 6 (Sunday).
1174
     *
1175
     * @return int Day of the week value
1176
     */
1177 57
    public static function WEEKDAY($dateValue = 1, $style = 1)
1178
    {
1179 57
        $dateValue = Functions::flattenSingleValue($dateValue);
1180 57
        $style = Functions::flattenSingleValue($style);
1181
1182 57
        if (!is_numeric($style)) {
1183 1
            return Functions::VALUE();
1184 56
        } elseif (($style < 1) || ($style > 3)) {
1185 1
            return Functions::NAN();
1186
        }
1187 55
        $style = floor($style);
1188
1189 55
        if ($dateValue === null) {
1190
            $dateValue = 1;
1191 55
        } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
1192 1
            return Functions::VALUE();
1193 54
        } elseif ($dateValue < 0.0) {
1194 1
            return Functions::NAN();
1195
        }
1196
1197
        // Execute function
1198 53
        $PHPDateObject = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($dateValue);
1199 53
        $DoW = $PHPDateObject->format('w');
1200
1201 53
        $firstDay = 1;
1202
        switch ($style) {
1203 53
            case 1:
1204 8
                ++$DoW;
1205 8
                break;
1206 45
            case 2:
1207 26
                if ($DoW == 0) {
1208 3
                    $DoW = 7;
1209
                }
1210 26
                break;
1211 19
            case 3:
1212 19
                if ($DoW == 0) {
1213 3
                    $DoW = 7;
1214
                }
1215 19
                $firstDay = 0;
1216 19
                --$DoW;
1217 19
                break;
1218
        }
1219 53
        if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_EXCEL) {
1220
            //    Test for Excel's 1900 leap year, and introduce the error as required
1221 53
            if (($PHPDateObject->format('Y') == 1900) && ($PHPDateObject->format('n') <= 2)) {
1222 2
                --$DoW;
1223 2
                if ($DoW < $firstDay) {
1224 1
                    $DoW += 7;
1225
                }
1226
            }
1227
        }
1228
1229 53
        return (int) $DoW;
1230
    }
1231
1232
    /**
1233
     * WEEKNUM.
1234
     *
1235
     * Returns the week of the year for a specified date.
1236
     * The WEEKNUM function considers the week containing January 1 to be the first week of the year.
1237
     * However, there is a European standard that defines the first week as the one with the majority
1238
     * of days (four or more) falling in the new year. This means that for years in which there are
1239
     * three days or less in the first week of January, the WEEKNUM function returns week numbers
1240
     * that are incorrect according to the European standard.
1241
     *
1242
     * Excel Function:
1243
     *        WEEKNUM(dateValue[,style])
1244
     *
1245
     * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
1246
     *                                    PHP DateTime object, or a standard date string
1247
     * @param int $method Week begins on Sunday or Monday
1248
     *                                        1 or omitted    Week begins on Sunday.
1249
     *                                        2                Week begins on Monday.
1250
     *
1251
     * @return int Week Number
1252
     */
1253 15
    public static function WEEKNUM($dateValue = 1, $method = 1)
1254
    {
1255 15
        $dateValue = Functions::flattenSingleValue($dateValue);
1256 15
        $method = Functions::flattenSingleValue($method);
1257
1258 15 View Code Duplication
        if (!is_numeric($method)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1259 1
            return Functions::VALUE();
1260 14
        } elseif (($method < 1) || ($method > 2)) {
1261 1
            return Functions::NAN();
1262
        }
1263 13
        $method = floor($method);
1264
1265 13
        if ($dateValue === null) {
1266
            $dateValue = 1;
1267 13
        } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
1268 1
            return Functions::VALUE();
1269 12
        } elseif ($dateValue < 0.0) {
1270 1
            return Functions::NAN();
1271
        }
1272
1273
        // Execute function
1274 11
        $PHPDateObject = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($dateValue);
1275 11
        $dayOfYear = $PHPDateObject->format('z');
1276 11
        $PHPDateObject->modify('-' . $dayOfYear . ' days');
1277 11
        $firstDayOfFirstWeek = $PHPDateObject->format('w');
1278 11
        $daysInFirstWeek = (6 - $firstDayOfFirstWeek + $method) % 7;
1279 11
        $interval = $dayOfYear - $daysInFirstWeek;
1280 11
        $weekOfYear = floor($interval / 7) + 1;
1281
1282 11
        if ($daysInFirstWeek) {
1283 8
            ++$weekOfYear;
1284
        }
1285
1286 11
        return (int) $weekOfYear;
1287
    }
1288
1289
    /**
1290
     * MONTHOFYEAR.
1291
     *
1292
     * Returns the month of a date represented by a serial number.
1293
     * The month is given as an integer, ranging from 1 (January) to 12 (December).
1294
     *
1295
     * Excel Function:
1296
     *        MONTH(dateValue)
1297
     *
1298
     * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
1299
     *                                    PHP DateTime object, or a standard date string
1300
     *
1301
     * @return int Month of the year
1302
     */
1303 21 View Code Duplication
    public static function MONTHOFYEAR($dateValue = 1)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1304
    {
1305 21
        $dateValue = Functions::flattenSingleValue($dateValue);
1306
1307 21
        if (empty($dateValue)) {
1308 2
            $dateValue = 1;
1309
        }
1310 21
        if (is_string($dateValue = self::getDateValue($dateValue))) {
1311 1
            return Functions::VALUE();
1312 20
        } elseif ($dateValue < 0.0) {
1313 1
            return Functions::NAN();
1314
        }
1315
1316
        // Execute function
1317 19
        $PHPDateObject = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($dateValue);
1318
1319 19
        return (int) $PHPDateObject->format('n');
1320
    }
1321
1322
    /**
1323
     * YEAR.
1324
     *
1325
     * Returns the year corresponding to a date.
1326
     * The year is returned as an integer in the range 1900-9999.
1327
     *
1328
     * Excel Function:
1329
     *        YEAR(dateValue)
1330
     *
1331
     * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
1332
     *                                    PHP DateTime object, or a standard date string
1333
     *
1334
     * @return int Year
1335
     */
1336 33 View Code Duplication
    public static function YEAR($dateValue = 1)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1337
    {
1338 33
        $dateValue = Functions::flattenSingleValue($dateValue);
1339
1340 33
        if ($dateValue === null) {
1341 1
            $dateValue = 1;
1342 32
        } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
1343 1
            return Functions::VALUE();
1344 31
        } elseif ($dateValue < 0.0) {
1345 1
            return Functions::NAN();
1346
        }
1347
1348
        // Execute function
1349 31
        $PHPDateObject = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($dateValue);
1350
1351 31
        return (int) $PHPDateObject->format('Y');
1352
    }
1353
1354
    /**
1355
     * HOUROFDAY.
1356
     *
1357
     * Returns the hour of a time value.
1358
     * The hour is given as an integer, ranging from 0 (12:00 A.M.) to 23 (11:00 P.M.).
1359
     *
1360
     * Excel Function:
1361
     *        HOUR(timeValue)
1362
     *
1363
     * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
1364
     *                                    PHP DateTime object, or a standard time string
1365
     *
1366
     * @return int Hour
1367
     */
1368 12 View Code Duplication
    public static function HOUROFDAY($timeValue = 0)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1369
    {
1370 12
        $timeValue = Functions::flattenSingleValue($timeValue);
1371
1372 12
        if (!is_numeric($timeValue)) {
1373 4
            if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
1374
                $testVal = strtok($timeValue, '/-: ');
1375
                if (strlen($testVal) < strlen($timeValue)) {
1376
                    return Functions::VALUE();
1377
                }
1378
            }
1379 4
            $timeValue = self::getTimeValue($timeValue);
0 ignored issues
show
Bug introduced by
It seems like $timeValue defined by self::getTimeValue($timeValue) on line 1379 can also be of type boolean or null or object; however, PhpOffice\PhpSpreadsheet...ateTime::getTimeValue() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1380 4
            if (is_string($timeValue)) {
1381 1
                return Functions::VALUE();
1382
            }
1383
        }
1384
        // Execute function
1385 11
        if ($timeValue >= 1) {
1386 3
            $timeValue = fmod($timeValue, 1);
1387 8
        } elseif ($timeValue < 0.0) {
1388 1
            return Functions::NAN();
1389
        }
1390 10
        $timeValue = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToTimestamp($timeValue);
1391
1392 10
        return (int) gmdate('G', $timeValue);
1393
    }
1394
1395
    /**
1396
     * MINUTE.
1397
     *
1398
     * Returns the minutes of a time value.
1399
     * The minute is given as an integer, ranging from 0 to 59.
1400
     *
1401
     * Excel Function:
1402
     *        MINUTE(timeValue)
1403
     *
1404
     * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
1405
     *                                    PHP DateTime object, or a standard time string
1406
     *
1407
     * @return int Minute
1408
     */
1409 12 View Code Duplication
    public static function MINUTE($timeValue = 0)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1410
    {
1411 12
        $timeValue = $timeTester = Functions::flattenSingleValue($timeValue);
0 ignored issues
show
Unused Code introduced by
$timeTester is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1412
1413 12
        if (!is_numeric($timeValue)) {
1414 4
            if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
1415
                $testVal = strtok($timeValue, '/-: ');
1416
                if (strlen($testVal) < strlen($timeValue)) {
1417
                    return Functions::VALUE();
1418
                }
1419
            }
1420 4
            $timeValue = self::getTimeValue($timeValue);
0 ignored issues
show
Bug introduced by
It seems like $timeValue defined by self::getTimeValue($timeValue) on line 1420 can also be of type boolean or null or object; however, PhpOffice\PhpSpreadsheet...ateTime::getTimeValue() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1421 4
            if (is_string($timeValue)) {
1422 1
                return Functions::VALUE();
1423
            }
1424
        }
1425
        // Execute function
1426 11
        if ($timeValue >= 1) {
1427 3
            $timeValue = fmod($timeValue, 1);
1428 8
        } elseif ($timeValue < 0.0) {
1429 1
            return Functions::NAN();
1430
        }
1431 10
        $timeValue = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToTimestamp($timeValue);
1432
1433 10
        return (int) gmdate('i', $timeValue);
1434
    }
1435
1436
    /**
1437
     * SECOND.
1438
     *
1439
     * Returns the seconds of a time value.
1440
     * The second is given as an integer in the range 0 (zero) to 59.
1441
     *
1442
     * Excel Function:
1443
     *        SECOND(timeValue)
1444
     *
1445
     * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
1446
     *                                    PHP DateTime object, or a standard time string
1447
     *
1448
     * @return int Second
1449
     */
1450 12 View Code Duplication
    public static function SECOND($timeValue = 0)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1451
    {
1452 12
        $timeValue = Functions::flattenSingleValue($timeValue);
1453
1454 12
        if (!is_numeric($timeValue)) {
1455 4
            if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
1456
                $testVal = strtok($timeValue, '/-: ');
1457
                if (strlen($testVal) < strlen($timeValue)) {
1458
                    return Functions::VALUE();
1459
                }
1460
            }
1461 4
            $timeValue = self::getTimeValue($timeValue);
0 ignored issues
show
Bug introduced by
It seems like $timeValue defined by self::getTimeValue($timeValue) on line 1461 can also be of type boolean or null or object; however, PhpOffice\PhpSpreadsheet...ateTime::getTimeValue() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1462 4
            if (is_string($timeValue)) {
1463 1
                return Functions::VALUE();
1464
            }
1465
        }
1466
        // Execute function
1467 11
        if ($timeValue >= 1) {
1468 3
            $timeValue = fmod($timeValue, 1);
1469 8
        } elseif ($timeValue < 0.0) {
1470 1
            return Functions::NAN();
1471
        }
1472 10
        $timeValue = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToTimestamp($timeValue);
1473
1474 10
        return (int) gmdate('s', $timeValue);
1475
    }
1476
1477
    /**
1478
     * EDATE.
1479
     *
1480
     * Returns the serial number that represents the date that is the indicated number of months
1481
     * before or after a specified date (the start_date).
1482
     * Use EDATE to calculate maturity dates or due dates that fall on the same day of the month
1483
     * as the date of issue.
1484
     *
1485
     * Excel Function:
1486
     *        EDATE(dateValue,adjustmentMonths)
1487
     *
1488
     * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
1489
     *                                        PHP DateTime object, or a standard date string
1490
     * @param int $adjustmentMonths The number of months before or after start_date.
1491
     *                                        A positive value for months yields a future date;
1492
     *                                        a negative value yields a past date.
1493
     *
1494
     * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
1495
     *                        depending on the value of the ReturnDateType flag
1496
     */
1497 17
    public static function EDATE($dateValue = 1, $adjustmentMonths = 0)
1498
    {
1499 17
        $dateValue = Functions::flattenSingleValue($dateValue);
1500 17
        $adjustmentMonths = Functions::flattenSingleValue($adjustmentMonths);
1501
1502 17
        if (!is_numeric($adjustmentMonths)) {
1503 1
            return Functions::VALUE();
1504
        }
1505 16
        $adjustmentMonths = floor($adjustmentMonths);
1506
1507 16
        if (is_string($dateValue = self::getDateValue($dateValue))) {
1508 1
            return Functions::VALUE();
1509
        }
1510
1511
        // Execute function
1512 15
        $PHPDateObject = self::adjustDateByMonths($dateValue, $adjustmentMonths);
1513
1514 15 View Code Duplication
        switch (Functions::getReturnDateType()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1515 15
            case Functions::RETURNDATE_EXCEL:
1516 13
                return (float) \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel($PHPDateObject);
0 ignored issues
show
Documentation introduced by
$PHPDateObject is of type object<DateTime>, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1517 2
            case Functions::RETURNDATE_PHP_NUMERIC:
1518 1
                return (int) \PhpOffice\PhpSpreadsheet\Shared\Date::excelToTimestamp(\PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel($PHPDateObject));
0 ignored issues
show
Documentation introduced by
\PhpOffice\PhpSpreadshee...ToExcel($PHPDateObject) is of type double|false, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$PHPDateObject is of type object<DateTime>, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1519 1
            case Functions::RETURNDATE_PHP_OBJECT:
1520 1
                return $PHPDateObject;
1521
        }
1522
    }
1523
1524
    /**
1525
     * EOMONTH.
1526
     *
1527
     * Returns the date value for the last day of the month that is the indicated number of months
1528
     * before or after start_date.
1529
     * Use EOMONTH to calculate maturity dates or due dates that fall on the last day of the month.
1530
     *
1531
     * Excel Function:
1532
     *        EOMONTH(dateValue,adjustmentMonths)
1533
     *
1534
     * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
1535
     *                                        PHP DateTime object, or a standard date string
1536
     * @param int $adjustmentMonths The number of months before or after start_date.
1537
     *                                        A positive value for months yields a future date;
1538
     *                                        a negative value yields a past date.
1539
     *
1540
     * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
1541
     *                        depending on the value of the ReturnDateType flag
1542
     */
1543 19
    public static function EOMONTH($dateValue = 1, $adjustmentMonths = 0)
1544
    {
1545 19
        $dateValue = Functions::flattenSingleValue($dateValue);
1546 19
        $adjustmentMonths = Functions::flattenSingleValue($adjustmentMonths);
1547
1548 19
        if (!is_numeric($adjustmentMonths)) {
1549 1
            return Functions::VALUE();
1550
        }
1551 18
        $adjustmentMonths = floor($adjustmentMonths);
1552
1553 18
        if (is_string($dateValue = self::getDateValue($dateValue))) {
1554 1
            return Functions::VALUE();
1555
        }
1556
1557
        // Execute function
1558 17
        $PHPDateObject = self::adjustDateByMonths($dateValue, $adjustmentMonths + 1);
1559 17
        $adjustDays = (int) $PHPDateObject->format('d');
1560 17
        $adjustDaysString = '-' . $adjustDays . ' days';
1561 17
        $PHPDateObject->modify($adjustDaysString);
1562
1563 17 View Code Duplication
        switch (Functions::getReturnDateType()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1564 17
            case Functions::RETURNDATE_EXCEL:
1565 15
                return (float) \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel($PHPDateObject);
0 ignored issues
show
Documentation introduced by
$PHPDateObject is of type object<DateTime>, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1566 2
            case Functions::RETURNDATE_PHP_NUMERIC:
1567 1
                return (int) \PhpOffice\PhpSpreadsheet\Shared\Date::excelToTimestamp(\PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel($PHPDateObject));
0 ignored issues
show
Documentation introduced by
\PhpOffice\PhpSpreadshee...ToExcel($PHPDateObject) is of type double|false, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$PHPDateObject is of type object<DateTime>, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1568 1
            case Functions::RETURNDATE_PHP_OBJECT:
1569 1
                return $PHPDateObject;
1570
        }
1571
    }
1572
}
1573