Completed
Push — develop ( e95e4d...4fd8e7 )
by Adrien
61:55
created

DateTime::DATEDIF()   D

Complexity

Conditions 23
Paths 19

Size

Total Lines 111
Code Lines 71

Duplication

Lines 17
Ratio 15.32 %

Code Coverage

Tests 64
CRAP Score 23

Importance

Changes 0
Metric Value
cc 23
eloc 71
nc 19
nop 3
dl 17
loc 111
ccs 64
cts 64
cp 1
crap 23
rs 4.6303
c 0
b 0
f 0

How to fix   Long Method    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 382
    public static function getDateValue($dateValue)
67
    {
68 382
        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 382
        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 1
     * @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 1
     */
192 1
    public static function DATENOW()
193 1
    {
194 1
        $saveTimeZone = date_default_timezone_get();
195 1
        date_default_timezone_set('UTC');
196 1
        $retValue = false;
197
        $excelDateTime = floor(Date::PHPToExcel(time()));
198
        switch (Functions::getReturnDateType()) {
199 1
            case Functions::RETURNDATE_EXCEL:
200 1
                $retValue = (float) $excelDateTime;
201 1
202
                break;
203
            case Functions::RETURNDATE_PHP_NUMERIC:
204
                $retValue = (int) Date::excelToTimestamp($excelDateTime);
205
206 1
                break;
207
            case Functions::RETURNDATE_PHP_OBJECT:
208 1
                $retValue = Date::excelToDateTimeObject($excelDateTime);
209
210
                break;
211
        }
212
        date_default_timezone_set($saveTimeZone);
213
214
        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 84
     *                                example, DATE(2008,1,-15) returns the serial number representing
263
     *                                December 16, 2007.
264 84
     *
265 84
     * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
266 84
     *                        depending on the value of the ReturnDateType flag
267
     */
268 84
    public static function DATE($year = 0, $month = 1, $day = 1)
269 3
    {
270
        $year = Functions::flattenSingleValue($year);
271
        $month = Functions::flattenSingleValue($month);
272 84
        $day = Functions::flattenSingleValue($day);
273 3
274
        if (($month !== null) && (!is_numeric($month))) {
275
            $month = Date::monthStringToNumber($month);
0 ignored issues
show
Bug introduced by
It seems like $month defined by \PhpOffice\PhpSpreadshee...hStringToNumber($month) on line 275 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...
276 84
        }
277 84
278 84
        if (($day !== null) && (!is_numeric($day))) {
279 84
            $day = Date::dayStringToNumber($day);
0 ignored issues
show
Bug introduced by
It seems like $day defined by \PhpOffice\PhpSpreadshee...dayStringToNumber($day) on line 279 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...
280 83
        }
281 84
282 3
        $year = ($year !== null) ? StringHelper::testStringAsNumeric($year) : 0;
283
        $month = ($month !== null) ? StringHelper::testStringAsNumeric($month) : 0;
284 81
        $day = ($day !== null) ? StringHelper::testStringAsNumeric($day) : 0;
285 81
        if ((!is_numeric($year)) ||
286 81
            (!is_numeric($month)) ||
287
            (!is_numeric($day))) {
288 81
            return Functions::VALUE();
289
        }
290 81
        $year = (int) $year;
291 2
        $month = (int) $month;
292
        $day = (int) $day;
293 79
294 1
        $baseYear = Date::getExcelCalendar();
295
        // Validate parameters
296
        if ($year < ($baseYear - 1900)) {
297 78
            return Functions::NAN();
298 7
        }
299
        if ((($baseYear - 1900) != 0) && ($year < $baseYear) && ($year >= 1900)) {
300
            return Functions::NAN();
301 78
        }
302
303 22
        if (($year < $baseYear) && ($year >= ($baseYear - 1900))) {
304 22
            $year += 1900;
305 22
        }
306 57
307
        if ($month < 1) {
308 8
            //    Handle year/month adjustment if month < 1
309 8
            --$month;
310
            $year += ceil($month / 12) - 1;
311
            $month = 13 - abs($month % 12);
312
        } elseif ($month > 12) {
313 78
            //    Handle year/month adjustment if month > 12
314 2
            $year += floor($month / 12);
315
            $month = ($month % 12);
316
        }
317
318 76
        // Re-validate the year parameter after adjustments
319 76
        if (($year < $baseYear) || ($year >= 10000)) {
320 76
            return Functions::NAN();
321 74
        }
322 2
323 1
        // Execute function
324 1
        $excelDateValue = Date::formattedPHPToExcel($year, $month, $day);
325 1 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...
326
            case Functions::RETURNDATE_EXCEL:
327
                return (float) $excelDateValue;
328
            case Functions::RETURNDATE_PHP_NUMERIC:
329
                return (int) Date::excelToTimestamp($excelDateValue);
330
            case Functions::RETURNDATE_PHP_OBJECT:
331
                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 25
     *                                    and seconds. For example, TIME(0,0,2000) = TIME(0,33,22) = .023148
358
     *                                    or 12:33:20 AM
359 25
     *
360 25
     * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
361 25
     *                        depending on the value of the ReturnDateType flag
362
     */
363 25
    public static function TIME($hour = 0, $minute = 0, $second = 0)
364 1
    {
365
        $hour = Functions::flattenSingleValue($hour);
366 25
        $minute = Functions::flattenSingleValue($minute);
367 6
        $second = Functions::flattenSingleValue($second);
368
369 25
        if ($hour == '') {
370 6
            $hour = 0;
371
        }
372
        if ($minute == '') {
373 25
            $minute = 0;
374 1
        }
375
        if ($second == '') {
376 24
            $second = 0;
377 24
        }
378 24
379
        if ((!is_numeric($hour)) || (!is_numeric($minute)) || (!is_numeric($second))) {
380 24
            return Functions::VALUE();
381 5
        }
382 5
        $hour = (int) $hour;
383 5
        $minute = (int) $minute;
384 5
        $second = (int) $second;
385
386 20 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...
387 1
            $minute += floor($second / 60);
388 1
            $second = 60 - abs($second % 60);
389
            if ($second == 60) {
390 24
                $second = 0;
391 8
            }
392 8
        } elseif ($second >= 60) {
393 8
            $minute += floor($second / 60);
394 8
            $second = $second % 60;
395
        }
396 17 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...
397 3
            $hour += floor($minute / 60);
398 3
            $minute = 60 - abs($minute % 60);
399
            if ($minute == 60) {
400
                $minute = 0;
401 24
            }
402 1
        } elseif ($minute >= 60) {
403 23
            $hour += floor($minute / 60);
404 3
            $minute = $minute % 60;
405
        }
406
407
        if ($hour > 23) {
408 22
            $hour = $hour % 24;
409 22
        } elseif ($hour < 0) {
410 20
            return Functions::NAN();
411 20
        }
412 20
413
        // Execute function
414
        switch (Functions::getReturnDateType()) {
415
            case Functions::RETURNDATE_EXCEL:
416 20
                $date = 0;
417 2
                $calendar = Date::getExcelCalendar();
418 1
                if ($calendar != Date::CALENDAR_WINDOWS_1900) {
419 1
                    $date = 1;
420 1
                }
421 1
422
                return (float) Date::formattedPHPToExcel($calendar, 1, $date, $hour, $minute, $second);
423
            case Functions::RETURNDATE_PHP_NUMERIC:
424
                return (int) Date::excelToTimestamp(Date::formattedPHPToExcel(1970, 1, 1, $hour, $minute, $second)); // -2147468400; //    -2147472000 + 3600
425
            case Functions::RETURNDATE_PHP_OBJECT:
426
                $dayAdjust = 0;
427 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...
428
                    $dayAdjust = floor($hour / 24);
429
                    $hour = 24 - abs($hour % 24);
430
                    if ($hour == 24) {
431 1
                        $hour = 0;
432 1
                    }
433
                } elseif ($hour >= 24) {
434
                    $dayAdjust = floor($hour / 24);
435
                    $hour = $hour % 24;
436 1
                }
437
                $phpDateObject = new \DateTime('1900-01-01 ' . $hour . ':' . $minute . ':' . $second);
438
                if ($dayAdjust != 0) {
439
                    $phpDateObject->modify($dayAdjust . ' days');
440
                }
441
442
                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 425
     *                                    from January 1, 1904, to December 31, 9999. DATEVALUE returns the
468
     *                                    #VALUE! error value if date_text is out of this range.
469 425
     *
470 425
     * @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 425
     */
473
    public static function DATEVALUE($dateValue = 1)
474 425
    {
475
        $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...
476 425
        $dateValue = trim(Functions::flattenSingleValue($dateValue), '"');
477 425
        //    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 425
        //    Convert separators (/ . or space) to hyphens (should also handle dot used for ordinals in some countries, e.g. Denmark, Germany)
480 392
        $dateValue = str_replace(['/', '.', '-', '  '], ' ', $dateValue);
481
482
        $yearFound = false;
483 392
        $t1 = explode(' ', $dateValue);
484 2
        foreach ($t1 as &$t) {
485
            if ((is_numeric($t)) && ($t > 31)) {
486 425
                if ($yearFound) {
487
                    return Functions::VALUE();
488
                }
489 425
                if ($t < 100) {
490
                    $t += 1900;
491 1
                }
492 424
                $yearFound = true;
493
            }
494 28
        }
495 4
        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...
496
            //    We've been fed a time value without any date
497 25
            return 0.0;
498 1
        } elseif (count($t1) == 2) {
499 1
            //    We only have two parts of the date: either day/month or month/year
500
            if ($yearFound) {
501 24
                array_unshift($t1, 1);
502
            } else {
503
                if ($t1[1] > 29) {
504
                    $t1[1] += 1900;
505 424
                    array_unshift($t1, 1);
506 424
                } else {
507
                    $t1[] = date('Y');
508 424
                }
509 424
            }
510 317
        }
511 317
        unset($t);
512 316
        $dateValue = implode(' ', $t1);
513 316
514 302
        $PHPDateArray = date_parse($dateValue);
515 302
        if (($PHPDateArray === false) || ($PHPDateArray['error_count'] > 0)) {
516 302
            $testVal1 = strtok($dateValue, '- ');
517
            if ($testVal1 !== false) {
518
                $testVal2 = strtok('- ');
519 316
                if ($testVal2 !== false) {
520
                    $testVal3 = strtok('- ');
521
                    if ($testVal3 === false) {
522 1
                        $testVal3 = strftime('%Y');
523
                    }
524 302
                } else {
525 2
                    return Functions::VALUE();
526
                }
527 302
            } else {
528 302
                return Functions::VALUE();
529 26
            }
530 26
            if ($testVal1 < 31 && $testVal2 < 12 && $testVal3 < 12 && strlen($testVal3) == 2) {
531 22
                $testVal3 += 2000;
532
            }
533
            $PHPDateArray = date_parse($testVal1 . '-' . $testVal2 . '-' . $testVal3);
534
            if (($PHPDateArray === false) || ($PHPDateArray['error_count'] > 0)) {
535
                $PHPDateArray = date_parse($testVal2 . '-' . $testVal1 . '-' . $testVal3);
536 393
                if (($PHPDateArray === false) || ($PHPDateArray['error_count'] > 0)) {
537
                    return Functions::VALUE();
538 393
                }
539
            }
540
        }
541 393
542 2
        if (($PHPDateArray !== false) && ($PHPDateArray['error_count'] == 0)) {
543
            // Execute function
544 391
            if ($PHPDateArray['year'] == '') {
545
                $PHPDateArray['year'] = strftime('%Y');
546
            }
547 391
            if ($PHPDateArray['year'] < 1900) {
548
                return Functions::VALUE();
549
            }
550 391
            if ($PHPDateArray['month'] == '') {
551 4
                $PHPDateArray['month'] = strftime('%m');
552
            }
553 388
            if ($PHPDateArray['day'] == '') {
554 388
                $PHPDateArray['day'] = strftime('%d');
555 388
            }
556 388
            if (!checkdate($PHPDateArray['month'], $PHPDateArray['day'], $PHPDateArray['year'])) {
557 388
                return Functions::VALUE();
558 388
            }
559 388
            $excelDateValue = floor(
560 388
                Date::formattedPHPToExcel(
561
                    $PHPDateArray['year'],
562
                    $PHPDateArray['month'],
563 388
                    $PHPDateArray['day'],
564 388
                    $PHPDateArray['hour'],
565 386
                    $PHPDateArray['minute'],
566 2
                    $PHPDateArray['second']
567 1
                )
568 1
            );
569 1 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...
570
                case Functions::RETURNDATE_EXCEL:
571
                    return (float) $excelDateValue;
572
                case Functions::RETURNDATE_PHP_NUMERIC:
573
                    return (int) Date::excelToTimestamp($excelDateValue);
574
                case Functions::RETURNDATE_PHP_OBJECT:
575
                    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 31
     *                                    within quotation marks that represent time.
600
     *                                    Date information in time_text is ignored.
601 31
     *
602 31
     * @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 31
     */
605 31
    public static function TIMEVALUE($timeValue)
606 1
    {
607 1
        $timeValue = trim(Functions::flattenSingleValue($timeValue), '"');
608
        $timeValue = str_replace(['/', '.'], '-', $timeValue);
609
610 31
        $arraySplit = preg_split('/[\/:\-\s]/', $timeValue);
611 31
        if ((count($arraySplit) == 2 || count($arraySplit) == 3) && $arraySplit[0] > 24) {
612 26
            $arraySplit[0] = ($arraySplit[0] % 24);
613
            $timeValue = implode(':', $arraySplit);
614
        }
615
616
        $PHPDateArray = date_parse($timeValue);
617
        if (($PHPDateArray !== false) && ($PHPDateArray['error_count'] == 0)) {
618
            if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
619
                $excelDateValue = Date::formattedPHPToExcel(
620
                    $PHPDateArray['year'],
621
                    $PHPDateArray['month'],
622 26
                    $PHPDateArray['day'],
623
                    $PHPDateArray['hour'],
624
                    $PHPDateArray['minute'],
625 26
                    $PHPDateArray['second']
626 26
                );
627 24
            } else {
628 2
                $excelDateValue = Date::formattedPHPToExcel(1900, 1, 1, $PHPDateArray['hour'], $PHPDateArray['minute'], $PHPDateArray['second']) - 1;
629 1
            }
630 1
631 1 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...
632
                case Functions::RETURNDATE_EXCEL:
633
                    return (float) $excelDateValue;
634
                case Functions::RETURNDATE_PHP_NUMERIC:
635 6
                    return (int) $phpDateValue = Date::excelToTimestamp($excelDateValue + 25569) - 3600;
636
                case Functions::RETURNDATE_PHP_OBJECT:
637
                    return new \DateTime('1900-01-01 ' . $PHPDateArray['hour'] . ':' . $PHPDateArray['minute'] . ':' . $PHPDateArray['second']);
638
            }
639
        }
640
641
        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 150
     * @param mixed $endDate Excel date serial value, PHP date/time stamp, PHP DateTime object
650
     *                                    or a standard date string
651 150
     * @param string $unit
652 150
     *
653 150
     * @return int Interval between the dates
654
     */
655 150
    public static function DATEDIF($startDate = 0, $endDate = 0, $unit = 'D')
656 1
    {
657
        $startDate = Functions::flattenSingleValue($startDate);
658 149
        $endDate = Functions::flattenSingleValue($endDate);
659 1
        $unit = strtoupper(Functions::flattenSingleValue($unit));
660
661
        if (is_string($startDate = self::getDateValue($startDate))) {
662
            return Functions::VALUE();
663 148
        }
664 1
        if (is_string($endDate = self::getDateValue($endDate))) {
665
            return Functions::VALUE();
666
        }
667
668 147
        // Validate parameters
669
        if ($startDate > $endDate) {
670 147
            return Functions::NAN();
671 147
        }
672 147
673 147
        // Execute function
674
        $difference = $endDate - $startDate;
675 147
676 147
        $PHPStartDateObject = Date::excelToDateTimeObject($startDate);
677 147
        $startDays = $PHPStartDateObject->format('j');
678 147
        $startMonths = $PHPStartDateObject->format('n');
679
        $startYears = $PHPStartDateObject->format('Y');
680 147
681
        $PHPEndDateObject = Date::excelToDateTimeObject($endDate);
682 147
        $endDays = $PHPEndDateObject->format('j');
683 64
        $endMonths = $PHPEndDateObject->format('n');
684 64
        $endYears = $PHPEndDateObject->format('Y');
685 83
686 15
        $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...
687
        switch ($unit) {
688 15
            case 'D':
689 3
                $retVal = (int) $difference;
690
691 15
                break;
692 68 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...
693 17
                $retVal = (int) ($endMonths - $startMonths) + ((int) ($endYears - $startYears) * 12);
694
                //    We're only interested in full months
695 17
                if ($endDays < $startDays) {
696 5
                    --$retVal;
697 12
                }
698
699 1
                break;
700
            case 'Y':
701 1
                $retVal = (int) ($endYears - $startYears);
702
                //    We're only interested in full months
703 17
                if ($endMonths < $startMonths) {
704 51
                    --$retVal;
705 15
                } elseif (($endMonths == $startMonths) && ($endDays < $startDays)) {
706 3
                    // Remove start month
707 3
                    --$retVal;
708 3
                    // Remove end month
709 3
                    --$retVal;
710
                }
711 12
712
                break;
713 15
            case 'MD':
714 36
                if ($endDays < $startDays) {
715 15
                    $retVal = $endDays;
716 15
                    $PHPEndDateObject->modify('-' . $endDays . ' days');
717 4
                    $adjustDays = $PHPEndDateObject->format('j');
718
                    $retVal += ($adjustDays - $startDays);
719
                } else {
720 15
                    $retVal = $endDays - $startDays;
721 3
                }
722
723 15
                break;
724 21 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...
725 20
                $retVal = (int) ($endMonths - $startMonths);
726 20
                if ($retVal < 0) {
727 9
                    $retVal += 12;
728 9
                }
729
                //    We're only interested in full months
730
                if ($endDays < $startDays) {
731 9
                    --$retVal;
732 9
                }
733 9
734
                break;
735 9
            case 'YD':
736
                $retVal = (int) $difference;
737
                if ($endYears > $startYears) {
738 9
                    $isLeapStartYear = $PHPStartDateObject->format('L');
739
                    $wasLeapEndYear = $PHPEndDateObject->format('L');
740
741 9
                    // Adjust end year to be as close as possible as start year
742 9
                    while ($PHPEndDateObject >= $PHPStartDateObject) {
743 9
                        $PHPEndDateObject->modify('-1 year');
744 1
                        $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...
745
                    }
746
                    $PHPEndDateObject->modify('+1 year');
747 20
748
                    // Get the result
749 1
                    $retVal = $PHPEndDateObject->diff($PHPStartDateObject)->days;
750
751
                    // Adjust for leap years cases
752 147
                    $isLeapEndYear = $PHPEndDateObject->format('L');
753
                    $limit = new \DateTime($PHPEndDateObject->format('Y-02-29'));
754
                    if (!$isLeapStartYear && !$wasLeapEndYear && $isLeapEndYear && $PHPEndDateObject >= $limit) {
755
                        --$retVal;
756
                    }
757
                }
758
759
                break;
760
            default:
761
                $retVal = Functions::VALUE();
762
        }
763
764
        return $retVal;
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 72
     *                                        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 72
     *                                        the starting date is earlier than the 30th of a month, the
788 72
     *                                        ending date becomes equal to the 1st of the next month;
789
     *                                        otherwise the ending date becomes equal to the 30th of the
790 72
     *                                        same month.
791 1
     *                                        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 71
     *                                        same month.
794 1
     *
795
     * @return int Number of days between start date and end date
796
     */
797 70
    public static function DAYS360($startDate = 0, $endDate = 0, $method = false)
798 2
    {
799
        $startDate = Functions::flattenSingleValue($startDate);
800
        $endDate = Functions::flattenSingleValue($endDate);
801
802 68
        if (is_string($startDate = self::getDateValue($startDate))) {
803 68
            return Functions::VALUE();
804 68
        }
805 68
        if (is_string($endDate = self::getDateValue($endDate))) {
806
            return Functions::VALUE();
807 68
        }
808 68
809 68
        if (!is_bool($method)) {
810 68
            return Functions::VALUE();
811
        }
812 68
813
        // Execute function
814
        $PHPStartDateObject = Date::excelToDateTimeObject($startDate);
815
        $startDay = $PHPStartDateObject->format('j');
816
        $startMonth = $PHPStartDateObject->format('n');
817
        $startYear = $PHPStartDateObject->format('Y');
818
819
        $PHPEndDateObject = Date::excelToDateTimeObject($endDate);
820
        $endDay = $PHPEndDateObject->format('j');
821
        $endMonth = $PHPEndDateObject->format('n');
822
        $endYear = $PHPEndDateObject->format('Y');
823
824
        return self::dateDiff360($startDay, $startMonth, $startYear, $endDay, $endMonth, $endYear, !$method);
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 89
     *                                    PHP DateTime object, or a standard date string
842
     * @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
843 89
     *                                    PHP DateTime object, or a standard date string
844 89
     * @param int $method Method used for the calculation
845 89
     *                                        0 or omitted    US (NASD) 30/360
846
     *                                        1                Actual/actual
847 89
     *                                        2                Actual/360
848 4
     *                                        3                Actual/365
849
     *                                        4                European 30/360
850 85
     *
851
     * @return float fraction of the year
852
     */
853
    public static function YEARFRAC($startDate = 0, $endDate = 0, $method = 0)
854 85
    {
855
        $startDate = Functions::flattenSingleValue($startDate);
856 84
        $endDate = Functions::flattenSingleValue($endDate);
857 22
        $method = Functions::flattenSingleValue($method);
858 62
859 19
        if (is_string($startDate = self::getDateValue($startDate))) {
860 19
            return Functions::VALUE();
861 19
        }
862 19
        if (is_string($endDate = self::getDateValue($endDate))) {
863 19
            return Functions::VALUE();
864 19
        }
865 13
866 3
        if (((is_numeric($method)) && (!is_string($method))) || ($method == '')) {
867 3
            switch ($method) {
868 3
                case 0:
869 3
                    return self::DAYS360($startDate, $endDate) / 360;
870 3
                case 1:
871 13
                    $days = self::DATEDIF($startDate, $endDate);
872
                    $startYear = self::YEAR($startDate);
873
                    $endYear = self::YEAR($endDate);
874
                    $years = $endYear - $startYear + 1;
875 6
                    $leapDays = 0;
876 6
                    if ($years == 1) {
877 6
                        if (self::isLeapYear($endYear)) {
878 6
                            $startMonth = self::MONTHOFYEAR($startDate);
879 6
                            $endMonth = self::MONTHOFYEAR($endDate);
880 6
                            $endDay = self::DAYOFMONTH($endDate);
881
                            if (($startMonth < 3) ||
882 6
                                (($endMonth * 100 + $endDay) >= (2 * 100 + 29))) {
883 6
                                $leapDays += 1;
884 6
                            }
885 6
                        }
886 6
                    } else {
887
                        for ($year = $startYear; $year <= $endYear; ++$year) {
888
                            if ($year == $startYear) {
889 2
                                $startMonth = self::MONTHOFYEAR($startDate);
890
                                $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...
891
                                if ($startMonth < 3) {
892 6
                                    $leapDays += (self::isLeapYear($year)) ? 1 : 0;
893 4
                                }
894
                            } elseif ($year == $endYear) {
895 4
                                $endMonth = self::MONTHOFYEAR($endDate);
896 1
                                $endDay = self::DAYOFMONTH($endDate);
897
                                if (($endMonth * 100 + $endDay) >= (2 * 100 + 29)) {
898
                                    $leapDays += (self::isLeapYear($year)) ? 1 : 0;
899 6
                                }
900
                            } else {
901
                                $leapDays += (self::isLeapYear($year)) ? 1 : 0;
902 19
                            }
903 43
                        }
904 14
                        if ($years == 2) {
905 29
                            if (($leapDays == 0) && (self::isLeapYear($startYear)) && ($days > 365)) {
906 14
                                $leapDays = 1;
907 15
                            } elseif ($days < 366) {
908 15
                                $years = 1;
909
                            }
910
                        }
911
                        $leapDays /= $years;
912 1
                    }
913
914
                    return $days / (365 + $leapDays);
915
                case 2:
916
                    return self::DATEDIF($startDate, $endDate) / 360;
917
                case 3:
918
                    return self::DATEDIF($startDate, $endDate) / 365;
919
                case 4:
920
                    return self::DAYS360($startDate, $endDate, true) / 360;
921
            }
922
        }
923
924
        return Functions::VALUE();
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 18
     * Excel Function:
936
     *        NETWORKDAYS(startDate,endDate[,holidays[,holiday[,...]]])
937
     *
938 18
     * @category Date/Time Functions
939 18
     *
940
     * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
941 18
     *                                            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 18
     *
945
     * @return int Interval between the dates
946
     */
947 18
    public static function NETWORKDAYS($startDate, $endDate, ...$dateArgs)
948 18
    {
949
        //    Retrieve the mandatory start and end date that are referenced in the function definition
950
        $startDate = Functions::flattenSingleValue($startDate);
951 18
        $endDate = Functions::flattenSingleValue($endDate);
952
        //    Get the optional days
953 18
        $dateArgs = Functions::flattenArray($dateArgs);
954 2
955 2
        //    Validate the start and end dates
956
        if (is_string($startDate = $sDate = self::getDateValue($startDate))) {
957
            return Functions::VALUE();
958
        }
959 18
        $startDate = (float) floor($startDate);
960 18
        if (is_string($endDate = $eDate = self::getDateValue($endDate))) {
961
            return Functions::VALUE();
962
        }
963 18
        $endDate = (float) floor($endDate);
964 18
965 2
        if ($sDate > $eDate) {
966
            $startDate = $eDate;
967
            $endDate = $sDate;
968 18
        }
969 18
970 18
        // Execute function
971 14
        $startDoW = 6 - self::WEEKDAY($startDate, 2);
972
        if ($startDoW < 0) {
973
            $startDoW = 0;
974
        }
975 18
        $endDoW = self::WEEKDAY($endDate, 2);
976 18
        if ($endDoW >= 6) {
977 4
            $endDoW = 0;
978
        }
979
980 4
        $wholeWeekDays = floor(($endDate - $startDate) / 7) * 5;
981 4
        $partWeekDays = $endDoW + $startDoW;
982 4
        if ($partWeekDays > 5) {
983 4
            $partWeekDays -= 5;
984
        }
985
986
        //    Test any extra holiday parameters
987
        $holidayCountedArray = [];
988 18
        foreach ($dateArgs as $holidayDate) {
989 2
            if (is_string($holidayDate = self::getDateValue($holidayDate))) {
990
                return Functions::VALUE();
991
            }
992 16 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...
993
                if ((self::WEEKDAY($holidayDate, 2) < 6) && (!in_array($holidayDate, $holidayCountedArray))) {
994
                    --$partWeekDays;
995
                    $holidayCountedArray[] = $holidayDate;
996
                }
997
            }
998
        }
999
1000
        if ($sDate > $eDate) {
1001
            return 0 - ($wholeWeekDays + $partWeekDays);
1002
        }
1003
1004
        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 13
     *
1018
     * @category Date/Time Functions
1019
     *
1020 13
     * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
1021 13
     *                                        PHP DateTime object, or a standard date string
1022
     * @param int $endDays The number of nonweekend and nonholiday days before or after
1023 13
     *                                        startDate. A positive value for days yields a future date; a
1024
     *                                        negative value yields a past date.
1025 13
     *
1026 1
     * @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 12
     */
1029 12
    public static function WORKDAY($startDate, $endDays, ...$dateArgs)
1030
    {
1031 12
        //    Retrieve the mandatory start date and days that are referenced in the function definition
1032
        $startDate = Functions::flattenSingleValue($startDate);
1033
        $endDays = Functions::flattenSingleValue($endDays);
1034
        //    Get the optional days
1035 12
        $dateArgs = Functions::flattenArray($dateArgs);
1036
1037
        if ((is_string($startDate = self::getDateValue($startDate))) || (!is_numeric($endDays))) {
1038
            return Functions::VALUE();
1039 12
        }
1040 12
        $startDate = (float) floor($startDate);
1041 4
        $endDays = (int) floor($endDays);
1042 4
        //    If endDays is 0, we always return startDate
1043
        if ($endDays == 0) {
1044
            return $startDate;
1045
        }
1046 12
1047
        $decrementing = ($endDays < 0) ? true : false;
1048
1049 12
        //    Adjust the start date if it falls over a weekend
1050 12
1051 1
        $startDoW = self::WEEKDAY($startDate, 3);
1052
        if (self::WEEKDAY($startDate, 3) >= 5) {
1053
            $startDate += ($decrementing) ? -$startDoW + 4 : 7 - $startDoW;
1054
            ($decrementing) ? $endDays++ : $endDays--;
1055 12
        }
1056 4
1057 4
        //    Add endDays
1058 4
        $endDate = (float) $startDate + ((int) ($endDays / 5) * 7) + ($endDays % 5);
1059 4
1060
        //    Adjust the calculated end date if it falls over a weekend
1061
        $endDoW = self::WEEKDAY($endDate, 3);
1062 4
        if ($endDoW >= 5) {
1063 4
            $endDate += ($decrementing) ? -$endDoW + 4 : 7 - $endDoW;
1064
        }
1065
1066
        //    Test any extra holiday parameters
1067 4
        if (!empty($dateArgs)) {
1068 1
            $holidayCountedArray = $holidayDates = [];
1069
            foreach ($dateArgs as $holidayDate) {
1070 3
                if (($holidayDate !== null) && (trim($holidayDate) > '')) {
1071
                    if (is_string($holidayDate = self::getDateValue($holidayDate))) {
1072 4
                        return Functions::VALUE();
1073 4
                    }
1074 1
                    if (self::WEEKDAY($holidayDate, 3) < 5) {
1075 1
                        $holidayDates[] = $holidayDate;
1076 1
                    }
1077 1
                }
1078
            }
1079
            if ($decrementing) {
1080
                rsort($holidayDates, SORT_NUMERIC);
1081 3
            } else {
1082 3
                sort($holidayDates, SORT_NUMERIC);
1083 3
            }
1084 3
            foreach ($holidayDates as $holidayDate) {
1085
                if ($decrementing) {
1086 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...
1087
                        if (!in_array($holidayDate, $holidayCountedArray)) {
1088
                            --$endDate;
1089 4
                            $holidayCountedArray[] = $holidayDate;
1090 4
                        }
1091 4
                    }
1092 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...
1093
                    if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) {
1094
                        if (!in_array($holidayDate, $holidayCountedArray)) {
1095
                            ++$endDate;
1096 12
                            $holidayCountedArray[] = $holidayDate;
1097 12
                        }
1098 12
                    }
1099
                }
1100
                //    Adjust the calculated end date if it falls over a weekend
1101
                $endDoW = self::WEEKDAY($endDate, 3);
1102
                if ($endDoW >= 5) {
1103
                    $endDate += ($decrementing) ? -$endDoW + 4 : 7 - $endDoW;
1104
                }
1105
            }
1106
        }
1107
1108 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...
1109
            case Functions::RETURNDATE_EXCEL:
1110
                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 17
     *
1121
     * Returns the day of the month, for a specified date. The day is given as an integer
1122 17
     * ranging from 1 to 31.
1123
     *
1124 17
     * Excel Function:
1125
     *        DAY(dateValue)
1126 17
     *
1127 1
     * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
1128 16
     *                                    PHP DateTime object, or a standard date string
1129
     *
1130 16
     * @return int Day of the month
1131 1
     */
1132
    public static function DAYOFMONTH($dateValue = 1)
1133
    {
1134
        $dateValue = Functions::flattenSingleValue($dateValue);
1135 15
1136
        if ($dateValue === null) {
1137 15
            $dateValue = 1;
1138
        } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
1139
            return Functions::VALUE();
1140
        } elseif ($dateValue == 0.0) {
1141
            return 0;
1142
        } elseif ($dateValue < 0.0) {
1143
            return Functions::NAN();
1144
        }
1145
1146
        // Execute function
1147
        $PHPDateObject = Date::excelToDateTimeObject($dateValue);
1148
1149
        return (int) $PHPDateObject->format('j');
1150
    }
1151
1152
    /**
1153
     * WEEKDAY.
1154
     *
1155
     * Returns the day of the week for a specified date. The day is given as an integer
1156
     * ranging from 0 to 7 (dependent on the requested style).
1157
     *
1158 57
     * Excel Function:
1159
     *        WEEKDAY(dateValue[,style])
1160 57
     *
1161 57
     * @param int $dateValue Excel date serial value (float), PHP date timestamp (integer),
1162
     *                                    PHP DateTime object, or a standard date string
1163 57
     * @param int $style A number that determines the type of return value
1164 1
     *                                        1 or omitted    Numbers 1 (Sunday) through 7 (Saturday).
1165 56
     *                                        2                Numbers 1 (Monday) through 7 (Sunday).
1166 1
     *                                        3                Numbers 0 (Monday) through 6 (Sunday).
1167
     *
1168 55
     * @return int Day of the week value
1169
     */
1170 55
    public static function WEEKDAY($dateValue = 1, $style = 1)
1171
    {
1172 55
        $dateValue = Functions::flattenSingleValue($dateValue);
1173 1
        $style = Functions::flattenSingleValue($style);
1174 54
1175 1
        if (!is_numeric($style)) {
1176
            return Functions::VALUE();
1177
        } elseif (($style < 1) || ($style > 3)) {
1178
            return Functions::NAN();
1179 53
        }
1180 53
        $style = floor($style);
1181
1182 53
        if ($dateValue === null) {
1183
            $dateValue = 1;
1184 53
        } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
1185 8
            return Functions::VALUE();
1186 8
        } elseif ($dateValue < 0.0) {
1187 45
            return Functions::NAN();
1188 26
        }
1189 3
1190
        // Execute function
1191 26
        $PHPDateObject = Date::excelToDateTimeObject($dateValue);
1192 19
        $DoW = $PHPDateObject->format('w');
1193 19
1194 3
        $firstDay = 1;
1195
        switch ($style) {
1196 19
            case 1:
1197 19
                ++$DoW;
1198 19
1199
                break;
1200 53
            case 2:
1201
                if ($DoW == 0) {
1202 53
                    $DoW = 7;
1203 2
                }
1204 2
1205 1
                break;
1206
            case 3:
1207
                if ($DoW == 0) {
1208
                    $DoW = 7;
1209
                }
1210 53
                $firstDay = 0;
1211
                --$DoW;
1212
1213
                break;
1214
        }
1215
        if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_EXCEL) {
1216
            //    Test for Excel's 1900 leap year, and introduce the error as required
1217
            if (($PHPDateObject->format('Y') == 1900) && ($PHPDateObject->format('n') <= 2)) {
1218
                --$DoW;
1219
                if ($DoW < $firstDay) {
1220
                    $DoW += 7;
1221
                }
1222
            }
1223
        }
1224
1225
        return (int) $DoW;
1226
    }
1227
1228
    /**
1229
     * WEEKNUM.
1230
     *
1231
     * Returns the week of the year for a specified date.
1232
     * The WEEKNUM function considers the week containing January 1 to be the first week of the year.
1233
     * However, there is a European standard that defines the first week as the one with the majority
1234 15
     * of days (four or more) falling in the new year. This means that for years in which there are
1235
     * three days or less in the first week of January, the WEEKNUM function returns week numbers
1236 15
     * that are incorrect according to the European standard.
1237 15
     *
1238
     * Excel Function:
1239 15
     *        WEEKNUM(dateValue[,style])
1240 1
     *
1241 14
     * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
1242 1
     *                                    PHP DateTime object, or a standard date string
1243
     * @param int $method Week begins on Sunday or Monday
1244 13
     *                                        1 or omitted    Week begins on Sunday.
1245
     *                                        2                Week begins on Monday.
1246 13
     *
1247
     * @return int Week Number
1248 13
     */
1249 1
    public static function WEEKNUM($dateValue = 1, $method = 1)
1250 12
    {
1251 1
        $dateValue = Functions::flattenSingleValue($dateValue);
1252
        $method = Functions::flattenSingleValue($method);
1253
1254
        if (!is_numeric($method)) {
1255 11
            return Functions::VALUE();
1256 11
        } elseif (($method < 1) || ($method > 2)) {
1257 11
            return Functions::NAN();
1258 11
        }
1259 11
        $method = floor($method);
1260 11
1261 11
        if ($dateValue === null) {
1262
            $dateValue = 1;
1263 11
        } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
1264 8
            return Functions::VALUE();
1265
        } elseif ($dateValue < 0.0) {
1266
            return Functions::NAN();
1267 11
        }
1268
1269
        // Execute function
1270
        $PHPDateObject = Date::excelToDateTimeObject($dateValue);
1271
        $dayOfYear = $PHPDateObject->format('z');
1272
        $PHPDateObject->modify('-' . $dayOfYear . ' days');
1273
        $firstDayOfFirstWeek = $PHPDateObject->format('w');
1274
        $daysInFirstWeek = (6 - $firstDayOfFirstWeek + $method) % 7;
1275
        $interval = $dayOfYear - $daysInFirstWeek;
1276
        $weekOfYear = floor($interval / 7) + 1;
1277
1278
        if ($daysInFirstWeek) {
1279
            ++$weekOfYear;
1280
        }
1281
1282
        return (int) $weekOfYear;
1283
    }
1284 21
1285
    /**
1286 21
     * MONTHOFYEAR.
1287
     *
1288 21
     * Returns the month of a date represented by a serial number.
1289 2
     * The month is given as an integer, ranging from 1 (January) to 12 (December).
1290
     *
1291 21
     * Excel Function:
1292 1
     *        MONTH(dateValue)
1293 20
     *
1294 1
     * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
1295
     *                                    PHP DateTime object, or a standard date string
1296
     *
1297
     * @return int Month of the year
1298 19
     */
1299 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...
1300 19
    {
1301
        $dateValue = Functions::flattenSingleValue($dateValue);
1302
1303
        if (empty($dateValue)) {
1304
            $dateValue = 1;
1305
        }
1306
        if (is_string($dateValue = self::getDateValue($dateValue))) {
1307
            return Functions::VALUE();
1308
        } elseif ($dateValue < 0.0) {
1309
            return Functions::NAN();
1310
        }
1311
1312
        // Execute function
1313
        $PHPDateObject = Date::excelToDateTimeObject($dateValue);
1314
1315
        return (int) $PHPDateObject->format('n');
1316
    }
1317 33
1318
    /**
1319 33
     * YEAR.
1320
     *
1321 33
     * Returns the year corresponding to a date.
1322 1
     * The year is returned as an integer in the range 1900-9999.
1323 32
     *
1324 1
     * Excel Function:
1325 31
     *        YEAR(dateValue)
1326 1
     *
1327
     * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
1328
     *                                    PHP DateTime object, or a standard date string
1329
     *
1330 31
     * @return int Year
1331
     */
1332 31 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...
1333
    {
1334
        $dateValue = Functions::flattenSingleValue($dateValue);
1335
1336
        if ($dateValue === null) {
1337
            $dateValue = 1;
1338
        } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
1339
            return Functions::VALUE();
1340
        } elseif ($dateValue < 0.0) {
1341
            return Functions::NAN();
1342
        }
1343
1344
        // Execute function
1345
        $PHPDateObject = Date::excelToDateTimeObject($dateValue);
1346
1347
        return (int) $PHPDateObject->format('Y');
1348
    }
1349 12
1350
    /**
1351 12
     * HOUROFDAY.
1352
     *
1353 12
     * Returns the hour of a time value.
1354 4
     * The hour is given as an integer, ranging from 0 (12:00 A.M.) to 23 (11:00 P.M.).
1355
     *
1356
     * Excel Function:
1357
     *        HOUR(timeValue)
1358
     *
1359
     * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
1360 4
     *                                    PHP DateTime object, or a standard time string
1361 4
     *
1362 1
     * @return int Hour
1363
     */
1364 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...
1365
    {
1366 11
        $timeValue = Functions::flattenSingleValue($timeValue);
1367 3
1368 8
        if (!is_numeric($timeValue)) {
1369 1
            if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
1370
                $testVal = strtok($timeValue, '/-: ');
1371 10
                if (strlen($testVal) < strlen($timeValue)) {
1372
                    return Functions::VALUE();
1373 10
                }
1374
            }
1375
            $timeValue = self::getTimeValue($timeValue);
0 ignored issues
show
Bug introduced by
It seems like $timeValue defined by self::getTimeValue($timeValue) on line 1375 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...
1376
            if (is_string($timeValue)) {
1377
                return Functions::VALUE();
1378
            }
1379
        }
1380
        // Execute function
1381
        if ($timeValue >= 1) {
1382
            $timeValue = fmod($timeValue, 1);
1383
        } elseif ($timeValue < 0.0) {
1384
            return Functions::NAN();
1385
        }
1386
        $timeValue = Date::excelToTimestamp($timeValue);
1387
1388
        return (int) gmdate('G', $timeValue);
1389
    }
1390 12
1391
    /**
1392 12
     * MINUTE.
1393
     *
1394 12
     * Returns the minutes of a time value.
1395 4
     * The minute is given as an integer, ranging from 0 to 59.
1396
     *
1397
     * Excel Function:
1398
     *        MINUTE(timeValue)
1399
     *
1400
     * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
1401 4
     *                                    PHP DateTime object, or a standard time string
1402 4
     *
1403 1
     * @return int Minute
1404
     */
1405 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...
1406
    {
1407 11
        $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...
1408 3
1409 8
        if (!is_numeric($timeValue)) {
1410 1
            if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
1411
                $testVal = strtok($timeValue, '/-: ');
1412 10
                if (strlen($testVal) < strlen($timeValue)) {
1413
                    return Functions::VALUE();
1414 10
                }
1415
            }
1416
            $timeValue = self::getTimeValue($timeValue);
0 ignored issues
show
Bug introduced by
It seems like $timeValue defined by self::getTimeValue($timeValue) on line 1416 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...
1417
            if (is_string($timeValue)) {
1418
                return Functions::VALUE();
1419
            }
1420
        }
1421
        // Execute function
1422
        if ($timeValue >= 1) {
1423
            $timeValue = fmod($timeValue, 1);
1424
        } elseif ($timeValue < 0.0) {
1425
            return Functions::NAN();
1426
        }
1427
        $timeValue = Date::excelToTimestamp($timeValue);
1428
1429
        return (int) gmdate('i', $timeValue);
1430
    }
1431 12
1432
    /**
1433 12
     * SECOND.
1434
     *
1435 12
     * Returns the seconds of a time value.
1436 4
     * The second is given as an integer in the range 0 (zero) to 59.
1437
     *
1438
     * Excel Function:
1439
     *        SECOND(timeValue)
1440
     *
1441
     * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
1442 4
     *                                    PHP DateTime object, or a standard time string
1443 4
     *
1444 1
     * @return int Second
1445
     */
1446 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...
1447
    {
1448 11
        $timeValue = Functions::flattenSingleValue($timeValue);
1449 3
1450 8
        if (!is_numeric($timeValue)) {
1451 1
            if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
1452
                $testVal = strtok($timeValue, '/-: ');
1453 10
                if (strlen($testVal) < strlen($timeValue)) {
1454
                    return Functions::VALUE();
1455 10
                }
1456
            }
1457
            $timeValue = self::getTimeValue($timeValue);
0 ignored issues
show
Bug introduced by
It seems like $timeValue defined by self::getTimeValue($timeValue) on line 1457 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...
1458
            if (is_string($timeValue)) {
1459
                return Functions::VALUE();
1460
            }
1461
        }
1462
        // Execute function
1463
        if ($timeValue >= 1) {
1464
            $timeValue = fmod($timeValue, 1);
1465
        } elseif ($timeValue < 0.0) {
1466
            return Functions::NAN();
1467
        }
1468
        $timeValue = Date::excelToTimestamp($timeValue);
1469
1470
        return (int) gmdate('s', $timeValue);
1471
    }
1472
1473
    /**
1474
     * EDATE.
1475
     *
1476
     * Returns the serial number that represents the date that is the indicated number of months
1477
     * before or after a specified date (the start_date).
1478 17
     * Use EDATE to calculate maturity dates or due dates that fall on the same day of the month
1479
     * as the date of issue.
1480 17
     *
1481 17
     * Excel Function:
1482
     *        EDATE(dateValue,adjustmentMonths)
1483 17
     *
1484 1
     * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
1485
     *                                        PHP DateTime object, or a standard date string
1486 16
     * @param int $adjustmentMonths The number of months before or after start_date.
1487
     *                                        A positive value for months yields a future date;
1488 16
     *                                        a negative value yields a past date.
1489 1
     *
1490
     * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
1491
     *                        depending on the value of the ReturnDateType flag
1492
     */
1493 15
    public static function EDATE($dateValue = 1, $adjustmentMonths = 0)
1494
    {
1495 15
        $dateValue = Functions::flattenSingleValue($dateValue);
1496 15
        $adjustmentMonths = Functions::flattenSingleValue($adjustmentMonths);
1497 13
1498 2
        if (!is_numeric($adjustmentMonths)) {
1499 1
            return Functions::VALUE();
1500 1
        }
1501 1
        $adjustmentMonths = floor($adjustmentMonths);
1502
1503
        if (is_string($dateValue = self::getDateValue($dateValue))) {
1504
            return Functions::VALUE();
1505
        }
1506
1507
        // Execute function
1508
        $PHPDateObject = self::adjustDateByMonths($dateValue, $adjustmentMonths);
1509
1510 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...
1511
            case Functions::RETURNDATE_EXCEL:
1512
                return (float) Date::PHPToExcel($PHPDateObject);
1513
            case Functions::RETURNDATE_PHP_NUMERIC:
1514
                return (int) Date::excelToTimestamp(Date::PHPToExcel($PHPDateObject));
0 ignored issues
show
Security Bug introduced by
It seems like \PhpOffice\PhpSpreadshee...ToExcel($PHPDateObject) targeting PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel() can also be of type false; however, PhpOffice\PhpSpreadsheet...ate::excelToTimestamp() does only seem to accept double|integer, did you maybe forget to handle an error condition?
Loading history...
1515
            case Functions::RETURNDATE_PHP_OBJECT:
1516
                return $PHPDateObject;
1517
        }
1518
    }
1519
1520
    /**
1521
     * EOMONTH.
1522
     *
1523
     * Returns the date value for the last day of the month that is the indicated number of months
1524 19
     * before or after start_date.
1525
     * Use EOMONTH to calculate maturity dates or due dates that fall on the last day of the month.
1526 19
     *
1527 19
     * Excel Function:
1528
     *        EOMONTH(dateValue,adjustmentMonths)
1529 19
     *
1530 1
     * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
1531
     *                                        PHP DateTime object, or a standard date string
1532 18
     * @param int $adjustmentMonths The number of months before or after start_date.
1533
     *                                        A positive value for months yields a future date;
1534 18
     *                                        a negative value yields a past date.
1535 1
     *
1536
     * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
1537
     *                        depending on the value of the ReturnDateType flag
1538
     */
1539 17
    public static function EOMONTH($dateValue = 1, $adjustmentMonths = 0)
1540 17
    {
1541 17
        $dateValue = Functions::flattenSingleValue($dateValue);
1542 17
        $adjustmentMonths = Functions::flattenSingleValue($adjustmentMonths);
1543
1544 17
        if (!is_numeric($adjustmentMonths)) {
1545 17
            return Functions::VALUE();
1546 15
        }
1547 2
        $adjustmentMonths = floor($adjustmentMonths);
1548 1
1549 1
        if (is_string($dateValue = self::getDateValue($dateValue))) {
1550 1
            return Functions::VALUE();
1551
        }
1552
1553
        // Execute function
1554
        $PHPDateObject = self::adjustDateByMonths($dateValue, $adjustmentMonths + 1);
1555
        $adjustDays = (int) $PHPDateObject->format('d');
1556
        $adjustDaysString = '-' . $adjustDays . ' days';
1557
        $PHPDateObject->modify($adjustDaysString);
1558
1559 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...
1560
            case Functions::RETURNDATE_EXCEL:
1561
                return (float) Date::PHPToExcel($PHPDateObject);
1562
            case Functions::RETURNDATE_PHP_NUMERIC:
1563
                return (int) Date::excelToTimestamp(Date::PHPToExcel($PHPDateObject));
0 ignored issues
show
Security Bug introduced by
It seems like \PhpOffice\PhpSpreadshee...ToExcel($PHPDateObject) targeting PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel() can also be of type false; however, PhpOffice\PhpSpreadsheet...ate::excelToTimestamp() does only seem to accept double|integer, did you maybe forget to handle an error condition?
Loading history...
1564
            case Functions::RETURNDATE_PHP_OBJECT:
1565
                return $PHPDateObject;
1566
        }
1567
    }
1568
}
1569