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

Week::number()   B

Complexity

Conditions 9
Paths 35

Size

Total Lines 38
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 9

Importance

Changes 0
Metric Value
cc 9
eloc 25
nc 35
nop 2
dl 0
loc 38
ccs 25
cts 25
cp 1
crap 9
rs 8.0555
c 0
b 0
f 0
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
4
5
use DateTime;
6
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
7
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
8
use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper;
9
10
class Week
11
{
12
    /**
13
     * WEEKNUM.
14
     *
15
     * Returns the week of the year for a specified date.
16
     * The WEEKNUM function considers the week containing January 1 to be the first week of the year.
17
     * However, there is a European standard that defines the first week as the one with the majority
18
     * of days (four or more) falling in the new year. This means that for years in which there are
19
     * three days or less in the first week of January, the WEEKNUM function returns week numbers
20
     * that are incorrect according to the European standard.
21
     *
22
     * Excel Function:
23
     *        WEEKNUM(dateValue[,style])
24
     *
25
     * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
26
     *                                    PHP DateTime object, or a standard date string
27
     * @param int $method Week begins on Sunday or Monday
28
     *                                        1 or omitted    Week begins on Sunday.
29
     *                                        2                Week begins on Monday.
30
     *                                        11               Week begins on Monday.
31
     *                                        12               Week begins on Tuesday.
32
     *                                        13               Week begins on Wednesday.
33
     *                                        14               Week begins on Thursday.
34
     *                                        15               Week begins on Friday.
35
     *                                        16               Week begins on Saturday.
36
     *                                        17               Week begins on Sunday.
37
     *                                        21               ISO (Jan. 4 is week 1, begins on Monday).
38
     *
39
     * @return int|string Week Number
40
     */
41 163
    public static function number($dateValue, $method = Constants::STARTWEEK_SUNDAY)
42
    {
43 163
        $origDateValueNull = empty($dateValue);
44
45
        try {
46 163
            $method = self::validateMethod($method);
47 147
            if ($dateValue === null) { // boolean not allowed
48 10
                $dateValue = (SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_MAC_1904 || $method === Constants::DOW_SUNDAY) ? 0 : 1;
49
            }
50 147
            $dateValue = self::validateDateValue($dateValue);
51 128
            if (!$dateValue && self::buggyWeekNum1900($method)) {
52
                // This seems to be an additional Excel bug.
53 128
                return 0;
54
            }
55 35
        } catch (Exception $e) {
56 35
            return $e->getMessage();
57
        }
58
59
        // Execute function
60 124
        $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
61 124
        if ($method == Constants::STARTWEEK_MONDAY_ISO) {
62 36
            Helpers::silly1900($PHPDateObject);
63
64 36
            return (int) $PHPDateObject->format('W');
65
        }
66 88
        if (self::buggyWeekNum1904($method, $origDateValueNull, $PHPDateObject)) {
67 3
            return 0;
68
        }
69 85
        Helpers::silly1900($PHPDateObject, '+ 5 years'); // 1905 calendar matches
70 85
        $dayOfYear = (int) $PHPDateObject->format('z');
71 85
        $PHPDateObject->modify('-' . $dayOfYear . ' days');
72 85
        $firstDayOfFirstWeek = (int) $PHPDateObject->format('w');
73 85
        $daysInFirstWeek = (6 - $firstDayOfFirstWeek + $method) % 7;
74 85
        $daysInFirstWeek += 7 * !$daysInFirstWeek;
75 85
        $endFirstWeek = $daysInFirstWeek - 1;
76 85
        $weekOfYear = floor(($dayOfYear - $endFirstWeek + 13) / 7);
77
78 85
        return (int) $weekOfYear;
79
    }
80
81
    /**
82
     * ISOWEEKNUM.
83
     *
84
     * Returns the ISO 8601 week number of the year for a specified date.
85
     *
86
     * Excel Function:
87
     *        ISOWEEKNUM(dateValue)
88
     *
89
     * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
90
     *                                    PHP DateTime object, or a standard date string
91
     *
92
     * @return int|string Week Number
93
     */
94 59
    public static function isoWeekNumber($dateValue)
95
    {
96 59
        if (self::apparentBug($dateValue)) {
97 3
            return 52;
98
        }
99
100
        try {
101 56
            $dateValue = Helpers::getDateValue($dateValue);
102 7
        } catch (Exception $e) {
103 7
            return $e->getMessage();
104
        }
105
106
        // Execute function
107 49
        $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
108 49
        Helpers::silly1900($PHPDateObject);
109
110 49
        return (int) $PHPDateObject->format('W');
111
    }
112
113
    /**
114
     * WEEKDAY.
115
     *
116
     * Returns the day of the week for a specified date. The day is given as an integer
117
     * ranging from 0 to 7 (dependent on the requested style).
118
     *
119
     * Excel Function:
120
     *        WEEKDAY(dateValue[,style])
121
     *
122
     * @param null|float|int|string $dateValue Excel date serial value (float), PHP date timestamp (integer),
123
     *                                    PHP DateTime object, or a standard date string
124
     * @param mixed $style A number that determines the type of return value
125
     *                                        1 or omitted    Numbers 1 (Sunday) through 7 (Saturday).
126
     *                                        2                Numbers 1 (Monday) through 7 (Sunday).
127
     *                                        3                Numbers 0 (Monday) through 6 (Sunday).
128
     *
129
     * @return int|string Day of the week value
130
     */
131 95
    public static function day($dateValue, $style = 1)
132
    {
133
        try {
134 95
            $dateValue = Helpers::getDateValue($dateValue);
135 93
            $style = self::validateStyle($style);
136 7
        } catch (Exception $e) {
137 7
            return $e->getMessage();
138
        }
139
140
        // Execute function
141 88
        $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
142 88
        Helpers::silly1900($PHPDateObject);
143 88
        $DoW = (int) $PHPDateObject->format('w');
144
145
        switch ($style) {
146 88
            case 1:
147 17
                ++$DoW;
148
149 17
                break;
150 71
            case 2:
151 37
                $DoW = self::dow0Becomes7($DoW);
152
153 37
                break;
154 34
            case 3:
155 34
                $DoW = self::dow0Becomes7($DoW) - 1;
156
157 34
                break;
158
        }
159
160 88
        return $DoW;
161
    }
162
163
    /**
164
     * @param mixed $style expect int
165
     */
166 93
    private static function validateStyle($style): int
167
    {
168 93
        $style = Functions::flattenSingleValue($style);
169
170 93
        if (!is_numeric($style)) {
171 1
            throw new Exception(Functions::VALUE());
172
        }
173 92
        $style = (int) $style;
174 92
        if (($style < 1) || ($style > 3)) {
175 4
            throw new Exception(Functions::NAN());
176
        }
177
178 88
        return $style;
179
    }
180
181 71
    private static function dow0Becomes7(int $DoW): int
182
    {
183 71
        return ($DoW === 0) ? 7 : $DoW;
184
    }
185
186
    /**
187
     * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
188
     *                                    PHP DateTime object, or a standard date string
189
     */
190 59
    private static function apparentBug($dateValue): bool
191
    {
192 59
        if (SharedDateHelper::getExcelCalendar() !== SharedDateHelper::CALENDAR_MAC_1904) {
193 31
            if (is_bool($dateValue)) {
194 2
                return true;
195
            }
196 29
            if (is_numeric($dateValue) && !((int) $dateValue)) {
197 1
                return true;
198
            }
199
        }
200
201 56
        return false;
202
    }
203
204
    /**
205
     * Validate dateValue parameter.
206
     *
207
     * @param mixed $dateValue
208
     */
209 147
    private static function validateDateValue($dateValue): float
210
    {
211 147
        if (is_bool($dateValue)) {
212 4
            throw new Exception(Functions::VALUE());
213
        }
214
215 143
        return Helpers::getDateValue($dateValue);
216
    }
217
218
    /**
219
     * Validate method parameter.
220
     *
221
     * @param mixed $method
222
     */
223 163
    private static function validateMethod($method): int
224
    {
225 163
        if ($method === null) {
226 2
            $method = Constants::STARTWEEK_SUNDAY;
227
        }
228 163
        $method = Functions::flattenSingleValue($method);
229 163
        if (!is_numeric($method)) {
230 2
            throw new Exception(Functions::VALUE());
231
        }
232
233 161
        $method = (int) $method;
234 161
        if (!array_key_exists($method, Constants::METHODARR)) {
235 14
            throw new Exception(Functions::NAN());
236
        }
237 147
        $method = Constants::METHODARR[$method];
238
239 147
        return $method;
240
    }
241
242 36
    private static function buggyWeekNum1900(int $method): bool
243
    {
244 36
        return $method === Constants::DOW_SUNDAY && SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_WINDOWS_1900;
245
    }
246
247 88
    private static function buggyWeekNum1904(int $method, bool $origNull, DateTime $dateObject): bool
248
    {
249
        // This appears to be another Excel bug.
250
251 88
        return $method === Constants::DOW_SUNDAY && SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_MAC_1904 &&
252 88
            !$origNull && $dateObject->format('Y-m-d') === '1904-01-01';
253
    }
254
}
255