Failed Conditions
Push — develop ( a6bb49...7a4cbd )
by Adrien
36:15
created

DateTime::TIMEVALUE()   B

Complexity

Conditions 10
Paths 18

Size

Total Lines 37

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 11.9507

Importance

Changes 0
Metric Value
cc 10
nc 18
nop 1
dl 0
loc 37
ccs 19
cts 26
cp 0.7308
crap 11.9507
rs 7.6666
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Calculation;
4
5
use PhpOffice\PhpSpreadsheet\Shared\Date;
6
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
7
8
class DateTime
9
{
10
    /**
11
     * Identify if a year is a leap year or not.
12
     *
13
     * @param int $year The year to test
14
     *
15
     * @return bool TRUE if the year is a leap year, otherwise FALSE
16
     */
17 20
    public static function isLeapYear($year)
18
    {
19 20
        return (($year % 4) == 0) && (($year % 100) != 0) || (($year % 400) == 0);
20
    }
21
22
    /**
23
     * Return the number of days between two dates based on a 360 day calendar.
24
     *
25
     * @param int $startDay Day of month of the start date
26
     * @param int $startMonth Month of the start date
27
     * @param int $startYear Year of the start date
28
     * @param int $endDay Day of month of the start date
29
     * @param int $endMonth Month of the start date
30
     * @param int $endYear Year of the start date
31
     * @param bool $methodUS Whether to use the US method or the European method of calculation
32
     *
33
     * @return int Number of days between the start date and the end date
34
     */
35 68
    private static function dateDiff360($startDay, $startMonth, $startYear, $endDay, $endMonth, $endYear, $methodUS)
36
    {
37 68
        if ($startDay == 31) {
38 12
            --$startDay;
39 56
        } elseif ($methodUS && ($startMonth == 2 && ($startDay == 29 || ($startDay == 28 && !self::isLeapYear($startYear))))) {
40 1
            $startDay = 30;
41
        }
42 68
        if ($endDay == 31) {
43 20
            if ($methodUS && $startDay != 30) {
44 9
                $endDay = 1;
45 9
                if ($endMonth == 12) {
46 3
                    ++$endYear;
47 3
                    $endMonth = 1;
48
                } else {
49 9
                    ++$endMonth;
50
                }
51
            } else {
52 11
                $endDay = 30;
53
            }
54
        }
55
56 68
        return $endDay + $endMonth * 30 + $endYear * 360 - $startDay - $startMonth * 30 - $startYear * 360;
57
    }
58
59
    /**
60
     * getDateValue.
61
     *
62
     * @param string $dateValue
63
     *
64
     * @return mixed Excel date/time serial value, or string if error
65
     */
66 385
    public static function getDateValue($dateValue)
67
    {
68 385
        if (!is_numeric($dateValue)) {
69 349
            if ((is_string($dateValue)) &&
70 349
                (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC)) {
71
                return Functions::VALUE();
72
            }
73 349
            if ((is_object($dateValue)) && ($dateValue instanceof \DateTime)) {
74
                $dateValue = Date::PHPToExcel($dateValue);
75
            } else {
76 349
                $saveReturnDateType = Functions::getReturnDateType();
77 349
                Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
78 349
                $dateValue = self::DATEVALUE($dateValue);
79 349
                Functions::setReturnDateType($saveReturnDateType);
80
            }
81
        }
82
83 385
        return $dateValue;
84
    }
85
86
    /**
87
     * getTimeValue.
88
     *
89
     * @param string $timeValue
90
     *
91
     * @return mixed Excel date/time serial value, or string if error
92
     */
93 12
    private static function getTimeValue($timeValue)
94
    {
95 12
        $saveReturnDateType = Functions::getReturnDateType();
96 12
        Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
97 12
        $timeValue = self::TIMEVALUE($timeValue);
98 12
        Functions::setReturnDateType($saveReturnDateType);
99
100 12
        return $timeValue;
101
    }
102
103 32
    private static function adjustDateByMonths($dateValue = 0, $adjustmentMonths = 0)
104
    {
105
        // Execute function
106 32
        $PHPDateObject = Date::excelToDateTimeObject($dateValue);
107 32
        $oMonth = (int) $PHPDateObject->format('m');
108 32
        $oYear = (int) $PHPDateObject->format('Y');
109
110 32
        $adjustmentMonthsString = (string) $adjustmentMonths;
111 32
        if ($adjustmentMonths > 0) {
112 15
            $adjustmentMonthsString = '+' . $adjustmentMonths;
113
        }
114 32
        if ($adjustmentMonths != 0) {
115 26
            $PHPDateObject->modify($adjustmentMonthsString . ' months');
116
        }
117 32
        $nMonth = (int) $PHPDateObject->format('m');
118 32
        $nYear = (int) $PHPDateObject->format('Y');
119
120 32
        $monthDiff = ($nMonth - $oMonth) + (($nYear - $oYear) * 12);
121 32
        if ($monthDiff != $adjustmentMonths) {
122 5
            $adjustDays = (int) $PHPDateObject->format('d');
123 5
            $adjustDaysString = '-' . $adjustDays . ' days';
124 5
            $PHPDateObject->modify($adjustDaysString);
125
        }
126
127 32
        return $PHPDateObject;
128
    }
129
130
    /**
131
     * DATETIMENOW.
132
     *
133
     * Returns the current date and time.
134
     * The NOW function is useful when you need to display the current date and time on a worksheet or
135
     * calculate a value based on the current date and time, and have that value updated each time you
136
     * open the worksheet.
137
     *
138
     * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
139
     * and time format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
140
     *
141
     * Excel Function:
142
     *        NOW()
143
     *
144
     * @category Date/Time Functions
145
     *
146
     * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
147
     *                        depending on the value of the ReturnDateType flag
148
     */
149
    public static function DATETIMENOW()
150
    {
151
        $saveTimeZone = date_default_timezone_get();
152
        date_default_timezone_set('UTC');
153
        $retValue = false;
154
        switch (Functions::getReturnDateType()) {
155
            case Functions::RETURNDATE_EXCEL:
156
                $retValue = (float) Date::PHPToExcel(time());
157
158
                break;
159
            case Functions::RETURNDATE_PHP_NUMERIC:
160
                $retValue = (int) time();
161
162
                break;
163
            case Functions::RETURNDATE_PHP_OBJECT:
164
                $retValue = new \DateTime();
165
166
                break;
167
        }
168
        date_default_timezone_set($saveTimeZone);
169
170
        return $retValue;
171
    }
172
173
    /**
174
     * DATENOW.
175
     *
176
     * Returns the current date.
177
     * The NOW function is useful when you need to display the current date and time on a worksheet or
178
     * calculate a value based on the current date and time, and have that value updated each time you
179
     * open the worksheet.
180
     *
181
     * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
182
     * and time format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
183
     *
184
     * Excel Function:
185
     *        TODAY()
186
     *
187
     * @category Date/Time Functions
188
     *
189
     * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
190
     *                        depending on the value of the ReturnDateType flag
191
     */
192 1
    public static function DATENOW()
193
    {
194 1
        $saveTimeZone = date_default_timezone_get();
195 1
        date_default_timezone_set('UTC');
196 1
        $retValue = false;
197 1
        $excelDateTime = floor(Date::PHPToExcel(time()));
198 1
        switch (Functions::getReturnDateType()) {
199 1
            case Functions::RETURNDATE_EXCEL:
200
                $retValue = (float) $excelDateTime;
201
202
                break;
203 1
            case Functions::RETURNDATE_PHP_NUMERIC:
204 1
                $retValue = (int) Date::excelToTimestamp($excelDateTime);
205
206 1
                break;
207
            case Functions::RETURNDATE_PHP_OBJECT:
208
                $retValue = Date::excelToDateTimeObject($excelDateTime);
209
210
                break;
211
        }
212 1
        date_default_timezone_set($saveTimeZone);
213
214 1
        return $retValue;
215
    }
216
217
    /**
218
     * DATE.
219
     *
220
     * The DATE function returns a value that represents a particular date.
221
     *
222
     * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
223
     * format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
224
     *
225
     * Excel Function:
226
     *        DATE(year,month,day)
227
     *
228
     * PhpSpreadsheet is a lot more forgiving than MS Excel when passing non numeric values to this function.
229
     * A Month name or abbreviation (English only at this point) such as 'January' or 'Jan' will still be accepted,
230
     *     as will a day value with a suffix (e.g. '21st' rather than simply 21); again only English language.
231
     *
232
     * @category Date/Time Functions
233
     *
234
     * @param int $year The value of the year argument can include one to four digits.
235
     *                                Excel interprets the year argument according to the configured
236
     *                                date system: 1900 or 1904.
237
     *                                If year is between 0 (zero) and 1899 (inclusive), Excel adds that
238
     *                                value to 1900 to calculate the year. For example, DATE(108,1,2)
239
     *                                returns January 2, 2008 (1900+108).
240
     *                                If year is between 1900 and 9999 (inclusive), Excel uses that
241
     *                                value as the year. For example, DATE(2008,1,2) returns January 2,
242
     *                                2008.
243
     *                                If year is less than 0 or is 10000 or greater, Excel returns the
244
     *                                #NUM! error value.
245
     * @param int $month A positive or negative integer representing the month of the year
246
     *                                from 1 to 12 (January to December).
247
     *                                If month is greater than 12, month adds that number of months to
248
     *                                the first month in the year specified. For example, DATE(2008,14,2)
249
     *                                returns the serial number representing February 2, 2009.
250
     *                                If month is less than 1, month subtracts the magnitude of that
251
     *                                number of months, plus 1, from the first month in the year
252
     *                                specified. For example, DATE(2008,-3,2) returns the serial number
253
     *                                representing September 2, 2007.
254
     * @param int $day A positive or negative integer representing the day of the month
255
     *                                from 1 to 31.
256
     *                                If day is greater than the number of days in the month specified,
257
     *                                day adds that number of days to the first day in the month. For
258
     *                                example, DATE(2008,1,35) returns the serial number representing
259
     *                                February 4, 2008.
260
     *                                If day is less than 1, day subtracts the magnitude that number of
261
     *                                days, plus one, from the first day of the month specified. For
262
     *                                example, DATE(2008,1,-15) returns the serial number representing
263
     *                                December 16, 2007.
264
     *
265
     * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
266
     *                        depending on the value of the ReturnDateType flag
267
     */
268 84
    public static function DATE($year = 0, $month = 1, $day = 1)
269
    {
270 84
        $year = Functions::flattenSingleValue($year);
271 84
        $month = Functions::flattenSingleValue($month);
272 84
        $day = Functions::flattenSingleValue($day);
273
274 84
        if (($month !== null) && (!is_numeric($month))) {
275 3
            $month = Date::monthStringToNumber($month);
276
        }
277
278 84
        if (($day !== null) && (!is_numeric($day))) {
279 3
            $day = Date::dayStringToNumber($day);
280
        }
281
282 84
        $year = ($year !== null) ? StringHelper::testStringAsNumeric($year) : 0;
283 84
        $month = ($month !== null) ? StringHelper::testStringAsNumeric($month) : 0;
284 84
        $day = ($day !== null) ? StringHelper::testStringAsNumeric($day) : 0;
285 84
        if ((!is_numeric($year)) ||
286 83
            (!is_numeric($month)) ||
287 84
            (!is_numeric($day))) {
288 3
            return Functions::VALUE();
289
        }
290 81
        $year = (int) $year;
291 81
        $month = (int) $month;
292 81
        $day = (int) $day;
293
294 81
        $baseYear = Date::getExcelCalendar();
295
        // Validate parameters
296 81
        if ($year < ($baseYear - 1900)) {
297 2
            return Functions::NAN();
298
        }
299 79
        if ((($baseYear - 1900) != 0) && ($year < $baseYear) && ($year >= 1900)) {
300 1
            return Functions::NAN();
301
        }
302
303 78
        if (($year < $baseYear) && ($year >= ($baseYear - 1900))) {
304 7
            $year += 1900;
305
        }
306
307 78
        if ($month < 1) {
308
            //    Handle year/month adjustment if month < 1
309 22
            --$month;
310 22
            $year += ceil($month / 12) - 1;
311 22
            $month = 13 - abs($month % 12);
312 57
        } elseif ($month > 12) {
313
            //    Handle year/month adjustment if month > 12
314 8
            $year += floor($month / 12);
315 8
            $month = ($month % 12);
316
        }
317
318
        // Re-validate the year parameter after adjustments
319 78
        if (($year < $baseYear) || ($year >= 10000)) {
320 2
            return Functions::NAN();
321
        }
322
323
        // Execute function
324 76
        $excelDateValue = Date::formattedPHPToExcel($year, $month, $day);
2 ignored issues
show
Bug introduced by
It seems like $month can also be of type double; however, parameter $month of PhpOffice\PhpSpreadsheet...::formattedPHPToExcel() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

324
        $excelDateValue = Date::formattedPHPToExcel($year, /** @scrutinizer ignore-type */ $month, $day);
Loading history...
Bug introduced by
It seems like $year can also be of type double; however, parameter $year of PhpOffice\PhpSpreadsheet...::formattedPHPToExcel() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

324
        $excelDateValue = Date::formattedPHPToExcel(/** @scrutinizer ignore-type */ $year, $month, $day);
Loading history...
325 76
        switch (Functions::getReturnDateType()) {
326 76
            case Functions::RETURNDATE_EXCEL:
327 74
                return (float) $excelDateValue;
328 2
            case Functions::RETURNDATE_PHP_NUMERIC:
329 1
                return (int) Date::excelToTimestamp($excelDateValue);
330 1
            case Functions::RETURNDATE_PHP_OBJECT:
331 1
                return Date::excelToDateTimeObject($excelDateValue);
332
        }
333
    }
334
335
    /**
336
     * TIME.
337
     *
338
     * The TIME function returns a value that represents a particular time.
339
     *
340
     * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the time
341
     * format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
342
     *
343
     * Excel Function:
344
     *        TIME(hour,minute,second)
345
     *
346
     * @category Date/Time Functions
347
     *
348
     * @param int $hour A number from 0 (zero) to 32767 representing the hour.
349
     *                                    Any value greater than 23 will be divided by 24 and the remainder
350
     *                                    will be treated as the hour value. For example, TIME(27,0,0) =
351
     *                                    TIME(3,0,0) = .125 or 3:00 AM.
352
     * @param int $minute A number from 0 to 32767 representing the minute.
353
     *                                    Any value greater than 59 will be converted to hours and minutes.
354
     *                                    For example, TIME(0,750,0) = TIME(12,30,0) = .520833 or 12:30 PM.
355
     * @param int $second A number from 0 to 32767 representing the second.
356
     *                                    Any value greater than 59 will be converted to hours, minutes,
357
     *                                    and seconds. For example, TIME(0,0,2000) = TIME(0,33,22) = .023148
358
     *                                    or 12:33:20 AM
359
     *
360
     * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
361
     *                        depending on the value of the ReturnDateType flag
362
     */
363 25
    public static function TIME($hour = 0, $minute = 0, $second = 0)
364
    {
365 25
        $hour = Functions::flattenSingleValue($hour);
366 25
        $minute = Functions::flattenSingleValue($minute);
367 25
        $second = Functions::flattenSingleValue($second);
368
369 25
        if ($hour == '') {
370 1
            $hour = 0;
371
        }
372 25
        if ($minute == '') {
373 6
            $minute = 0;
374
        }
375 25
        if ($second == '') {
376 6
            $second = 0;
377
        }
378
379 25
        if ((!is_numeric($hour)) || (!is_numeric($minute)) || (!is_numeric($second))) {
380 1
            return Functions::VALUE();
381
        }
382 24
        $hour = (int) $hour;
383 24
        $minute = (int) $minute;
384 24
        $second = (int) $second;
385
386 24
        if ($second < 0) {
387 5
            $minute += floor($second / 60);
388 5
            $second = 60 - abs($second % 60);
389 5
            if ($second == 60) {
390 5
                $second = 0;
391
            }
392 20
        } elseif ($second >= 60) {
393 1
            $minute += floor($second / 60);
394 1
            $second = $second % 60;
395
        }
396 24
        if ($minute < 0) {
397 8
            $hour += floor($minute / 60);
398 8
            $minute = 60 - abs($minute % 60);
399 8
            if ($minute == 60) {
400 8
                $minute = 0;
401
            }
402 17
        } elseif ($minute >= 60) {
403 3
            $hour += floor($minute / 60);
404 3
            $minute = $minute % 60;
405
        }
406
407 24
        if ($hour > 23) {
408 1
            $hour = $hour % 24;
409 23
        } elseif ($hour < 0) {
410 3
            return Functions::NAN();
411
        }
412
413
        // Execute function
414 22
        switch (Functions::getReturnDateType()) {
415 22
            case Functions::RETURNDATE_EXCEL:
416 20
                $date = 0;
417 20
                $calendar = Date::getExcelCalendar();
418 20
                if ($calendar != Date::CALENDAR_WINDOWS_1900) {
419
                    $date = 1;
420
                }
421
422 20
                return (float) Date::formattedPHPToExcel($calendar, 1, $date, $hour, $minute, $second);
3 ignored issues
show
Bug introduced by
It seems like $hour can also be of type double; however, parameter $hours of PhpOffice\PhpSpreadsheet...::formattedPHPToExcel() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

422
                return (float) Date::formattedPHPToExcel($calendar, 1, $date, /** @scrutinizer ignore-type */ $hour, $minute, $second);
Loading history...
Bug introduced by
It seems like $second can also be of type double; however, parameter $seconds of PhpOffice\PhpSpreadsheet...::formattedPHPToExcel() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

422
                return (float) Date::formattedPHPToExcel($calendar, 1, $date, $hour, $minute, /** @scrutinizer ignore-type */ $second);
Loading history...
Bug introduced by
It seems like $minute can also be of type double; however, parameter $minutes of PhpOffice\PhpSpreadsheet...::formattedPHPToExcel() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

422
                return (float) Date::formattedPHPToExcel($calendar, 1, $date, $hour, /** @scrutinizer ignore-type */ $minute, $second);
Loading history...
423 2
            case Functions::RETURNDATE_PHP_NUMERIC:
424 1
                return (int) Date::excelToTimestamp(Date::formattedPHPToExcel(1970, 1, 1, $hour, $minute, $second)); // -2147468400; //    -2147472000 + 3600
425 1
            case Functions::RETURNDATE_PHP_OBJECT:
426 1
                $dayAdjust = 0;
427 1
                if ($hour < 0) {
428
                    $dayAdjust = floor($hour / 24);
429
                    $hour = 24 - abs($hour % 24);
430
                    if ($hour == 24) {
431
                        $hour = 0;
432
                    }
433 1
                } elseif ($hour >= 24) {
434
                    $dayAdjust = floor($hour / 24);
435
                    $hour = $hour % 24;
436
                }
437 1
                $phpDateObject = new \DateTime('1900-01-01 ' . $hour . ':' . $minute . ':' . $second);
438 1
                if ($dayAdjust != 0) {
439
                    $phpDateObject->modify($dayAdjust . ' days');
440
                }
441
442 1
                return $phpDateObject;
443
        }
444
    }
445
446
    /**
447
     * DATEVALUE.
448
     *
449
     * Returns a value that represents a particular date.
450
     * Use DATEVALUE to convert a date represented by a text string to an Excel or PHP date/time stamp
451
     * value.
452
     *
453
     * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
454
     * format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
455
     *
456
     * Excel Function:
457
     *        DATEVALUE(dateValue)
458
     *
459
     * @category Date/Time Functions
460
     *
461
     * @param string $dateValue Text that represents a date in a Microsoft Excel date format.
462
     *                                    For example, "1/30/2008" or "30-Jan-2008" are text strings within
463
     *                                    quotation marks that represent dates. Using the default date
464
     *                                    system in Excel for Windows, date_text must represent a date from
465
     *                                    January 1, 1900, to December 31, 9999. Using the default date
466
     *                                    system in Excel for the Macintosh, date_text must represent a date
467
     *                                    from January 1, 1904, to December 31, 9999. DATEVALUE returns the
468
     *                                    #VALUE! error value if date_text is out of this range.
469
     *
470
     * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
471
     *                        depending on the value of the ReturnDateType flag
472
     */
473 425
    public static function DATEVALUE($dateValue = 1)
474
    {
475 425
        $dateValueOrig = $dateValue;
0 ignored issues
show
Unused Code introduced by
The assignment to $dateValueOrig is dead and can be removed.
Loading history...
476 425
        $dateValue = trim(Functions::flattenSingleValue($dateValue), '"');
477
        //    Strip any ordinals because they're allowed in Excel (English only)
478 425
        $dateValue = preg_replace('/(\d)(st|nd|rd|th)([ -\/])/Ui', '$1$3', $dateValue);
479
        //    Convert separators (/ . or space) to hyphens (should also handle dot used for ordinals in some countries, e.g. Denmark, Germany)
480 425
        $dateValue = str_replace(['/', '.', '-', '  '], ' ', $dateValue);
481
482 425
        $yearFound = false;
483 425
        $t1 = explode(' ', $dateValue);
484 425
        foreach ($t1 as &$t) {
485 425
            if ((is_numeric($t)) && ($t > 31)) {
486 392
                if ($yearFound) {
487
                    return Functions::VALUE();
488
                }
489 392
                if ($t < 100) {
490 2
                    $t += 1900;
491
                }
492 425
                $yearFound = true;
493
            }
494
        }
495 425
        if ((count($t1) == 1) && (strpos($t, ':') != false)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $t seems to be defined by a foreach iteration on line 484. Are you sure the iterator is never empty, otherwise this variable is not defined?
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...
496
            //    We've been fed a time value without any date
497 1
            return 0.0;
498 424
        } elseif (count($t1) == 2) {
499
            //    We only have two parts of the date: either day/month or month/year
500 28
            if ($yearFound) {
501 4
                array_unshift($t1, 1);
502
            } else {
503 25
                if ($t1[1] > 29) {
504 1
                    $t1[1] += 1900;
505 1
                    array_unshift($t1, 1);
506
                } else {
507 24
                    $t1[] = date('Y');
508
                }
509
            }
510
        }
511 424
        unset($t);
512 424
        $dateValue = implode(' ', $t1);
513
514 424
        $PHPDateArray = date_parse($dateValue);
515 424
        if (($PHPDateArray === false) || ($PHPDateArray['error_count'] > 0)) {
516 317
            $testVal1 = strtok($dateValue, '- ');
517 317
            if ($testVal1 !== false) {
518 316
                $testVal2 = strtok('- ');
519 316
                if ($testVal2 !== false) {
520 302
                    $testVal3 = strtok('- ');
521 302
                    if ($testVal3 === false) {
522 302
                        $testVal3 = strftime('%Y');
523
                    }
524
                } else {
525 316
                    return Functions::VALUE();
526
                }
527
            } else {
528 1
                return Functions::VALUE();
529
            }
530 302
            if ($testVal1 < 31 && $testVal2 < 12 && $testVal3 < 12 && strlen($testVal3) == 2) {
531 2
                $testVal3 += 2000;
532
            }
533 302
            $PHPDateArray = date_parse($testVal1 . '-' . $testVal2 . '-' . $testVal3);
534 302
            if (($PHPDateArray === false) || ($PHPDateArray['error_count'] > 0)) {
535 26
                $PHPDateArray = date_parse($testVal2 . '-' . $testVal1 . '-' . $testVal3);
536 26
                if (($PHPDateArray === false) || ($PHPDateArray['error_count'] > 0)) {
537 22
                    return Functions::VALUE();
538
                }
539
            }
540
        }
541
542 393
        if (($PHPDateArray !== false) && ($PHPDateArray['error_count'] == 0)) {
543
            // Execute function
544 393
            if ($PHPDateArray['year'] == '') {
545
                $PHPDateArray['year'] = strftime('%Y');
546
            }
547 393
            if ($PHPDateArray['year'] < 1900) {
548 2
                return Functions::VALUE();
549
            }
550 391
            if ($PHPDateArray['month'] == '') {
551
                $PHPDateArray['month'] = strftime('%m');
552
            }
553 391
            if ($PHPDateArray['day'] == '') {
554
                $PHPDateArray['day'] = strftime('%d');
555
            }
556 391
            if (!checkdate($PHPDateArray['month'], $PHPDateArray['day'], $PHPDateArray['year'])) {
557 4
                return Functions::VALUE();
558
            }
559 388
            $excelDateValue = floor(
560 388
                Date::formattedPHPToExcel(
561 388
                    $PHPDateArray['year'],
562 388
                    $PHPDateArray['month'],
563 388
                    $PHPDateArray['day'],
564 388
                    $PHPDateArray['hour'],
565 388
                    $PHPDateArray['minute'],
566 388
                    $PHPDateArray['second']
567
                )
568
            );
569 388
            switch (Functions::getReturnDateType()) {
570 388
                case Functions::RETURNDATE_EXCEL:
571 386
                    return (float) $excelDateValue;
572 2
                case Functions::RETURNDATE_PHP_NUMERIC:
573 1
                    return (int) Date::excelToTimestamp($excelDateValue);
574 1
                case Functions::RETURNDATE_PHP_OBJECT:
575 1
                    return new \DateTime($PHPDateArray['year'] . '-' . $PHPDateArray['month'] . '-' . $PHPDateArray['day'] . ' 00:00:00');
576
            }
577
        }
578
579
        return Functions::VALUE();
580
    }
581
582
    /**
583
     * TIMEVALUE.
584
     *
585
     * Returns a value that represents a particular time.
586
     * Use TIMEVALUE to convert a time represented by a text string to an Excel or PHP date/time stamp
587
     * value.
588
     *
589
     * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the time
590
     * format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
591
     *
592
     * Excel Function:
593
     *        TIMEVALUE(timeValue)
594
     *
595
     * @category Date/Time Functions
596
     *
597
     * @param string $timeValue A text string that represents a time in any one of the Microsoft
598
     *                                    Excel time formats; for example, "6:45 PM" and "18:45" text strings
599
     *                                    within quotation marks that represent time.
600
     *                                    Date information in time_text is ignored.
601
     *
602
     * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
603
     *                        depending on the value of the ReturnDateType flag
604
     */
605 31
    public static function TIMEVALUE($timeValue)
606
    {
607 31
        $timeValue = trim(Functions::flattenSingleValue($timeValue), '"');
608 31
        $timeValue = str_replace(['/', '.'], '-', $timeValue);
609
610 31
        $arraySplit = preg_split('/[\/:\-\s]/', $timeValue);
611 31
        if ((count($arraySplit) == 2 || count($arraySplit) == 3) && $arraySplit[0] > 24) {
1 ignored issue
show
Bug introduced by
It seems like $arraySplit can also be of type false; however, parameter $var of count() does only seem to accept Countable|array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

611
        if ((count(/** @scrutinizer ignore-type */ $arraySplit) == 2 || count($arraySplit) == 3) && $arraySplit[0] > 24) {
Loading history...
612 1
            $arraySplit[0] = ($arraySplit[0] % 24);
613 1
            $timeValue = implode(':', $arraySplit);
1 ignored issue
show
Bug introduced by
It seems like $arraySplit can also be of type false; however, parameter $pieces of implode() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

613
            $timeValue = implode(':', /** @scrutinizer ignore-type */ $arraySplit);
Loading history...
614
        }
615
616 31
        $PHPDateArray = date_parse($timeValue);
617 31
        if (($PHPDateArray !== false) && ($PHPDateArray['error_count'] == 0)) {
618 26
            if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
619
                $excelDateValue = Date::formattedPHPToExcel(
620
                    $PHPDateArray['year'],
621
                    $PHPDateArray['month'],
622
                    $PHPDateArray['day'],
623
                    $PHPDateArray['hour'],
624
                    $PHPDateArray['minute'],
625
                    $PHPDateArray['second']
626
                );
627
            } else {
628 26
                $excelDateValue = Date::formattedPHPToExcel(1900, 1, 1, $PHPDateArray['hour'], $PHPDateArray['minute'], $PHPDateArray['second']) - 1;
629
            }
630
631 26
            switch (Functions::getReturnDateType()) {
632 26
                case Functions::RETURNDATE_EXCEL:
633 24
                    return (float) $excelDateValue;
634 2
                case Functions::RETURNDATE_PHP_NUMERIC:
635 1
                    return (int) $phpDateValue = Date::excelToTimestamp($excelDateValue + 25569) - 3600;
0 ignored issues
show
Unused Code introduced by
The assignment to $phpDateValue is dead and can be removed.
Loading history...
636 1
                case Functions::RETURNDATE_PHP_OBJECT:
637 1
                    return new \DateTime('1900-01-01 ' . $PHPDateArray['hour'] . ':' . $PHPDateArray['minute'] . ':' . $PHPDateArray['second']);
638
            }
639
        }
640
641 6
        return Functions::VALUE();
642
    }
643
644
    /**
645
     * DATEDIF.
646
     *
647
     * @param mixed $startDate Excel date serial value, PHP date/time stamp, PHP DateTime object
648
     *                                    or a standard date string
649
     * @param mixed $endDate Excel date serial value, PHP date/time stamp, PHP DateTime object
650
     *                                    or a standard date string
651
     * @param string $unit
652
     *
653
     * @return int Interval between the dates
654
     */
655 150
    public static function DATEDIF($startDate = 0, $endDate = 0, $unit = 'D')
656
    {
657 150
        $startDate = Functions::flattenSingleValue($startDate);
658 150
        $endDate = Functions::flattenSingleValue($endDate);
659 150
        $unit = strtoupper(Functions::flattenSingleValue($unit));
660
661 150
        if (is_string($startDate = self::getDateValue($startDate))) {
662 1
            return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type integer.
Loading history...
663
        }
664 149
        if (is_string($endDate = self::getDateValue($endDate))) {
665 1
            return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type integer.
Loading history...
666
        }
667
668
        // Validate parameters
669 148
        if ($startDate > $endDate) {
670 1
            return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type integer.
Loading history...
671
        }
672
673
        // Execute function
674 147
        $difference = $endDate - $startDate;
675
676 147
        $PHPStartDateObject = Date::excelToDateTimeObject($startDate);
677 147
        $startDays = $PHPStartDateObject->format('j');
678 147
        $startMonths = $PHPStartDateObject->format('n');
679 147
        $startYears = $PHPStartDateObject->format('Y');
680
681 147
        $PHPEndDateObject = Date::excelToDateTimeObject($endDate);
682 147
        $endDays = $PHPEndDateObject->format('j');
683 147
        $endMonths = $PHPEndDateObject->format('n');
684 147
        $endYears = $PHPEndDateObject->format('Y');
685
686 147
        $retVal = Functions::NAN();
0 ignored issues
show
Unused Code introduced by
The assignment to $retVal is dead and can be removed.
Loading history...
687
        switch ($unit) {
688 147
            case 'D':
689 64
                $retVal = (int) $difference;
690
691 64
                break;
692 83
            case 'M':
693 15
                $retVal = (int) ($endMonths - $startMonths) + ((int) ($endYears - $startYears) * 12);
694
                //    We're only interested in full months
695 15
                if ($endDays < $startDays) {
696 3
                    --$retVal;
697
                }
698
699 15
                break;
700 68
            case 'Y':
701 17
                $retVal = (int) ($endYears - $startYears);
702
                //    We're only interested in full months
703 17
                if ($endMonths < $startMonths) {
704 5
                    --$retVal;
705 12
                } elseif (($endMonths == $startMonths) && ($endDays < $startDays)) {
706
                    // Remove start month
707 1
                    --$retVal;
708
                    // Remove end month
709 1
                    --$retVal;
710
                }
711
712 17
                break;
713 51
            case 'MD':
714 15
                if ($endDays < $startDays) {
715 3
                    $retVal = $endDays;
716 3
                    $PHPEndDateObject->modify('-' . $endDays . ' days');
717 3
                    $adjustDays = $PHPEndDateObject->format('j');
718 3
                    $retVal += ($adjustDays - $startDays);
719
                } else {
720 12
                    $retVal = $endDays - $startDays;
721
                }
722
723 15
                break;
724 36
            case 'YM':
725 15
                $retVal = (int) ($endMonths - $startMonths);
726 15
                if ($retVal < 0) {
727 4
                    $retVal += 12;
728
                }
729
                //    We're only interested in full months
730 15
                if ($endDays < $startDays) {
731 3
                    --$retVal;
732
                }
733
734 15
                break;
735 21
            case 'YD':
736 20
                $retVal = (int) $difference;
737 20
                if ($endYears > $startYears) {
738 9
                    $isLeapStartYear = $PHPStartDateObject->format('L');
739 9
                    $wasLeapEndYear = $PHPEndDateObject->format('L');
740
741
                    // Adjust end year to be as close as possible as start year
742 9
                    while ($PHPEndDateObject >= $PHPStartDateObject) {
743 9
                        $PHPEndDateObject->modify('-1 year');
744 9
                        $endYears = $PHPEndDateObject->format('Y');
0 ignored issues
show
Unused Code introduced by
The assignment to $endYears is dead and can be removed.
Loading history...
745
                    }
746 9
                    $PHPEndDateObject->modify('+1 year');
747
748
                    // Get the result
749 9
                    $retVal = $PHPEndDateObject->diff($PHPStartDateObject)->days;
750
751
                    // Adjust for leap years cases
752 9
                    $isLeapEndYear = $PHPEndDateObject->format('L');
753 9
                    $limit = new \DateTime($PHPEndDateObject->format('Y-02-29'));
754 9
                    if (!$isLeapStartYear && !$wasLeapEndYear && $isLeapEndYear && $PHPEndDateObject >= $limit) {
755 1
                        --$retVal;
756
                    }
757
                }
758
759 20
                break;
760
            default:
761 1
                $retVal = Functions::VALUE();
762
        }
763
764 147
        return $retVal;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $retVal also could return the type string which is incompatible with the documented return type integer.
Loading history...
765
    }
766
767
    /**
768
     * DAYS360.
769
     *
770
     * Returns the number of days between two dates based on a 360-day year (twelve 30-day months),
771
     * which is used in some accounting calculations. Use this function to help compute payments if
772
     * your accounting system is based on twelve 30-day months.
773
     *
774
     * Excel Function:
775
     *        DAYS360(startDate,endDate[,method])
776
     *
777
     * @category Date/Time Functions
778
     *
779
     * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
780
     *                                        PHP DateTime object, or a standard date string
781
     * @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
782
     *                                        PHP DateTime object, or a standard date string
783
     * @param bool $method US or European Method
784
     *                                        FALSE or omitted: U.S. (NASD) method. If the starting date is
785
     *                                        the last day of a month, it becomes equal to the 30th of the
786
     *                                        same month. If the ending date is the last day of a month and
787
     *                                        the starting date is earlier than the 30th of a month, the
788
     *                                        ending date becomes equal to the 1st of the next month;
789
     *                                        otherwise the ending date becomes equal to the 30th of the
790
     *                                        same month.
791
     *                                        TRUE: European method. Starting dates and ending dates that
792
     *                                        occur on the 31st of a month become equal to the 30th of the
793
     *                                        same month.
794
     *
795
     * @return int Number of days between start date and end date
796
     */
797 72
    public static function DAYS360($startDate = 0, $endDate = 0, $method = false)
798
    {
799 72
        $startDate = Functions::flattenSingleValue($startDate);
800 72
        $endDate = Functions::flattenSingleValue($endDate);
801
802 72
        if (is_string($startDate = self::getDateValue($startDate))) {
803 1
            return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type integer.
Loading history...
804
        }
805 71
        if (is_string($endDate = self::getDateValue($endDate))) {
806 1
            return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type integer.
Loading history...
807
        }
808
809 70
        if (!is_bool($method)) {
0 ignored issues
show
introduced by
The condition is_bool($method) is always true.
Loading history...
810 2
            return Functions::VALUE();
811
        }
812
813
        // Execute function
814 68
        $PHPStartDateObject = Date::excelToDateTimeObject($startDate);
815 68
        $startDay = $PHPStartDateObject->format('j');
816 68
        $startMonth = $PHPStartDateObject->format('n');
817 68
        $startYear = $PHPStartDateObject->format('Y');
818
819 68
        $PHPEndDateObject = Date::excelToDateTimeObject($endDate);
820 68
        $endDay = $PHPEndDateObject->format('j');
821 68
        $endMonth = $PHPEndDateObject->format('n');
822 68
        $endYear = $PHPEndDateObject->format('Y');
823
824 68
        return self::dateDiff360($startDay, $startMonth, $startYear, $endDay, $endMonth, $endYear, !$method);
0 ignored issues
show
Bug introduced by
$startMonth of type string is incompatible with the type integer expected by parameter $startMonth of PhpOffice\PhpSpreadsheet...DateTime::dateDiff360(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

824
        return self::dateDiff360($startDay, /** @scrutinizer ignore-type */ $startMonth, $startYear, $endDay, $endMonth, $endYear, !$method);
Loading history...
Bug introduced by
$endDay of type string is incompatible with the type integer expected by parameter $endDay of PhpOffice\PhpSpreadsheet...DateTime::dateDiff360(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

824
        return self::dateDiff360($startDay, $startMonth, $startYear, /** @scrutinizer ignore-type */ $endDay, $endMonth, $endYear, !$method);
Loading history...
Bug introduced by
$startDay of type string is incompatible with the type integer expected by parameter $startDay of PhpOffice\PhpSpreadsheet...DateTime::dateDiff360(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

824
        return self::dateDiff360(/** @scrutinizer ignore-type */ $startDay, $startMonth, $startYear, $endDay, $endMonth, $endYear, !$method);
Loading history...
Bug introduced by
$endMonth of type string is incompatible with the type integer expected by parameter $endMonth of PhpOffice\PhpSpreadsheet...DateTime::dateDiff360(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

824
        return self::dateDiff360($startDay, $startMonth, $startYear, $endDay, /** @scrutinizer ignore-type */ $endMonth, $endYear, !$method);
Loading history...
Bug introduced by
$startYear of type string is incompatible with the type integer expected by parameter $startYear of PhpOffice\PhpSpreadsheet...DateTime::dateDiff360(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

824
        return self::dateDiff360($startDay, $startMonth, /** @scrutinizer ignore-type */ $startYear, $endDay, $endMonth, $endYear, !$method);
Loading history...
Bug introduced by
$endYear of type string is incompatible with the type integer expected by parameter $endYear of PhpOffice\PhpSpreadsheet...DateTime::dateDiff360(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

824
        return self::dateDiff360($startDay, $startMonth, $startYear, $endDay, $endMonth, /** @scrutinizer ignore-type */ $endYear, !$method);
Loading history...
825
    }
826
827
    /**
828
     * YEARFRAC.
829
     *
830
     * Calculates the fraction of the year represented by the number of whole days between two dates
831
     * (the start_date and the end_date).
832
     * Use the YEARFRAC worksheet function to identify the proportion of a whole year's benefits or
833
     * obligations to assign to a specific term.
834
     *
835
     * Excel Function:
836
     *        YEARFRAC(startDate,endDate[,method])
837
     *
838
     * @category Date/Time Functions
839
     *
840
     * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
841
     *                                    PHP DateTime object, or a standard date string
842
     * @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
843
     *                                    PHP DateTime object, or a standard date string
844
     * @param int $method Method used for the calculation
845
     *                                        0 or omitted    US (NASD) 30/360
846
     *                                        1                Actual/actual
847
     *                                        2                Actual/360
848
     *                                        3                Actual/365
849
     *                                        4                European 30/360
850
     *
851
     * @return float fraction of the year
852
     */
853 89
    public static function YEARFRAC($startDate = 0, $endDate = 0, $method = 0)
854
    {
855 89
        $startDate = Functions::flattenSingleValue($startDate);
856 89
        $endDate = Functions::flattenSingleValue($endDate);
857 89
        $method = Functions::flattenSingleValue($method);
858
859 89
        if (is_string($startDate = self::getDateValue($startDate))) {
860 4
            return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
861
        }
862 85
        if (is_string($endDate = self::getDateValue($endDate))) {
863
            return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
864
        }
865
866 85
        if (((is_numeric($method)) && (!is_string($method))) || ($method == '')) {
867
            switch ($method) {
868 84
                case 0:
869 22
                    return self::DAYS360($startDate, $endDate) / 360;
870 62
                case 1:
871 19
                    $days = self::DATEDIF($startDate, $endDate);
872 19
                    $startYear = self::YEAR($startDate);
873 19
                    $endYear = self::YEAR($endDate);
874 19
                    $years = $endYear - $startYear + 1;
875 19
                    $leapDays = 0;
876 19
                    if ($years == 1) {
877 13
                        if (self::isLeapYear($endYear)) {
878 3
                            $startMonth = self::MONTHOFYEAR($startDate);
879 3
                            $endMonth = self::MONTHOFYEAR($endDate);
880 3
                            $endDay = self::DAYOFMONTH($endDate);
881 3
                            if (($startMonth < 3) ||
882 3
                                (($endMonth * 100 + $endDay) >= (2 * 100 + 29))) {
883 13
                                $leapDays += 1;
884
                            }
885
                        }
886
                    } else {
887 6
                        for ($year = $startYear; $year <= $endYear; ++$year) {
888 6
                            if ($year == $startYear) {
889 6
                                $startMonth = self::MONTHOFYEAR($startDate);
890 6
                                $startDay = self::DAYOFMONTH($startDate);
0 ignored issues
show
Unused Code introduced by
The assignment to $startDay is dead and can be removed.
Loading history...
891 6
                                if ($startMonth < 3) {
892 6
                                    $leapDays += (self::isLeapYear($year)) ? 1 : 0;
893
                                }
894 6
                            } elseif ($year == $endYear) {
895 6
                                $endMonth = self::MONTHOFYEAR($endDate);
896 6
                                $endDay = self::DAYOFMONTH($endDate);
897 6
                                if (($endMonth * 100 + $endDay) >= (2 * 100 + 29)) {
898 6
                                    $leapDays += (self::isLeapYear($year)) ? 1 : 0;
899
                                }
900
                            } else {
901 2
                                $leapDays += (self::isLeapYear($year)) ? 1 : 0;
902
                            }
903
                        }
904 6
                        if ($years == 2) {
905 4
                            if (($leapDays == 0) && (self::isLeapYear($startYear)) && ($days > 365)) {
906
                                $leapDays = 1;
907 4
                            } elseif ($days < 366) {
908 1
                                $years = 1;
909
                            }
910
                        }
911 6
                        $leapDays /= $years;
912
                    }
913
914 19
                    return $days / (365 + $leapDays);
915 43
                case 2:
916 14
                    return self::DATEDIF($startDate, $endDate) / 360;
917 29
                case 3:
918 14
                    return self::DATEDIF($startDate, $endDate) / 365;
919 15
                case 4:
920 15
                    return self::DAYS360($startDate, $endDate, true) / 360;
921
            }
922
        }
923
924 1
        return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
925
    }
926
927
    /**
928
     * NETWORKDAYS.
929
     *
930
     * Returns the number of whole working days between start_date and end_date. Working days
931
     * exclude weekends and any dates identified in holidays.
932
     * Use NETWORKDAYS to calculate employee benefits that accrue based on the number of days
933
     * worked during a specific term.
934
     *
935
     * Excel Function:
936
     *        NETWORKDAYS(startDate,endDate[,holidays[,holiday[,...]]])
937
     *
938
     * @category Date/Time Functions
939
     *
940
     * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
941
     *                                            PHP DateTime object, or a standard date string
942
     * @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
943
     *                                            PHP DateTime object, or a standard date string
944
     *
945
     * @return int Interval between the dates
946
     */
947 18
    public static function NETWORKDAYS($startDate, $endDate, ...$dateArgs)
948
    {
949
        //    Retrieve the mandatory start and end date that are referenced in the function definition
950 18
        $startDate = Functions::flattenSingleValue($startDate);
951 18
        $endDate = Functions::flattenSingleValue($endDate);
952
        //    Get the optional days
953 18
        $dateArgs = Functions::flattenArray($dateArgs);
954
955
        //    Validate the start and end dates
956 18
        if (is_string($startDate = $sDate = self::getDateValue($startDate))) {
957
            return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type integer.
Loading history...
958
        }
959 18
        $startDate = (float) floor($startDate);
960 18
        if (is_string($endDate = $eDate = self::getDateValue($endDate))) {
961
            return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type integer.
Loading history...
962
        }
963 18
        $endDate = (float) floor($endDate);
964
965 18
        if ($sDate > $eDate) {
966 2
            $startDate = $eDate;
967 2
            $endDate = $sDate;
968
        }
969
970
        // Execute function
971 18
        $startDoW = 6 - self::WEEKDAY($startDate, 2);
972 18
        if ($startDoW < 0) {
973
            $startDoW = 0;
974
        }
975 18
        $endDoW = self::WEEKDAY($endDate, 2);
976 18
        if ($endDoW >= 6) {
977 2
            $endDoW = 0;
978
        }
979
980 18
        $wholeWeekDays = floor(($endDate - $startDate) / 7) * 5;
981 18
        $partWeekDays = $endDoW + $startDoW;
982 18
        if ($partWeekDays > 5) {
983 14
            $partWeekDays -= 5;
984
        }
985
986
        //    Test any extra holiday parameters
987 18
        $holidayCountedArray = [];
988 18
        foreach ($dateArgs as $holidayDate) {
989 4
            if (is_string($holidayDate = self::getDateValue($holidayDate))) {
990
                return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type integer.
Loading history...
991
            }
992 4
            if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) {
993 4
                if ((self::WEEKDAY($holidayDate, 2) < 6) && (!in_array($holidayDate, $holidayCountedArray))) {
994 4
                    --$partWeekDays;
995 4
                    $holidayCountedArray[] = $holidayDate;
996
                }
997
            }
998
        }
999
1000 18
        if ($sDate > $eDate) {
1001 2
            return 0 - ($wholeWeekDays + $partWeekDays);
1002
        }
1003
1004 16
        return $wholeWeekDays + $partWeekDays;
1005
    }
1006
1007
    /**
1008
     * WORKDAY.
1009
     *
1010
     * Returns the date that is the indicated number of working days before or after a date (the
1011
     * starting date). Working days exclude weekends and any dates identified as holidays.
1012
     * Use WORKDAY to exclude weekends or holidays when you calculate invoice due dates, expected
1013
     * delivery times, or the number of days of work performed.
1014
     *
1015
     * Excel Function:
1016
     *        WORKDAY(startDate,endDays[,holidays[,holiday[,...]]])
1017
     *
1018
     * @category Date/Time Functions
1019
     *
1020
     * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
1021
     *                                        PHP DateTime object, or a standard date string
1022
     * @param int $endDays The number of nonweekend and nonholiday days before or after
1023
     *                                        startDate. A positive value for days yields a future date; a
1024
     *                                        negative value yields a past date.
1025
     *
1026
     * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
1027
     *                        depending on the value of the ReturnDateType flag
1028
     */
1029 13
    public static function WORKDAY($startDate, $endDays, ...$dateArgs)
1030
    {
1031
        //    Retrieve the mandatory start date and days that are referenced in the function definition
1032 13
        $startDate = Functions::flattenSingleValue($startDate);
1033 13
        $endDays = Functions::flattenSingleValue($endDays);
1034
        //    Get the optional days
1035 13
        $dateArgs = Functions::flattenArray($dateArgs);
1036
1037 13
        if ((is_string($startDate = self::getDateValue($startDate))) || (!is_numeric($endDays))) {
1038 1
            return Functions::VALUE();
1039
        }
1040 12
        $startDate = (float) floor($startDate);
1041 12
        $endDays = (int) floor($endDays);
1042
        //    If endDays is 0, we always return startDate
1043 12
        if ($endDays == 0) {
1044
            return $startDate;
1045
        }
1046
1047 12
        $decrementing = ($endDays < 0) ? true : false;
1048
1049
        //    Adjust the start date if it falls over a weekend
1050
1051 12
        $startDoW = self::WEEKDAY($startDate, 3);
0 ignored issues
show
Bug introduced by
$startDate of type double is incompatible with the type integer expected by parameter $dateValue of PhpOffice\PhpSpreadsheet...ion\DateTime::WEEKDAY(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1051
        $startDoW = self::WEEKDAY(/** @scrutinizer ignore-type */ $startDate, 3);
Loading history...
1052 12
        if (self::WEEKDAY($startDate, 3) >= 5) {
1053 4
            $startDate += ($decrementing) ? -$startDoW + 4 : 7 - $startDoW;
1054 4
            ($decrementing) ? $endDays++ : $endDays--;
1055
        }
1056
1057
        //    Add endDays
1058 12
        $endDate = (float) $startDate + ((int) ($endDays / 5) * 7) + ($endDays % 5);
1059
1060
        //    Adjust the calculated end date if it falls over a weekend
1061 12
        $endDoW = self::WEEKDAY($endDate, 3);
1062 12
        if ($endDoW >= 5) {
1063 1
            $endDate += ($decrementing) ? -$endDoW + 4 : 7 - $endDoW;
1064
        }
1065
1066
        //    Test any extra holiday parameters
1067 12
        if (!empty($dateArgs)) {
1068 4
            $holidayCountedArray = $holidayDates = [];
1069 4
            foreach ($dateArgs as $holidayDate) {
1070 4
                if (($holidayDate !== null) && (trim($holidayDate) > '')) {
1071 4
                    if (is_string($holidayDate = self::getDateValue($holidayDate))) {
1072
                        return Functions::VALUE();
1073
                    }
1074 4
                    if (self::WEEKDAY($holidayDate, 3) < 5) {
1075 4
                        $holidayDates[] = $holidayDate;
1076
                    }
1077
                }
1078
            }
1079 4
            if ($decrementing) {
1080 1
                rsort($holidayDates, SORT_NUMERIC);
1081
            } else {
1082 3
                sort($holidayDates, SORT_NUMERIC);
1083
            }
1084 4
            foreach ($holidayDates as $holidayDate) {
1085 4
                if ($decrementing) {
1086 1
                    if (($holidayDate <= $startDate) && ($holidayDate >= $endDate)) {
1087 1
                        if (!in_array($holidayDate, $holidayCountedArray)) {
1088 1
                            --$endDate;
1089 1
                            $holidayCountedArray[] = $holidayDate;
1090
                        }
1091
                    }
1092
                } else {
1093 3
                    if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) {
1094 3
                        if (!in_array($holidayDate, $holidayCountedArray)) {
1095 3
                            ++$endDate;
1096 3
                            $holidayCountedArray[] = $holidayDate;
1097
                        }
1098
                    }
1099
                }
1100
                //    Adjust the calculated end date if it falls over a weekend
1101 4
                $endDoW = self::WEEKDAY($endDate, 3);
1102 4
                if ($endDoW >= 5) {
1103 4
                    $endDate += ($decrementing) ? -$endDoW + 4 : 7 - $endDoW;
1104
                }
1105
            }
1106
        }
1107
1108 12
        switch (Functions::getReturnDateType()) {
1109 12
            case Functions::RETURNDATE_EXCEL:
1110 12
                return (float) $endDate;
1111
            case Functions::RETURNDATE_PHP_NUMERIC:
1112
                return (int) Date::excelToTimestamp($endDate);
1113
            case Functions::RETURNDATE_PHP_OBJECT:
1114
                return Date::excelToDateTimeObject($endDate);
1115
        }
1116
    }
1117
1118
    /**
1119
     * DAYOFMONTH.
1120
     *
1121
     * Returns the day of the month, for a specified date. The day is given as an integer
1122
     * ranging from 1 to 31.
1123
     *
1124
     * Excel Function:
1125
     *        DAY(dateValue)
1126
     *
1127
     * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
1128
     *                                    PHP DateTime object, or a standard date string
1129
     *
1130
     * @return int Day of the month
1131
     */
1132 20
    public static function DAYOFMONTH($dateValue = 1)
1133
    {
1134 20
        $dateValue = Functions::flattenSingleValue($dateValue);
1135
1136 20
        if ($dateValue === null) {
1137
            $dateValue = 1;
1138 20
        } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
1139 1
            return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type integer.
Loading history...
1140
        }
1141
1142 19
        if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_EXCEL) {
1143 19
            if ($dateValue < 0.0) {
1144 1
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type integer.
Loading history...
1145 18
            } elseif ($dateValue < 1.0) {
1146 2
                return 0;
1147
            }
1148
        }
1149
1150
        // Execute function
1151 19
        $PHPDateObject = Date::excelToDateTimeObject($dateValue);
1152
1153 19
        return (int) $PHPDateObject->format('j');
1154
    }
1155
1156
    /**
1157
     * WEEKDAY.
1158
     *
1159
     * Returns the day of the week for a specified date. The day is given as an integer
1160
     * ranging from 0 to 7 (dependent on the requested style).
1161
     *
1162
     * Excel Function:
1163
     *        WEEKDAY(dateValue[,style])
1164
     *
1165
     * @param int $dateValue Excel date serial value (float), PHP date timestamp (integer),
1166
     *                                    PHP DateTime object, or a standard date string
1167
     * @param int $style A number that determines the type of return value
1168
     *                                        1 or omitted    Numbers 1 (Sunday) through 7 (Saturday).
1169
     *                                        2                Numbers 1 (Monday) through 7 (Sunday).
1170
     *                                        3                Numbers 0 (Monday) through 6 (Sunday).
1171
     *
1172
     * @return int Day of the week value
1173
     */
1174 57
    public static function WEEKDAY($dateValue = 1, $style = 1)
1175
    {
1176 57
        $dateValue = Functions::flattenSingleValue($dateValue);
1177 57
        $style = Functions::flattenSingleValue($style);
1178
1179 57
        if (!is_numeric($style)) {
1180 1
            return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type integer.
Loading history...
1181 56
        } elseif (($style < 1) || ($style > 3)) {
1182 1
            return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type integer.
Loading history...
1183
        }
1184 55
        $style = floor($style);
1185
1186 55
        if ($dateValue === null) {
1187
            $dateValue = 1;
1188 55
        } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
1189 1
            return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type integer.
Loading history...
1190 54
        } elseif ($dateValue < 0.0) {
1191 1
            return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type integer.
Loading history...
1192
        }
1193
1194
        // Execute function
1195 53
        $PHPDateObject = Date::excelToDateTimeObject($dateValue);
1196 53
        $DoW = $PHPDateObject->format('w');
1197
1198 53
        $firstDay = 1;
1199
        switch ($style) {
1200 53
            case 1:
1201 8
                ++$DoW;
1202
1203 8
                break;
1204 45
            case 2:
1205 26
                if ($DoW == 0) {
1206 3
                    $DoW = 7;
1207
                }
1208
1209 26
                break;
1210 19
            case 3:
1211 19
                if ($DoW == 0) {
1212 3
                    $DoW = 7;
1213
                }
1214 19
                $firstDay = 0;
1215 19
                --$DoW;
1216
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
        if (!is_numeric($method)) {
1259 1
            return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type integer.
Loading history...
1260 14
        } elseif (($method < 1) || ($method > 2)) {
1261 1
            return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type integer.
Loading history...
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();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type integer.
Loading history...
1269 12
        } elseif ($dateValue < 0.0) {
1270 1
            return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type integer.
Loading history...
1271
        }
1272
1273
        // Execute function
1274 11
        $PHPDateObject = 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
    public static function MONTHOFYEAR($dateValue = 1)
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();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type integer.
Loading history...
1312 20
        } elseif ($dateValue < 0.0) {
1313 1
            return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type integer.
Loading history...
1314
        }
1315
1316
        // Execute function
1317 19
        $PHPDateObject = 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
    public static function YEAR($dateValue = 1)
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();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type integer.
Loading history...
1344 31
        } elseif ($dateValue < 0.0) {
1345 1
            return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type integer.
Loading history...
1346
        }
1347
1348
        // Execute function
1349 31
        $PHPDateObject = 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
    public static function HOUROFDAY($timeValue = 0)
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();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type integer.
Loading history...
1377
                }
1378
            }
1379 4
            $timeValue = self::getTimeValue($timeValue);
1380 4
            if (is_string($timeValue)) {
1381 1
                return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type integer.
Loading history...
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();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type integer.
Loading history...
1389
        }
1390 10
        $timeValue = 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
    public static function MINUTE($timeValue = 0)
1410
    {
1411 12
        $timeValue = $timeTester = Functions::flattenSingleValue($timeValue);
0 ignored issues
show
Unused Code introduced by
The assignment to $timeTester is dead and can be removed.
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();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type integer.
Loading history...
1418
                }
1419
            }
1420 4
            $timeValue = self::getTimeValue($timeValue);
1421 4
            if (is_string($timeValue)) {
1422 1
                return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type integer.
Loading history...
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();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type integer.
Loading history...
1430
        }
1431 10
        $timeValue = 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
    public static function SECOND($timeValue = 0)
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();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type integer.
Loading history...
1459
                }
1460
            }
1461 4
            $timeValue = self::getTimeValue($timeValue);
1462 4
            if (is_string($timeValue)) {
1463 1
                return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type integer.
Loading history...
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();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type integer.
Loading history...
1471
        }
1472 10
        $timeValue = 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
        switch (Functions::getReturnDateType()) {
1515 15
            case Functions::RETURNDATE_EXCEL:
1516 13
                return (float) Date::PHPToExcel($PHPDateObject);
1517 2
            case Functions::RETURNDATE_PHP_NUMERIC:
1518 1
                return (int) Date::excelToTimestamp(Date::PHPToExcel($PHPDateObject));
1 ignored issue
show
Bug introduced by
It seems like PhpOffice\PhpSpreadsheet...ToExcel($PHPDateObject) can also be of type false; however, parameter $excelTimestamp of PhpOffice\PhpSpreadsheet...ate::excelToTimestamp() does only seem to accept integer|double, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1518
                return (int) Date::excelToTimestamp(/** @scrutinizer ignore-type */ Date::PHPToExcel($PHPDateObject));
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
        switch (Functions::getReturnDateType()) {
1564 17
            case Functions::RETURNDATE_EXCEL:
1565 15
                return (float) Date::PHPToExcel($PHPDateObject);
1566 2
            case Functions::RETURNDATE_PHP_NUMERIC:
1567 1
                return (int) Date::excelToTimestamp(Date::PHPToExcel($PHPDateObject));
1 ignored issue
show
Bug introduced by
It seems like PhpOffice\PhpSpreadsheet...ToExcel($PHPDateObject) can also be of type false; however, parameter $excelTimestamp of PhpOffice\PhpSpreadsheet...ate::excelToTimestamp() does only seem to accept integer|double, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1567
                return (int) Date::excelToTimestamp(/** @scrutinizer ignore-type */ Date::PHPToExcel($PHPDateObject));
Loading history...
1568 1
            case Functions::RETURNDATE_PHP_OBJECT:
1569 1
                return $PHPDateObject;
1570
        }
1571
    }
1572
}
1573