Skip to content

Commit a7eb260

Browse files
author
MarkBaker
committed
The WORKDAY() function accepts 2 "static" arguments that could be passed as arrays; but also accepts a set of trailing date arguments that are accepted as an array by the splat operator. Only the first two arguments should be tested for returning array values; but the logic still needs to work with the full argument set.
Provide a separate "subset" method in the `ArrayEnabled` Trait, that allows a subset of arguments to be tested for array returns. Set up basic tests for `WORKDAY()`
1 parent 6cabd97 commit a7eb260

File tree

3 files changed

+79
-6
lines changed

3 files changed

+79
-6
lines changed

src/PhpSpreadsheet/Calculation/ArrayEnabled.php

+21
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,27 @@ protected static function evaluateArrayArguments(callable $method, ...$arguments
3737
self::initialiseHelper($arguments);
3838
$arguments = self::$arrayArgumentHelper->arguments();
3939

40+
return self::processArguments($method, ...$arguments);
41+
}
42+
43+
/**
44+
* @param mixed ...$arguments
45+
*/
46+
protected static function evaluateArrayArgumentsSubset(callable $method, int $limit, ...$arguments): array
47+
{
48+
self::initialiseHelper(array_slice($arguments, 0, $limit));
49+
$trailingArguments = array_slice($arguments, $limit);
50+
$arguments = self::$arrayArgumentHelper->arguments();
51+
$arguments = array_merge($arguments, $trailingArguments);
52+
53+
return self::processArguments($method, ...$arguments);
54+
}
55+
56+
/**
57+
* @param mixed ...$arguments
58+
*/
59+
private static function processArguments(callable $method, ...$arguments): array
60+
{
4061
if (self::$arrayArgumentHelper->hasArrayArgument() === false) {
4162
return [$method(...$arguments)];
4263
}

src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php

+27-6
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@
22

33
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
44

5+
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
56
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
67
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
78

89
class WorkDay
910
{
11+
use ArrayEnabled;
12+
1013
/**
1114
* WORKDAY.
1215
*
@@ -18,18 +21,32 @@ class WorkDay
1821
* Excel Function:
1922
* WORKDAY(startDate,endDays[,holidays[,holiday[,...]]])
2023
*
21-
* @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
24+
* @param array|mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
2225
* PHP DateTime object, or a standard date string
23-
* @param int $endDays The number of nonweekend and nonholiday days before or after
26+
* Or can be an array of date values
27+
* @param array|int $endDays The number of nonweekend and nonholiday days before or after
2428
* startDate. A positive value for days yields a future date; a
2529
* negative value yields a past date.
30+
* Or can be an array of int values
2631
* @param mixed $dateArgs
2732
*
28-
* @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
33+
* @return array|mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
2934
* depending on the value of the ReturnDateType flag
35+
* If an array of values is passed for the $startDate or $endDays,arguments, then the returned result
36+
* will also be an array with matching dimensions
3037
*/
3138
public static function date($startDate, $endDays, ...$dateArgs)
3239
{
40+
if (is_array($startDate) || is_array($endDays)) {
41+
return self::evaluateArrayArgumentsSubset(
42+
[self::class, __FUNCTION__],
43+
2,
44+
$startDate,
45+
$endDays,
46+
...$dateArgs
47+
);
48+
}
49+
3350
// Retrieve the mandatory start date and days that are referenced in the function definition
3451
try {
3552
$startDate = Helpers::getDateValue($startDate);
@@ -64,9 +81,9 @@ public static function date($startDate, $endDays, ...$dateArgs)
6481
private static function incrementing(float $startDate, int $endDays, array $holidayArray)
6582
{
6683
// Adjust the start date if it falls over a weekend
67-
6884
$startDoW = self::getWeekDay($startDate, 3);
69-
if (self::getWeekDay($startDate, 3) >= 5) {
85+
/** int $startDoW */
86+
if ($startDoW >= 5) {
7087
$startDate += 7 - $startDoW;
7188
--$endDays;
7289
}
@@ -78,6 +95,7 @@ private static function incrementing(float $startDate, int $endDays, array $holi
7895
++$endDate;
7996
// Adjust the calculated end date if it falls over a weekend
8097
$endDow = self::getWeekDay($endDate, 3);
98+
/** int $endDoW */
8199
if ($endDow >= 5) {
82100
$endDate += 7 - $endDow;
83101
}
@@ -128,7 +146,8 @@ private static function decrementing(float $startDate, int $endDays, array $holi
128146
// Adjust the start date if it falls over a weekend
129147

130148
$startDoW = self::getWeekDay($startDate, 3);
131-
if (self::getWeekDay($startDate, 3) >= 5) {
149+
/** int $startDoW */
150+
if ($startDoW >= 5) {
132151
$startDate += -$startDoW + 4;
133152
++$endDays;
134153
}
@@ -140,6 +159,7 @@ private static function decrementing(float $startDate, int $endDays, array $holi
140159
--$endDate;
141160
// Adjust the calculated end date if it falls over a weekend
142161
$endDow = self::getWeekDay($endDate, 3);
162+
/** int $endDoW */
143163
if ($endDow >= 5) {
144164
$endDate += 4 - $endDow;
145165
}
@@ -172,6 +192,7 @@ private static function decrementingArray(float $startDate, float $endDate, arra
172192
}
173193
// Adjust the calculated end date if it falls over a weekend
174194
$endDoW = self::getWeekDay($endDate, 3);
195+
/** int $endDoW */
175196
if ($endDoW >= 5) {
176197
$endDate += -$endDoW + 4;
177198
}

tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/WorkDayTest.php

+31
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\DateTime;
44

5+
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
6+
57
class WorkDayTest extends AllSetupTeardown
68
{
79
/**
@@ -49,4 +51,33 @@ public function providerWORKDAY(): array
4951
{
5052
return require 'tests/data/Calculation/DateTime/WORKDAY.php';
5153
}
54+
55+
/**
56+
* @dataProvider providerWorkDayArray
57+
*/
58+
public function testWorkDayArray(array $expectedResult, string $startDate, string $endDays, ?string $holidays): void
59+
{
60+
$calculation = Calculation::getInstance();
61+
62+
if ($holidays === null) {
63+
$formula = "=WORKDAY({$startDate}, {$endDays})";
64+
} else {
65+
$formula = "=WORKDAY({$startDate}, {$endDays}, {$holidays})";
66+
}
67+
$result = $calculation->_calculateFormulaValue($formula);
68+
self::assertEqualsWithDelta($expectedResult, $result, 1.0e-14);
69+
}
70+
71+
public function providerWorkDayArray(): array
72+
{
73+
return [
74+
'row vector #1' => [[[44595, 44596, 44599]], '{"2022-02-01", "2022-02-02", "2022-02-03"}', '2', null],
75+
'column vector #1' => [[[44595], [44596], [44599]], '{"2022-02-01"; "2022-02-02"; "2022-02-03"}', '2', null],
76+
'matrix #1' => [[[44595, 44596], [44599, 44600]], '{"2022-02-01", "2022-02-02"; "2022-02-03", "2022-02-04"}', '2', null],
77+
'row vector #2' => [[[44595, 44596]], '"2022-02-01"', '{2, 3}', null],
78+
'column vector #2' => [[[44595], [44596]], '"2022-02-01"', '{2; 3}', null],
79+
'row vector with Holiday' => [[[44596, 44599]], '"2022-02-01"', '{2, 3}', '{"2022-02-02"}'],
80+
'row vector with Holidays' => [[[44599, 44600]], '"2022-02-01"', '{2, 3}', '{"2022-02-02", "2022-02-03"}'],
81+
];
82+
}
5283
}

0 commit comments

Comments
 (0)