Merge branch 'master' into issue-907-absolute-path-in-Target
This commit is contained in:
commit
11f8a02194
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -8,3 +8,6 @@
|
|||
*.project
|
||||
/.settings
|
||||
/.idea
|
||||
|
||||
## mkdocs output
|
||||
/site
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
93
CHANGELOG.md
93
CHANGELOG.md
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
},
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,5 @@
|
|||
document.addEventListener("DOMContentLoaded", function() {
|
||||
document.querySelectorAll("table").forEach(function(table) {
|
||||
table.classList.add("docutils");
|
||||
});
|
||||
});
|
||||
|
|
@ -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 |
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Add a comment to a cell
|
||||
|
||||
To add a comment to a cell, use the following code. The example below
|
||||
|
|
|
|||
|
|
@ -5,3 +5,5 @@ edit_uri: edit/master/docs/
|
|||
theme: readthedocs
|
||||
extra_css:
|
||||
- extra/extra.css
|
||||
extra_javascript:
|
||||
- extra/extrajs.js
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -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)\.$~'
|
||||
|
|
@ -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__);
|
||||
|
|
@ -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__);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
@ -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());
|
||||
|
|
@ -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());
|
||||
|
|
@ -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());
|
||||
|
|
@ -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());
|
||||
|
|
@ -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());
|
||||
|
|
@ -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());
|
||||
|
|
@ -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());
|
||||
|
|
@ -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());
|
||||
|
|
@ -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());
|
||||
|
|
@ -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());
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
|
|
@ -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());
|
||||
|
|
@ -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());
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
|
|
@ -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());
|
||||
|
|
@ -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());
|
||||
|
|
@ -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());
|
||||
|
|
@ -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());
|
||||
}
|
||||
|
|
@ -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());
|
||||
|
|
@ -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());
|
||||
}
|
||||
|
|
@ -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());
|
||||
|
|
@ -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());
|
||||
}
|
||||
|
|
@ -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());
|
||||
|
|
@ -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());
|
||||
|
|
@ -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()}");
|
||||
}
|
||||
|
|
@ -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()}");
|
||||
|
|
@ -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()}");
|
||||
}
|
||||
|
|
@ -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()}");
|
||||
}
|
||||
|
|
@ -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()}");
|
||||
}
|
||||
|
|
@ -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()}");
|
||||
}
|
||||
|
|
@ -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()}");
|
||||
}
|
||||
|
|
@ -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()}");
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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,
|
||||
];
|
||||
}
|
||||
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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';
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
Loading…
Reference in New Issue