Completed
Push — develop ( 10da9b...962915 )
by Adrien
34:52
created

DateTime::EOMONTH()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 29
Code Lines 19

Duplication

Lines 8
Ratio 27.59 %

Code Coverage

Tests 19
CRAP Score 6.0045

Importance

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

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

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

Loading history...
331 75
            case Functions::RETURNDATE_EXCEL:
332 73
                return (float) $excelDateValue;
333 2
            case Functions::RETURNDATE_PHP_NUMERIC:
334 1
                return (integer) \PhpOffice\PhpSpreadsheet\Shared\Date::excelToTimestamp($excelDateValue);
335 1
            case Functions::RETURNDATE_PHP_OBJECT:
336 1
                return \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($excelDateValue);
337
        }
338
    }
339
340
    /**
341
     * TIME
342
     *
343
     * The TIME function returns a value that represents a particular time.
344
     *
345
     * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the time
346
     * format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
347
     *
348
     * Excel Function:
349
     *        TIME(hour,minute,second)
350
     *
351
     * @category Date/Time Functions
352
     * @param    int        $hour        A number from 0 (zero) to 32767 representing the hour.
353
     *                                    Any value greater than 23 will be divided by 24 and the remainder
354
     *                                    will be treated as the hour value. For example, TIME(27,0,0) =
355
     *                                    TIME(3,0,0) = .125 or 3:00 AM.
356
     * @param    int        $minute        A number from 0 to 32767 representing the minute.
357
     *                                    Any value greater than 59 will be converted to hours and minutes.
358
     *                                    For example, TIME(0,750,0) = TIME(12,30,0) = .520833 or 12:30 PM.
359
     * @param    int        $second        A number from 0 to 32767 representing the second.
360
     *                                    Any value greater than 59 will be converted to hours, minutes,
361
     *                                    and seconds. For example, TIME(0,0,2000) = TIME(0,33,22) = .023148
362
     *                                    or 12:33:20 AM
363
     * @return    mixed    Excel date/time serial value, PHP date/time serial value or PHP date/time object,
364
     *                        depending on the value of the ReturnDateType flag
365
     */
366 23
    public static function TIME($hour = 0, $minute = 0, $second = 0)
367
    {
368 23
        $hour = Functions::flattenSingleValue($hour);
369 23
        $minute = Functions::flattenSingleValue($minute);
370 23
        $second = Functions::flattenSingleValue($second);
371
372 23
        if ($hour == '') {
373
            $hour = 0;
374
        }
375 23
        if ($minute == '') {
376 5
            $minute = 0;
377
        }
378 23
        if ($second == '') {
379 5
            $second = 0;
380
        }
381
382 23
        if ((!is_numeric($hour)) || (!is_numeric($minute)) || (!is_numeric($second))) {
383 1
            return Functions::VALUE();
384
        }
385 22
        $hour = (integer) $hour;
386 22
        $minute = (integer) $minute;
387 22
        $second = (integer) $second;
388
389 22 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...
390 4
            $minute += floor($second / 60);
391 4
            $second = 60 - abs($second % 60);
392 4
            if ($second == 60) {
393 4
                $second = 0;
394
            }
395 18
        } elseif ($second >= 60) {
396 1
            $minute += floor($second / 60);
397 1
            $second = $second % 60;
398
        }
399 22 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...
400 7
            $hour += floor($minute / 60);
401 7
            $minute = 60 - abs($minute % 60);
402 7
            if ($minute == 60) {
403 7
                $minute = 0;
404
            }
405 15
        } elseif ($minute >= 60) {
406 3
            $hour += floor($minute / 60);
407 3
            $minute = $minute % 60;
408
        }
409
410 22
        if ($hour > 23) {
411 1
            $hour = $hour % 24;
412 21
        } elseif ($hour < 0) {
413 2
            return Functions::NAN();
414
        }
415
416
        // Execute function
417 20
        switch (Functions::getReturnDateType()) {
418 20
            case Functions::RETURNDATE_EXCEL:
419 19
                $date = 0;
420 19
                $calendar = \PhpOffice\PhpSpreadsheet\Shared\Date::getExcelCalendar();
421 19
                if ($calendar != \PhpOffice\PhpSpreadsheet\Shared\Date::CALENDAR_WINDOWS_1900) {
422
                    $date = 1;
423
                }
424
425 19
                return (float) \PhpOffice\PhpSpreadsheet\Shared\Date::formattedPHPToExcel($calendar, 1, $date, $hour, $minute, $second);
426 1
            case Functions::RETURNDATE_PHP_NUMERIC:
427
                return (integer) \PhpOffice\PhpSpreadsheet\Shared\Date::excelToTimestamp(\PhpOffice\PhpSpreadsheet\Shared\Date::formattedPHPToExcel(1970, 1, 1, $hour, $minute, $second)); // -2147468400; //    -2147472000 + 3600
428 1
            case Functions::RETURNDATE_PHP_OBJECT:
429 1
                $dayAdjust = 0;
430 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...
431
                    $dayAdjust = floor($hour / 24);
432
                    $hour = 24 - abs($hour % 24);
433
                    if ($hour == 24) {
434
                        $hour = 0;
435
                    }
436 1
                } elseif ($hour >= 24) {
437
                    $dayAdjust = floor($hour / 24);
438
                    $hour = $hour % 24;
439
                }
440 1
                $phpDateObject = new \DateTime('1900-01-01 ' . $hour . ':' . $minute . ':' . $second);
441 1
                if ($dayAdjust != 0) {
442
                    $phpDateObject->modify($dayAdjust . ' days');
443
                }
444
445 1
                return $phpDateObject;
446
        }
447
    }
448
449
    /**
450
     * DATEVALUE
451
     *
452
     * Returns a value that represents a particular date.
453
     * Use DATEVALUE to convert a date represented by a text string to an Excel or PHP date/time stamp
454
     * value.
455
     *
456
     * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
457
     * format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
458
     *
459
     * Excel Function:
460
     *        DATEVALUE(dateValue)
461
     *
462
     * @category Date/Time Functions
463
     * @param    string    $dateValue        Text that represents a date in a Microsoft Excel date format.
464
     *                                    For example, "1/30/2008" or "30-Jan-2008" are text strings within
465
     *                                    quotation marks that represent dates. Using the default date
466
     *                                    system in Excel for Windows, date_text must represent a date from
467
     *                                    January 1, 1900, to December 31, 9999. Using the default date
468
     *                                    system in Excel for the Macintosh, date_text must represent a date
469
     *                                    from January 1, 1904, to December 31, 9999. DATEVALUE returns the
470
     *                                    #VALUE! error value if date_text is out of this range.
471
     * @return    mixed    Excel date/time serial value, PHP date/time serial value or PHP date/time object,
472
     *                        depending on the value of the ReturnDateType flag
473
     */
474 236
    public static function DATEVALUE($dateValue = 1)
475
    {
476 236
        $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...
477 236
        $dateValue = trim(Functions::flattenSingleValue($dateValue), '"');
478
        //    Strip any ordinals because they're allowed in Excel (English only)
479 236
        $dateValue = preg_replace('/(\d)(st|nd|rd|th)([ -\/])/Ui', '$1$3', $dateValue);
480
        //    Convert separators (/ . or space) to hyphens (should also handle dot used for ordinals in some countries, e.g. Denmark, Germany)
481 236
        $dateValue = str_replace(['/', '.', '-', '  '], [' ', ' ', ' ', ' '], $dateValue);
482
483 236
        $yearFound = false;
484 236
        $t1 = explode(' ', $dateValue);
485 236
        foreach ($t1 as &$t) {
486 236
            if ((is_numeric($t)) && ($t > 31)) {
487 205
                if ($yearFound) {
488
                    return Functions::VALUE();
489
                } else {
490 205
                    if ($t < 100) {
491 2
                        $t += 1900;
492
                    }
493 236
                    $yearFound = true;
494
                }
495
            }
496
        }
497 236
        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...
498
            //    We've been fed a time value without any date
499 1
            return 0.0;
500 235
        } elseif (count($t1) == 2) {
501
            //    We only have two parts of the date: either day/month or month/year
502 27
            if ($yearFound) {
503 3
                array_unshift($t1, 1);
504
            } else {
505 24
                if ($t1[1] > 29) {
506 1
                    $t1[1] += 1900;
507 1
                    array_unshift($t1, 1);
508
                } else {
509 23
                    array_push($t1, date('Y'));
510
                }
511
            }
512
        }
513 235
        unset($t);
514 235
        $dateValue = implode(' ', $t1);
515
516 235
        $PHPDateArray = date_parse($dateValue);
517 235
        if (($PHPDateArray === false) || ($PHPDateArray['error_count'] > 0)) {
518 132
            $testVal1 = strtok($dateValue, '- ');
519 132
            if ($testVal1 !== false) {
520 131
                $testVal2 = strtok('- ');
521 131
                if ($testVal2 !== false) {
522 119
                    $testVal3 = strtok('- ');
523 119
                    if ($testVal3 === false) {
524 119
                        $testVal3 = strftime('%Y');
525
                    }
526
                } else {
527 131
                    return Functions::VALUE();
528
                }
529
            } else {
530 1
                return Functions::VALUE();
531
            }
532 119
            if ($testVal1 < 31 && $testVal2 < 12 && $testVal3 < 12 && strlen($testVal3) == 2) {
533 1
                $testVal3 += 2000;
534
            }
535 119
            $PHPDateArray = date_parse($testVal1 . '-' . $testVal2 . '-' . $testVal3);
536 119
            if (($PHPDateArray === false) || ($PHPDateArray['error_count'] > 0)) {
537 25
                $PHPDateArray = date_parse($testVal2 . '-' . $testVal1 . '-' . $testVal3);
538 25
                if (($PHPDateArray === false) || ($PHPDateArray['error_count'] > 0)) {
539 22
                    return Functions::VALUE();
540
                }
541
            }
542
        }
543
544 206
        if (($PHPDateArray !== false) && ($PHPDateArray['error_count'] == 0)) {
545
            // Execute function
546 206
            if ($PHPDateArray['year'] == '') {
547
                $PHPDateArray['year'] = strftime('%Y');
548
            }
549 206
            if ($PHPDateArray['year'] < 1900) {
550 2
                return Functions::VALUE();
551
            }
552 204
            if ($PHPDateArray['month'] == '') {
553
                $PHPDateArray['month'] = strftime('%m');
554
            }
555 204
            if ($PHPDateArray['day'] == '') {
556
                $PHPDateArray['day'] = strftime('%d');
557
            }
558 204
            if (!checkdate($PHPDateArray['month'], $PHPDateArray['day'], $PHPDateArray['year'])) {
559 3
                return Functions::VALUE();
560
            }
561 201
            $excelDateValue = floor(
562 201
                \PhpOffice\PhpSpreadsheet\Shared\Date::formattedPHPToExcel(
563 201
                    $PHPDateArray['year'],
564 201
                    $PHPDateArray['month'],
565 201
                    $PHPDateArray['day'],
566 201
                    $PHPDateArray['hour'],
567 201
                    $PHPDateArray['minute'],
568 201
                    $PHPDateArray['second']
569
                )
570
            );
571 201 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...
572 201
                case Functions::RETURNDATE_EXCEL:
573 199
                    return (float) $excelDateValue;
574 2
                case Functions::RETURNDATE_PHP_NUMERIC:
575 1
                    return (integer) \PhpOffice\PhpSpreadsheet\Shared\Date::excelToTimestamp($excelDateValue);
576 1
                case Functions::RETURNDATE_PHP_OBJECT:
577 1
                    return new \DateTime($PHPDateArray['year'] . '-' . $PHPDateArray['month'] . '-' . $PHPDateArray['day'] . ' 00:00:00');
578
            }
579
        }
580
581
        return Functions::VALUE();
582
    }
583
584
    /**
585
     * TIMEVALUE
586
     *
587
     * Returns a value that represents a particular time.
588
     * Use TIMEVALUE to convert a time represented by a text string to an Excel or PHP date/time stamp
589
     * value.
590
     *
591
     * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the time
592
     * format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
593
     *
594
     * Excel Function:
595
     *        TIMEVALUE(timeValue)
596
     *
597
     * @category Date/Time Functions
598
     * @param    string    $timeValue        A text string that represents a time in any one of the Microsoft
599
     *                                    Excel time formats; for example, "6:45 PM" and "18:45" text strings
600
     *                                    within quotation marks that represent time.
601
     *                                    Date information in time_text is ignored.
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 21
    public static function TIMEVALUE($timeValue)
606
    {
607 21
        $timeValue = trim(Functions::flattenSingleValue($timeValue), '"');
608 21
        $timeValue = str_replace(['/', '.'], ['-', '-'], $timeValue);
609
610 21
        $arraySplit = preg_split('/[\/:\-\s]/', $timeValue);
611 21
        if ((count($arraySplit) == 2 || count($arraySplit) == 3) && $arraySplit[0] > 24) {
612 1
            $arraySplit[0] = ($arraySplit[0] % 24);
613 1
            $timeValue = implode(':', $arraySplit);
614
        }
615
616 21
        $PHPDateArray = date_parse($timeValue);
617 21
        if (($PHPDateArray !== false) && ($PHPDateArray['error_count'] == 0)) {
618 18
            if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
619
                $excelDateValue = \PhpOffice\PhpSpreadsheet\Shared\Date::formattedPHPToExcel(
620
                    $PHPDateArray['year'],
621
                    $PHPDateArray['month'],
622
                    $PHPDateArray['day'],
623
                    $PHPDateArray['hour'],
624
                    $PHPDateArray['minute'],
625
                    $PHPDateArray['second']
626
                );
627
            } else {
628 18
                $excelDateValue = \PhpOffice\PhpSpreadsheet\Shared\Date::formattedPHPToExcel(1900, 1, 1, $PHPDateArray['hour'], $PHPDateArray['minute'], $PHPDateArray['second']) - 1;
629
            }
630
631 18 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 18
                case Functions::RETURNDATE_EXCEL:
633 17
                    return (float) $excelDateValue;
634 1
                case Functions::RETURNDATE_PHP_NUMERIC:
635
                    return (integer) $phpDateValue = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToTimestamp($excelDateValue + 25569) - 3600;
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 3
        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
     * @return    int    Interval between the dates
653
     */
654 10
    public static function DATEDIF($startDate = 0, $endDate = 0, $unit = 'D')
655
    {
656 10
        $startDate = Functions::flattenSingleValue($startDate);
657 10
        $endDate = Functions::flattenSingleValue($endDate);
658 10
        $unit = strtoupper(Functions::flattenSingleValue($unit));
659
660 10
        if (is_string($startDate = self::getDateValue($startDate))) {
661
            return Functions::VALUE();
662
        }
663 10
        if (is_string($endDate = self::getDateValue($endDate))) {
664
            return Functions::VALUE();
665
        }
666
667
        // Validate parameters
668 10
        if ($startDate >= $endDate) {
669
            return Functions::NAN();
670
        }
671
672
        // Execute function
673 10
        $difference = $endDate - $startDate;
674
675 10
        $PHPStartDateObject = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($startDate);
676 10
        $startDays = $PHPStartDateObject->format('j');
677 10
        $startMonths = $PHPStartDateObject->format('n');
678 10
        $startYears = $PHPStartDateObject->format('Y');
679
680 10
        $PHPEndDateObject = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($endDate);
681 10
        $endDays = $PHPEndDateObject->format('j');
682 10
        $endMonths = $PHPEndDateObject->format('n');
683 10
        $endYears = $PHPEndDateObject->format('Y');
684
685 10
        $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...
686
        switch ($unit) {
687 10
            case 'D':
688 10
                $retVal = intval($difference);
689 10
                break;
690 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...
691
                $retVal = intval($endMonths - $startMonths) + (intval($endYears - $startYears) * 12);
692
                //    We're only interested in full months
693
                if ($endDays < $startDays) {
694
                    --$retVal;
695
                }
696
                break;
697
            case 'Y':
698
                $retVal = intval($endYears - $startYears);
699
                //    We're only interested in full months
700
                if ($endMonths < $startMonths) {
701
                    --$retVal;
702
                } elseif (($endMonths == $startMonths) && ($endDays < $startDays)) {
703
                    // Remove start month
704
                    --$retVal;
705
                    // Remove end month
706
                    --$retVal;
707
                }
708
                break;
709
            case 'MD':
710
                if ($endDays < $startDays) {
711
                    $retVal = $endDays;
712
                    $PHPEndDateObject->modify('-' . $endDays . ' days');
713
                    $adjustDays = $PHPEndDateObject->format('j');
714
                    $retVal += ($adjustDays - $startDays);
715
                } else {
716
                    $retVal = $endDays - $startDays;
717
                }
718
                break;
719 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...
720
                $retVal = intval($endMonths - $startMonths);
721
                if ($retVal < 0) {
722
                    $retVal += 12;
723
                }
724
                //    We're only interested in full months
725
                if ($endDays < $startDays) {
726
                    --$retVal;
727
                }
728
                break;
729
            case 'YD':
730
                $retVal = intval($difference);
731
                if ($endYears > $startYears) {
732
                    while ($endYears > $startYears) {
733
                        $PHPEndDateObject->modify('-1 year');
734
                        $endYears = $PHPEndDateObject->format('Y');
735
                    }
736
                    $retVal = $PHPEndDateObject->format('z') - $PHPStartDateObject->format('z');
737
                    if ($retVal < 0) {
738
                        $retVal += 365;
739
                    }
740
                }
741
                break;
742
            default:
743
                $retVal = Functions::VALUE();
744
        }
745
746 10
        return $retVal;
747
    }
748
749
    /**
750
     * DAYS360
751
     *
752
     * Returns the number of days between two dates based on a 360-day year (twelve 30-day months),
753
     * which is used in some accounting calculations. Use this function to help compute payments if
754
     * your accounting system is based on twelve 30-day months.
755
     *
756
     * Excel Function:
757
     *        DAYS360(startDate,endDate[,method])
758
     *
759
     * @category Date/Time Functions
760
     * @param    mixed        $startDate        Excel date serial value (float), PHP date timestamp (integer),
761
     *                                        PHP DateTime object, or a standard date string
762
     * @param    mixed        $endDate        Excel date serial value (float), PHP date timestamp (integer),
763
     *                                        PHP DateTime object, or a standard date string
764
     * @param    bool        $method            US or European Method
765
     *                                        FALSE or omitted: U.S. (NASD) method. If the starting date is
766
     *                                        the last day of a month, it becomes equal to the 30th of the
767
     *                                        same month. If the ending date is the last day of a month and
768
     *                                        the starting date is earlier than the 30th of a month, the
769
     *                                        ending date becomes equal to the 1st of the next month;
770
     *                                        otherwise the ending date becomes equal to the 30th of the
771
     *                                        same month.
772
     *                                        TRUE: European method. Starting dates and ending dates that
773
     *                                        occur on the 31st of a month become equal to the 30th of the
774
     *                                        same month.
775
     * @return    int        Number of days between start date and end date
776
     */
777 46
    public static function DAYS360($startDate = 0, $endDate = 0, $method = false)
778
    {
779 46
        $startDate = Functions::flattenSingleValue($startDate);
780 46
        $endDate = Functions::flattenSingleValue($endDate);
781
782 46
        if (is_string($startDate = self::getDateValue($startDate))) {
783 1
            return Functions::VALUE();
784
        }
785 45
        if (is_string($endDate = self::getDateValue($endDate))) {
786 1
            return Functions::VALUE();
787
        }
788
789 44
        if (!is_bool($method)) {
790 2
            return Functions::VALUE();
791
        }
792
793
        // Execute function
794 42
        $PHPStartDateObject = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($startDate);
795 42
        $startDay = $PHPStartDateObject->format('j');
796 42
        $startMonth = $PHPStartDateObject->format('n');
797 42
        $startYear = $PHPStartDateObject->format('Y');
798
799 42
        $PHPEndDateObject = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($endDate);
800 42
        $endDay = $PHPEndDateObject->format('j');
801 42
        $endMonth = $PHPEndDateObject->format('n');
802 42
        $endYear = $PHPEndDateObject->format('Y');
803
804 42
        return self::dateDiff360($startDay, $startMonth, $startYear, $endDay, $endMonth, $endYear, !$method);
805
    }
806
807
    /**
808
     * YEARFRAC
809
     *
810
     * Calculates the fraction of the year represented by the number of whole days between two dates
811
     * (the start_date and the end_date).
812
     * Use the YEARFRAC worksheet function to identify the proportion of a whole year's benefits or
813
     * obligations to assign to a specific term.
814
     *
815
     * Excel Function:
816
     *        YEARFRAC(startDate,endDate[,method])
817
     *
818
     * @category Date/Time Functions
819
     * @param    mixed    $startDate        Excel date serial value (float), PHP date timestamp (integer),
820
     *                                    PHP DateTime object, or a standard date string
821
     * @param    mixed    $endDate        Excel date serial value (float), PHP date timestamp (integer),
822
     *                                    PHP DateTime object, or a standard date string
823
     * @param    int    $method            Method used for the calculation
824
     *                                        0 or omitted    US (NASD) 30/360
825
     *                                        1                Actual/actual
826
     *                                        2                Actual/360
827
     *                                        3                Actual/365
828
     *                                        4                European 30/360
829
     * @return    float    fraction of the year
830
     */
831 25
    public static function YEARFRAC($startDate = 0, $endDate = 0, $method = 0)
832
    {
833 25
        $startDate = Functions::flattenSingleValue($startDate);
834 25
        $endDate = Functions::flattenSingleValue($endDate);
835 25
        $method = Functions::flattenSingleValue($method);
836
837 25
        if (is_string($startDate = self::getDateValue($startDate))) {
838 4
            return Functions::VALUE();
839
        }
840 21
        if (is_string($endDate = self::getDateValue($endDate))) {
841
            return Functions::VALUE();
842
        }
843
844 21
        if (((is_numeric($method)) && (!is_string($method))) || ($method == '')) {
845
            switch ($method) {
846 20
                case 0:
847 9
                    return self::DAYS360($startDate, $endDate) / 360;
848 11
                case 1:
849 7
                    $days = self::DATEDIF($startDate, $endDate);
850 7
                    $startYear = self::YEAR($startDate);
851 7
                    $endYear = self::YEAR($endDate);
852 7
                    $years = $endYear - $startYear + 1;
853 7
                    $leapDays = 0;
854 7
                    if ($years == 1) {
855 4
                        if (self::isLeapYear($endYear)) {
856 2
                            $startMonth = self::MONTHOFYEAR($startDate);
857 2
                            $endMonth = self::MONTHOFYEAR($endDate);
858 2
                            $endDay = self::DAYOFMONTH($endDate);
859 2
                            if (($startMonth < 3) ||
860 2
                                (($endMonth * 100 + $endDay) >= (2 * 100 + 29))) {
861 4
                                $leapDays += 1;
862
                            }
863
                        }
864
                    } else {
865 3
                        for ($year = $startYear; $year <= $endYear; ++$year) {
866 3
                            if ($year == $startYear) {
867 3
                                $startMonth = self::MONTHOFYEAR($startDate);
868 3
                                $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...
869 3
                                if ($startMonth < 3) {
870 3
                                    $leapDays += (self::isLeapYear($year)) ? 1 : 0;
871
                                }
872 3
                            } elseif ($year == $endYear) {
873 3
                                $endMonth = self::MONTHOFYEAR($endDate);
874 3
                                $endDay = self::DAYOFMONTH($endDate);
875 3
                                if (($endMonth * 100 + $endDay) >= (2 * 100 + 29)) {
876 3
                                    $leapDays += (self::isLeapYear($year)) ? 1 : 0;
877
                                }
878
                            } else {
879 1
                                $leapDays += (self::isLeapYear($year)) ? 1 : 0;
880
                            }
881
                        }
882 3
                        if ($years == 2) {
883 2
                            if (($leapDays == 0) && (self::isLeapYear($startYear)) && ($days > 365)) {
884
                                $leapDays = 1;
885 2
                            } elseif ($days < 366) {
886 1
                                $years = 1;
887
                            }
888
                        }
889 3
                        $leapDays /= $years;
890
                    }
891
892 7
                    return $days / (365 + $leapDays);
893 4
                case 2:
894 1
                    return self::DATEDIF($startDate, $endDate) / 360;
895 3
                case 3:
896 1
                    return self::DATEDIF($startDate, $endDate) / 365;
897 2
                case 4:
898 2
                    return self::DAYS360($startDate, $endDate, true) / 360;
899
            }
900
        }
901
902 1
        return Functions::VALUE();
903
    }
904
905
    /**
906
     * NETWORKDAYS
907
     *
908
     * Returns the number of whole working days between start_date and end_date. Working days
909
     * exclude weekends and any dates identified in holidays.
910
     * Use NETWORKDAYS to calculate employee benefits that accrue based on the number of days
911
     * worked during a specific term.
912
     *
913
     * Excel Function:
914
     *        NETWORKDAYS(startDate,endDate[,holidays[,holiday[,...]]])
915
     *
916
     * @category Date/Time Functions
917
     * @param    mixed            $startDate        Excel date serial value (float), PHP date timestamp (integer),
918
     *                                            PHP DateTime object, or a standard date string
919
     * @param    mixed            $endDate        Excel date serial value (float), PHP date timestamp (integer),
920
     *                                            PHP DateTime object, or a standard date string
921
     * @return    int            Interval between the dates
922
     */
923 18
    public static function NETWORKDAYS($startDate, $endDate)
924
    {
925
        //    Retrieve the mandatory start and end date that are referenced in the function definition
926 18
        $startDate = Functions::flattenSingleValue($startDate);
927 18
        $endDate = Functions::flattenSingleValue($endDate);
928
        //    Flush the mandatory start and end date that are referenced in the function definition, and get the optional days
929 18
        $dateArgs = Functions::flattenArray(func_get_args());
930 18
        array_shift($dateArgs);
931 18
        array_shift($dateArgs);
932
933
        //    Validate the start and end dates
934 18
        if (is_string($startDate = $sDate = self::getDateValue($startDate))) {
935
            return Functions::VALUE();
936
        }
937 18
        $startDate = (float) floor($startDate);
938 18
        if (is_string($endDate = $eDate = self::getDateValue($endDate))) {
939
            return Functions::VALUE();
940
        }
941 18
        $endDate = (float) floor($endDate);
942
943 18
        if ($sDate > $eDate) {
944 2
            $startDate = $eDate;
945 2
            $endDate = $sDate;
946
        }
947
948
        // Execute function
949 18
        $startDoW = 6 - self::DAYOFWEEK($startDate, 2);
950 18
        if ($startDoW < 0) {
951
            $startDoW = 0;
952
        }
953 18
        $endDoW = self::DAYOFWEEK($endDate, 2);
954 18
        if ($endDoW >= 6) {
955 2
            $endDoW = 0;
956
        }
957
958 18
        $wholeWeekDays = floor(($endDate - $startDate) / 7) * 5;
959 18
        $partWeekDays = $endDoW + $startDoW;
960 18
        if ($partWeekDays > 5) {
961 14
            $partWeekDays -= 5;
962
        }
963
964
        //    Test any extra holiday parameters
965 18
        $holidayCountedArray = [];
966 18
        foreach ($dateArgs as $holidayDate) {
967 4
            if (is_string($holidayDate = self::getDateValue($holidayDate))) {
968
                return Functions::VALUE();
969
            }
970 4
            if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) {
971 4
                if ((self::DAYOFWEEK($holidayDate, 2) < 6) && (!in_array($holidayDate, $holidayCountedArray))) {
972 4
                    --$partWeekDays;
973 4
                    $holidayCountedArray[] = $holidayDate;
974
                }
975
            }
976
        }
977
978 18
        if ($sDate > $eDate) {
979 2
            return 0 - ($wholeWeekDays + $partWeekDays);
980
        }
981
982 16
        return $wholeWeekDays + $partWeekDays;
983
    }
984
985
    /**
986
     * WORKDAY
987
     *
988
     * Returns the date that is the indicated number of working days before or after a date (the
989
     * starting date). Working days exclude weekends and any dates identified as holidays.
990
     * Use WORKDAY to exclude weekends or holidays when you calculate invoice due dates, expected
991
     * delivery times, or the number of days of work performed.
992
     *
993
     * Excel Function:
994
     *        WORKDAY(startDate,endDays[,holidays[,holiday[,...]]])
995
     *
996
     * @category Date/Time Functions
997
     * @param    mixed        $startDate        Excel date serial value (float), PHP date timestamp (integer),
998
     *                                        PHP DateTime object, or a standard date string
999
     * @param    int        $endDays        The number of nonweekend and nonholiday days before or after
1000
     *                                        startDate. A positive value for days yields a future date; a
1001
     *                                        negative value yields a past date.
1002
     * @return    mixed    Excel date/time serial value, PHP date/time serial value or PHP date/time object,
1003
     *                        depending on the value of the ReturnDateType flag
1004
     */
1005 13
    public static function WORKDAY($startDate, $endDays)
1006
    {
1007
        //    Retrieve the mandatory start date and days that are referenced in the function definition
1008 13
        $startDate = Functions::flattenSingleValue($startDate);
1009 13
        $endDays = Functions::flattenSingleValue($endDays);
1010
        //    Flush the mandatory start date and days that are referenced in the function definition, and get the optional days
1011 13
        $dateArgs = Functions::flattenArray(func_get_args());
1012 13
        array_shift($dateArgs);
1013 13
        array_shift($dateArgs);
1014
1015 13
        if ((is_string($startDate = self::getDateValue($startDate))) || (!is_numeric($endDays))) {
1016 1
            return Functions::VALUE();
1017
        }
1018 12
        $startDate = (float) floor($startDate);
1019 12
        $endDays = (int) floor($endDays);
1020
        //    If endDays is 0, we always return startDate
1021 12
        if ($endDays == 0) {
1022
            return $startDate;
1023
        }
1024
1025 12
        $decrementing = ($endDays < 0) ? true : false;
1026
1027
        //    Adjust the start date if it falls over a weekend
1028
1029 12
        $startDoW = self::DAYOFWEEK($startDate, 3);
1030 12
        if (self::DAYOFWEEK($startDate, 3) >= 5) {
1031 4
            $startDate += ($decrementing) ? -$startDoW + 4 : 7 - $startDoW;
1032 4
            ($decrementing) ? $endDays++ : $endDays--;
1033
        }
1034
1035
        //    Add endDays
1036 12
        $endDate = (float) $startDate + (intval($endDays / 5) * 7) + ($endDays % 5);
1037
1038
        //    Adjust the calculated end date if it falls over a weekend
1039 12
        $endDoW = self::DAYOFWEEK($endDate, 3);
1040 12
        if ($endDoW >= 5) {
1041 1
            $endDate += ($decrementing) ? -$endDoW + 4 : 7 - $endDoW;
1042
        }
1043
1044
        //    Test any extra holiday parameters
1045 12
        if (!empty($dateArgs)) {
1046 4
            $holidayCountedArray = $holidayDates = [];
1047 4
            foreach ($dateArgs as $holidayDate) {
1048 4
                if (($holidayDate !== null) && (trim($holidayDate) > '')) {
1049 4
                    if (is_string($holidayDate = self::getDateValue($holidayDate))) {
1050
                        return Functions::VALUE();
1051
                    }
1052 4
                    if (self::DAYOFWEEK($holidayDate, 3) < 5) {
1053 4
                        $holidayDates[] = $holidayDate;
1054
                    }
1055
                }
1056
            }
1057 4
            if ($decrementing) {
1058 1
                rsort($holidayDates, SORT_NUMERIC);
1059
            } else {
1060 3
                sort($holidayDates, SORT_NUMERIC);
1061
            }
1062 4
            foreach ($holidayDates as $holidayDate) {
1063 4
                if ($decrementing) {
1064 1 View Code Duplication
                    if (($holidayDate <= $startDate) && ($holidayDate >= $endDate)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1065 1
                        if (!in_array($holidayDate, $holidayCountedArray)) {
1066 1
                            --$endDate;
1067 1
                            $holidayCountedArray[] = $holidayDate;
1068
                        }
1069
                    }
1070 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...
1071 3
                    if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) {
1072 3
                        if (!in_array($holidayDate, $holidayCountedArray)) {
1073 3
                            ++$endDate;
1074 3
                            $holidayCountedArray[] = $holidayDate;
1075
                        }
1076
                    }
1077
                }
1078
                //    Adjust the calculated end date if it falls over a weekend
1079 4
                $endDoW = self::DAYOFWEEK($endDate, 3);
1080 4
                if ($endDoW >= 5) {
1081 4
                    $endDate += ($decrementing) ? -$endDoW + 4 : 7 - $endDoW;
1082
                }
1083
            }
1084
        }
1085
1086 12 View Code Duplication
        switch (Functions::getReturnDateType()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1087 12
            case Functions::RETURNDATE_EXCEL:
1088 12
                return (float) $endDate;
1089
            case Functions::RETURNDATE_PHP_NUMERIC:
1090
                return (integer) \PhpOffice\PhpSpreadsheet\Shared\Date::excelToTimestamp($endDate);
1091
            case Functions::RETURNDATE_PHP_OBJECT:
1092
                return \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($endDate);
1093
        }
1094
    }
1095
1096
    /**
1097
     * DAYOFMONTH
1098
     *
1099
     * Returns the day of the month, for a specified date. The day is given as an integer
1100
     * ranging from 1 to 31.
1101
     *
1102
     * Excel Function:
1103
     *        DAY(dateValue)
1104
     *
1105
     * @param    mixed    $dateValue        Excel date serial value (float), PHP date timestamp (integer),
1106
     *                                    PHP DateTime object, or a standard date string
1107
     * @return    int        Day of the month
1108
     */
1109 13
    public static function DAYOFMONTH($dateValue = 1)
1110
    {
1111 13
        $dateValue = Functions::flattenSingleValue($dateValue);
1112
1113 13
        if ($dateValue === null) {
1114
            $dateValue = 1;
1115 13
        } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
1116 1
            return Functions::VALUE();
1117 12
        } elseif ($dateValue == 0.0) {
1118
            return 0;
1119 12
        } elseif ($dateValue < 0.0) {
1120 1
            return Functions::NAN();
1121
        }
1122
1123
        // Execute function
1124 11
        $PHPDateObject = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($dateValue);
1125
1126 11
        return (int) $PHPDateObject->format('j');
1127
    }
1128
1129
    /**
1130
     * DAYOFWEEK
1131
     *
1132
     * Returns the day of the week for a specified date. The day is given as an integer
1133
     * ranging from 0 to 7 (dependent on the requested style).
1134
     *
1135
     * Excel Function:
1136
     *        WEEKDAY(dateValue[,style])
1137
     *
1138
     * @param    int    $dateValue        Excel date serial value (float), PHP date timestamp (integer),
1139
     *                                    PHP DateTime object, or a standard date string
1140
     * @param    int        $style            A number that determines the type of return value
1141
     *                                        1 or omitted    Numbers 1 (Sunday) through 7 (Saturday).
1142
     *                                        2                Numbers 1 (Monday) through 7 (Sunday).
1143
     *                                        3                Numbers 0 (Monday) through 6 (Sunday).
1144
     * @return    int        Day of the week value
1145
     */
1146 30
    public static function DAYOFWEEK($dateValue = 1, $style = 1)
1147
    {
1148 30
        $dateValue = Functions::flattenSingleValue($dateValue);
1149 30
        $style = Functions::flattenSingleValue($style);
1150
1151 30
        if (!is_numeric($style)) {
1152
            return Functions::VALUE();
1153 30
        } elseif (($style < 1) || ($style > 3)) {
1154
            return Functions::NAN();
1155
        }
1156 30
        $style = floor($style);
1157
1158 30
        if ($dateValue === null) {
1159
            $dateValue = 1;
1160 30
        } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
1161
            return Functions::VALUE();
1162 30
        } elseif ($dateValue < 0.0) {
1163
            return Functions::NAN();
1164
        }
1165
1166
        // Execute function
1167 30
        $PHPDateObject = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($dateValue);
1168 30
        $DoW = $PHPDateObject->format('w');
1169
1170 30
        $firstDay = 1;
1171
        switch ($style) {
1172 30
            case 1:
1173
                ++$DoW;
1174
                break;
1175 30
            case 2:
1176 18
                if ($DoW == 0) {
1177 2
                    $DoW = 7;
1178
                }
1179 18
                break;
1180 12
            case 3:
1181 12
                if ($DoW == 0) {
1182 2
                    $DoW = 7;
1183
                }
1184 12
                $firstDay = 0;
1185 12
                --$DoW;
1186 12
                break;
1187
        }
1188 30
        if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_EXCEL) {
1189
            //    Test for Excel's 1900 leap year, and introduce the error as required
1190 30
            if (($PHPDateObject->format('Y') == 1900) && ($PHPDateObject->format('n') <= 2)) {
1191
                --$DoW;
1192
                if ($DoW < $firstDay) {
1193
                    $DoW += 7;
1194
                }
1195
            }
1196
        }
1197
1198 30
        return (int) $DoW;
1199
    }
1200
1201
    /**
1202
     * WEEKNUM
1203
     *
1204
     * Returns the week of the year for a specified date.
1205
     * The WEEKNUM function considers the week containing January 1 to be the first week of the year.
1206
     * However, there is a European standard that defines the first week as the one with the majority
1207
     * of days (four or more) falling in the new year. This means that for years in which there are
1208
     * three days or less in the first week of January, the WEEKNUM function returns week numbers
1209
     * that are incorrect according to the European standard.
1210
     *
1211
     * Excel Function:
1212
     *        WEEKNUM(dateValue[,style])
1213
     *
1214
     * @param    mixed    $dateValue        Excel date serial value (float), PHP date timestamp (integer),
1215
     *                                    PHP DateTime object, or a standard date string
1216
     * @param    int    $method            Week begins on Sunday or Monday
1217
     *                                        1 or omitted    Week begins on Sunday.
1218
     *                                        2                Week begins on Monday.
1219
     * @return    int        Week Number
1220
     */
1221 15
    public static function WEEKNUM($dateValue = 1, $method = 1)
1222
    {
1223 15
        $dateValue = Functions::flattenSingleValue($dateValue);
1224 15
        $method = Functions::flattenSingleValue($method);
1225
1226 15 View Code Duplication
        if (!is_numeric($method)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1227 1
            return Functions::VALUE();
1228 14
        } elseif (($method < 1) || ($method > 2)) {
1229 1
            return Functions::NAN();
1230
        }
1231 13
        $method = floor($method);
1232
1233 13
        if ($dateValue === null) {
1234
            $dateValue = 1;
1235 13
        } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
1236 1
            return Functions::VALUE();
1237 12
        } elseif ($dateValue < 0.0) {
1238 1
            return Functions::NAN();
1239
        }
1240
1241
        // Execute function
1242 11
        $PHPDateObject = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($dateValue);
1243 11
        $dayOfYear = $PHPDateObject->format('z');
1244 11
        $PHPDateObject->modify('-' . $dayOfYear . ' days');
1245 11
        $firstDayOfFirstWeek = $PHPDateObject->format('w');
1246 11
        $daysInFirstWeek = (6 - $firstDayOfFirstWeek + $method) % 7;
1247 11
        $interval = $dayOfYear - $daysInFirstWeek;
1248 11
        $weekOfYear = floor($interval / 7) + 1;
1249
1250 11
        if ($daysInFirstWeek) {
1251 8
            ++$weekOfYear;
1252
        }
1253
1254 11
        return (int) $weekOfYear;
1255
    }
1256
1257
    /**
1258
     * MONTHOFYEAR
1259
     *
1260
     * Returns the month of a date represented by a serial number.
1261
     * The month is given as an integer, ranging from 1 (January) to 12 (December).
1262
     *
1263
     * Excel Function:
1264
     *        MONTH(dateValue)
1265
     *
1266
     * @param    mixed    $dateValue        Excel date serial value (float), PHP date timestamp (integer),
1267
     *                                    PHP DateTime object, or a standard date string
1268
     * @return    int        Month of the year
1269
     */
1270 17 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...
1271
    {
1272 17
        $dateValue = Functions::flattenSingleValue($dateValue);
1273
1274 17
        if (empty($dateValue)) {
1275 2
            $dateValue = 1;
1276
        }
1277 17
        if (is_string($dateValue = self::getDateValue($dateValue))) {
1278 1
            return Functions::VALUE();
1279 16
        } elseif ($dateValue < 0.0) {
1280 1
            return Functions::NAN();
1281
        }
1282
1283
        // Execute function
1284 15
        $PHPDateObject = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($dateValue);
1285
1286 15
        return (int) $PHPDateObject->format('n');
1287
    }
1288
1289
    /**
1290
     * YEAR
1291
     *
1292
     * Returns the year corresponding to a date.
1293
     * The year is returned as an integer in the range 1900-9999.
1294
     *
1295
     * Excel Function:
1296
     *        YEAR(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
     * @return    int        Year
1301
     */
1302 21 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...
1303
    {
1304 21
        $dateValue = Functions::flattenSingleValue($dateValue);
1305
1306 21
        if ($dateValue === null) {
1307 1
            $dateValue = 1;
1308 20
        } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
1309 1
            return Functions::VALUE();
1310 19
        } elseif ($dateValue < 0.0) {
1311 1
            return Functions::NAN();
1312
        }
1313
1314
        // Execute function
1315 19
        $PHPDateObject = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($dateValue);
1316
1317 19
        return (int) $PHPDateObject->format('Y');
1318
    }
1319
1320
    /**
1321
     * HOUROFDAY
1322
     *
1323
     * Returns the hour of a time value.
1324
     * The hour is given as an integer, ranging from 0 (12:00 A.M.) to 23 (11:00 P.M.).
1325
     *
1326
     * Excel Function:
1327
     *        HOUR(timeValue)
1328
     *
1329
     * @param    mixed    $timeValue        Excel date serial value (float), PHP date timestamp (integer),
1330
     *                                    PHP DateTime object, or a standard time string
1331
     * @return    int        Hour
1332
     */
1333 12 View Code Duplication
    public static function HOUROFDAY($timeValue = 0)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1334
    {
1335 12
        $timeValue = Functions::flattenSingleValue($timeValue);
1336
1337 12
        if (!is_numeric($timeValue)) {
1338 4
            if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
1339
                $testVal = strtok($timeValue, '/-: ');
1340
                if (strlen($testVal) < strlen($timeValue)) {
1341
                    return Functions::VALUE();
1342
                }
1343
            }
1344 4
            $timeValue = self::getTimeValue($timeValue);
0 ignored issues
show
Bug introduced by
It seems like $timeValue defined by self::getTimeValue($timeValue) on line 1344 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...
1345 4
            if (is_string($timeValue)) {
1346 1
                return Functions::VALUE();
1347
            }
1348
        }
1349
        // Execute function
1350 11
        if ($timeValue >= 1) {
1351 3
            $timeValue = fmod($timeValue, 1);
1352 8
        } elseif ($timeValue < 0.0) {
1353 1
            return Functions::NAN();
1354
        }
1355 10
        $timeValue = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToTimestamp($timeValue);
1356
1357 10
        return (int) gmdate('G', $timeValue);
1358
    }
1359
1360
    /**
1361
     * MINUTEOFHOUR
1362
     *
1363
     * Returns the minutes of a time value.
1364
     * The minute is given as an integer, ranging from 0 to 59.
1365
     *
1366
     * Excel Function:
1367
     *        MINUTE(timeValue)
1368
     *
1369
     * @param    mixed    $timeValue        Excel date serial value (float), PHP date timestamp (integer),
1370
     *                                    PHP DateTime object, or a standard time string
1371
     * @return    int        Minute
1372
     */
1373 View Code Duplication
    public static function MINUTEOFHOUR($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...
1374
    {
1375
        $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...
1376
1377
        if (!is_numeric($timeValue)) {
1378
            if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
1379
                $testVal = strtok($timeValue, '/-: ');
1380
                if (strlen($testVal) < strlen($timeValue)) {
1381
                    return Functions::VALUE();
1382
                }
1383
            }
1384
            $timeValue = self::getTimeValue($timeValue);
0 ignored issues
show
Bug introduced by
It seems like $timeValue defined by self::getTimeValue($timeValue) on line 1384 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...
1385
            if (is_string($timeValue)) {
1386
                return Functions::VALUE();
1387
            }
1388
        }
1389
        // Execute function
1390
        if ($timeValue >= 1) {
1391
            $timeValue = fmod($timeValue, 1);
1392
        } elseif ($timeValue < 0.0) {
1393
            return Functions::NAN();
1394
        }
1395
        $timeValue = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToTimestamp($timeValue);
1396
1397
        return (int) gmdate('i', $timeValue);
1398
    }
1399
1400
    /**
1401
     * SECONDOFMINUTE
1402
     *
1403
     * Returns the seconds of a time value.
1404
     * The second is given as an integer in the range 0 (zero) to 59.
1405
     *
1406
     * Excel Function:
1407
     *        SECOND(timeValue)
1408
     *
1409
     * @param    mixed    $timeValue        Excel date serial value (float), PHP date timestamp (integer),
1410
     *                                    PHP DateTime object, or a standard time string
1411
     * @return    int        Second
1412
     */
1413 View Code Duplication
    public static function SECONDOFMINUTE($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...
1414
    {
1415
        $timeValue = Functions::flattenSingleValue($timeValue);
1416
1417
        if (!is_numeric($timeValue)) {
1418
            if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
1419
                $testVal = strtok($timeValue, '/-: ');
1420
                if (strlen($testVal) < strlen($timeValue)) {
1421
                    return Functions::VALUE();
1422
                }
1423
            }
1424
            $timeValue = self::getTimeValue($timeValue);
0 ignored issues
show
Bug introduced by
It seems like $timeValue defined by self::getTimeValue($timeValue) on line 1424 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...
1425
            if (is_string($timeValue)) {
1426
                return Functions::VALUE();
1427
            }
1428
        }
1429
        // Execute function
1430
        if ($timeValue >= 1) {
1431
            $timeValue = fmod($timeValue, 1);
1432
        } elseif ($timeValue < 0.0) {
1433
            return Functions::NAN();
1434
        }
1435
        $timeValue = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToTimestamp($timeValue);
1436
1437
        return (int) gmdate('s', $timeValue);
1438
    }
1439
1440
    /**
1441
     * EDATE
1442
     *
1443
     * Returns the serial number that represents the date that is the indicated number of months
1444
     * before or after a specified date (the start_date).
1445
     * Use EDATE to calculate maturity dates or due dates that fall on the same day of the month
1446
     * as the date of issue.
1447
     *
1448
     * Excel Function:
1449
     *        EDATE(dateValue,adjustmentMonths)
1450
     *
1451
     * @param    mixed    $dateValue            Excel date serial value (float), PHP date timestamp (integer),
1452
     *                                        PHP DateTime object, or a standard date string
1453
     * @param    int        $adjustmentMonths    The number of months before or after start_date.
1454
     *                                        A positive value for months yields a future date;
1455
     *                                        a negative value yields a past date.
1456
     * @return    mixed    Excel date/time serial value, PHP date/time serial value or PHP date/time object,
1457
     *                        depending on the value of the ReturnDateType flag
1458
     */
1459 17
    public static function EDATE($dateValue = 1, $adjustmentMonths = 0)
1460
    {
1461 17
        $dateValue = Functions::flattenSingleValue($dateValue);
1462 17
        $adjustmentMonths = Functions::flattenSingleValue($adjustmentMonths);
1463
1464 17
        if (!is_numeric($adjustmentMonths)) {
1465 1
            return Functions::VALUE();
1466
        }
1467 16
        $adjustmentMonths = floor($adjustmentMonths);
1468
1469 16
        if (is_string($dateValue = self::getDateValue($dateValue))) {
1470 1
            return Functions::VALUE();
1471
        }
1472
1473
        // Execute function
1474 15
        $PHPDateObject = self::adjustDateByMonths($dateValue, $adjustmentMonths);
1475
1476 15 View Code Duplication
        switch (Functions::getReturnDateType()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

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

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1479 2
            case Functions::RETURNDATE_PHP_NUMERIC:
1480 1
                return (integer) \PhpOffice\PhpSpreadsheet\Shared\Date::excelToTimestamp(\PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel($PHPDateObject));
0 ignored issues
show
Documentation introduced by
$PHPDateObject is of type object<DateTime>, but the function expects a integer.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
\PhpOffice\PhpSpreadshee...ToExcel($PHPDateObject) is of type double|false, but the function expects a integer.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1481 1
            case Functions::RETURNDATE_PHP_OBJECT:
1482 1
                return $PHPDateObject;
1483
        }
1484
    }
1485
1486
    /**
1487
     * EOMONTH
1488
     *
1489
     * Returns the date value for the last day of the month that is the indicated number of months
1490
     * before or after start_date.
1491
     * Use EOMONTH to calculate maturity dates or due dates that fall on the last day of the month.
1492
     *
1493
     * Excel Function:
1494
     *        EOMONTH(dateValue,adjustmentMonths)
1495
     *
1496
     * @param    mixed    $dateValue            Excel date serial value (float), PHP date timestamp (integer),
1497
     *                                        PHP DateTime object, or a standard date string
1498
     * @param    int        $adjustmentMonths    The number of months before or after start_date.
1499
     *                                        A positive value for months yields a future date;
1500
     *                                        a negative value yields a past date.
1501
     * @return    mixed    Excel date/time serial value, PHP date/time serial value or PHP date/time object,
1502
     *                        depending on the value of the ReturnDateType flag
1503
     */
1504 19
    public static function EOMONTH($dateValue = 1, $adjustmentMonths = 0)
1505
    {
1506 19
        $dateValue = Functions::flattenSingleValue($dateValue);
1507 19
        $adjustmentMonths = Functions::flattenSingleValue($adjustmentMonths);
1508
1509 19
        if (!is_numeric($adjustmentMonths)) {
1510 1
            return Functions::VALUE();
1511
        }
1512 18
        $adjustmentMonths = floor($adjustmentMonths);
1513
1514 18
        if (is_string($dateValue = self::getDateValue($dateValue))) {
1515 1
            return Functions::VALUE();
1516
        }
1517
1518
        // Execute function
1519 17
        $PHPDateObject = self::adjustDateByMonths($dateValue, $adjustmentMonths + 1);
1520 17
        $adjustDays = (int) $PHPDateObject->format('d');
1521 17
        $adjustDaysString = '-' . $adjustDays . ' days';
1522 17
        $PHPDateObject->modify($adjustDaysString);
1523
1524 17 View Code Duplication
        switch (Functions::getReturnDateType()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

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

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1527 2
            case Functions::RETURNDATE_PHP_NUMERIC:
1528 1
                return (integer) \PhpOffice\PhpSpreadsheet\Shared\Date::excelToTimestamp(\PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel($PHPDateObject));
0 ignored issues
show
Documentation introduced by
$PHPDateObject is of type object<DateTime>, but the function expects a integer.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
\PhpOffice\PhpSpreadshee...ToExcel($PHPDateObject) is of type double|false, but the function expects a integer.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1529 1
            case Functions::RETURNDATE_PHP_OBJECT:
1530 1
                return $PHPDateObject;
1531
        }
1532
    }
1533
}
1534