Codex's updates...

This commit is contained in:
2026-10-18 19:27:17 -04:00
parent a1643eb1b7
commit 02223083f6
338 changed files with 14757 additions and 7986 deletions
@@ -24,7 +24,7 @@ class Date
* Excel Function:
* DATE(year,month,day)
*
* PhpSpreadsheet is a lot more forgiving than MS Excel when passing non numeric values to this function.
* PhpSpreadsheet is a lot more forgiving than MS Excel when passing non-numeric values to this function.
* A Month name or abbreviation (English only at this point) such as 'January' or 'Jan' will still be accepted,
* as will a day value with a suffix (e.g. '21st' rather than simply 21); again only English language.
*
@@ -46,13 +46,22 @@ class DateValue
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $dateValue);
}
return self::fromString2($dateValue, null);
}
/**
* @return array<mixed>|DateTime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* depending on the value of the ReturnDateType flag
*/
public static function fromString2(null|string|int|bool|float $dateValue, ?int $calendar = null): array|string|float|int|DateTime
{
// try to parse as date iff there is at least one digit
if (is_string($dateValue) && preg_match('/\d/', $dateValue) !== 1) {
return ExcelError::VALUE();
}
$dti = new DateTimeImmutable();
$baseYear = SharedDateHelper::getExcelCalendar();
$baseYear = $calendar ?? SharedDateHelper::getExcelCalendar();
$dateValue = trim((string) $dateValue, '"');
// Strip any ordinals because they're allowed in Excel (English only)
$dateValue = (string) preg_replace('/(\d)(st|nd|rd|th)([ -\/])/Ui', '$1$3', $dateValue);
@@ -86,7 +95,7 @@ class DateValue
return self::finalResults($PHPDateArray, $dti, $baseYear);
}
/** @param mixed[] $t1 */
/** @param list<float|int|string> $t1 */
private static function t1ToString(array $t1, DateTimeImmutable $dti, bool $yearFound): string
{
if (count($t1) == 2) {
@@ -118,7 +127,7 @@ class DateValue
if (!Helpers::dateParseSucceeded($PHPDateArray)) {
// If original count was 1, we've already returned.
// If it was 2, we added another.
// Therefore, neither of the first 2 stroks below can fail.
// Therefore, neither of the first 2 strtoks below can fail.
$testVal1 = strtok($dateValue, '- ');
$testVal2 = strtok('- ');
$testVal3 = strtok('- ') ?: $dti->format('Y');
@@ -160,9 +169,9 @@ class DateValue
$day = self::getInt($PHPDateArray, 'day');
$year = self::getInt($PHPDateArray, 'year');
if (!checkdate($month, $day, $year)) {
return ($year === 1900 && $month === 2 && $day === 29) ? Helpers::returnIn3FormatsFloat(60.0) : ExcelError::VALUE();
return ($year === 1900 && $month === 2 && $day === 29) ? Helpers::returnIn3FormatsFloat(60.0, calendar: $baseYear) : ExcelError::VALUE();
}
$retValue = Helpers::returnIn3FormatsArray($PHPDateArray, true);
$retValue = Helpers::returnIn3FormatsArray($PHPDateArray, true, calendar: $baseYear);
}
return $retValue;
@@ -50,11 +50,9 @@ class Days
$days = ExcelError::VALUE();
$diff = $PHPStartDateObject->diff($PHPEndDateObject);
if (!is_bool($diff->days)) {
$days = $diff->days;
if ($diff->invert) {
$days = -$days;
}
$days = $diff->days;
if ($diff->invert) {
$days = -$days;
}
return $days;
@@ -76,7 +76,7 @@ class Days360
}
/**
* Return the number of days between two dates based on a 360 day calendar.
* Return the number of days between two dates based on a 360-day calendar.
*/
private static function dateDiff360(int $startDay, int $startMonth, int $startYear, int $endDay, int $endMonth, int $endYear, bool $methodUS): int
{
@@ -7,6 +7,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper;
use Throwable;
class Helpers
{
@@ -29,10 +30,10 @@ class Helpers
*
* @return float Excel date/time serial value
*/
public static function getDateValue(mixed $dateValue, bool $allowBool = true): float
public static function getDateValue(mixed $dateValue, bool $allowBool = true, ?int $calendar = null): float
{
if (is_object($dateValue)) {
$retval = SharedDateHelper::PHPToExcel($dateValue);
$retval = SharedDateHelper::PHPToExcel($dateValue, calendar: $calendar);
if (is_bool($retval)) {
throw new Exception(ExcelError::VALUE());
}
@@ -40,7 +41,7 @@ class Helpers
return $retval;
}
self::nullFalseTrueToNumber($dateValue, $allowBool);
self::nullFalseTrueToNumber($dateValue, $allowBool, $calendar);
if (!is_numeric($dateValue)) {
$saveReturnDateType = Functions::getReturnDateType();
Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
@@ -56,6 +57,12 @@ class Helpers
throw new Exception(ExcelError::NAN());
}
try {
SharedDateHelper::excelToDateTimeObject((float) $dateValue, calendar: $calendar);
} catch (Throwable) {
throw new Exception(ExcelError::NAN());
}
return (float) $dateValue;
}
@@ -80,10 +87,10 @@ class Helpers
*
* @param float|int $dateValue date to be adjusted
*/
public static function adjustDateByMonths($dateValue = 0, float $adjustmentMonths = 0): DateTime
public static function adjustDateByMonths($dateValue = 0, float $adjustmentMonths = 0, ?int $calendar = null): DateTime
{
// Execute function
$PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
$PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue, calendar: $calendar);
$oMonth = (int) $PHPDateObject->format('m');
$oYear = (int) $PHPDateObject->format('Y');
@@ -134,7 +141,7 @@ class Helpers
*
* @param array{year: int, month: int, day: int, hour: int, minute: int, second: int} $dateArray
*/
public static function returnIn3FormatsArray(array $dateArray, bool $noFrac = false): DateTime|float|int
public static function returnIn3FormatsArray(array $dateArray, bool $noFrac = false, ?int $calendar = null): DateTime|float|int
{
$retType = Functions::getReturnDateType();
if ($retType === Functions::RETURNDATE_PHP_DATETIME_OBJECT) {
@@ -154,58 +161,60 @@ class Helpers
$dateArray['day'],
$dateArray['hour'],
$dateArray['minute'],
$dateArray['second']
$dateArray['second'],
calendar: $calendar
);
if ($retType === Functions::RETURNDATE_EXCEL) {
return $noFrac ? floor($excelDateValue) : $excelDateValue;
}
// RETURNDATE_UNIX_TIMESTAMP)
return SharedDateHelper::excelToTimestamp($excelDateValue);
return SharedDateHelper::excelToTimestamp($excelDateValue, calendar: $calendar);
}
/**
* Return result in one of three formats.
*/
public static function returnIn3FormatsFloat(float $excelDateValue): float|int|DateTime
public static function returnIn3FormatsFloat(float $excelDateValue, ?int $calendar = null): float|int|DateTime
{
$retType = Functions::getReturnDateType();
if ($retType === Functions::RETURNDATE_EXCEL) {
return $excelDateValue;
}
if ($retType === Functions::RETURNDATE_UNIX_TIMESTAMP) {
return SharedDateHelper::excelToTimestamp($excelDateValue);
return SharedDateHelper::excelToTimestamp($excelDateValue, calendar: $calendar);
}
// RETURNDATE_PHP_DATETIME_OBJECT
return SharedDateHelper::excelToDateTimeObject($excelDateValue);
return SharedDateHelper::excelToDateTimeObject($excelDateValue, calendar: $calendar);
}
/**
* Return result in one of three formats.
*/
public static function returnIn3FormatsObject(DateTime $PHPDateObject): DateTime|float|int
public static function returnIn3FormatsObject(DateTime $PHPDateObject, ?int $calendar = null): DateTime|float|int
{
$retType = Functions::getReturnDateType();
if ($retType === Functions::RETURNDATE_PHP_DATETIME_OBJECT) {
return $PHPDateObject;
}
if ($retType === Functions::RETURNDATE_EXCEL) {
return (float) SharedDateHelper::PHPToExcel($PHPDateObject);
return (float) SharedDateHelper::PHPToExcel($PHPDateObject, calendar: $calendar);
}
// RETURNDATE_UNIX_TIMESTAMP
$stamp = SharedDateHelper::PHPToExcel($PHPDateObject);
$stamp = SharedDateHelper::PHPToExcel($PHPDateObject, calendar: $calendar);
$stamp = is_bool($stamp) ? ((int) $stamp) : $stamp;
return SharedDateHelper::excelToTimestamp($stamp);
return SharedDateHelper::excelToTimestamp($stamp, calendar: $calendar);
}
private static function baseDate(): int
private static function baseDate(?int $calendar): int
{
if (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE) {
return 0;
}
if (SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_MAC_1904) {
$calendar ??= SharedDateHelper::getExcelCalendar();
if ($calendar === SharedDateHelper::CALENDAR_MAC_1904) {
return 0;
}
@@ -215,10 +224,10 @@ class Helpers
/**
* Many functions accept null/false/true argument treated as 0/0/1.
*/
public static function nullFalseTrueToNumber(mixed &$number, bool $allowBool = true): void
public static function nullFalseTrueToNumber(mixed &$number, bool $allowBool = true, ?int $calendar = null): void
{
$number = Functions::flattenSingleValue($number);
$nullVal = self::baseDate();
$nullVal = self::baseDate($calendar);
if ($number === null) {
$number = $nullVal;
} elseif ($allowBool && is_bool($number)) {
@@ -4,7 +4,9 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper;
use Throwable;
class TimeParts
{
@@ -44,6 +46,11 @@ class TimeParts
}
// Execute function
try {
SharedDateHelper::excelToDateTimeObject($timeValue);
} catch (Throwable) {
return ExcelError::NAN();
}
$timeValue = fmod($timeValue, 1);
$timeValue = SharedDateHelper::excelToDateTimeObject($timeValue);
SharedDateHelper::roundMicroseconds($timeValue);
@@ -85,6 +92,11 @@ class TimeParts
}
// Execute function
try {
SharedDateHelper::excelToDateTimeObject($timeValue);
} catch (Throwable) {
return ExcelError::NAN();
}
$timeValue = fmod($timeValue, 1);
$timeValue = SharedDateHelper::excelToDateTimeObject($timeValue);
SharedDateHelper::roundMicroseconds($timeValue);
@@ -126,6 +138,11 @@ class TimeParts
}
// Execute function
try {
SharedDateHelper::excelToDateTimeObject($timeValue);
} catch (Throwable) {
return ExcelError::NAN();
}
$timeValue = fmod($timeValue, 1);
$timeValue = SharedDateHelper::excelToDateTimeObject($timeValue);
SharedDateHelper::roundMicroseconds($timeValue);
@@ -3,7 +3,7 @@
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use Composer\Pcre\Preg;
use Datetime;
use DateTime;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
@@ -45,12 +45,12 @@ class TimeValue
* Date information in time_text is ignored.
* Or can be an array of date/time values
*
* @return array<mixed>|Datetime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* @return array<mixed>|DateTime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* depending on the value of the ReturnDateType flag
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function fromString(null|array|string|int|bool|float $timeValue): array|string|Datetime|int|float
public static function fromString(null|array|string|int|bool|float $timeValue): array|string|DateTime|int|float
{
if (is_array($timeValue)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $timeValue);
@@ -88,7 +88,7 @@ class TimeValue
} elseif ($retType === Functions::RETURNDATE_UNIX_TIMESTAMP) {
$retValue = (int) SharedDateHelper::excelToTimestamp($excelDateValue + 25569) - 3600;
} else {
$retValue = new Datetime('1900-01-01 ' . $PHPDateArray['hour'] . ':' . $PHPDateArray['minute'] . ':' . $PHPDateArray['second']);
$retValue = new DateTime('1900-01-01 ' . $PHPDateArray['hour'] . ':' . $PHPDateArray['minute'] . ':' . $PHPDateArray['second']);
}
}