|
2 | 2 |
|
3 | 3 | namespace PhpOffice\PhpSpreadsheet\Calculation\Financial;
|
4 | 4 |
|
| 5 | +use DateTime; |
5 | 6 | use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
|
6 | 7 | use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
7 | 8 | use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstants;
|
@@ -73,7 +74,7 @@ public static function COUPDAYBS(
|
73 | 74 | return abs((float) DateTimeExcel\Days::between($prev, $settlement));
|
74 | 75 | }
|
75 | 76 |
|
76 |
| - return DateTimeExcel\YearFrac::fraction($prev, $settlement, $basis) * $daysPerYear; |
| 77 | + return (float) DateTimeExcel\YearFrac::fraction($prev, $settlement, $basis) * $daysPerYear; |
77 | 78 | }
|
78 | 79 |
|
79 | 80 | /**
|
@@ -208,7 +209,7 @@ public static function COUPDAYSNC(
|
208 | 209 | }
|
209 | 210 | }
|
210 | 211 |
|
211 |
| - return DateTimeExcel\YearFrac::fraction($settlement, $next, $basis) * $daysPerYear; |
| 212 | + return (float) DateTimeExcel\YearFrac::fraction($settlement, $next, $basis) * $daysPerYear; |
212 | 213 | }
|
213 | 214 |
|
214 | 215 | /**
|
@@ -322,7 +323,7 @@ public static function COUPNUM(
|
322 | 323 | FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
323 | 324 | );
|
324 | 325 |
|
325 |
| - return (int) ceil($yearsBetweenSettlementAndMaturity * $frequency); |
| 326 | + return (int) ceil((float) $yearsBetweenSettlementAndMaturity * $frequency); |
326 | 327 | }
|
327 | 328 |
|
328 | 329 | /**
|
@@ -379,28 +380,33 @@ public static function COUPPCD(
|
379 | 380 | return self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_PREVIOUS);
|
380 | 381 | }
|
381 | 382 |
|
382 |
| - private static function couponFirstPeriodDate($settlement, $maturity, int $frequency, $next) |
| 383 | + private static function monthsDiff(DateTime $result, int $months, string $plusOrMinus, int $day, bool $lastDayFlag): void |
| 384 | + { |
| 385 | + $result->setDate((int) $result->format('Y'), (int) $result->format('m'), 1); |
| 386 | + $result->modify("$plusOrMinus $months months"); |
| 387 | + $daysInMonth = (int) $result->format('t'); |
| 388 | + $result->setDate((int) $result->format('Y'), (int) $result->format('m'), $lastDayFlag ? $daysInMonth : min($day, $daysInMonth)); |
| 389 | + } |
| 390 | + |
| 391 | + private static function couponFirstPeriodDate(float $settlement, float $maturity, int $frequency, bool $next): float |
383 | 392 | {
|
384 | 393 | $months = 12 / $frequency;
|
385 | 394 |
|
386 | 395 | $result = Date::excelToDateTimeObject($maturity);
|
387 |
| - $maturityEoM = Helpers::isLastDayOfMonth($result); |
| 396 | + $day = (int) $result->format('d'); |
| 397 | + $lastDayFlag = Helpers::isLastDayOfMonth($result); |
388 | 398 |
|
389 | 399 | while ($settlement < Date::PHPToExcel($result)) {
|
390 |
| - $result->modify('-' . $months . ' months'); |
| 400 | + self::monthsDiff($result, $months, '-', $day, $lastDayFlag); |
391 | 401 | }
|
392 | 402 | if ($next === true) {
|
393 |
| - $result->modify('+' . $months . ' months'); |
394 |
| - } |
395 |
| - |
396 |
| - if ($maturityEoM === true) { |
397 |
| - $result->modify('-1 day'); |
| 403 | + self::monthsDiff($result, $months, '+', $day, $lastDayFlag); |
398 | 404 | }
|
399 | 405 |
|
400 |
| - return Date::PHPToExcel($result); |
| 406 | + return (float) Date::PHPToExcel($result); |
401 | 407 | }
|
402 | 408 |
|
403 |
| - private static function validateCouponPeriod($settlement, $maturity): void |
| 409 | + private static function validateCouponPeriod(float $settlement, float $maturity): void |
404 | 410 | {
|
405 | 411 | if ($settlement >= $maturity) {
|
406 | 412 | throw new Exception(Functions::NAN());
|
|
0 commit comments