Merge branch 'issue-907-absolute-path-in-Target' of https://github.com/tiagof/PhpSpreadsheet into issue-907-absolute-path-in-Target

This commit is contained in:
Tiago Fernandes 2021-04-19 14:40:33 +01:00
commit 107f707df6
1169 changed files with 58515 additions and 20835 deletions

1
.gitattributes vendored
View File

@ -3,7 +3,6 @@
/.github export-ignore
/.gitignore export-ignore
/.php_cs.dist export-ignore
/.sami.php export-ignore
/.scrutinizer.yml export-ignore
/CHANGELOG.PHPExcel.md export-ignore
/bin export-ignore

View File

@ -5,12 +5,17 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
experimental:
- false
php-version:
- '7.2'
- '7.3'
- '7.4'
- '8.0'
- '8.1'
include:
- php-version: '8.1'
experimental: true
name: PHP ${{ matrix.php-version }}
@ -38,13 +43,13 @@ jobs:
- name: Delete composer lock file
id: composer-lock
if: ${{ matrix.php-version == '8.0' || matrix.php-version == '8.1' }}
if: ${{ matrix.php-version == '8.1' }}
run: |
rm composer.lock
echo "::set-output name=flags::--ignore-platform-reqs"
- name: Install dependencies
run: composer install --no-progress --prefer-dist --optimize-autoloader ${{ steps.composer-lock.outputs.flags }}
run: composer update --no-progress --prefer-dist --optimize-autoloader ${{ steps.composer-lock.outputs.flags }}
- name: Setup problem matchers for PHP
run: echo "::add-matcher::${{ runner.tool_cache }}/php.json"
@ -52,8 +57,10 @@ jobs:
- name: Setup problem matchers for PHPUnit
run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
- name: Test with PHPUnit
run: ./vendor/bin/phpunit
- name: "Run PHPUnit tests (Experimental: ${{ matrix.experimental }})"
env:
FAILURE_ACTION: "${{ matrix.experimental == true }}"
run: vendor/bin/phpunit --verbose || $FAILURE_ACTION
php-cs-fixer:
runs-on: ubuntu-latest
@ -117,6 +124,37 @@ jobs:
- name: Code style with PHP_CodeSniffer
run: ./vendor/bin/phpcs -q --report=checkstyle | cs2pr
phpstan:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup PHP, with composer and extensions
uses: shivammathur/setup-php@v2
with:
php-version: 7.4
extensions: ctype, dom, gd, iconv, fileinfo, libxml, mbstring, simplexml, xml, xmlreader, xmlwriter, zip, zlib
coverage: none
tools: cs2pr
- name: Get composer cache directory
id: composer-cache
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Cache composer dependencies
uses: actions/cache@v2
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install dependencies
run: composer install --no-progress --prefer-dist --optimize-autoloader
- name: Static analysis with PHPStan
run: ./vendor/bin/phpstan analyse
coverage:
runs-on: ubuntu-latest
steps:

3
.gitignore vendored
View File

@ -8,3 +8,6 @@
*.project
/.settings
/.idea
## mkdocs output
/site

View File

@ -160,7 +160,7 @@ return PhpCsFixer\Config::create()
'php_unit_test_annotation' => true,
'php_unit_test_case_static_method_calls' => ['call_type' => 'self'],
'php_unit_test_class_requires_covers' => false, // We don't care as much as we should about coverage
'phpdoc_add_missing_param_annotation' => true,
'phpdoc_add_missing_param_annotation' => false, // Don't add things that bring no value
'phpdoc_align' => false, // Waste of time
'phpdoc_annotation_without_dot' => true,
'phpdoc_indent' => true,

View File

@ -9,7 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org).
### Added
- CSV Reader - Best Guess for Encoding, and Handle Null-string Escape [#1647](https://github.com/PHPOffice/PhpSpreadsheet/issues/1647)
- Implemented the CHITEST(), CHISQ.DIST() and CHISQ.INV() and equivalent Statistical functions, for both left- and right-tailed distributions.
- Support for ActiveSheet and SelectedCells in the ODS Reader and Writer. [PR #1908](https://github.com/PHPOffice/PhpSpreadsheet/pull/1908)
### Changed
@ -25,22 +26,106 @@ and this project adheres to [Semantic Versioning](https://semver.org).
### Fixed
- Fixed issue where array key check for existince before accessing arrays in Xlsx.php. [PR #1970](https://github.com/PHPOffice/PhpSpreadsheet/pull/1970)
- Fixed issue with quoted strings in number format mask rendered with toFormattedString() [Issue 1972#](https://github.com/PHPOffice/PhpSpreadsheet/issues/1972) [PR #1978](https://github.com/PHPOffice/PhpSpreadsheet/pull/1978)
- Fixed issue with percentage formats in number format mask rendered with toFormattedString() [Issue 1929#](https://github.com/PHPOffice/PhpSpreadsheet/issues/1929) [PR #1928](https://github.com/PHPOffice/PhpSpreadsheet/pull/1928)
- Fixed issue with _ spacing character in number format mask corrupting output from toFormattedString() [Issue 1924#](https://github.com/PHPOffice/PhpSpreadsheet/issues/1924) [PR #1927](https://github.com/PHPOffice/PhpSpreadsheet/pull/1927)
- Fix for [Issue #1887](https://github.com/PHPOffice/PhpSpreadsheet/issues/1887) - Lose Track of Selected Cells After Save
- Fixed issue with Xlsx@listWorksheetInfo not returning any data
- Fixed invalid arguments triggering mb_substr() error in LEFT(), MID() and RIGHT() text functions. [Issue #640](https://github.com/PHPOffice/PhpSpreadsheet/issues/640)
- Fix for [Issue #1916](https://github.com/PHPOffice/PhpSpreadsheet/issues/1916) - Invalid signature check for XML files
## 1.17.1 - 2021-03-01
### Added
- Implementation of the Excel `AVERAGEIFS()` functions as part of a restructuring of Database functions and Conditional Statistical functions.
- Support for date values and percentages in query parameters for Database functions, and the IF expressions in functions like COUNTIF() and AVERAGEIF(). [#1875](https://github.com/PHPOffice/PhpSpreadsheet/pull/1875)
- Support for booleans, and for wildcard text search in query parameters for Database functions, and the IF expressions in functions like COUNTIF() and AVERAGEIF(). [#1876](https://github.com/PHPOffice/PhpSpreadsheet/pull/1876)
- Implemented DataBar for conditional formatting in Xlsx, providing read/write and creation of (type, value, direction, fills, border, axis position, color settings) as DataBar options in Excel. [#1754](https://github.com/PHPOffice/PhpSpreadsheet/pull/1754)
- Alignment for ODS Writer [#1796](https://github.com/PHPOffice/PhpSpreadsheet/issues/1796)
- Basic implementation of the PERMUTATIONA() Statistical Function
### Changed
- Formula functions that previously called PHP functions directly are now processed through the Excel Functions classes; resolving issues with PHP8 stricter typing. [#1789](https://github.com/PHPOffice/PhpSpreadsheet/issues/1789)
The following MathTrig functions are affected:
`ABS()`, `ACOS()`, `ACOSH()`, `ASIN()`, `ASINH()`, `ATAN()`, `ATANH()`,
`COS()`, `COSH()`, `DEGREES()` (rad2deg), `EXP()`, `LN()` (log), `LOG10()`,
`RADIANS()` (deg2rad), `SIN()`, `SINH()`, `SQRT()`, `TAN()`, `TANH()`.
One TextData function is also affected: `REPT()` (str_repeat).
- `formatAsDate` correctly matches language metadata, reverting c55272e
- Formulae that previously crashed on sub function call returning excel error value now return said value.
The following functions are affected `CUMPRINC()`, `CUMIPMT()`, `AMORLINC()`,
`AMORDEGRC()`.
- Adapt some function error return value to match excel's error.
The following functions are affected `PPMT()`, `IPMT()`.
### Deprecated
- Calling many of the Excel formula functions directly rather than through the Calculation Engine.
The logic for these Functions is now being moved out of the categorised `Database`, `DateTime`, `Engineering`, `Financial`, `Logical`, `LookupRef`, `MathTrig`, `Statistical`, `TextData` and `Web` classes into small, dedicated classes for individual functions or related groups of functions.
This makes the logic in these classes easier to maintain; and will reduce the memory footprint required to execute formulae when calling these functions.
### Removed
- Nothing.
### Fixed
- Avoid Duplicate Titles When Reading Multiple HTML Files.[Issue #1823](https://github.com/PHPOffice/PhpSpreadsheet/issues/1823) [PR #1829](https://github.com/PHPOffice/PhpSpreadsheet/pull/1829)
- Fixed issue with Worksheet's `getCell()` method when trying to get a cell by defined name. [#1858](https://github.com/PHPOffice/PhpSpreadsheet/issues/1858)
- Fix possible endless loop in NumberFormat Masks [#1792](https://github.com/PHPOffice/PhpSpreadsheet/issues/1792)
- Fix problem resulting from literal dot inside quotes in number format masks. [PR #1830](https://github.com/PHPOffice/PhpSpreadsheet/pull/1830)
- Resolve Google Sheets Xlsx charts issue. Google Sheets uses oneCellAnchor positioning and does not include *Cache values in the exported Xlsx. [PR #1761](https://github.com/PHPOffice/PhpSpreadsheet/pull/1761)
- Fix for Xlsx Chart axis titles mapping to correct X or Y axis label when only one is present. [PR #1760](https://github.com/PHPOffice/PhpSpreadsheet/pull/1760)
- Fix For Null Exception on ODS Read of Page Settings. [#1772](https://github.com/PHPOffice/PhpSpreadsheet/issues/1772)
- Fix Xlsx reader overriding manually set number format with builtin number format. [PR #1805](https://github.com/PHPOffice/PhpSpreadsheet/pull/1805)
- Fix Xlsx reader cell alignment. [PR #1710](https://github.com/PHPOffice/PhpSpreadsheet/pull/1710)
- Fix for not yet implemented data-types in Open Document writer [Issue #1674](https://github.com/PHPOffice/PhpSpreadsheet/issues/1674)
- Fix XLSX reader when having a corrupt numeric cell data type [PR #1664](https://github.com/phpoffice/phpspreadsheet/pull/1664)
- Fix on `CUMPRINC()`, `CUMIPMT()`, `AMORLINC()`, `AMORDEGRC()` usage. When those functions called one of `YEARFRAC()`, `PPMT()`, `IPMT()` and they would get back an error value (represented as a string), trying to use numeral operands (`+`, `/`, `-`, `*`) on said return value and a number (`float or `int`) would fail.
## 1.16.0 - 2020-12-31
### Added
- CSV Reader - Best Guess for Encoding, and Handle Null-string Escape [#1647](https://github.com/PHPOffice/PhpSpreadsheet/issues/1647)
### Changed
- Updated the CONVERT() function to support all current MS Excel categories and Units of Measure.
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- Fixed issue with absolute path in worksheets' Target. [PR #1769](https://github.com/PHPOffice/PhpSpreadsheet/pull/1769)
- Fix for Xls Reader when SST has a bad length [#1592](https://github.com/PHPOffice/PhpSpreadsheet/issues/1592)
- Resolve Xlsx loader issue whe hyperlinks don't have a destination
- Resolve Xlsx loader issue whe hyperlinks don't have a destination
- Resolve issues when printer settings resources IDs clash with drawing IDs
- Resolve issue with SLK long filenames [#1612](https://github.com/PHPOffice/PhpSpreadsheet/issues/1612)
- ROUNDUP and ROUNDDOWN return incorrect results for values of 0 [#1627](https://github.com/phpoffice/phpspreadsheet/pull/1627)
- Apply Column and Row Styles to Existing Cells [#1712](https://github.com/PHPOffice/PhpSpreadsheet/issues/1712) [PR #1721](https://github.com/PHPOffice/PhpSpreadsheet/pull/1721)
- Resolve issues with defined names where worksheet doesn't exist (#1686)[https://github.com/PHPOffice/PhpSpreadsheet/issues/1686] and [#1723](https://github.com/PHPOffice/PhpSpreadsheet/issues/1723) - [PR #1742](https://github.com/PHPOffice/PhpSpreadsheet/pull/1742)
- Fix for issue [#1735](https://github.com/PHPOffice/PhpSpreadsheet/issues/1735) Incorrect activeSheetIndex after RemoveSheetByIndex - [PR #1743](https://github.com/PHPOffice/PhpSpreadsheet/pull/1743)
- Fix for issue [#1735](https://github.com/PHPOffice/PhpSpreadsheet/issues/1735) Incorrect activeSheetIndex after RemoveSheetByIndex - [PR #1743](https://github.com/PHPOffice/PhpSpreadsheet/pull/1743)
- Ensure that the list of shared formulae is maintained when an xlsx file is chunked with readFilter[Issue #169](https://github.com/PHPOffice/PhpSpreadsheet/issues/1669).
- Fix for notice during accessing "cached magnification factor" offset [#1354](https://github.com/PHPOffice/PhpSpreadsheet/pull/1354)
- Fix compatibility with ext-gd on php 8
### Security Fix (CVE-2020-7776)
- Prevent XSS through cell comments in the HTML Writer.
- Prevent XSS through cell comments in the HTML Writer.
## 1.15.0 - 2020-10-11

View File

@ -9,3 +9,12 @@ If you would like to contribute, here are some notes and guidelines:
- All code changes must be validated by `composer check`
- [Helpful article about forking](https://help.github.com/articles/fork-a-repo/ "Forking a GitHub repository")
- [Helpful article about pull requests](https://help.github.com/articles/using-pull-requests/ "Pull Requests")
## How to release
1. Complete CHANGELOG.md and commit
2. Create an annotated tag
1. `git tag -a 1.2.3`
2. Tag subject must be the version number, eg: `1.2.3`
3. Tag body must be a copy-paste of the changelog entries
3. Push tag with `git push --tags`, GitHub Actions will create a GitHub release automatically

View File

@ -1,7 +1,19 @@
{
"name": "phpoffice/phpspreadsheet",
"description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine",
"keywords": ["PHP", "OpenXML", "Excel", "xlsx", "xls", "ods", "gnumeric", "spreadsheet"],
"keywords": [
"PHP",
"OpenXML",
"Excel",
"xlsx",
"xls",
"ods",
"gnumeric",
"spreadsheet"
],
"config": {
"sort-packages": true
},
"homepage": "https://github.com/PHPOffice/PhpSpreadsheet",
"type": "library",
"license": "MIT",
@ -29,7 +41,8 @@
"check": [
"php-cs-fixer fix --ansi --dry-run --diff",
"phpcs",
"phpunit --color=always"
"phpunit --color=always",
"phpstan analyse --ansi"
],
"fix": [
"php-cs-fixer fix --ansi"
@ -39,35 +52,37 @@
]
},
"require": {
"php": "^7.2||^8.0",
"php": "^7.2 || ^8.0",
"ext-simplexml": "*",
"ext-ctype": "*",
"ext-dom": "*",
"ext-fileinfo": "*",
"ext-gd": "*",
"ext-iconv": "*",
"ext-fileinfo": "*",
"ext-libxml": "*",
"ext-mbstring": "*",
"ext-SimpleXML": "*",
"ext-xml": "*",
"ext-xmlreader": "*",
"ext-xmlwriter": "*",
"ext-zip": "*",
"ext-zlib": "*",
"ezyang/htmlpurifier": "^4.13",
"maennchen/zipstream-php": "^2.1",
"markbaker/complex": "^1.5||^2.0",
"markbaker/matrix": "^1.2||^2.0",
"psr/simple-cache": "^1.0",
"markbaker/complex": "^2.0",
"markbaker/matrix": "^2.0",
"psr/http-client": "^1.0",
"psr/http-factory": "^1.0",
"ezyang/htmlpurifier": "^4.13"
"psr/simple-cache": "^1.0"
},
"require-dev": {
"dompdf/dompdf": "^0.8.5",
"friendsofphp/php-cs-fixer": "^2.16",
"dompdf/dompdf": "^1.0",
"friendsofphp/php-cs-fixer": "^2.18",
"jpgraph/jpgraph": "^4.0",
"mpdf/mpdf": "^8.0",
"phpcompatibility/php-compatibility": "^9.3",
"phpunit/phpunit": "^8.5||^9.3",
"phpstan/phpstan": "^0.12.82",
"phpstan/phpstan-phpunit": "^0.12.18",
"phpunit/phpunit": "^8.5",
"squizlabs/php_codesniffer": "^3.5",
"tecnickcom/tcpdf": "^6.3"
},

1416
composer.lock generated

File diff suppressed because it is too large Load Diff

5
docs/extra/extrajs.js Normal file
View File

@ -0,0 +1,5 @@
document.addEventListener("DOMContentLoaded", function() {
document.querySelectorAll("table").forEach(function(table) {
table.classList.add("docutils");
});
});

View File

@ -89,7 +89,7 @@ php vendor/phpoffice/phpspreadsheet/samples/Basic/01_Simple.php
## Learn by documentation
For more in-depth documentation, you may read about an [overview of the
For more documentation in depth, you may read about an [overview of the
architecture](./topics/architecture.md),
[creating a spreadsheet](./topics/creating-spreadsheet.md),
[worksheets](./topics/worksheets.md),

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

View File

@ -580,6 +580,18 @@ $writer->setUseBOM(true);
$writer->save("05featuredemo.csv");
```
#### Writing CSV files with desired encoding
It can be set to output with the encoding that can be specified by PHP's mb_convert_encoding.
This looks like the following code:
```php
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Csv($spreadsheet);
$writer->setUseBOM(false);
$writer->setOutputEncoding('SJIS-WIN');
$writer->save("05featuredemo.csv");
```
#### Decimal and thousands separators
If the worksheet you are exporting contains numbers with decimal or

View File

@ -884,6 +884,44 @@ $spreadsheet->getActiveSheet()
);
```
### DataBar of Conditional formatting
The basics are the same as conditional formatting.
Additional DataBar object to conditional formatting.
For example, the following code will result in the conditional formatting shown in the image.
```php
$conditional = new Conditional();
$conditional->setConditionType(Conditional::CONDITION_DATABAR);
$conditional->setDataBar(new ConditionalDataBar());
$conditional->getDataBar()
->setMinimumConditionalFormatValueObject(new ConditionalFormatValueObject('num', '2'))
->setMaximumConditionalFormatValueObject(new ConditionalFormatValueObject('max'))
->setColor('FFFF555A');
$ext = $conditional
->getDataBar()
->setConditionalFormattingRuleExt(new ConditionalFormattingRuleExtension())
->getConditionalFormattingRuleExt();
$ext->setCfRule('dataBar');
$ext->setSqref('A1:A5'); // target CellCoordinates
$ext->setDataBarExt(new ConditionalDataBarExtension());
$ext->getDataBarExt()
->setMinimumConditionalFormatValueObject(new ConditionalFormatValueObject('num', '2'))
->setMaximumConditionalFormatValueObject(new ConditionalFormatValueObject('autoMax'))
->setMinLength(0)
->setMaxLength(100)
->setBorder(true)
->setDirection('rightToLeft')
->setNegativeBarBorderColorSameAsPositive(false)
->setBorderColor('FFFF555A')
->setNegativeFillColor('FFFF0000')
->setNegativeBorderColor('FFFF0000')
->setAxisColor('FF000000');
```
![10-databar-of-conditional-formatting.png](./images/10-databar-of-conditional-formatting.png)
## Add a comment to a cell
To add a comment to a cell, use the following code. The example below

View File

@ -5,3 +5,5 @@ edit_uri: edit/master/docs/
theme: readthedocs
extra_css:
- extra/extra.css
extra_javascript:
- extra/extrajs.js

8827
phpstan-baseline.neon Normal file

File diff suppressed because it is too large Load Diff

27
phpstan.neon.dist Normal file
View File

@ -0,0 +1,27 @@
includes:
- phpstan-baseline.neon
- vendor/phpstan/phpstan-phpunit/extension.neon
- vendor/phpstan/phpstan-phpunit/rules.neon
parameters:
level: max
paths:
- src/
- tests/
parallel:
processTimeout: 300.0
checkMissingIterableValueType: false
ignoreErrors:
- '~^Class GdImage not found\.$~'
- '~^Return typehint of method .* has invalid type GdImage\.$~'
- '~^Property .* has unknown class GdImage as its type\.$~'
- '~^Parameter .* of method .* has invalid typehint type GdImage\.$~'
# Accept a bit anything for assert methods
- '~^Parameter \#2 .* of static method PHPUnit\\Framework\\Assert\:\:assert\w+\(\) expects .*, .* given\.$~'
- '~^Method PhpOffice\\PhpSpreadsheetTests\\.*\:\:test.*\(\) has parameter \$args with no typehint specified\.$~'
# Ignore all JpGraph issues
- '~^Constant (MARK_CIRCLE|MARK_CROSS|MARK_DIAMOND|MARK_DTRIANGLE|MARK_FILLEDCIRCLE|MARK_SQUARE|MARK_STAR|MARK_UTRIANGLE|MARK_X|SIDE_RIGHT) not found\.$~'
- '~^Instantiated class (AccBarPlot|AccLinePlot|BarPlot|ContourPlot|Graph|GroupBarPlot|GroupBarPlot|LinePlot|LinePlot|PieGraph|PiePlot|PiePlot3D|PiePlotC|RadarGraph|RadarPlot|ScatterPlot|Spline|StockPlot) not found\.$~'
- '~^Call to method .*\(\) on an unknown class (AccBarPlot|AccLinePlot|BarPlot|ContourPlot|Graph|GroupBarPlot|GroupBarPlot|LinePlot|LinePlot|PieGraph|PiePlot|PiePlot3D|PiePlotC|RadarGraph|RadarPlot|ScatterPlot|Spline|StockPlot)\.$~'
- '~^Access to property .* on an unknown class (AccBarPlot|AccLinePlot|BarPlot|ContourPlot|Graph|GroupBarPlot|GroupBarPlot|LinePlot|LinePlot|PieGraph|PiePlot|PiePlot3D|PiePlotC|RadarGraph|RadarPlot|ScatterPlot|Spline|StockPlot)\.$~'

View File

@ -0,0 +1,43 @@
<?php
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Shared\Date;
require __DIR__ . '/../Header.php';
$helper->log('Load from Xls template');
$reader = IOFactory::createReader('Xls');
$spreadsheet = $reader->load(__DIR__ . '/../templates/30templatebiff5.xls');
$helper->log('Add new data to the template');
$data = [['title' => 'Excel for dummies',
'price' => 17.99,
'quantity' => 2,
],
['title' => 'PHP for dummies',
'price' => 15.99,
'quantity' => 1,
],
['title' => 'Inside OOP',
'price' => 12.95,
'quantity' => 1,
],
];
$spreadsheet->getActiveSheet()->setCellValue('D1', Date::PHPToExcel(time()));
$baseRow = 5;
foreach ($data as $r => $dataRow) {
$row = $baseRow + $r;
$spreadsheet->getActiveSheet()->insertNewRowBefore($row, 1);
$spreadsheet->getActiveSheet()->setCellValue('A' . $row, $r + 1)
->setCellValue('B' . $row, $dataRow['title'])
->setCellValue('C' . $row, $dataRow['price'])
->setCellValue('D' . $row, $dataRow['quantity'])
->setCellValue('E' . $row, '=C' . $row . '*D' . $row);
}
$spreadsheet->getActiveSheet()->removeRow($baseRow - 1, 1);
// Save
$helper->write($spreadsheet, __FILE__);

View File

@ -30,7 +30,7 @@ for ($col = 1; $col <= 50; ++$col) {
}
}
$d = microtime(true) - $t;
$helper->log('Add data (end) . time: ' . round((string) ($d . 2)) . ' s');
$helper->log('Add data (end) . time: ' . (string) round($d, 2) . ' s');
// Save
$helper->write($spreadsheet, __FILE__);

View File

@ -18,6 +18,10 @@ $helper->logRead('Xlsx', $filename2, $callStartTime);
foreach ($spreadsheet2->getSheetNames() as $sheetName) {
$sheet = $spreadsheet2->getSheetByName($sheetName);
if ($sheet === null) {
continue;
}
$sheet->setTitle($sheet->getTitle() . ' copied');
$spreadsheet1->addExternalSheet($sheet);
}

View File

@ -0,0 +1,35 @@
<?php
use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\Helpers as DateHelper;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns the accrued interest for a security that pays periodic interest.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
// Add some data
$arguments = [
['Issue Date', DateHelper::getDateValue('01-Jan-2012')],
['First Interest Date', DateHelper::getDateValue('01-Apr-2012')],
['Settlement Date', DateHelper::getDateValue('31-Dec-2013')],
['Annual Coupon Rate', 0.08],
['Par Value', 10000],
['Frequency', 4],
];
// Some basic formatting for the data
$worksheet->fromArray($arguments, null, 'A1');
$worksheet->getStyle('B1:B3')->getNumberFormat()->setFormatCode('dd-mmm-yyyy');
$worksheet->getStyle('B4')->getNumberFormat()->setFormatCode('0.00%');
$worksheet->getStyle('B5')->getNumberFormat()->setFormatCode('$#,##0.00');
// Now the formula
$worksheet->setCellValue('B10', '=ACCRINT(B1, B2, B3, B4, B5, B6)');
$worksheet->getStyle('B10')->getNumberFormat()->setFormatCode('$#,##0.00');
$helper->log($worksheet->getCell('B10')->getValue());
$helper->log('ACCRINT() Result is ' . $worksheet->getCell('B10')->getFormattedValue());

View File

@ -0,0 +1,33 @@
<?php
use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\Helpers as DateHelper;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns the accrued interest for a security that pays interest at maturity.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
// Add some data
$arguments = [
['Issue Date', DateHelper::getDateValue('01-Jan-2012')],
['Settlement Date', DateHelper::getDateValue('31-Dec-2012')],
['Annual Coupon Rate', 0.08],
['Par Value', 10000],
];
// Some basic formatting for the data
$worksheet->fromArray($arguments, null, 'A1');
$worksheet->getStyle('B1:B2')->getNumberFormat()->setFormatCode('dd-mmm-yyyy');
$worksheet->getStyle('B3')->getNumberFormat()->setFormatCode('0.00%');
$worksheet->getStyle('B4')->getNumberFormat()->setFormatCode('$#,##0.00');
// Now the formula
$worksheet->setCellValue('B6', '=ACCRINTM(B1, B2, B3, B4)');
$worksheet->getStyle('B6')->getNumberFormat()->setFormatCode('$#,##0.00');
$helper->log($worksheet->getCell('B6')->getValue());
$helper->log('ACCRINTM() Result is ' . $worksheet->getCell('B6')->getFormattedValue());

View File

@ -0,0 +1,38 @@
<?php
use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\Helpers as DateHelper;
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstants;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns the prorated linear depreciation of an asset for a specified accounting period.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
// Add some data
$arguments = [
['Cost', 150.00],
['Date Purchased', DateHelper::getDateValue('01-Jan-2015')],
['First Period Date', DateHelper::getDateValue('30-Sep-2015')],
['Salvage Value', 20.00],
['Number of Periods', 1],
['Depreciation Rate', 0.20],
['Basis', FinancialConstants::BASIS_DAYS_PER_YEAR_360_EUROPEAN],
];
// Some basic formatting for the data
$worksheet->fromArray($arguments, null, 'A1');
$worksheet->getStyle('B1')->getNumberFormat()->setFormatCode('$#,##0.00');
$worksheet->getStyle('B2:B3')->getNumberFormat()->setFormatCode('dd-mmm-yyyy');
$worksheet->getStyle('B4')->getNumberFormat()->setFormatCode('$#,##0.00');
$worksheet->getStyle('B6')->getNumberFormat()->setFormatCode('0.00%');
// Now the formula
$worksheet->setCellValue('B10', '=AMORDEGRC(B1, B2, B3, B4, B5, B6, B7)');
$worksheet->getStyle('B10')->getNumberFormat()->setFormatCode('$#,##0.00');
$helper->log($worksheet->getCell('B10')->getValue());
$helper->log('AMORDEGRC() Result is ' . $worksheet->getCell('B10')->getFormattedValue());

View File

@ -0,0 +1,38 @@
<?php
use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\Helpers as DateHelper;
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstants;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns the prorated linear depreciation of an asset for a specified accounting period.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
// Add some data
$arguments = [
['Cost', 150.00],
['Date Purchased', DateHelper::getDateValue('01-Jan-2015')],
['First Period Date', DateHelper::getDateValue('30-Sep-2015')],
['Salvage Value', 20.00],
['Period', 1],
['Depreciation Rate', 0.20],
['Basis', FinancialConstants::BASIS_DAYS_PER_YEAR_360_EUROPEAN],
];
// Some basic formatting for the data
$worksheet->fromArray($arguments, null, 'A1');
$worksheet->getStyle('B1')->getNumberFormat()->setFormatCode('$#,##0.00');
$worksheet->getStyle('B2:B3')->getNumberFormat()->setFormatCode('dd-mmm-yyyy');
$worksheet->getStyle('B4')->getNumberFormat()->setFormatCode('$#,##0.00');
$worksheet->getStyle('B6')->getNumberFormat()->setFormatCode('0.00%');
// Now the formula
$worksheet->setCellValue('B10', '=AMORLINC(B1, B2, B3, B4, B5, B6, B7)');
$worksheet->getStyle('B10')->getNumberFormat()->setFormatCode('$#,##0.00');
$helper->log($worksheet->getCell('B10')->getValue());
$helper->log('AMORLINC() Result is ' . $worksheet->getCell('B10')->getFormattedValue());

View File

@ -0,0 +1,29 @@
<?php
use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\Helpers as DateHelper;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns the number of days from the beginning of a coupon\'s period to the settlement date.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
// Add some data
$arguments = [
['Settlement Date', DateHelper::getDateValue('01-Jan-2011')],
['Maturity Date', DateHelper::getDateValue('25-Oct-2012')],
['Frequency', 4],
];
// Some basic formatting for the data
$worksheet->fromArray($arguments, null, 'A1');
$worksheet->getStyle('B1:B2')->getNumberFormat()->setFormatCode('dd-mmm-yyyy');
// Now the formula
$worksheet->setCellValue('B6', '=COUPDAYBS(B1, B2, B3)');
$helper->log($worksheet->getCell('B6')->getValue());
$helper->log('COUPDAYBS() Result is ' . $worksheet->getCell('B6')->getFormattedValue());

View File

@ -0,0 +1,29 @@
<?php
use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\Helpers as DateHelper;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns the number of days in the coupon period that contains the settlement date.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
// Add some data
$arguments = [
['Settlement Date', DateHelper::getDateValue('01-Jan-2011')],
['Maturity Date', DateHelper::getDateValue('25-Oct-2012')],
['Frequency', 4],
];
// Some basic formatting for the data
$worksheet->fromArray($arguments, null, 'A1');
$worksheet->getStyle('B1:B2')->getNumberFormat()->setFormatCode('dd-mmm-yyyy');
// Now the formula
$worksheet->setCellValue('B6', '=COUPDAYS(B1, B2, B3)');
$helper->log($worksheet->getCell('B6')->getValue());
$helper->log('COUPDAYS() Result is ' . $worksheet->getCell('B6')->getFormattedValue());

View File

@ -0,0 +1,29 @@
<?php
use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\Helpers as DateHelper;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns the number of days from the settlement date to the next coupon date.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
// Add some data
$arguments = [
['Settlement Date', DateHelper::getDateValue('01-Jan-2011')],
['Maturity Date', DateHelper::getDateValue('25-Oct-2012')],
['Frequency', 4],
];
// Some basic formatting for the data
$worksheet->fromArray($arguments, null, 'A1');
$worksheet->getStyle('B1:B2')->getNumberFormat()->setFormatCode('dd-mmm-yyyy');
// Now the formula
$worksheet->setCellValue('B6', '=COUPDAYSNC(B1, B2, B3)');
$helper->log($worksheet->getCell('B6')->getValue());
$helper->log('COUPDAYSNC() Result is ' . $worksheet->getCell('B6')->getFormattedValue());

View File

@ -0,0 +1,30 @@
<?php
use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\Helpers as DateHelper;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns the next coupon date, after the settlement date.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
// Add some data
$arguments = [
['Settlement Date', DateHelper::getDateValue('01-Jan-2011')],
['Maturity Date', DateHelper::getDateValue('25-Oct-2012')],
['Frequency', 4],
];
// Some basic formatting for the data
$worksheet->fromArray($arguments, null, 'A1');
$worksheet->getStyle('B1:B2')->getNumberFormat()->setFormatCode('dd-mmm-yyyy');
// Now the formula
$worksheet->setCellValue('B6', '=COUPNCD(B1, B2, B3)');
$worksheet->getStyle('B6')->getNumberFormat()->setFormatCode('dd-mmm-yyyy');
$helper->log($worksheet->getCell('B6')->getValue());
$helper->log('COUPNCD() Result is ' . $worksheet->getCell('B6')->getFormattedValue());

View File

@ -0,0 +1,30 @@
<?php
use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\Helpers as DateHelper;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns the number of coupons payable, between a security\'s settlement date and maturity date,');
$helper->log('rounded up to the nearest whole coupon.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
// Add some data
$arguments = [
['Settlement Date', DateHelper::getDateValue('01-Jan-2011')],
['Maturity Date', DateHelper::getDateValue('25-Oct-2012')],
['Frequency', 4],
];
// Some basic formatting for the data
$worksheet->fromArray($arguments, null, 'A1');
$worksheet->getStyle('B1:B2')->getNumberFormat()->setFormatCode('dd-mmm-yyyy');
// Now the formula
$worksheet->setCellValue('B6', '=COUPNUM(B1, B2, B3)');
$helper->log($worksheet->getCell('B6')->getValue());
$helper->log('COUPNUM() Result is ' . $worksheet->getCell('B6')->getFormattedValue());

View File

@ -0,0 +1,30 @@
<?php
use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\Helpers as DateHelper;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns the previous coupon date, before the settlement date for a security.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
// Add some data
$arguments = [
['Settlement Date', DateHelper::getDateValue('01-Jan-2011')],
['Maturity Date', DateHelper::getDateValue('25-Oct-2012')],
['Frequency', 4],
];
// Some basic formatting for the data
$worksheet->fromArray($arguments, null, 'A1');
$worksheet->getStyle('B1:B2')->getNumberFormat()->setFormatCode('dd-mmm-yyyy');
// Now the formula
$worksheet->setCellValue('B6', '=COUPPCD(B1, B2, B3)');
$worksheet->getStyle('B6')->getNumberFormat()->setFormatCode('dd-mmm-yyyy');
$helper->log($worksheet->getCell('B6')->getValue());
$helper->log('COUPPCD() Result is ' . $worksheet->getCell('B6')->getFormattedValue());

View File

@ -0,0 +1,38 @@
<?php
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns the cumulative interest paid on a loan or investment, between two specified periods.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
// Add some data
$arguments = [
['Interest Rate (per period)', 0.05 / 12],
['Number of Periods', 5 * 12],
['Present Value', 50000],
];
// Some basic formatting for the data
$worksheet->fromArray($arguments, null, 'A1');
$worksheet->getStyle('B1')->getNumberFormat()->setFormatCode('0.00%');
$worksheet->getStyle('B3')->getNumberFormat()->setFormatCode('$#,##0.00');
// Now the formula
$baseRow = 5;
for ($year = 1; $year <= 5; ++$year) {
$row = (string) ($baseRow + $year);
$yearStartPeriod = (int) $year * 12 - 11;
$yearEndPeriod = (int) $year * 12;
$worksheet->setCellValue("A{$row}", "Yr {$year}");
$worksheet->setCellValue("B{$row}", "=CUMIPMT(\$B\$1, \$B\$2, \$B\$3, {$yearStartPeriod}, {$yearEndPeriod}, 0)");
$worksheet->getStyle("B{$row}")->getNumberFormat()->setFormatCode('$#,##0.00;-$#,##0.00');
$helper->log($worksheet->getCell("B{$row}")->getValue());
$helper->log("CUMIPMT() Year {$year} Result is " . $worksheet->getCell("B{$row}")->getFormattedValue());
}

View File

@ -0,0 +1,38 @@
<?php
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns the cumulative payment on the principal of a loan or investment, between two specified periods.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
// Add some data
$arguments = [
['Interest Rate (per period)', 0.05 / 12],
['Number of Periods', 5 * 12],
['Present Value', 50000],
];
// Some basic formatting for the data
$worksheet->fromArray($arguments, null, 'A1');
$worksheet->getStyle('B1')->getNumberFormat()->setFormatCode('0.00%');
$worksheet->getStyle('B3')->getNumberFormat()->setFormatCode('$#,##0.00');
// Now the formula
$baseRow = 5;
for ($year = 1; $year <= 5; ++$year) {
$row = (string) ($baseRow + $year);
$yearStartPeriod = (int) $year * 12 - 11;
$yearEndPeriod = (int) $year * 12;
$worksheet->setCellValue("A{$row}", "Yr {$year}");
$worksheet->setCellValue("B{$row}", "=CUMPRINC(\$B\$1, \$B\$2, \$B\$3, {$yearStartPeriod}, {$yearEndPeriod}, 0)");
$worksheet->getStyle("B{$row}")->getNumberFormat()->setFormatCode('$#,##0.00;-$#,##0.00');
$helper->log($worksheet->getCell("B{$row}")->getValue());
$helper->log("CUMPRINC() Year {$year} Result is " . $worksheet->getCell("B{$row}")->getFormattedValue());
}

View File

@ -0,0 +1,50 @@
<?php
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns the depreciation of an asset, using the Fixed Declining Balance Method,');
$helper->log('for each period of the asset\'s lifetime.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
// Add some data
$arguments = [
['Cost Value', 10000],
['Salvage', 1000],
['Life', 5, 'Years'],
];
// Some basic formatting for the data
$worksheet->fromArray($arguments, null, 'A1');
$worksheet->getStyle('B1:B2')->getNumberFormat()->setFormatCode('$#,##0.00');
// Now the formula
$baseRow = 5;
for ($year = 1; $year <= 5; ++$year) {
$row = (string) ($baseRow + $year);
$worksheet->setCellValue("A{$row}", "Depreciation after Yr {$year}");
$worksheet->setCellValue("B{$row}", "=DB(\$B\$1, \$B\$2, \$B\$3, {$year})");
$worksheet->getStyle("B{$row}")->getNumberFormat()->setFormatCode('$#,##0.00;-$#,##0.00');
$helper->log($worksheet->getCell("B{$row}")->getValue());
$helper->log("DB() Year {$year} Result is " . $worksheet->getCell("B{$row}")->getFormattedValue());
}
$helper->log('And with depreciation only starting after 6 months.');
$baseRow = 12;
for ($year = 1; $year <= 6; ++$year) {
$row = (string) ($baseRow + $year);
$worksheet->setCellValue("A{$row}", "Depreciation after Yr {$year}");
$worksheet->setCellValue("B{$row}", "=DB(\$B\$1, \$B\$2, \$B\$3, {$year}, 6)");
$worksheet->getStyle("B{$row}")->getNumberFormat()->setFormatCode('$#,##0.00;-$#,##0.00');
$helper->log($worksheet->getCell("B{$row}")->getValue());
$helper->log("DB() Year {$year} Result is " . $worksheet->getCell("B{$row}")->getFormattedValue());
}

View File

@ -0,0 +1,36 @@
<?php
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns the depreciation of an asset, using the Double Declining Balance Method,');
$helper->log('for each period of the asset\'s lifetime.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
// Add some data
$arguments = [
['Cost Value', 10000],
['Salvage', 1000],
['Life', 5, 'Years'],
];
// Some basic formatting for the data
$worksheet->fromArray($arguments, null, 'A1');
$worksheet->getStyle('B1:B2')->getNumberFormat()->setFormatCode('$#,##0.00');
// Now the formula
$baseRow = 5;
for ($year = 1; $year <= 5; ++$year) {
$row = (string) ($baseRow + $year);
$worksheet->setCellValue("A{$row}", "Depreciation after Yr {$year}");
$worksheet->setCellValue("B{$row}", "=DDB(\$B\$1, \$B\$2, \$B\$3, {$year})");
$worksheet->getStyle("B{$row}")->getNumberFormat()->setFormatCode('$#,##0.00;-$#,##0.00');
$helper->log($worksheet->getCell("B{$row}")->getValue());
$helper->log("DDB() Year {$year} Result is " . $worksheet->getCell("B{$row}")->getFormattedValue());
}

View File

@ -0,0 +1,32 @@
<?php
use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\Helpers as DateHelper;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns the the Discount Rate for a security.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
// Add some data
$arguments = [
['Settlement Date', DateHelper::getDateValue('01-Apr-2016')],
['Maturity Date', DateHelper::getDateValue('31-Mar-2021')],
['Par Value', 95.00],
['Redemption Value', 100.00],
];
// Some basic formatting for the data
$worksheet->fromArray($arguments, null, 'A1');
$worksheet->getStyle('B1:B2')->getNumberFormat()->setFormatCode('dd-mmm-yyyy');
$worksheet->getStyle('B3:B4')->getNumberFormat()->setFormatCode('$#,##0.00');
// Now the formula
$worksheet->setCellValue('B7', '=DISC(B1, B2, B3, B4)');
$worksheet->getStyle('B7')->getNumberFormat()->setFormatCode('0.00%');
$helper->log($worksheet->getCell('B7')->getValue());
$helper->log('DISC() Result is ' . $worksheet->getCell('B7')->getFormattedValue());

View File

@ -0,0 +1,30 @@
<?php
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns the dollar value in fractional notation, into a dollar value expressed as a decimal.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
// Add some data
$arguments = [
[1.01, 16],
[1.1, 16],
[1.03, 32],
[1.3, 32],
[1.12, 32],
];
$worksheet->fromArray($arguments, null, 'A1');
// Now the formula
for ($row = 1; $row <= 5; ++$row) {
$worksheet->setCellValue("C{$row}", "=DOLLARDE(A{$row}, B{$row})");
$helper->log($worksheet->getCell("C{$row}")->getValue());
$helper->log('DOLLARDE() Result is ' . $worksheet->getCell("C{$row}")->getFormattedValue());
}

View File

@ -0,0 +1,30 @@
<?php
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns the dollar value expressed as a decimal number, into a dollar price, expressed as a fraction.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
// Add some data
$arguments = [
[1.0625, 16],
[1.625, 16],
[1.09375, 32],
[1.9375, 32],
[1.375, 32],
];
$worksheet->fromArray($arguments, null, 'A1');
// Now the formula
for ($row = 1; $row <= 5; ++$row) {
$worksheet->setCellValue("C{$row}", "=DOLLARFR(A{$row}, B{$row})");
$helper->log($worksheet->getCell("C{$row}")->getValue());
$helper->log('DOLLARFR() Result is ' . $worksheet->getCell("C{$row}")->getFormattedValue());
}

View File

@ -0,0 +1,31 @@
<?php
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns the effective annual interest rate for a given nominal interest rate and number of');
$helper->log('compounding periods per year.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
// Add some data
$arguments = [
[0.10, 4],
[0.10, 2],
[0.025, 2],
];
$worksheet->fromArray($arguments, null, 'A1');
$worksheet->getStyle('B1:B3')->getNumberFormat()->setFormatCode('0.00%');
// Now the formula
for ($row = 1; $row <= 3; ++$row) {
$worksheet->setCellValue("C{$row}", "=EFFECT(A{$row}, B{$row})");
$worksheet->getStyle("C{$row}")->getNumberFormat()->setFormatCode('0.00%');
$helper->log($worksheet->getCell("C{$row}")->getValue());
$helper->log('EFFECT() Result is ' . $worksheet->getCell("C{$row}")->getFormattedValue());
}

View File

@ -0,0 +1,36 @@
<?php
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns the Future Value of an investment with periodic constant payments and a constant interest rate.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
// Add some data
$arguments = [
['Interest Rate', 0.05, 0.10],
['Pament Frequency', 12, 4],
['Duration (Years)', 5, 4],
['Investment', -1000.00, -2000.00],
['Payment Type', 0, 1],
];
// Some basic formatting for the data
$worksheet->fromArray($arguments, null, 'A1');
$worksheet->getStyle('B1:C1')->getNumberFormat()->setFormatCode('0.00%');
$worksheet->getStyle('B4:C4')->getNumberFormat()->setFormatCode('$#,##0.00');
// Now the formula
$worksheet->setCellValue('B8', '=FV(B1/B2, B3*B2, B4)');
$worksheet->setCellValue('C8', '=FV(C1/C2, C3*C2, C4, null, C5)');
$worksheet->getStyle('B8:C8')->getNumberFormat()->setFormatCode('$#,##0.00');
$helper->log($worksheet->getCell('B8')->getValue());
$helper->log('FV() Result is ' . $worksheet->getCell('B8')->getFormattedValue());
$helper->log($worksheet->getCell('C6')->getValue());
$helper->log('FV() Result is ' . $worksheet->getCell('C8')->getFormattedValue());

View File

@ -0,0 +1,36 @@
<?php
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns the Future Value of an initial principal, after applying a series of compound interest rates.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
// Add some data
$arguments = [
['Principal'],
[10000.00],
[null],
['Schedule'],
[0.05],
[0.05],
[0.035],
[0.035],
[0.035],
];
// Some basic formatting for the data
$worksheet->fromArray($arguments, null, 'A1');
$worksheet->getStyle('A2')->getNumberFormat()->setFormatCode('$#,##0.00');
$worksheet->getStyle('A5:A9')->getNumberFormat()->setFormatCode('0.00%');
// Now the formula
$worksheet->setCellValue('B1', '=FVSCHEDULE(A2, A5:A9)');
$worksheet->getStyle('B1')->getNumberFormat()->setFormatCode('$#,##0.00');
$helper->log($worksheet->getCell('B1')->getValue());
$helper->log('FVSCHEDULE() Result is ' . $worksheet->getCell('B1')->getFormattedValue());

View File

@ -0,0 +1,32 @@
<?php
use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\Helpers as DateHelper;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns the interest rate for a fully invested security.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
// Add some data
$arguments = [
['Settlement Date', DateHelper::getDateValue('01-Apr-2005')],
['Maturity Date', DateHelper::getDateValue('31-Mar-2010')],
['Investment', 1000.00],
['Investment', 2125.00],
];
// Some basic formatting for the data
$worksheet->fromArray($arguments, null, 'A1');
$worksheet->getStyle('B1:B2')->getNumberFormat()->setFormatCode('dd-mmm-yyyy');
$worksheet->getStyle('B3:B4')->getNumberFormat()->setFormatCode('$#,##0.00');
// Now the formula
$worksheet->setCellValue('B7', '=INTRATE(B1, B2, B3, B4)');
$worksheet->getStyle('B7')->getNumberFormat()->setFormatCode('0.00%');
$helper->log($worksheet->getCell('B7')->getValue());
$helper->log('INTRATE() Result is ' . $worksheet->getCell('B7')->getFormattedValue());

View File

@ -0,0 +1,37 @@
<?php
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns the interest payment, during a specific period of a loan or investment that is paid in,');
$helper->log('constant periodic payments, with a constant interest rate.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
// Add some data
$arguments = [
['Interest Rate', 0.05],
['Number of Years', 5],
['Present Value', 50000.00],
];
// Some basic formatting for the data
$worksheet->fromArray($arguments, null, 'A1');
$worksheet->getStyle('B1')->getNumberFormat()->setFormatCode('0.00%');
$worksheet->getStyle('B3')->getNumberFormat()->setFormatCode('$#,##0.00');
// Now the formula
$baseRow = 6;
for ($month = 1; $month <= 12; ++$month) {
$row = (string) ($baseRow + $month);
$worksheet->setCellValue("A{$row}", "Payment for Mth {$month}");
$worksheet->setCellValue("B{$row}", "=IPMT(\$B\$1/12, {$month}, \$B\$2*12, \$B\$3)");
$worksheet->getStyle("B{$row}")->getNumberFormat()->setFormatCode('$#,##0.00;-$#,##0.00');
$helper->log($worksheet->getCell("B{$row}")->getValue());
$helper->log("IPMT() Month {$month} Result is " . $worksheet->getCell("B{$row}")->getFormattedValue());
}

View File

@ -0,0 +1,38 @@
<?php
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns the Internal Rate of Return for a supplied series of periodic cash flows.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
// Add some data
$arguments = [
['Initial Investment', -100.00],
['Year 1 Income', 20.00],
['Year 2 Income', 24.00, 'IRR after 3 Years'],
['Year 3 Income', 28.80],
['Year 4 Income', 34.56, 'IRR after 5 Years'],
['Year 5 Income', 41.47],
];
// Some basic formatting for the data
$worksheet->fromArray($arguments, null, 'A1');
$worksheet->getStyle('B1:B6')->getNumberFormat()->setFormatCode('$#,##0.00;-$#,##0.00');
// Now the formula
$worksheet->setCellValue('C4', '=IRR(B1:B4)');
$worksheet->getStyle('C4')->getNumberFormat()->setFormatCode('0.00%');
$helper->log($worksheet->getCell('C4')->getValue());
$helper->log('IRR() Result is ' . $worksheet->getCell('C4')->getFormattedValue());
$worksheet->setCellValue('C6', '=IRR(B1:B6)');
$worksheet->getStyle('C6')->getNumberFormat()->setFormatCode('0.00%');
$helper->log($worksheet->getCell('C6')->getValue());
$helper->log('IRR() Result is ' . $worksheet->getCell('C6')->getFormattedValue());

View File

@ -0,0 +1,36 @@
<?php
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns the interest paid during a specific period of a loan or investment.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
// Add some data
$arguments = [
['Interest Rate', 0.05],
['Number of Years', 5],
['Present Value', 50000.00],
];
// Some basic formatting for the data
$worksheet->fromArray($arguments, null, 'A1');
$worksheet->getStyle('B1')->getNumberFormat()->setFormatCode('0.00%');
$worksheet->getStyle('B3')->getNumberFormat()->setFormatCode('$#,##0.00');
// Now the formula
$baseRow = 6;
for ($month = 1; $month <= 12; ++$month) {
$row = (string) ($baseRow + $month);
$worksheet->setCellValue("A{$row}", "Payment for Mth {$month}");
$worksheet->setCellValue("B{$row}", "=ISPMT(\$B\$1/12, {$month}, \$B\$2*12, \$B\$3)");
$worksheet->getStyle("B{$row}")->getNumberFormat()->setFormatCode('$#,##0.00;-$#,##0.00');
$helper->log($worksheet->getCell("B{$row}")->getValue());
$helper->log("ISPMT() Month {$month} Result is " . $worksheet->getCell("B{$row}")->getFormattedValue());
}

View File

@ -0,0 +1,42 @@
<?php
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns the Modified Internal Rate of Return for a supplied series of periodic cash flows.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
// Add some data
$arguments = [
['Initial Investment', -100.00],
['Year 1 Income', 18.00],
['Year 2 Income', 22.50, 'MIRR after 3 Years'],
['Year 3 Income', 28.00],
['Year 4 Income', 35.50, 'MIRR after 5 Years'],
['Year 5 Income', 45.00],
[null],
['Finance Rate', 0.055],
['Re-invest Rate', 0.05],
];
// Some basic formatting for the data
$worksheet->fromArray($arguments, null, 'A1');
$worksheet->getStyle('B1:B6')->getNumberFormat()->setFormatCode('$#,##0.00;-$#,##0.00');
$worksheet->getStyle('B8:B9')->getNumberFormat()->setFormatCode('0.00%');
// Now the formula
$worksheet->setCellValue('C4', '=MIRR(B1:B4, B8, B9)');
$worksheet->getStyle('C4')->getNumberFormat()->setFormatCode('0.00%');
$helper->log($worksheet->getCell('C4')->getValue());
$helper->log('MIRR() Result is ' . $worksheet->getCell('C4')->getFormattedValue());
$worksheet->setCellValue('C6', '=MIRR(B1:B6, B8, B9)');
$worksheet->getStyle('C6')->getNumberFormat()->setFormatCode('0.00%');
$helper->log($worksheet->getCell('C6')->getValue());
$helper->log('MIRR() Result is ' . $worksheet->getCell('C6')->getFormattedValue());

View File

@ -0,0 +1,31 @@
<?php
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns the nominal interest rate for a given effective interest rate and number of');
$helper->log('compounding periods per year.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
// Add some data
$arguments = [
[0.10, 4],
[0.10, 2],
[0.025, 12],
];
$worksheet->fromArray($arguments, null, 'A1');
$worksheet->getStyle('B1:B3')->getNumberFormat()->setFormatCode('0.00%');
// Now the formula
for ($row = 1; $row <= 3; ++$row) {
$worksheet->setCellValue("C{$row}", "=NOMINAL(A{$row}, B{$row})");
$worksheet->getStyle("C{$row}")->getNumberFormat()->setFormatCode('0.00%');
$helper->log($worksheet->getCell("C{$row}")->getValue());
$helper->log('NOMINAL() Result is ' . $worksheet->getCell("C{$row}")->getFormattedValue());
}

View File

@ -0,0 +1,39 @@
<?php
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstants;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns the number of periods required to pay off a loan, for a constant periodic payment');
$helper->log('and a constant interest rate.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
// Add some data
$arguments = [
['Interest Rate', 0.04, 0.06],
['Payments per Year', 1, 4],
['Payment Amount', -6000.00, -2000],
['Present Value', 50000, 60000],
['Future Value', null, 30000],
['Payment Type', null, FinancialConstants::PAYMENT_BEGINNING_OF_PERIOD],
];
// Some basic formatting for the data
$worksheet->fromArray($arguments, null, 'A1');
$worksheet->getStyle('B1:C1')->getNumberFormat()->setFormatCode('0.00%');
$worksheet->getStyle('B3:C5')->getNumberFormat()->setFormatCode('$#,##0.00');
// Now the formula
$worksheet->setCellValue('B8', '=NPER(B1/B2, B3, B4)');
$helper->log($worksheet->getCell('B8')->getValue());
$helper->log('NPER() Result is ' . $worksheet->getCell('B8')->getFormattedValue());
$worksheet->setCellValue('C8', '=NPER(C1/C2, C3, C4, C5, C6)');
$helper->log($worksheet->getCell('C8')->getValue());
$helper->log('NPER() Result is ' . $worksheet->getCell('C8')->getFormattedValue());

View File

@ -0,0 +1,43 @@
<?php
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns the Net Present Value of an investment, based on a supplied discount rate,');
$helper->log('and a series of future payments and income.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
// Add some data
$arguments = [
['Annual Discount Rate', 0.02, 0.05],
['Initial Investment Cost', -5000.00, -10000],
['Return from Year 1', 800.00, 2000.00],
['Return from Year 2', 950.00, 2400.00],
['Return from Year 3', 1080.00, 2900.00],
['Return from Year 4', 1220.00, 3500.00],
['Return from Year 5', 1500.00, 4100.00],
];
// Some basic formatting for the data
$worksheet->fromArray($arguments, null, 'A1');
$worksheet->getStyle('B1:C1')->getNumberFormat()->setFormatCode('0.00%');
$worksheet->getStyle('B2:C7')->getNumberFormat()->setFormatCode('$#,##0.00');
// Now the formula
// When initial investment is made at the end of the first period
$worksheet->setCellValue('B10', '=NPV(B1, B2:B7)');
$worksheet->getStyle('B10')->getNumberFormat()->setFormatCode('$#,##0.00');
$helper->log($worksheet->getCell('B10')->getValue());
$helper->log('NPV() Result is ' . $worksheet->getCell('B10')->getFormattedValue());
// When initial investment is made at the start of the first period
$worksheet->setCellValue('C10', '=NPV(C1, C3:C7) + C2');
$worksheet->getStyle('C10')->getNumberFormat()->setFormatCode('$#,##0.00');
$helper->log($worksheet->getCell('C10')->getValue());
$helper->log('NPV() Result is ' . $worksheet->getCell('C10')->getFormattedValue());

View File

@ -0,0 +1,22 @@
<?php
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns a text reference to a single cell in a worksheet.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
$worksheet->getCell('A1')->setValue('=ADDRESS(2,3)');
$worksheet->getCell('A2')->setValue('=ADDRESS(2,3,2)');
$worksheet->getCell('A3')->setValue('=ADDRESS(2,3,2,FALSE)');
$worksheet->getCell('A4')->setValue('=ADDRESS(2,3,1,FALSE,"[Book1]Sheet1")');
$worksheet->getCell('A5')->setValue('=ADDRESS(2,3,1,FALSE,"EXCEL SHEET")');
for ($row = 1; $row <= 5; ++$row) {
$cell = $worksheet->getCell("A{$row}");
$helper->log("A{$row}: {$cell->getValue()} => {$cell->getCalculatedValue()}");
}

View File

@ -0,0 +1,23 @@
<?php
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns the column index of a cell.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
$worksheet->getCell('A1')->setValue('=COLUMN(C13)');
$worksheet->getCell('A2')->setValue('=COLUMN(E13:G15)');
$worksheet->getCell('F1')->setValue('=COLUMN()');
for ($row = 1; $row <= 2; ++$row) {
$cell = $worksheet->getCell("A{$row}");
$helper->log("A{$row}: {$cell->getValue()} => {$cell->getCalculatedValue()}");
}
$cell = $worksheet->getCell('F1');
$helper->log("F1: {$cell->getValue()} => {$cell->getCalculatedValue()}");

View File

@ -0,0 +1,21 @@
<?php
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns the number of columns in an array or reference.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
$worksheet->getCell('A1')->setValue('=COLUMNS(C1:G4)');
$worksheet->getCell('A2')->setValue('=COLUMNS({1,2,3;4,5,6})');
$worksheet->getCell('A3')->setValue('=ROWS(C1:E4 D3:G5)');
$worksheet->getCell('A4')->setValue('=COLUMNS(1:1)');
for ($row = 1; $row <= 4; ++$row) {
$cell = $worksheet->getCell("A{$row}");
$helper->log("A{$row}: {$cell->getValue()} => {$cell->getCalculatedValue()}");
}

View File

@ -0,0 +1,39 @@
<?php
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns the row index of a cell.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
$data1 = [
['Apples', 'Lemons'],
['Bananas', 'Pears'],
];
$data2 = [
[4, 6],
[5, 3],
[6, 9],
[7, 5],
[8, 3],
];
$worksheet->fromArray($data1, null, 'A1');
$worksheet->fromArray($data2, null, 'C1');
$worksheet->getCell('A11')->setValue('=INDEX(A1:B2, 2, 2)');
$worksheet->getCell('A12')->setValue('=INDEX(A1:B2, 2, 1)');
$worksheet->getCell('A13')->setValue('=INDEX({1,2;3,4}, 0, 2)');
$worksheet->getCell('A14')->setValue('=INDEX(C1:C5, 5)');
$worksheet->getCell('A15')->setValue('=INDEX(C1:D5, 5, 2)');
$worksheet->getCell('A16')->setValue('=SUM(INDEX(C1:D5, 5, 0))');
for ($row = 11; $row <= 16; ++$row) {
$cell = $worksheet->getCell("A{$row}");
$helper->log("A{$row}: {$cell->getValue()} => {$cell->getCalculatedValue()}");
}

View File

@ -0,0 +1,33 @@
<?php
use PhpOffice\PhpSpreadsheet\NamedRange;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns the cell specified by a text string.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
$data = [
[8, 9, 0],
[3, 4, 5],
[9, 1, 3],
[4, 6, 2],
];
$worksheet->fromArray($data, null, 'C1');
$spreadsheet->addNamedRange(new NamedRange('NAMED_RANGE_FOR_CELL_D4', $worksheet, '="$D$4"'));
$worksheet->getCell('A1')->setValue('=INDIRECT("C1")');
$worksheet->getCell('A2')->setValue('=INDIRECT("D"&4)');
$worksheet->getCell('A3')->setValue('=INDIRECT("E"&ROW())');
$worksheet->getCell('A4')->setValue('=SUM(INDIRECT("$C$4:$E$4"))');
$worksheet->getCell('A5')->setValue('=INDIRECT(NAMED_RANGE_FOR_CELL_D4)');
for ($row = 1; $row <= 5; ++$row) {
$cell = $worksheet->getCell("A{$row}");
$helper->log("A{$row}: {$cell->getValue()} => {$cell->getCalculatedValue()}");
}

View File

@ -0,0 +1,33 @@
<?php
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns a cell range that is a specified number of rows and columns from a cell or range of cells.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
$data = [
[null, 'Week 1', 'Week 2', 'Week 3', 'Week 4'],
['Sunday', 4500, 2200, 3800, 1500],
['Monday', 5500, 6100, 5200, 4800],
['Tuesday', 7000, 6200, 5000, 7100],
['Wednesday', 8000, 4000, 3900, 7600],
['Thursday', 5900, 5500, 6900, 7100],
['Friday', 4900, 6300, 6900, 5200],
['Saturday', 3500, 3900, 5100, 4100],
];
$worksheet->fromArray($data, null, 'A3');
$worksheet->getCell('H1')->setValue('=OFFSET(A3, 3, 1)');
$worksheet->getCell('H2')->setValue('=SUM(OFFSET(A3, 3, 1, 1, 4))');
$worksheet->getCell('H3')->setValue('=SUM(OFFSET(B3:E3, 3, 0))');
$worksheet->getCell('H4')->setValue('=SUM(OFFSET(E3, 1, -3, 7))');
for ($row = 1; $row <= 4; ++$row) {
$cell = $worksheet->getCell("H{$row}");
$helper->log("H{$row}: {$cell->getValue()} => {$cell->getCalculatedValue()}");
}

View File

@ -0,0 +1,20 @@
<?php
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns the row index of a cell.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
$worksheet->getCell('A1')->setValue('=ROW(C13)');
$worksheet->getCell('A2')->setValue('=ROW(E19:G21)');
$worksheet->getCell('A3')->setValue('=ROW()');
for ($row = 1; $row <= 3; ++$row) {
$cell = $worksheet->getCell("A{$row}");
$helper->log("A{$row}: {$cell->getValue()} => {$cell->getCalculatedValue()}");
}

View File

@ -0,0 +1,20 @@
<?php
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../../Header.php';
$helper->log('Returns the row index of a cell.');
// Create new PhpSpreadsheet object
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
$worksheet->getCell('A1')->setValue('=ROWS(C1:E4)');
$worksheet->getCell('A2')->setValue('=ROWS({1,2,3;4,5,6})');
$worksheet->getCell('A3')->setValue('=ROWS(C1:E4 D3:G5)');
for ($row = 1; $row <= 3; ++$row) {
$cell = $worksheet->getCell("A{$row}");
$helper->log("A{$row}: {$cell->getValue()} => {$cell->getCalculatedValue()}");
}

View File

@ -5,6 +5,11 @@ use PhpOffice\PhpSpreadsheet\Settings;
require __DIR__ . '/../Header.php';
if (PHP_VERSION_ID >= 80000) {
$helper->log('Jpgraph no longer runs against PHP8');
exit;
}
// Change these values to select the Rendering library that you wish to use
Settings::setChartRenderer(\PhpOffice\PhpSpreadsheet\Chart\Renderer\JpGraph::class);

View File

@ -1,5 +1,6 @@
<?php
use PhpOffice\PhpSpreadsheet\Shared\File;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../Header.php';
@ -35,7 +36,7 @@ $helper->log('Write link: ' . $baseUrl);
$drawing->setWorksheet($aSheet);
$filename = tempnam(\PhpOffice\PhpSpreadsheet\Shared\File::sysGetTempDir(), 'phpspreadsheet-test');
$filename = File::temporaryFilename();
$writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, $inputFileType);
$writer->save($filename);

Binary file not shown.

Binary file not shown.

View File

@ -31,7 +31,9 @@ $spreadsheet->getProperties()->setCreator('Maarten Balliauw')
$helper->log('Add some data');
$spreadsheet->setActiveSheetIndex(0);
$spreadsheet->getActiveSheet()->setCellValue('B1', 'Invoice');
$spreadsheet->getActiveSheet()->setCellValue('D1', Date::PHPToExcel(gmmktime(0, 0, 0, date('m'), date('d'), date('Y'))));
$date = new DateTime('now');
$date->setTime(0, 0, 0);
$spreadsheet->getActiveSheet()->setCellValue('D1', Date::PHPToExcel($date));
$spreadsheet->getActiveSheet()->getStyle('D1')->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_DATE_XLSX15);
$spreadsheet->getActiveSheet()->setCellValue('E1', '#12566');

File diff suppressed because it is too large Load Diff

View File

@ -2,126 +2,11 @@
namespace PhpOffice\PhpSpreadsheet\Calculation;
/**
* @deprecated 1.17.0
*/
class Database
{
/**
* fieldExtract.
*
* Extracts the column ID to use for the data field.
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
* first row of the list contains labels for each column.
* @param mixed $field Indicates which column is used in the function. Enter the
* column label enclosed between double quotation marks, such as
* "Age" or "Yield," or a number (without quotation marks) that
* represents the position of the column within the list: 1 for
* the first column, 2 for the second column, and so on.
*
* @return null|string
*/
private static function fieldExtract($database, $field)
{
$field = strtoupper(Functions::flattenSingleValue($field));
$fieldNames = array_map('strtoupper', array_shift($database));
if (is_numeric($field)) {
$keys = array_keys($fieldNames);
return $keys[$field - 1];
}
$key = array_search($field, $fieldNames);
return ($key) ? $key : null;
}
/**
* filter.
*
* Parses the selection criteria, extracts the database rows that match those criteria, and
* returns that subset of rows.
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
* first row of the list contains labels for each column.
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
* You can use any range for the criteria argument, as long as it
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return array of mixed
*/
private static function filter($database, $criteria)
{
$fieldNames = array_shift($database);
$criteriaNames = array_shift($criteria);
// Convert the criteria into a set of AND/OR conditions with [:placeholders]
$testConditions = $testValues = [];
$testConditionsCount = 0;
foreach ($criteriaNames as $key => $criteriaName) {
$testCondition = [];
$testConditionCount = 0;
foreach ($criteria as $row => $criterion) {
if ($criterion[$key] > '') {
$testCondition[] = '[:' . $criteriaName . ']' . Functions::ifCondition($criterion[$key]);
++$testConditionCount;
}
}
if ($testConditionCount > 1) {
$testConditions[] = 'OR(' . implode(',', $testCondition) . ')';
++$testConditionsCount;
} elseif ($testConditionCount == 1) {
$testConditions[] = $testCondition[0];
++$testConditionsCount;
}
}
if ($testConditionsCount > 1) {
$testConditionSet = 'AND(' . implode(',', $testConditions) . ')';
} elseif ($testConditionsCount == 1) {
$testConditionSet = $testConditions[0];
}
// Loop through each row of the database
foreach ($database as $dataRow => $dataValues) {
// Substitute actual values from the database row for our [:placeholders]
$testConditionList = $testConditionSet;
foreach ($criteriaNames as $key => $criteriaName) {
$k = array_search($criteriaName, $fieldNames);
if (isset($dataValues[$k])) {
$dataValue = $dataValues[$k];
$dataValue = (is_string($dataValue)) ? Calculation::wrapResult(strtoupper($dataValue)) : $dataValue;
$testConditionList = str_replace('[:' . $criteriaName . ']', $dataValue, $testConditionList);
}
}
// evaluate the criteria against the row data
$result = Calculation::getInstance()->_calculateFormulaValue('=' . $testConditionList);
// If the row failed to meet the criteria, remove it from the database
if (!$result) {
unset($database[$dataRow]);
}
}
return $database;
}
private static function getFilteredColumn($database, $field, $criteria)
{
// reduce the database to a set of rows that match all the criteria
$database = self::filter($database, $criteria);
// extract an array of values for the requested column
$colData = [];
foreach ($database as $row) {
$colData[] = $row[$field];
}
return $colData;
}
/**
* DAVERAGE.
*
@ -130,6 +15,10 @@ class Database
* Excel Function:
* DAVERAGE(database,field,criteria)
*
* @Deprecated 1.17.0
*
* @see Use the evaluate() method in the Database\DAverage class instead
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
@ -145,19 +34,11 @@ class Database
* the column label in which you specify a condition for the
* column.
*
* @return float|string
* @return null|float|string
*/
public static function DAVERAGE($database, $field, $criteria)
{
$field = self::fieldExtract($database, $field);
if ($field === null) {
return null;
}
// Return
return Statistical::AVERAGE(
self::getFilteredColumn($database, $field, $criteria)
);
return Database\DAverage::evaluate($database, $field, $criteria);
}
/**
@ -169,14 +50,15 @@ class Database
* Excel Function:
* DCOUNT(database,[field],criteria)
*
* Excel Function:
* DAVERAGE(database,field,criteria)
* @Deprecated 1.17.0
*
* @see Use the evaluate() method in the Database\DCount class instead
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
* first row of the list contains labels for each column.
* @param int|string $field Indicates which column is used in the function. Enter the
* @param null|int|string $field Indicates which column is used in the function. Enter the
* column label enclosed between double quotation marks, such as
* "Age" or "Yield," or a number (without quotation marks) that
* represents the position of the column within the list: 1 for
@ -194,15 +76,7 @@ class Database
*/
public static function DCOUNT($database, $field, $criteria)
{
$field = self::fieldExtract($database, $field);
if ($field === null) {
return null;
}
// Return
return Statistical::COUNT(
self::getFilteredColumn($database, $field, $criteria)
);
return Database\DCount::evaluate($database, $field, $criteria);
}
/**
@ -213,11 +87,15 @@ class Database
* Excel Function:
* DCOUNTA(database,[field],criteria)
*
* @Deprecated 1.17.0
*
* @see Use the evaluate() method in the Database\DCountA class instead
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
* first row of the list contains labels for each column.
* @param int|string $field Indicates which column is used in the function. Enter the
* @param null|int|string $field Indicates which column is used in the function. Enter the
* column label enclosed between double quotation marks, such as
* "Age" or "Yield," or a number (without quotation marks) that
* represents the position of the column within the list: 1 for
@ -229,29 +107,10 @@ class Database
* column.
*
* @return int
*
* @TODO The field argument is optional. If field is omitted, DCOUNTA counts all records in the
* database that match the criteria.
*/
public static function DCOUNTA($database, $field, $criteria)
{
$field = self::fieldExtract($database, $field);
if ($field === null) {
return null;
}
// reduce the database to a set of rows that match all the criteria
$database = self::filter($database, $criteria);
// extract an array of values for the requested column
$colData = [];
foreach ($database as $row) {
$colData[] = $row[$field];
}
// Return
return Statistical::COUNTA(
self::getFilteredColumn($database, $field, $criteria)
);
return Database\DCountA::evaluate($database, $field, $criteria);
}
/**
@ -263,6 +122,10 @@ class Database
* Excel Function:
* DGET(database,field,criteria)
*
* @Deprecated 1.17.0
*
* @see Use the evaluate() method in the Database\DGet class instead
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
@ -282,18 +145,7 @@ class Database
*/
public static function DGET($database, $field, $criteria)
{
$field = self::fieldExtract($database, $field);
if ($field === null) {
return null;
}
// Return
$colData = self::getFilteredColumn($database, $field, $criteria);
if (count($colData) > 1) {
return Functions::NAN();
}
return $colData[0];
return Database\DGet::evaluate($database, $field, $criteria);
}
/**
@ -305,6 +157,10 @@ class Database
* Excel Function:
* DMAX(database,field,criteria)
*
* @Deprecated 1.17.0
*
* @see Use the evaluate() method in the Database\DMax class instead
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
@ -324,15 +180,7 @@ class Database
*/
public static function DMAX($database, $field, $criteria)
{
$field = self::fieldExtract($database, $field);
if ($field === null) {
return null;
}
// Return
return Statistical::MAX(
self::getFilteredColumn($database, $field, $criteria)
);
return Database\DMax::evaluate($database, $field, $criteria);
}
/**
@ -344,6 +192,10 @@ class Database
* Excel Function:
* DMIN(database,field,criteria)
*
* @Deprecated 1.17.0
*
* @see Use the evaluate() method in the Database\DMin class instead
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
@ -363,15 +215,7 @@ class Database
*/
public static function DMIN($database, $field, $criteria)
{
$field = self::fieldExtract($database, $field);
if ($field === null) {
return null;
}
// Return
return Statistical::MIN(
self::getFilteredColumn($database, $field, $criteria)
);
return Database\DMin::evaluate($database, $field, $criteria);
}
/**
@ -382,6 +226,10 @@ class Database
* Excel Function:
* DPRODUCT(database,field,criteria)
*
* @Deprecated 1.17.0
*
* @see Use the evaluate() method in the Database\DProduct class instead
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
@ -397,19 +245,11 @@ class Database
* the column label in which you specify a condition for the
* column.
*
* @return float
* @return float|string
*/
public static function DPRODUCT($database, $field, $criteria)
{
$field = self::fieldExtract($database, $field);
if ($field === null) {
return null;
}
// Return
return MathTrig::PRODUCT(
self::getFilteredColumn($database, $field, $criteria)
);
return Database\DProduct::evaluate($database, $field, $criteria);
}
/**
@ -421,6 +261,10 @@ class Database
* Excel Function:
* DSTDEV(database,field,criteria)
*
* @Deprecated 1.17.0
*
* @see Use the evaluate() method in the Database\DStDev class instead
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
@ -440,15 +284,7 @@ class Database
*/
public static function DSTDEV($database, $field, $criteria)
{
$field = self::fieldExtract($database, $field);
if ($field === null) {
return null;
}
// Return
return Statistical::STDEV(
self::getFilteredColumn($database, $field, $criteria)
);
return Database\DStDev::evaluate($database, $field, $criteria);
}
/**
@ -460,6 +296,10 @@ class Database
* Excel Function:
* DSTDEVP(database,field,criteria)
*
* @Deprecated 1.17.0
*
* @see Use the evaluate() method in the Database\DStDevP class instead
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
@ -479,15 +319,7 @@ class Database
*/
public static function DSTDEVP($database, $field, $criteria)
{
$field = self::fieldExtract($database, $field);
if ($field === null) {
return null;
}
// Return
return Statistical::STDEVP(
self::getFilteredColumn($database, $field, $criteria)
);
return Database\DStDevP::evaluate($database, $field, $criteria);
}
/**
@ -498,6 +330,10 @@ class Database
* Excel Function:
* DSUM(database,field,criteria)
*
* @Deprecated 1.17.0
*
* @see Use the evaluate() method in the Database\DSum class instead
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
@ -513,19 +349,11 @@ class Database
* the column label in which you specify a condition for the
* column.
*
* @return float
* @return float|string
*/
public static function DSUM($database, $field, $criteria)
{
$field = self::fieldExtract($database, $field);
if ($field === null) {
return null;
}
// Return
return MathTrig::SUM(
self::getFilteredColumn($database, $field, $criteria)
);
return Database\DSum::evaluate($database, $field, $criteria);
}
/**
@ -537,6 +365,10 @@ class Database
* Excel Function:
* DVAR(database,field,criteria)
*
* @Deprecated 1.17.0
*
* @see Use the evaluate() method in the Database\DVar class instead
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
@ -556,15 +388,7 @@ class Database
*/
public static function DVAR($database, $field, $criteria)
{
$field = self::fieldExtract($database, $field);
if ($field === null) {
return null;
}
// Return
return Statistical::VARFunc(
self::getFilteredColumn($database, $field, $criteria)
);
return Database\DVar::evaluate($database, $field, $criteria);
}
/**
@ -576,6 +400,10 @@ class Database
* Excel Function:
* DVARP(database,field,criteria)
*
* @Deprecated 1.17.0
*
* @see Use the evaluate() method in the Database\DVarP class instead
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
@ -595,14 +423,6 @@ class Database
*/
public static function DVARP($database, $field, $criteria)
{
$field = self::fieldExtract($database, $field);
if ($field === null) {
return null;
}
// Return
return Statistical::VARP(
self::getFilteredColumn($database, $field, $criteria)
);
return Database\DVarP::evaluate($database, $field, $criteria);
}
}

View File

@ -0,0 +1,45 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Database;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Averages;
class DAverage extends DatabaseAbstract
{
/**
* DAVERAGE.
*
* Averages the values in a column of a list or database that match conditions you specify.
*
* Excel Function:
* DAVERAGE(database,field,criteria)
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
* first row of the list contains labels for each column.
* @param int|string $field Indicates which column is used in the function. Enter the
* column label enclosed between double quotation marks, such as
* "Age" or "Yield," or a number (without quotation marks) that
* represents the position of the column within the list: 1 for
* the first column, 2 for the second column, and so on.
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
* You can use any range for the criteria argument, as long as it
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return null|float|string
*/
public static function evaluate($database, $field, $criteria)
{
$field = self::fieldExtract($database, $field);
if ($field === null) {
return null;
}
return Averages::average(
self::getFilteredColumn($database, $field, $criteria)
);
}
}

View File

@ -0,0 +1,43 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Database;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Counts;
class DCount extends DatabaseAbstract
{
/**
* DCOUNT.
*
* Counts the cells that contain numbers in a column of a list or database that match conditions
* that you specify.
*
* Excel Function:
* DCOUNT(database,[field],criteria)
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
* first row of the list contains labels for each column.
* @param null|int|string $field Indicates which column is used in the function. Enter the
* column label enclosed between double quotation marks, such as
* "Age" or "Yield," or a number (without quotation marks) that
* represents the position of the column within the list: 1 for
* the first column, 2 for the second column, and so on.
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
* You can use any range for the criteria argument, as long as it
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return int
*/
public static function evaluate($database, $field, $criteria)
{
$field = self::fieldExtract($database, $field);
return Counts::COUNT(
self::getFilteredColumn($database, $field, $criteria)
);
}
}

View File

@ -0,0 +1,42 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Database;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Counts;
class DCountA extends DatabaseAbstract
{
/**
* DCOUNTA.
*
* Counts the nonblank cells in a column of a list or database that match conditions that you specify.
*
* Excel Function:
* DCOUNTA(database,[field],criteria)
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
* first row of the list contains labels for each column.
* @param int|string $field Indicates which column is used in the function. Enter the
* column label enclosed between double quotation marks, such as
* "Age" or "Yield," or a number (without quotation marks) that
* represents the position of the column within the list: 1 for
* the first column, 2 for the second column, and so on.
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
* You can use any range for the criteria argument, as long as it
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return int
*/
public static function evaluate($database, $field, $criteria)
{
$field = self::fieldExtract($database, $field);
return Counts::COUNTA(
self::getFilteredColumn($database, $field ?? 0, $criteria)
);
}
}

View File

@ -0,0 +1,51 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Database;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
class DGet extends DatabaseAbstract
{
/**
* DGET.
*
* Extracts a single value from a column of a list or database that matches conditions that you
* specify.
*
* Excel Function:
* DGET(database,field,criteria)
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
* first row of the list contains labels for each column.
* @param int|string $field Indicates which column is used in the function. Enter the
* column label enclosed between double quotation marks, such as
* "Age" or "Yield," or a number (without quotation marks) that
* represents the position of the column within the list: 1 for
* the first column, 2 for the second column, and so on.
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
* You can use any range for the criteria argument, as long as it
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return mixed
*/
public static function evaluate($database, $field, $criteria)
{
$field = self::fieldExtract($database, $field);
if ($field === null) {
return null;
}
$columnData = self::getFilteredColumn($database, $field, $criteria);
if (count($columnData) > 1) {
return Functions::NAN();
}
$row = array_pop($columnData);
return array_pop($row);
}
}

View File

@ -0,0 +1,46 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Database;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Maximum;
class DMax extends DatabaseAbstract
{
/**
* DMAX.
*
* Returns the largest number in a column of a list or database that matches conditions you that
* specify.
*
* Excel Function:
* DMAX(database,field,criteria)
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
* first row of the list contains labels for each column.
* @param int|string $field Indicates which column is used in the function. Enter the
* column label enclosed between double quotation marks, such as
* "Age" or "Yield," or a number (without quotation marks) that
* represents the position of the column within the list: 1 for
* the first column, 2 for the second column, and so on.
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
* You can use any range for the criteria argument, as long as it
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return null|float|string
*/
public static function evaluate($database, $field, $criteria)
{
$field = self::fieldExtract($database, $field);
if ($field === null) {
return null;
}
return Maximum::MAX(
self::getFilteredColumn($database, $field, $criteria)
);
}
}

View File

@ -0,0 +1,46 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Database;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Minimum;
class DMin extends DatabaseAbstract
{
/**
* DMIN.
*
* Returns the smallest number in a column of a list or database that matches conditions you that
* specify.
*
* Excel Function:
* DMIN(database,field,criteria)
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
* first row of the list contains labels for each column.
* @param int|string $field Indicates which column is used in the function. Enter the
* column label enclosed between double quotation marks, such as
* "Age" or "Yield," or a number (without quotation marks) that
* represents the position of the column within the list: 1 for
* the first column, 2 for the second column, and so on.
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
* You can use any range for the criteria argument, as long as it
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return null|float|string
*/
public static function evaluate($database, $field, $criteria)
{
$field = self::fieldExtract($database, $field);
if ($field === null) {
return null;
}
return Minimum::MIN(
self::getFilteredColumn($database, $field, $criteria)
);
}
}

View File

@ -0,0 +1,45 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Database;
use PhpOffice\PhpSpreadsheet\Calculation\MathTrig;
class DProduct extends DatabaseAbstract
{
/**
* DPRODUCT.
*
* Multiplies the values in a column of a list or database that match conditions that you specify.
*
* Excel Function:
* DPRODUCT(database,field,criteria)
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
* first row of the list contains labels for each column.
* @param int|string $field Indicates which column is used in the function. Enter the
* column label enclosed between double quotation marks, such as
* "Age" or "Yield," or a number (without quotation marks) that
* represents the position of the column within the list: 1 for
* the first column, 2 for the second column, and so on.
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
* You can use any range for the criteria argument, as long as it
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return null|float|string
*/
public static function evaluate($database, $field, $criteria)
{
$field = self::fieldExtract($database, $field);
if ($field === null) {
return null;
}
return MathTrig\Product::funcProduct(
self::getFilteredColumn($database, $field, $criteria)
);
}
}

View File

@ -0,0 +1,46 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Database;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical\StandardDeviations;
class DStDev extends DatabaseAbstract
{
/**
* DSTDEV.
*
* Estimates the standard deviation of a population based on a sample by using the numbers in a
* column of a list or database that match conditions that you specify.
*
* Excel Function:
* DSTDEV(database,field,criteria)
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
* first row of the list contains labels for each column.
* @param int|string $field Indicates which column is used in the function. Enter the
* column label enclosed between double quotation marks, such as
* "Age" or "Yield," or a number (without quotation marks) that
* represents the position of the column within the list: 1 for
* the first column, 2 for the second column, and so on.
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
* You can use any range for the criteria argument, as long as it
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return null|float|string
*/
public static function evaluate($database, $field, $criteria)
{
$field = self::fieldExtract($database, $field);
if ($field === null) {
return null;
}
return StandardDeviations::STDEV(
self::getFilteredColumn($database, $field, $criteria)
);
}
}

View File

@ -0,0 +1,46 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Database;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical\StandardDeviations;
class DStDevP extends DatabaseAbstract
{
/**
* DSTDEVP.
*
* Calculates the standard deviation of a population based on the entire population by using the
* numbers in a column of a list or database that match conditions that you specify.
*
* Excel Function:
* DSTDEVP(database,field,criteria)
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
* first row of the list contains labels for each column.
* @param int|string $field Indicates which column is used in the function. Enter the
* column label enclosed between double quotation marks, such as
* "Age" or "Yield," or a number (without quotation marks) that
* represents the position of the column within the list: 1 for
* the first column, 2 for the second column, and so on.
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
* You can use any range for the criteria argument, as long as it
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return null|float|string
*/
public static function evaluate($database, $field, $criteria)
{
$field = self::fieldExtract($database, $field);
if ($field === null) {
return null;
}
return StandardDeviations::STDEVP(
self::getFilteredColumn($database, $field, $criteria)
);
}
}

View File

@ -0,0 +1,45 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Database;
use PhpOffice\PhpSpreadsheet\Calculation\MathTrig;
class DSum extends DatabaseAbstract
{
/**
* DSUM.
*
* Adds the numbers in a column of a list or database that match conditions that you specify.
*
* Excel Function:
* DSUM(database,field,criteria)
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
* first row of the list contains labels for each column.
* @param int|string $field Indicates which column is used in the function. Enter the
* column label enclosed between double quotation marks, such as
* "Age" or "Yield," or a number (without quotation marks) that
* represents the position of the column within the list: 1 for
* the first column, 2 for the second column, and so on.
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
* You can use any range for the criteria argument, as long as it
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return null|float|string
*/
public static function evaluate($database, $field, $criteria)
{
$field = self::fieldExtract($database, $field);
if ($field === null) {
return null;
}
return MathTrig\Sum::funcSum(
self::getFilteredColumn($database, $field, $criteria)
);
}
}

View File

@ -0,0 +1,46 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Database;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Variances;
class DVar extends DatabaseAbstract
{
/**
* DVAR.
*
* Estimates the variance of a population based on a sample by using the numbers in a column
* of a list or database that match conditions that you specify.
*
* Excel Function:
* DVAR(database,field,criteria)
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
* first row of the list contains labels for each column.
* @param int|string $field Indicates which column is used in the function. Enter the
* column label enclosed between double quotation marks, such as
* "Age" or "Yield," or a number (without quotation marks) that
* represents the position of the column within the list: 1 for
* the first column, 2 for the second column, and so on.
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
* You can use any range for the criteria argument, as long as it
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return null|float|string (string if result is an error)
*/
public static function evaluate($database, $field, $criteria)
{
$field = self::fieldExtract($database, $field);
if ($field === null) {
return null;
}
return Variances::VAR(
self::getFilteredColumn($database, $field, $criteria)
);
}
}

View File

@ -0,0 +1,46 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Database;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Variances;
class DVarP extends DatabaseAbstract
{
/**
* DVARP.
*
* Calculates the variance of a population based on the entire population by using the numbers
* in a column of a list or database that match conditions that you specify.
*
* Excel Function:
* DVARP(database,field,criteria)
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
* first row of the list contains labels for each column.
* @param int|string $field Indicates which column is used in the function. Enter the
* column label enclosed between double quotation marks, such as
* "Age" or "Yield," or a number (without quotation marks) that
* represents the position of the column within the list: 1 for
* the first column, 2 for the second column, and so on.
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
* You can use any range for the criteria argument, as long as it
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return null|float|string (string if result is an error)
*/
public static function evaluate($database, $field, $criteria)
{
$field = self::fieldExtract($database, $field);
if ($field === null) {
return null;
}
return Variances::VARP(
self::getFilteredColumn($database, $field, $criteria)
);
}
}

View File

@ -0,0 +1,174 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Database;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Calculation\Internal\WildcardMatch;
abstract class DatabaseAbstract
{
abstract public static function evaluate($database, $field, $criteria);
/**
* fieldExtract.
*
* Extracts the column ID to use for the data field.
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
* first row of the list contains labels for each column.
* @param mixed $field Indicates which column is used in the function. Enter the
* column label enclosed between double quotation marks, such as
* "Age" or "Yield," or a number (without quotation marks) that
* represents the position of the column within the list: 1 for
* the first column, 2 for the second column, and so on.
*/
protected static function fieldExtract(array $database, $field): ?int
{
$field = strtoupper(Functions::flattenSingleValue($field));
if ($field === '') {
return null;
}
$fieldNames = array_map('strtoupper', array_shift($database));
if (is_numeric($field)) {
return ((int) $field) - 1;
}
$key = array_search($field, array_values($fieldNames), true);
return ($key !== false) ? (int) $key : null;
}
/**
* filter.
*
* Parses the selection criteria, extracts the database rows that match those criteria, and
* returns that subset of rows.
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
* first row of the list contains labels for each column.
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
* You can use any range for the criteria argument, as long as it
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return mixed[]
*/
protected static function filter(array $database, array $criteria): array
{
$fieldNames = array_shift($database);
$criteriaNames = array_shift($criteria);
// Convert the criteria into a set of AND/OR conditions with [:placeholders]
$query = self::buildQuery($criteriaNames, $criteria);
// Loop through each row of the database
return self::executeQuery($database, $query, $criteriaNames, $fieldNames);
}
protected static function getFilteredColumn(array $database, ?int $field, array $criteria): array
{
// reduce the database to a set of rows that match all the criteria
$database = self::filter($database, $criteria);
$defaultReturnColumnValue = ($field === null) ? 1 : null;
// extract an array of values for the requested column
$columnData = [];
foreach ($database as $rowKey => $row) {
$keys = array_keys($row);
$key = $keys[$field] ?? null;
$columnKey = $key ?? 'A';
$columnData[$rowKey][$columnKey] = $row[$key] ?? $defaultReturnColumnValue;
}
return $columnData;
}
private static function buildQuery(array $criteriaNames, array $criteria): string
{
$baseQuery = [];
foreach ($criteria as $key => $criterion) {
foreach ($criterion as $field => $value) {
$criterionName = $criteriaNames[$field];
if ($value !== null && $value !== '') {
$condition = self::buildCondition($value, $criterionName);
$baseQuery[$key][] = $condition;
}
}
}
$rowQuery = array_map(
function ($rowValue) {
return (count($rowValue) > 1) ? 'AND(' . implode(',', $rowValue) . ')' : $rowValue[0];
},
$baseQuery
);
return (count($rowQuery) > 1) ? 'OR(' . implode(',', $rowQuery) . ')' : $rowQuery[0];
}
private static function buildCondition($criterion, string $criterionName): string
{
$ifCondition = Functions::ifCondition($criterion);
// Check for wildcard characters used in the condition
$result = preg_match('/(?<operator>[^"]*)(?<operand>".*[*?].*")/ui', $ifCondition, $matches);
if ($result !== 1) {
return "[:{$criterionName}]{$ifCondition}";
}
$trueFalse = ($matches['operator'] !== '<>');
$wildcard = WildcardMatch::wildcard($matches['operand']);
$condition = "WILDCARDMATCH([:{$criterionName}],{$wildcard})";
if ($trueFalse === false) {
$condition = "NOT({$condition})";
}
return $condition;
}
private static function executeQuery(array $database, string $query, array $criteria, array $fields): array
{
foreach ($database as $dataRow => $dataValues) {
// Substitute actual values from the database row for our [:placeholders]
$conditions = $query;
foreach ($criteria as $criterion) {
$conditions = self::processCondition($criterion, $fields, $dataValues, $conditions);
}
// evaluate the criteria against the row data
$result = Calculation::getInstance()->_calculateFormulaValue('=' . $conditions);
// If the row failed to meet the criteria, remove it from the database
if ($result !== true) {
unset($database[$dataRow]);
}
}
return $database;
}
private static function processCondition(string $criterion, array $fields, array $dataValues, string $conditions)
{
$key = array_search($criterion, $fields, true);
$dataValue = 'NULL';
if (is_bool($dataValues[$key])) {
$dataValue = ($dataValues[$key]) ? 'TRUE' : 'FALSE';
} elseif ($dataValues[$key] !== null) {
$dataValue = $dataValues[$key];
// escape quotes if we have a string containing quotes
if (is_string($dataValue) && strpos($dataValue, '"') !== false) {
$dataValue = str_replace('"', '""', $dataValue);
}
$dataValue = (is_string($dataValue)) ? Calculation::wrapResult(strtoupper($dataValue)) : $dataValue;
}
return str_replace('[:' . $criterion . ']', $dataValue, $conditions);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,37 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
class Constants
{
// Constants currently used by WeekNum; will eventually be used by WEEKDAY
const STARTWEEK_SUNDAY = 1;
const STARTWEEK_MONDAY = 2;
const STARTWEEK_MONDAY_ALT = 11;
const STARTWEEK_TUESDAY = 12;
const STARTWEEK_WEDNESDAY = 13;
const STARTWEEK_THURSDAY = 14;
const STARTWEEK_FRIDAY = 15;
const STARTWEEK_SATURDAY = 16;
const STARTWEEK_SUNDAY_ALT = 17;
const DOW_SUNDAY = 1;
const DOW_MONDAY = 2;
const DOW_TUESDAY = 3;
const DOW_WEDNESDAY = 4;
const DOW_THURSDAY = 5;
const DOW_FRIDAY = 6;
const DOW_SATURDAY = 7;
const STARTWEEK_MONDAY_ISO = 21;
const METHODARR = [
self::STARTWEEK_SUNDAY => self::DOW_SUNDAY,
self::DOW_MONDAY,
self::STARTWEEK_MONDAY_ALT => self::DOW_MONDAY,
self::DOW_TUESDAY,
self::DOW_WEDNESDAY,
self::DOW_THURSDAY,
self::DOW_FRIDAY,
self::DOW_SATURDAY,
self::DOW_SUNDAY,
self::STARTWEEK_MONDAY_ISO => self::STARTWEEK_MONDAY_ISO,
];
}

View File

@ -0,0 +1,146 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use DateInterval;
use DateTime;
use Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Shared\Date;
class DateDif
{
/**
* DATEDIF.
*
* @param mixed $startDate Excel date serial value, PHP date/time stamp, PHP DateTime object
* or a standard date string
* @param mixed $endDate Excel date serial value, PHP date/time stamp, PHP DateTime object
* or a standard date string
* @param string $unit
*
* @return int|string Interval between the dates
*/
public static function funcDateDif($startDate, $endDate, $unit = 'D')
{
try {
$startDate = Helpers::getDateValue($startDate);
$endDate = Helpers::getDateValue($endDate);
$difference = self::initialDiff($startDate, $endDate);
$unit = strtoupper(Functions::flattenSingleValue($unit));
} catch (Exception $e) {
return $e->getMessage();
}
// Execute function
$PHPStartDateObject = Date::excelToDateTimeObject($startDate);
$startDays = (int) $PHPStartDateObject->format('j');
$startMonths = (int) $PHPStartDateObject->format('n');
$startYears = (int) $PHPStartDateObject->format('Y');
$PHPEndDateObject = Date::excelToDateTimeObject($endDate);
$endDays = (int) $PHPEndDateObject->format('j');
$endMonths = (int) $PHPEndDateObject->format('n');
$endYears = (int) $PHPEndDateObject->format('Y');
$PHPDiffDateObject = $PHPEndDateObject->diff($PHPStartDateObject);
$retVal = false;
$retVal = self::replaceRetValue($retVal, $unit, 'D') ?? self::datedifD($difference);
$retVal = self::replaceRetValue($retVal, $unit, 'M') ?? self::datedifM($PHPDiffDateObject);
$retVal = self::replaceRetValue($retVal, $unit, 'MD') ?? self::datedifMD($startDays, $endDays, $PHPEndDateObject, $PHPDiffDateObject);
$retVal = self::replaceRetValue($retVal, $unit, 'Y') ?? self::datedifY($PHPDiffDateObject);
$retVal = self::replaceRetValue($retVal, $unit, 'YD') ?? self::datedifYD($difference, $startYears, $endYears, $PHPStartDateObject, $PHPEndDateObject);
$retVal = self::replaceRetValue($retVal, $unit, 'YM') ?? self::datedifYM($PHPDiffDateObject);
return is_bool($retVal) ? Functions::VALUE() : $retVal;
}
private static function initialDiff(float $startDate, float $endDate): float
{
// Validate parameters
if ($startDate > $endDate) {
throw new Exception(Functions::NAN());
}
return $endDate - $startDate;
}
/**
* Decide whether it's time to set retVal.
*
* @param bool|int $retVal
*
* @return null|bool|int
*/
private static function replaceRetValue($retVal, string $unit, string $compare)
{
if ($retVal !== false || $unit !== $compare) {
return $retVal;
}
return null;
}
private static function datedifD(float $difference): int
{
return (int) $difference;
}
private static function datedifM(DateInterval $PHPDiffDateObject): int
{
return 12 * (int) $PHPDiffDateObject->format('%y') + (int) $PHPDiffDateObject->format('%m');
}
private static function datedifMD(int $startDays, int $endDays, DateTime $PHPEndDateObject, DateInterval $PHPDiffDateObject): int
{
if ($endDays < $startDays) {
$retVal = $endDays;
$PHPEndDateObject->modify('-' . $endDays . ' days');
$adjustDays = (int) $PHPEndDateObject->format('j');
$retVal += ($adjustDays - $startDays);
} else {
$retVal = (int) $PHPDiffDateObject->format('%d');
}
return $retVal;
}
private static function datedifY(DateInterval $PHPDiffDateObject): int
{
return (int) $PHPDiffDateObject->format('%y');
}
private static function datedifYD(float $difference, int $startYears, int $endYears, DateTime $PHPStartDateObject, DateTime $PHPEndDateObject): int
{
$retVal = (int) $difference;
if ($endYears > $startYears) {
$isLeapStartYear = $PHPStartDateObject->format('L');
$wasLeapEndYear = $PHPEndDateObject->format('L');
// Adjust end year to be as close as possible as start year
while ($PHPEndDateObject >= $PHPStartDateObject) {
$PHPEndDateObject->modify('-1 year');
$endYears = $PHPEndDateObject->format('Y');
}
$PHPEndDateObject->modify('+1 year');
// Get the result
$retVal = $PHPEndDateObject->diff($PHPStartDateObject)->days;
// Adjust for leap years cases
$isLeapEndYear = $PHPEndDateObject->format('L');
$limit = new DateTime($PHPEndDateObject->format('Y-02-29'));
if (!$isLeapStartYear && !$wasLeapEndYear && $isLeapEndYear && $PHPEndDateObject >= $limit) {
--$retVal;
}
}
return (int) $retVal;
}
private static function datedifYM(DateInterval $PHPDiffDateObject): int
{
return (int) $PHPDiffDateObject->format('%m');
}
}

View File

@ -0,0 +1,151 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use DateTimeImmutable;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Shared\Date;
class DateValue
{
/**
* DATEVALUE.
*
* Returns a value that represents a particular date.
* Use DATEVALUE to convert a date represented by a text string to an Excel or PHP date/time stamp
* value.
*
* NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
* format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
*
* Excel Function:
* DATEVALUE(dateValue)
*
* @param string $dateValue Text that represents a date in a Microsoft Excel date format.
* For example, "1/30/2008" or "30-Jan-2008" are text strings within
* quotation marks that represent dates. Using the default date
* system in Excel for Windows, date_text must represent a date from
* January 1, 1900, to December 31, 9999. Using the default date
* system in Excel for the Macintosh, date_text must represent a date
* from January 1, 1904, to December 31, 9999. DATEVALUE returns the
* #VALUE! error value if date_text is out of this range.
*
* @return mixed 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 funcDateValue($dateValue)
{
$dti = new DateTimeImmutable();
$baseYear = Date::getExcelCalendar();
$dateValue = trim(Functions::flattenSingleValue($dateValue), '"');
// Strip any ordinals because they're allowed in Excel (English only)
$dateValue = preg_replace('/(\d)(st|nd|rd|th)([ -\/])/Ui', '$1$3', $dateValue);
// Convert separators (/ . or space) to hyphens (should also handle dot used for ordinals in some countries, e.g. Denmark, Germany)
$dateValue = str_replace(['/', '.', '-', ' '], ' ', $dateValue);
$yearFound = false;
$t1 = explode(' ', $dateValue);
$t = '';
foreach ($t1 as &$t) {
if ((is_numeric($t)) && ($t > 31)) {
if ($yearFound) {
return Functions::VALUE();
}
if ($t < 100) {
$t += 1900;
}
$yearFound = true;
}
}
if (count($t1) === 1) {
// We've been fed a time value without any date
return ((strpos($t, ':') === false)) ? Functions::Value() : 0.0;
}
unset($t);
$dateValue = self::t1ToString($t1, $dti, $yearFound);
$PHPDateArray = self::setUpArray($dateValue, $dti);
return self::finalResults($PHPDateArray, $dti, $baseYear);
}
private static function t1ToString(array $t1, DateTimeImmutable $dti, bool $yearFound): string
{
if (count($t1) == 2) {
// We only have two parts of the date: either day/month or month/year
if ($yearFound) {
array_unshift($t1, 1);
} else {
if (is_numeric($t1[1]) && $t1[1] > 29) {
$t1[1] += 1900;
array_unshift($t1, 1);
} else {
$t1[] = $dti->format('Y');
}
}
}
$dateValue = implode(' ', $t1);
return $dateValue;
}
/**
* Parse date.
*
* @return array|bool
*/
private static function setUpArray(string $dateValue, DateTimeImmutable $dti)
{
$PHPDateArray = date_parse($dateValue);
if (($PHPDateArray === false) || ($PHPDateArray['error_count'] > 0)) {
// 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.
$testVal1 = strtok($dateValue, '- ');
$testVal2 = strtok('- ');
$testVal3 = strtok('- ') ?: $dti->format('Y');
Helpers::adjustYear($testVal1, $testVal2, $testVal3);
$PHPDateArray = date_parse($testVal1 . '-' . $testVal2 . '-' . $testVal3);
if (($PHPDateArray === false) || ($PHPDateArray['error_count'] > 0)) {
$PHPDateArray = date_parse($testVal2 . '-' . $testVal1 . '-' . $testVal3);
}
}
return $PHPDateArray;
}
/**
* Final results.
*
* @param array|false $PHPDateArray
*
* @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* depending on the value of the ReturnDateType flag
*/
private static function finalResults($PHPDateArray, DateTimeImmutable $dti, int $baseYear)
{
$retValue = Functions::Value();
if (($PHPDateArray !== false) && ($PHPDateArray['error_count'] == 0)) {
// Execute function
Helpers::replaceIfEmpty($PHPDateArray['year'], $dti->format('Y'));
if ($PHPDateArray['year'] < $baseYear) {
return Functions::VALUE();
}
Helpers::replaceIfEmpty($PHPDateArray['month'], $dti->format('m'));
Helpers::replaceIfEmpty($PHPDateArray['day'], $dti->format('d'));
$PHPDateArray['hour'] = 0;
$PHPDateArray['minute'] = 0;
$PHPDateArray['second'] = 0;
$month = (int) $PHPDateArray['month'];
$day = (int) $PHPDateArray['day'];
$year = (int) $PHPDateArray['year'];
if (!checkdate($month, $day, $year)) {
return ($year === 1900 && $month === 2 && $day === 29) ? Helpers::returnIn3FormatsFloat(60.0) : Functions::VALUE();
}
$retValue = Helpers::returnIn3FormatsArray($PHPDateArray, true);
}
return $retValue;
}
}

View File

@ -0,0 +1,168 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Shared\Date;
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
class Datefunc
{
/**
* DATE.
*
* The DATE function returns a value that represents a particular date.
*
* NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
* format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
*
* Excel Function:
* DATE(year,month,day)
*
* 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.
*
* @param int $year The value of the year argument can include one to four digits.
* Excel interprets the year argument according to the configured
* date system: 1900 or 1904.
* If year is between 0 (zero) and 1899 (inclusive), Excel adds that
* value to 1900 to calculate the year. For example, DATE(108,1,2)
* returns January 2, 2008 (1900+108).
* If year is between 1900 and 9999 (inclusive), Excel uses that
* value as the year. For example, DATE(2008,1,2) returns January 2,
* 2008.
* If year is less than 0 or is 10000 or greater, Excel returns the
* #NUM! error value.
* @param int $month A positive or negative integer representing the month of the year
* from 1 to 12 (January to December).
* If month is greater than 12, month adds that number of months to
* the first month in the year specified. For example, DATE(2008,14,2)
* returns the serial number representing February 2, 2009.
* If month is less than 1, month subtracts the magnitude of that
* number of months, plus 1, from the first month in the year
* specified. For example, DATE(2008,-3,2) returns the serial number
* representing September 2, 2007.
* @param int $day A positive or negative integer representing the day of the month
* from 1 to 31.
* If day is greater than the number of days in the month specified,
* day adds that number of days to the first day in the month. For
* example, DATE(2008,1,35) returns the serial number representing
* February 4, 2008.
* If day is less than 1, day subtracts the magnitude that number of
* days, plus one, from the first day of the month specified. For
* example, DATE(2008,1,-15) returns the serial number representing
* December 16, 2007.
*
* @return mixed 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 funcDate($year, $month, $day)
{
$baseYear = Date::getExcelCalendar();
try {
$year = self::getYear($year, $baseYear);
$month = self::getMonth($month);
$day = self::getDay($day);
self::adjustYearMonth($year, $month, $baseYear);
} catch (Exception $e) {
return $e->getMessage();
}
// Execute function
$excelDateValue = Date::formattedPHPToExcel($year, $month, $day);
return Helpers::returnIn3FormatsFloat($excelDateValue);
}
/**
* Convert year from multiple formats to int.
*
* @param mixed $year
*/
private static function getYear($year, int $baseYear): int
{
$year = Functions::flattenSingleValue($year);
$year = ($year !== null) ? StringHelper::testStringAsNumeric($year) : 0;
if (!is_numeric($year)) {
throw new Exception(Functions::VALUE());
}
$year = (int) $year;
if ($year < ($baseYear - 1900)) {
throw new Exception(Functions::NAN());
}
if ((($baseYear - 1900) !== 0) && ($year < $baseYear) && ($year >= 1900)) {
throw new Exception(Functions::NAN());
}
if (($year < $baseYear) && ($year >= ($baseYear - 1900))) {
$year += 1900;
}
return $year;
}
/**
* Convert month from multiple formats to int.
*
* @param mixed $month
*/
private static function getMonth($month): int
{
$month = Functions::flattenSingleValue($month);
if (($month !== null) && (!is_numeric($month))) {
$month = Date::monthStringToNumber($month);
}
$month = ($month !== null) ? StringHelper::testStringAsNumeric($month) : 0;
if (!is_numeric($month)) {
throw new Exception(Functions::VALUE());
}
return (int) $month;
}
/**
* Convert day from multiple formats to int.
*
* @param mixed $day
*/
private static function getDay($day): int
{
$day = Functions::flattenSingleValue($day);
if (($day !== null) && (!is_numeric($day))) {
$day = Date::dayStringToNumber($day);
}
$day = ($day !== null) ? StringHelper::testStringAsNumeric($day) : 0;
if (!is_numeric($day)) {
throw new Exception(Functions::VALUE());
}
return (int) $day;
}
private static function adjustYearMonth(int &$year, int &$month, int $baseYear): void
{
if ($month < 1) {
// Handle year/month adjustment if month < 1
--$month;
$year += ceil($month / 12) - 1;
$month = 13 - abs($month % 12);
} elseif ($month > 12) {
// Handle year/month adjustment if month > 12
$year += floor($month / 12);
$month = ($month % 12);
}
// Re-validate the year parameter after adjustments
if (($year < $baseYear) || ($year >= 10000)) {
throw new Exception(Functions::NAN());
}
}
}

View File

@ -0,0 +1,61 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Shared\Date;
class Day
{
/**
* DAYOFMONTH.
*
* Returns the day of the month, for a specified date. The day is given as an integer
* ranging from 1 to 31.
*
* Excel Function:
* DAY(dateValue)
*
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
*
* @return int|string Day of the month
*/
public static function funcDay($dateValue)
{
$weirdResult = self::weirdCondition($dateValue);
if ($weirdResult >= 0) {
return $weirdResult;
}
try {
$dateValue = Helpers::getDateValue($dateValue);
} catch (Exception $e) {
return $e->getMessage();
}
// Execute function
$PHPDateObject = Date::excelToDateTimeObject($dateValue);
return (int) $PHPDateObject->format('j');
}
private static function weirdCondition($dateValue): int
{
// Excel does not treat 0 consistently for DAY vs. (MONTH or YEAR)
if (Date::getExcelCalendar() === DATE::CALENDAR_WINDOWS_1900 && Functions::getCompatibilityMode() == Functions::COMPATIBILITY_EXCEL) {
if (is_bool($dateValue)) {
return (int) $dateValue;
}
if ($dateValue === null) {
return 0;
}
if (is_numeric($dateValue) && $dateValue < 1 && $dateValue >= 0) {
return 0;
}
}
return -1;
}
}

View File

@ -0,0 +1,51 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use DateTimeInterface;
use Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Shared\Date;
class Days
{
/**
* DAYS.
*
* Returns the number of days between two dates
*
* Excel Function:
* DAYS(endDate, startDate)
*
* @param DateTimeInterface|float|int|string $endDate Excel date serial value (float),
* PHP date timestamp (integer), PHP DateTime object, or a standard date string
* @param DateTimeInterface|float|int|string $startDate Excel date serial value (float),
* PHP date timestamp (integer), PHP DateTime object, or a standard date string
*
* @return int|string Number of days between start date and end date or an error
*/
public static function funcDays($endDate, $startDate)
{
try {
$startDate = Helpers::getDateValue($startDate);
$endDate = Helpers::getDateValue($endDate);
} catch (Exception $e) {
return $e->getMessage();
}
// Execute function
$PHPStartDateObject = Date::excelToDateTimeObject($startDate);
$PHPEndDateObject = Date::excelToDateTimeObject($endDate);
$days = Functions::VALUE();
$diff = $PHPStartDateObject->diff($PHPEndDateObject);
if ($diff !== false && !is_bool($diff->days)) {
$days = $diff->days;
if ($diff->invert) {
$days = -$days;
}
}
return $days;
}
}

View File

@ -0,0 +1,106 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Shared\Date;
class Days360
{
/**
* DAYS360.
*
* Returns the number of days between two dates based on a 360-day year (twelve 30-day months),
* which is used in some accounting calculations. Use this function to help compute payments if
* your accounting system is based on twelve 30-day months.
*
* Excel Function:
* DAYS360(startDate,endDate[,method])
*
* @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* @param mixed $method US or European Method as a bool
* FALSE or omitted: U.S. (NASD) method. If the starting date is
* the last day of a month, it becomes equal to the 30th of the
* same month. If the ending date is the last day of a month and
* the starting date is earlier than the 30th of a month, the
* ending date becomes equal to the 1st of the next month;
* otherwise the ending date becomes equal to the 30th of the
* same month.
* TRUE: European method. Starting dates and ending dates that
* occur on the 31st of a month become equal to the 30th of the
* same month.
*
* @return int|string Number of days between start date and end date
*/
public static function funcDays360($startDate = 0, $endDate = 0, $method = false)
{
try {
$startDate = Helpers::getDateValue($startDate);
$endDate = Helpers::getDateValue($endDate);
} catch (Exception $e) {
return $e->getMessage();
}
if (!is_bool($method)) {
return Functions::VALUE();
}
// Execute function
$PHPStartDateObject = Date::excelToDateTimeObject($startDate);
$startDay = $PHPStartDateObject->format('j');
$startMonth = $PHPStartDateObject->format('n');
$startYear = $PHPStartDateObject->format('Y');
$PHPEndDateObject = Date::excelToDateTimeObject($endDate);
$endDay = $PHPEndDateObject->format('j');
$endMonth = $PHPEndDateObject->format('n');
$endYear = $PHPEndDateObject->format('Y');
return self::dateDiff360((int) $startDay, (int) $startMonth, (int) $startYear, (int) $endDay, (int) $endMonth, (int) $endYear, !$method);
}
/**
* 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
{
$startDay = self::getStartDay($startDay, $startMonth, $startYear, $methodUS);
$endDay = self::getEndDay($endDay, $endMonth, $endYear, $startDay, $methodUS);
return $endDay + $endMonth * 30 + $endYear * 360 - $startDay - $startMonth * 30 - $startYear * 360;
}
private static function getStartDay(int $startDay, int $startMonth, int $startYear, bool $methodUS): int
{
if ($startDay == 31) {
--$startDay;
} elseif ($methodUS && ($startMonth == 2 && ($startDay == 29 || ($startDay == 28 && !Helpers::isLeapYear($startYear))))) {
$startDay = 30;
}
return $startDay;
}
private static function getEndDay(int $endDay, int &$endMonth, int &$endYear, int $startDay, bool $methodUS): int
{
if ($endDay == 31) {
if ($methodUS && $startDay != 30) {
$endDay = 1;
if ($endMonth == 12) {
++$endYear;
$endMonth = 1;
} else {
++$endMonth;
}
} else {
$endDay = 30;
}
}
return $endDay;
}
}

View File

@ -0,0 +1,45 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use Exception;
use PhpOffice\PhpSpreadsheet\Shared\Date;
class EDate
{
/**
* EDATE.
*
* Returns the serial number that represents the date that is the indicated number of months
* before or after a specified date (the start_date).
* Use EDATE to calculate maturity dates or due dates that fall on the same day of the month
* as the date of issue.
*
* Excel Function:
* EDATE(dateValue,adjustmentMonths)
*
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* @param int $adjustmentMonths The number of months before or after start_date.
* A positive value for months yields a future date;
* a negative value yields a past date.
*
* @return mixed 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 funcEDate($dateValue, $adjustmentMonths)
{
try {
$dateValue = Helpers::getDateValue($dateValue, false);
$adjustmentMonths = Helpers::validateNumericNull($adjustmentMonths);
} catch (Exception $e) {
return $e->getMessage();
}
$adjustmentMonths = floor($adjustmentMonths);
// Execute function
$PHPDateObject = Helpers::adjustDateByMonths($dateValue, $adjustmentMonths);
return Helpers::returnIn3FormatsObject($PHPDateObject);
}
}

View File

@ -0,0 +1,47 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use Exception;
use PhpOffice\PhpSpreadsheet\Shared\Date;
class EoMonth
{
/**
* EOMONTH.
*
* Returns the date value for the last day of the month that is the indicated number of months
* before or after start_date.
* Use EOMONTH to calculate maturity dates or due dates that fall on the last day of the month.
*
* Excel Function:
* EOMONTH(dateValue,adjustmentMonths)
*
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* @param int $adjustmentMonths The number of months before or after start_date.
* A positive value for months yields a future date;
* a negative value yields a past date.
*
* @return mixed 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 funcEoMonth($dateValue, $adjustmentMonths)
{
try {
$dateValue = Helpers::getDateValue($dateValue, false);
$adjustmentMonths = Helpers::validateNumericNull($adjustmentMonths);
} catch (Exception $e) {
return $e->getMessage();
}
$adjustmentMonths = floor($adjustmentMonths);
// Execute function
$PHPDateObject = Helpers::adjustDateByMonths($dateValue, $adjustmentMonths + 1);
$adjustDays = (int) $PHPDateObject->format('d');
$adjustDaysString = '-' . $adjustDays . ' days';
$PHPDateObject->modify($adjustDaysString);
return Helpers::returnIn3FormatsObject($PHPDateObject);
}
}

View File

@ -0,0 +1,275 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use DateTime;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Shared\Date;
class Helpers
{
/**
* Identify if a year is a leap year or not.
*
* @param int|string $year The year to test
*
* @return bool TRUE if the year is a leap year, otherwise FALSE
*/
public static function isLeapYear($year): bool
{
return (($year % 4) === 0) && (($year % 100) !== 0) || (($year % 400) === 0);
}
/**
* getDateValue.
*
* @param mixed $dateValue
*
* @return float Excel date/time serial value
*/
public static function getDateValue($dateValue, bool $allowBool = true): float
{
if (is_object($dateValue)) {
$retval = Date::PHPToExcel($dateValue);
if (is_bool($retval)) {
throw new Exception(Functions::VALUE());
}
return $retval;
}
self::nullFalseTrueToNumber($dateValue, $allowBool);
if (!is_numeric($dateValue)) {
$saveReturnDateType = Functions::getReturnDateType();
Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
$dateValue = DateValue::funcDateValue($dateValue);
Functions::setReturnDateType($saveReturnDateType);
if (!is_numeric($dateValue)) {
throw new Exception(Functions::VALUE());
}
}
if ($dateValue < 0 && Functions::getCompatibilityMode() !== Functions::COMPATIBILITY_OPENOFFICE) {
throw new Exception(Functions::NAN());
}
return (float) $dateValue;
}
/**
* getTimeValue.
*
* @param string $timeValue
*
* @return mixed Excel date/time serial value, or string if error
*/
public static function getTimeValue($timeValue)
{
$saveReturnDateType = Functions::getReturnDateType();
Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
$timeValue = TimeValue::funcTimeValue($timeValue);
Functions::setReturnDateType($saveReturnDateType);
return $timeValue;
}
public static function adjustDateByMonths($dateValue = 0, $adjustmentMonths = 0)
{
// Execute function
$PHPDateObject = Date::excelToDateTimeObject($dateValue);
$oMonth = (int) $PHPDateObject->format('m');
$oYear = (int) $PHPDateObject->format('Y');
$adjustmentMonthsString = (string) $adjustmentMonths;
if ($adjustmentMonths > 0) {
$adjustmentMonthsString = '+' . $adjustmentMonths;
}
if ($adjustmentMonths != 0) {
$PHPDateObject->modify($adjustmentMonthsString . ' months');
}
$nMonth = (int) $PHPDateObject->format('m');
$nYear = (int) $PHPDateObject->format('Y');
$monthDiff = ($nMonth - $oMonth) + (($nYear - $oYear) * 12);
if ($monthDiff != $adjustmentMonths) {
$adjustDays = (int) $PHPDateObject->format('d');
$adjustDaysString = '-' . $adjustDays . ' days';
$PHPDateObject->modify($adjustDaysString);
}
return $PHPDateObject;
}
/**
* Help reduce perceived complexity of some tests.
*
* @param mixed $value
* @param mixed $altValue
*/
public static function replaceIfEmpty(&$value, $altValue): void
{
$value = $value ?: $altValue;
}
/**
* Adjust year in ambiguous situations.
*/
public static function adjustYear(string $testVal1, string $testVal2, string &$testVal3): void
{
if (!is_numeric($testVal1) || $testVal1 < 31) {
if (!is_numeric($testVal2) || $testVal2 < 12) {
if (is_numeric($testVal3) && $testVal3 < 12) {
$testVal3 += 2000;
}
}
}
}
/**
* Return result in one of three formats.
*
* @return mixed
*/
public static function returnIn3FormatsArray(array $dateArray, bool $noFrac = false)
{
$retType = Functions::getReturnDateType();
if ($retType === Functions::RETURNDATE_PHP_DATETIME_OBJECT) {
return new DateTime(
$dateArray['year']
. '-' . $dateArray['month']
. '-' . $dateArray['day']
. ' ' . $dateArray['hour']
. ':' . $dateArray['minute']
. ':' . $dateArray['second']
);
}
$excelDateValue =
Date::formattedPHPToExcel(
$dateArray['year'],
$dateArray['month'],
$dateArray['day'],
$dateArray['hour'],
$dateArray['minute'],
$dateArray['second']
);
if ($retType === Functions::RETURNDATE_EXCEL) {
return $noFrac ? floor($excelDateValue) : (float) $excelDateValue;
}
// RETURNDATE_UNIX_TIMESTAMP)
return (int) Date::excelToTimestamp($excelDateValue);
}
/**
* Return result in one of three formats.
*
* @return mixed
*/
public static function returnIn3FormatsFloat(float $excelDateValue)
{
$retType = Functions::getReturnDateType();
if ($retType === Functions::RETURNDATE_EXCEL) {
return $excelDateValue;
}
if ($retType === Functions::RETURNDATE_UNIX_TIMESTAMP) {
return (int) Date::excelToTimestamp($excelDateValue);
}
// RETURNDATE_PHP_DATETIME_OBJECT
return Date::excelToDateTimeObject($excelDateValue);
}
/**
* Return result in one of three formats.
*
* @return mixed
*/
public static function returnIn3FormatsObject(DateTime $PHPDateObject)
{
$retType = Functions::getReturnDateType();
if ($retType === Functions::RETURNDATE_PHP_DATETIME_OBJECT) {
return $PHPDateObject;
}
if ($retType === Functions::RETURNDATE_EXCEL) {
return (float) Date::PHPToExcel($PHPDateObject);
}
// RETURNDATE_UNIX_TIMESTAMP
return (int) Date::excelToTimestamp(Date::PHPToExcel($PHPDateObject));
}
private static function baseDate(): int
{
if (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE) {
return 0;
}
if (Date::getExcelCalendar() === Date::CALENDAR_MAC_1904) {
return 0;
}
return 1;
}
/**
* Many functions accept null/false/true argument treated as 0/0/1.
*
* @param mixed $number
*/
public static function nullFalseTrueToNumber(&$number, bool $allowBool = true): void
{
$number = Functions::flattenSingleValue($number);
$nullVal = self::baseDate();
if ($number === null) {
$number = $nullVal;
} elseif ($allowBool && is_bool($number)) {
$number = $nullVal + (int) $number;
}
}
/**
* Many functions accept null argument treated as 0.
*
* @param mixed $number
*
* @return float|int
*/
public static function validateNumericNull($number)
{
$number = Functions::flattenSingleValue($number);
if ($number === null) {
return 0;
}
if (is_numeric($number)) {
return $number;
}
throw new Exception(Functions::VALUE());
}
/**
* Many functions accept null/false/true argument treated as 0/0/1.
*
* @param mixed $number
*
* @return float
*/
public static function validateNotNegative($number)
{
if (!is_numeric($number)) {
throw new Exception(Functions::VALUE());
}
if ($number >= 0) {
return (float) $number;
}
throw new Exception(Functions::NAN());
}
public static function silly1900(DateTime $PHPDateObject, string $mod = '-1 day'): void
{
$isoDate = $PHPDateObject->format('c');
if ($isoDate < '1900-03-01') {
$PHPDateObject->modify($mod);
}
}
}

View File

@ -0,0 +1,44 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Shared\Date;
class Hour
{
/**
* HOUROFDAY.
*
* Returns the hour of a time value.
* The hour is given as an integer, ranging from 0 (12:00 A.M.) to 23 (11:00 P.M.).
*
* Excel Function:
* HOUR(timeValue)
*
* @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard time string
*
* @return int|string Hour
*/
public static function funcHour($timeValue)
{
try {
$timeValue = Functions::flattenSingleValue($timeValue);
Helpers::nullFalseTrueToNumber($timeValue);
if (!is_numeric($timeValue)) {
$timeValue = Helpers::getTimeValue($timeValue);
}
Helpers::validateNotNegative($timeValue);
} catch (Exception $e) {
return $e->getMessage();
}
// Execute function
$timeValue = fmod($timeValue, 1);
$timeValue = Date::excelToDateTimeObject($timeValue);
return (int) $timeValue->format('H');
}
}

View File

@ -0,0 +1,55 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use Exception;
use PhpOffice\PhpSpreadsheet\Shared\Date;
class IsoWeekNum
{
/**
* ISOWEEKNUM.
*
* Returns the ISO 8601 week number of the year for a specified date.
*
* Excel Function:
* ISOWEEKNUM(dateValue)
*
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
*
* @return int|string Week Number
*/
public static function funcIsoWeekNum($dateValue)
{
if (self::apparentBug($dateValue)) {
return 52;
}
try {
$dateValue = Helpers::getDateValue($dateValue);
} catch (Exception $e) {
return $e->getMessage();
}
// Execute function
$PHPDateObject = Date::excelToDateTimeObject($dateValue);
Helpers::silly1900($PHPDateObject);
return (int) $PHPDateObject->format('W');
}
private static function apparentBug($dateValue): bool
{
if (Date::getExcelCalendar() !== DATE::CALENDAR_MAC_1904) {
if (is_bool($dateValue)) {
return true;
}
if (is_numeric($dateValue) && !((int) $dateValue)) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,44 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Shared\Date;
class Minute
{
/**
* MINUTE.
*
* Returns the minutes of a time value.
* The minute is given as an integer, ranging from 0 to 59.
*
* Excel Function:
* MINUTE(timeValue)
*
* @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard time string
*
* @return int|string Minute
*/
public static function funcMinute($timeValue)
{
try {
$timeValue = Functions::flattenSingleValue($timeValue);
Helpers::nullFalseTrueToNumber($timeValue);
if (!is_numeric($timeValue)) {
$timeValue = Helpers::getTimeValue($timeValue);
}
Helpers::validateNotNegative($timeValue);
} catch (Exception $e) {
return $e->getMessage();
}
// Execute function
$timeValue = fmod($timeValue, 1);
$timeValue = Date::excelToDateTimeObject($timeValue);
return (int) $timeValue->format('i');
}
}

View File

@ -0,0 +1,40 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use Exception;
use PhpOffice\PhpSpreadsheet\Shared\Date;
class Month
{
/**
* MONTHOFYEAR.
*
* Returns the month of a date represented by a serial number.
* The month is given as an integer, ranging from 1 (January) to 12 (December).
*
* Excel Function:
* MONTH(dateValue)
*
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
*
* @return int|string Month of the year
*/
public static function funcMonth($dateValue)
{
try {
$dateValue = Helpers::getDateValue($dateValue);
} catch (Exception $e) {
return $e->getMessage();
}
if ($dateValue < 1 && Date::getExcelCalendar() === DATE::CALENDAR_WINDOWS_1900) {
return 1;
}
// Execute function
$PHPDateObject = Date::excelToDateTimeObject($dateValue);
return (int) $PHPDateObject->format('n');
}
}

View File

@ -0,0 +1,102 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Shared\Date;
class NetworkDays
{
/**
* NETWORKDAYS.
*
* Returns the number of whole working days between start_date and end_date. Working days
* exclude weekends and any dates identified in holidays.
* Use NETWORKDAYS to calculate employee benefits that accrue based on the number of days
* worked during a specific term.
*
* Excel Function:
* NETWORKDAYS(startDate,endDate[,holidays[,holiday[,...]]])
*
* @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
*
* @return int|string Interval between the dates
*/
public static function funcNetworkDays($startDate, $endDate, ...$dateArgs)
{
try {
// Retrieve the mandatory start and end date that are referenced in the function definition
$sDate = Helpers::getDateValue($startDate);
$eDate = Helpers::getDateValue($endDate);
$startDate = min($sDate, $eDate);
$endDate = max($sDate, $eDate);
// Get the optional days
$dateArgs = Functions::flattenArray($dateArgs);
// Test any extra holiday parameters
$holidayArray = [];
foreach ($dateArgs as $holidayDate) {
$holidayArray[] = Helpers::getDateValue($holidayDate);
}
} catch (Exception $e) {
return $e->getMessage();
}
// Execute function
$startDow = self::calcStartDow($startDate);
$endDow = self::calcEndDow($endDate);
$wholeWeekDays = (int) floor(($endDate - $startDate) / 7) * 5;
$partWeekDays = self::calcPartWeekDays($startDow, $endDow);
// Test any extra holiday parameters
$holidayCountedArray = [];
foreach ($holidayArray as $holidayDate) {
if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) {
if ((WeekDay::funcWeekDay($holidayDate, 2) < 6) && (!in_array($holidayDate, $holidayCountedArray))) {
--$partWeekDays;
$holidayCountedArray[] = $holidayDate;
}
}
}
return self::applySign($wholeWeekDays + $partWeekDays, $sDate, $eDate);
}
private static function calcStartDow(float $startDate): int
{
$startDow = 6 - (int) WeekDay::funcWeekDay($startDate, 2);
if ($startDow < 0) {
$startDow = 5;
}
return $startDow;
}
private static function calcEndDow(float $endDate): int
{
$endDow = (int) WeekDay::funcWeekDay($endDate, 2);
if ($endDow >= 6) {
$endDow = 0;
}
return $endDow;
}
private static function calcPartWeekDays(int $startDow, int $endDow): int
{
$partWeekDays = $endDow + $startDow;
if ($partWeekDays > 5) {
$partWeekDays -= 5;
}
return $partWeekDays;
}
private static function applySign(int $result, float $sDate, float $eDate)
{
return ($sDate > $eDate) ? -$result : $result;
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use DateTimeImmutable;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
class Now
{
/**
* DATETIMENOW.
*
* Returns the current date and time.
* The NOW function is useful when you need to display the current date and time on a worksheet or
* calculate a value based on the current date and time, and have that value updated each time you
* open the worksheet.
*
* NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
* and time format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
*
* Excel Function:
* NOW()
*
* @return mixed 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 funcNow()
{
$dti = new DateTimeImmutable();
$dateArray = date_parse($dti->format('c'));
return is_array($dateArray) ? Helpers::returnIn3FormatsArray($dateArray) : Functions::VALUE();
}
}

View File

@ -0,0 +1,44 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Shared\Date;
class Second
{
/**
* MINUTE.
*
* Returns the minutes of a time value.
* The minute is given as an integer, ranging from 0 to 59.
*
* Excel Function:
* MINUTE(timeValue)
*
* @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard time string
*
* @return int|string Minute
*/
public static function funcSecond($timeValue)
{
try {
$timeValue = Functions::flattenSingleValue($timeValue);
Helpers::nullFalseTrueToNumber($timeValue);
if (!is_numeric($timeValue)) {
$timeValue = Helpers::getTimeValue($timeValue);
}
Helpers::validateNotNegative($timeValue);
} catch (Exception $e) {
return $e->getMessage();
}
// Execute function
$timeValue = fmod($timeValue, 1);
$timeValue = Date::excelToDateTimeObject($timeValue);
return (int) $timeValue->format('s');
}
}

View File

@ -0,0 +1,116 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use DateTime;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Shared\Date;
class Time
{
/**
* TIME.
*
* The TIME function returns a value that represents a particular time.
*
* NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the time
* format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
*
* Excel Function:
* TIME(hour,minute,second)
*
* @param int $hour A number from 0 (zero) to 32767 representing the hour.
* Any value greater than 23 will be divided by 24 and the remainder
* will be treated as the hour value. For example, TIME(27,0,0) =
* TIME(3,0,0) = .125 or 3:00 AM.
* @param int $minute A number from 0 to 32767 representing the minute.
* Any value greater than 59 will be converted to hours and minutes.
* For example, TIME(0,750,0) = TIME(12,30,0) = .520833 or 12:30 PM.
* @param int $second A number from 0 to 32767 representing the second.
* Any value greater than 59 will be converted to hours, minutes,
* and seconds. For example, TIME(0,0,2000) = TIME(0,33,22) = .023148
* or 12:33:20 AM
*
* @return mixed 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 funcTime($hour, $minute, $second)
{
try {
$hour = self::toIntWithNullBool($hour);
$minute = self::toIntWithNullBool($minute);
$second = self::toIntWithNullBool($second);
} catch (Exception $e) {
return $e->getMessage();
}
self::adjustSecond($second, $minute);
self::adjustMinute($minute, $hour);
if ($hour > 23) {
$hour = $hour % 24;
} elseif ($hour < 0) {
return Functions::NAN();
}
// Execute function
$retType = Functions::getReturnDateType();
if ($retType === Functions::RETURNDATE_EXCEL) {
$calendar = Date::getExcelCalendar();
$date = (int) ($calendar !== Date::CALENDAR_WINDOWS_1900);
return (float) Date::formattedPHPToExcel($calendar, 1, $date, $hour, $minute, $second);
}
if ($retType === Functions::RETURNDATE_UNIX_TIMESTAMP) {
return (int) Date::excelToTimestamp(Date::formattedPHPToExcel(1970, 1, 1, $hour, $minute, $second)); // -2147468400; // -2147472000 + 3600
}
// RETURNDATE_PHP_DATETIME_OBJECT
// Hour has already been normalized (0-23) above
$phpDateObject = new DateTime('1900-01-01 ' . $hour . ':' . $minute . ':' . $second);
return $phpDateObject;
}
private static function adjustSecond(int &$second, int &$minute): void
{
if ($second < 0) {
$minute += floor($second / 60);
$second = 60 - abs($second % 60);
if ($second == 60) {
$second = 0;
}
} elseif ($second >= 60) {
$minute += floor($second / 60);
$second = $second % 60;
}
}
private static function adjustMinute(int &$minute, int &$hour): void
{
if ($minute < 0) {
$hour += floor($minute / 60);
$minute = 60 - abs($minute % 60);
if ($minute == 60) {
$minute = 0;
}
} elseif ($minute >= 60) {
$hour += floor($minute / 60);
$minute = $minute % 60;
}
}
private static function toIntWithNullBool($value): int
{
$value = Functions::flattenSingleValue($value);
$value = $value ?? 0;
if (is_bool($value)) {
$value = (int) $value;
}
if (!is_numeric($value)) {
throw new Exception(Functions::VALUE());
}
return (int) $value;
}
}

View File

@ -0,0 +1,61 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use Datetime;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Shared\Date;
class TimeValue
{
/**
* TIMEVALUE.
*
* Returns a value that represents a particular time.
* Use TIMEVALUE to convert a time represented by a text string to an Excel or PHP date/time stamp
* value.
*
* NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the time
* format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
*
* Excel Function:
* TIMEVALUE(timeValue)
*
* @param string $timeValue A text string that represents a time in any one of the Microsoft
* Excel time formats; for example, "6:45 PM" and "18:45" text strings
* within quotation marks that represent time.
* Date information in time_text is ignored.
*
* @return mixed 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 funcTimeValue($timeValue)
{
$timeValue = trim(Functions::flattenSingleValue($timeValue), '"');
$timeValue = str_replace(['/', '.'], '-', $timeValue);
$arraySplit = preg_split('/[\/:\-\s]/', $timeValue);
if ((count($arraySplit) == 2 || count($arraySplit) == 3) && $arraySplit[0] > 24) {
$arraySplit[0] = ($arraySplit[0] % 24);
$timeValue = implode(':', $arraySplit);
}
$PHPDateArray = date_parse($timeValue);
$retValue = Functions::VALUE();
if (($PHPDateArray !== false) && ($PHPDateArray['error_count'] == 0)) {
// OpenOffice-specific code removed - it works just like Excel
$excelDateValue = Date::formattedPHPToExcel(1900, 1, 1, $PHPDateArray['hour'], $PHPDateArray['minute'], $PHPDateArray['second']) - 1;
$retType = Functions::getReturnDateType();
if ($retType === Functions::RETURNDATE_EXCEL) {
$retValue = (float) $excelDateValue;
} elseif ($retType === Functions::RETURNDATE_UNIX_TIMESTAMP) {
$retValue = (int) $phpDateValue = Date::excelToTimestamp($excelDateValue + 25569) - 3600;
} else {
$retValue = new DateTime('1900-01-01 ' . $PHPDateArray['hour'] . ':' . $PHPDateArray['minute'] . ':' . $PHPDateArray['second']);
}
}
return $retValue;
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use DateTimeImmutable;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
class Today
{
/**
* DATENOW.
*
* Returns the current date.
* The NOW function is useful when you need to display the current date and time on a worksheet or
* calculate a value based on the current date and time, and have that value updated each time you
* open the worksheet.
*
* NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
* and time format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
*
* Excel Function:
* TODAY()
*
* @return mixed 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 funcToday()
{
$dti = new DateTimeImmutable();
$dateArray = date_parse($dti->format('c'));
return is_array($dateArray) ? Helpers::returnIn3FormatsArray($dateArray, true) : Functions::VALUE();
}
}

View File

@ -0,0 +1,80 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Shared\Date;
class WeekDay
{
/**
* WEEKDAY.
*
* Returns the day of the week for a specified date. The day is given as an integer
* ranging from 0 to 7 (dependent on the requested style).
*
* Excel Function:
* WEEKDAY(dateValue[,style])
*
* @param null|float|int|string $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* @param int $style A number that determines the type of return value
* 1 or omitted Numbers 1 (Sunday) through 7 (Saturday).
* 2 Numbers 1 (Monday) through 7 (Sunday).
* 3 Numbers 0 (Monday) through 6 (Sunday).
*
* @return int|string Day of the week value
*/
public static function funcWeekDay($dateValue, $style = 1)
{
try {
$dateValue = Helpers::getDateValue($dateValue);
$style = self::validateStyle($style);
} catch (Exception $e) {
return $e->getMessage();
}
// Execute function
$PHPDateObject = Date::excelToDateTimeObject($dateValue);
Helpers::silly1900($PHPDateObject);
$DoW = (int) $PHPDateObject->format('w');
switch ($style) {
case 1:
++$DoW;
break;
case 2:
$DoW = self::dow0Becomes7($DoW);
break;
case 3:
$DoW = self::dow0Becomes7($DoW) - 1;
break;
}
return $DoW;
}
private static function validateStyle($style): int
{
$style = Functions::flattenSingleValue($style);
if (!is_numeric($style)) {
throw new Exception(Functions::VALUE());
}
$style = (int) $style;
if (($style < 1) || ($style > 3)) {
throw new Exception(Functions::NAN());
}
return $style;
}
private static function dow0Becomes7(int $DoW): int
{
return ($DoW === 0) ? 7 : $DoW;
}
}

View File

@ -0,0 +1,130 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use DateTime;
use Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Shared\Date;
class WeekNum
{
/**
* WEEKNUM.
*
* Returns the week of the year for a specified date.
* The WEEKNUM function considers the week containing January 1 to be the first week of the year.
* However, there is a European standard that defines the first week as the one with the majority
* of days (four or more) falling in the new year. This means that for years in which there are
* three days or less in the first week of January, the WEEKNUM function returns week numbers
* that are incorrect according to the European standard.
*
* Excel Function:
* WEEKNUM(dateValue[,style])
*
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* @param int $method Week begins on Sunday or Monday
* 1 or omitted Week begins on Sunday.
* 2 Week begins on Monday.
* 11 Week begins on Monday.
* 12 Week begins on Tuesday.
* 13 Week begins on Wednesday.
* 14 Week begins on Thursday.
* 15 Week begins on Friday.
* 16 Week begins on Saturday.
* 17 Week begins on Sunday.
* 21 ISO (Jan. 4 is week 1, begins on Monday).
*
* @return int|string Week Number
*/
public static function funcWeekNum($dateValue, $method = Constants::STARTWEEK_SUNDAY)
{
$origDateValueNull = empty($dateValue);
try {
$method = self::validateMethod($method);
if ($dateValue === null) { // boolean not allowed
$dateValue = (Date::getExcelCalendar() === DATE::CALENDAR_MAC_1904 || $method === Constants::DOW_SUNDAY) ? 0 : 1;
}
$dateValue = self::validateDateValue($dateValue);
if (!$dateValue && self::buggyWeekNum1900($method)) {
// This seems to be an additional Excel bug.
return 0;
}
} catch (Exception $e) {
return $e->getMessage();
}
// Execute function
$PHPDateObject = Date::excelToDateTimeObject($dateValue);
if ($method == Constants::STARTWEEK_MONDAY_ISO) {
Helpers::silly1900($PHPDateObject);
return (int) $PHPDateObject->format('W');
}
if (self::buggyWeekNum1904($method, $origDateValueNull, $PHPDateObject)) {
return 0;
}
Helpers::silly1900($PHPDateObject, '+ 5 years'); // 1905 calendar matches
$dayOfYear = (int) $PHPDateObject->format('z');
$PHPDateObject->modify('-' . $dayOfYear . ' days');
$firstDayOfFirstWeek = (int) $PHPDateObject->format('w');
$daysInFirstWeek = (6 - $firstDayOfFirstWeek + $method) % 7;
$daysInFirstWeek += 7 * !$daysInFirstWeek;
$endFirstWeek = $daysInFirstWeek - 1;
$weekOfYear = floor(($dayOfYear - $endFirstWeek + 13) / 7);
return (int) $weekOfYear;
}
/**
* Validate dateValue parameter.
*
* @param mixed $dateValue
*/
private static function validateDateValue($dateValue): float
{
if (is_bool($dateValue)) {
throw new Exception(Functions::VALUE());
}
return Helpers::getDateValue($dateValue);
}
/**
* Validate method parameter.
*
* @param mixed $method
*/
private static function validateMethod($method): int
{
if ($method === null) {
$method = Constants::STARTWEEK_SUNDAY;
}
$method = Functions::flattenSingleValue($method);
if (!is_numeric($method)) {
throw new Exception(Functions::VALUE());
}
$method = (int) $method;
if (!array_key_exists($method, Constants::METHODARR)) {
throw new Exception(Functions::NAN());
}
$method = Constants::METHODARR[$method];
return $method;
}
private static function buggyWeekNum1900(int $method): bool
{
return $method === Constants::DOW_SUNDAY && Date::getExcelCalendar() === Date::CALENDAR_WINDOWS_1900;
}
private static function buggyWeekNum1904(int $method, bool $origNull, DateTime $dateObject): bool
{
// This appears to be another Excel bug.
return $method === Constants::DOW_SUNDAY && Date::getExcelCalendar() === Date::CALENDAR_MAC_1904 && !$origNull && $dateObject->format('Y-m-d') === '1904-01-01';
}
}

View File

@ -0,0 +1,184 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Shared\Date;
class WorkDay
{
/**
* WORKDAY.
*
* Returns the date that is the indicated number of working days before or after a date (the
* starting date). Working days exclude weekends and any dates identified as holidays.
* Use WORKDAY to exclude weekends or holidays when you calculate invoice due dates, expected
* delivery times, or the number of days of work performed.
*
* Excel Function:
* WORKDAY(startDate,endDays[,holidays[,holiday[,...]]])
*
* @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* @param int $endDays The number of nonweekend and nonholiday days before or after
* startDate. A positive value for days yields a future date; a
* negative value yields a past date.
*
* @return mixed 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 funcWorkDay($startDate, $endDays, ...$dateArgs)
{
// Retrieve the mandatory start date and days that are referenced in the function definition
try {
$startDate = Helpers::getDateValue($startDate);
$endDays = Helpers::validateNumericNull($endDays);
$dateArgs = Functions::flattenArray($dateArgs);
$holidayArray = [];
foreach ($dateArgs as $holidayDate) {
$holidayArray[] = Helpers::getDateValue($holidayDate);
}
} catch (Exception $e) {
return $e->getMessage();
}
$startDate = (float) floor($startDate);
$endDays = (int) floor($endDays);
// If endDays is 0, we always return startDate
if ($endDays == 0) {
return $startDate;
}
if ($endDays < 0) {
return self::decrementing($startDate, $endDays, $holidayArray);
}
return self::incrementing($startDate, $endDays, $holidayArray);
}
/**
* Use incrementing logic to determine Workday.
*
* @return mixed
*/
private static function incrementing(float $startDate, int $endDays, array $holidayArray)
{
// Adjust the start date if it falls over a weekend
$startDoW = WeekDay::funcWeekDay($startDate, 3);
if (WeekDay::funcWeekDay($startDate, 3) >= 5) {
$startDate += 7 - $startDoW;
--$endDays;
}
// Add endDays
$endDate = (float) $startDate + ((int) ($endDays / 5) * 7);
$endDays = $endDays % 5;
while ($endDays > 0) {
++$endDate;
// Adjust the calculated end date if it falls over a weekend
$endDow = WeekDay::funcWeekDay($endDate, 3);
if ($endDow >= 5) {
$endDate += 7 - $endDow;
}
--$endDays;
}
// Test any extra holiday parameters
if (!empty($holidayArray)) {
$endDate = self::incrementingArray($startDate, $endDate, $holidayArray);
}
return Helpers::returnIn3FormatsFloat($endDate);
}
private static function incrementingArray(float $startDate, float $endDate, array $holidayArray): float
{
$holidayCountedArray = $holidayDates = [];
foreach ($holidayArray as $holidayDate) {
if (WeekDay::funcWeekDay($holidayDate, 3) < 5) {
$holidayDates[] = $holidayDate;
}
}
sort($holidayDates, SORT_NUMERIC);
foreach ($holidayDates as $holidayDate) {
if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) {
if (!in_array($holidayDate, $holidayCountedArray)) {
++$endDate;
$holidayCountedArray[] = $holidayDate;
}
}
// Adjust the calculated end date if it falls over a weekend
$endDoW = WeekDay::funcWeekDay($endDate, 3);
if ($endDoW >= 5) {
$endDate += 7 - $endDoW;
}
}
return $endDate;
}
/**
* Use decrementing logic to determine Workday.
*
* @return mixed
*/
private static function decrementing(float $startDate, int $endDays, array $holidayArray)
{
// Adjust the start date if it falls over a weekend
$startDoW = WeekDay::funcWeekDay($startDate, 3);
if (WeekDay::funcWeekDay($startDate, 3) >= 5) {
// @phpstan-ignore-next-line
$startDate += -$startDoW + 4;
++$endDays;
}
// Add endDays
$endDate = (float) $startDate + ((int) ($endDays / 5) * 7);
$endDays = $endDays % 5;
while ($endDays < 0) {
--$endDate;
// Adjust the calculated end date if it falls over a weekend
$endDow = WeekDay::funcWeekDay($endDate, 3);
if ($endDow >= 5) {
$endDate += 4 - $endDow;
}
++$endDays;
}
// Test any extra holiday parameters
if (!empty($holidayArray)) {
$endDate = self::decrementingArray($startDate, $endDate, $holidayArray);
}
return Helpers::returnIn3FormatsFloat($endDate);
}
private static function decrementingArray(float $startDate, float $endDate, array $holidayArray): float
{
$holidayCountedArray = $holidayDates = [];
foreach ($holidayArray as $holidayDate) {
if (WeekDay::funcWeekDay($holidayDate, 3) < 5) {
$holidayDates[] = $holidayDate;
}
}
rsort($holidayDates, SORT_NUMERIC);
foreach ($holidayDates as $holidayDate) {
if (($holidayDate <= $startDate) && ($holidayDate >= $endDate)) {
if (!in_array($holidayDate, $holidayCountedArray)) {
--$endDate;
$holidayCountedArray[] = $holidayDate;
}
}
// Adjust the calculated end date if it falls over a weekend
$endDoW = WeekDay::funcWeekDay($endDate, 3);
if ($endDoW >= 5) {
// @phpstan-ignore-next-line
$endDate += -$endDoW + 4;
}
}
return $endDate;
}
}

View File

@ -0,0 +1,40 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use Exception;
use PhpOffice\PhpSpreadsheet\Shared\Date;
class Year
{
/**
* YEAR.
*
* Returns the year corresponding to a date.
* The year is returned as an integer in the range 1900-9999.
*
* Excel Function:
* YEAR(dateValue)
*
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
*
* @return int|string Year
*/
public static function funcYear($dateValue)
{
try {
$dateValue = Helpers::getDateValue($dateValue);
} catch (Exception $e) {
return $e->getMessage();
}
if ($dateValue < 1 && Date::getExcelCalendar() === DATE::CALENDAR_WINDOWS_1900) {
return 1900;
}
// Execute function
$PHPDateObject = Date::excelToDateTimeObject($dateValue);
return (int) $PHPDateObject->format('Y');
}
}

Some files were not shown because too many files have changed in this diff Show More