Difference.php 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. <?php
  2. namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
  3. use DateInterval;
  4. use DateTime;
  5. use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
  6. use PhpOffice\PhpSpreadsheet\Calculation\Exception;
  7. use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
  8. use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper;
  9. class Difference
  10. {
  11. use ArrayEnabled;
  12. /**
  13. * DATEDIF.
  14. *
  15. * @param mixed $startDate Excel date serial value, PHP date/time stamp, PHP DateTime object
  16. * or a standard date string
  17. * Or can be an array of date values
  18. * @param mixed $endDate Excel date serial value, PHP date/time stamp, PHP DateTime object
  19. * or a standard date string
  20. * Or can be an array of date values
  21. * @param array|string $unit
  22. * Or can be an array of unit values
  23. *
  24. * @return array|int|string Interval between the dates
  25. * If an array of values is passed for the $startDate or $endDays,arguments, then the returned result
  26. * will also be an array with matching dimensions
  27. */
  28. public static function interval($startDate, $endDate, $unit = 'D')
  29. {
  30. if (is_array($startDate) || is_array($endDate) || is_array($unit)) {
  31. return self::evaluateArrayArguments([self::class, __FUNCTION__], $startDate, $endDate, $unit);
  32. }
  33. try {
  34. $startDate = Helpers::getDateValue($startDate);
  35. $endDate = Helpers::getDateValue($endDate);
  36. $difference = self::initialDiff($startDate, $endDate);
  37. $unit = strtoupper($unit);
  38. } catch (Exception $e) {
  39. return $e->getMessage();
  40. }
  41. // Execute function
  42. $PHPStartDateObject = SharedDateHelper::excelToDateTimeObject($startDate);
  43. $startDays = (int) $PHPStartDateObject->format('j');
  44. //$startMonths = (int) $PHPStartDateObject->format('n');
  45. $startYears = (int) $PHPStartDateObject->format('Y');
  46. $PHPEndDateObject = SharedDateHelper::excelToDateTimeObject($endDate);
  47. $endDays = (int) $PHPEndDateObject->format('j');
  48. //$endMonths = (int) $PHPEndDateObject->format('n');
  49. $endYears = (int) $PHPEndDateObject->format('Y');
  50. $PHPDiffDateObject = $PHPEndDateObject->diff($PHPStartDateObject);
  51. $retVal = false;
  52. $retVal = self::replaceRetValue($retVal, $unit, 'D') ?? self::datedifD($difference);
  53. $retVal = self::replaceRetValue($retVal, $unit, 'M') ?? self::datedifM($PHPDiffDateObject);
  54. $retVal = self::replaceRetValue($retVal, $unit, 'MD') ?? self::datedifMD($startDays, $endDays, $PHPEndDateObject, $PHPDiffDateObject);
  55. $retVal = self::replaceRetValue($retVal, $unit, 'Y') ?? self::datedifY($PHPDiffDateObject);
  56. $retVal = self::replaceRetValue($retVal, $unit, 'YD') ?? self::datedifYD($difference, $startYears, $endYears, $PHPStartDateObject, $PHPEndDateObject);
  57. $retVal = self::replaceRetValue($retVal, $unit, 'YM') ?? self::datedifYM($PHPDiffDateObject);
  58. return is_bool($retVal) ? ExcelError::VALUE() : $retVal;
  59. }
  60. private static function initialDiff(float $startDate, float $endDate): float
  61. {
  62. // Validate parameters
  63. if ($startDate > $endDate) {
  64. throw new Exception(ExcelError::NAN());
  65. }
  66. return $endDate - $startDate;
  67. }
  68. /**
  69. * Decide whether it's time to set retVal.
  70. *
  71. * @param bool|int $retVal
  72. *
  73. * @return null|bool|int
  74. */
  75. private static function replaceRetValue($retVal, string $unit, string $compare)
  76. {
  77. if ($retVal !== false || $unit !== $compare) {
  78. return $retVal;
  79. }
  80. return null;
  81. }
  82. private static function datedifD(float $difference): int
  83. {
  84. return (int) $difference;
  85. }
  86. private static function datedifM(DateInterval $PHPDiffDateObject): int
  87. {
  88. return 12 * (int) $PHPDiffDateObject->format('%y') + (int) $PHPDiffDateObject->format('%m');
  89. }
  90. private static function datedifMD(int $startDays, int $endDays, DateTime $PHPEndDateObject, DateInterval $PHPDiffDateObject): int
  91. {
  92. if ($endDays < $startDays) {
  93. $retVal = $endDays;
  94. $PHPEndDateObject->modify('-' . $endDays . ' days');
  95. $adjustDays = (int) $PHPEndDateObject->format('j');
  96. $retVal += ($adjustDays - $startDays);
  97. } else {
  98. $retVal = (int) $PHPDiffDateObject->format('%d');
  99. }
  100. return $retVal;
  101. }
  102. private static function datedifY(DateInterval $PHPDiffDateObject): int
  103. {
  104. return (int) $PHPDiffDateObject->format('%y');
  105. }
  106. private static function datedifYD(float $difference, int $startYears, int $endYears, DateTime $PHPStartDateObject, DateTime $PHPEndDateObject): int
  107. {
  108. $retVal = (int) $difference;
  109. if ($endYears > $startYears) {
  110. $isLeapStartYear = $PHPStartDateObject->format('L');
  111. $wasLeapEndYear = $PHPEndDateObject->format('L');
  112. // Adjust end year to be as close as possible as start year
  113. while ($PHPEndDateObject >= $PHPStartDateObject) {
  114. $PHPEndDateObject->modify('-1 year');
  115. //$endYears = $PHPEndDateObject->format('Y');
  116. }
  117. $PHPEndDateObject->modify('+1 year');
  118. // Get the result
  119. $retVal = (int) $PHPEndDateObject->diff($PHPStartDateObject)->days;
  120. // Adjust for leap years cases
  121. $isLeapEndYear = $PHPEndDateObject->format('L');
  122. $limit = new DateTime($PHPEndDateObject->format('Y-02-29'));
  123. if (!$isLeapStartYear && !$wasLeapEndYear && $isLeapEndYear && $PHPEndDateObject >= $limit) {
  124. --$retVal;
  125. }
  126. }
  127. return (int) $retVal;
  128. }
  129. private static function datedifYM(DateInterval $PHPDiffDateObject): int
  130. {
  131. return (int) $PHPDiffDateObject->format('%m');
  132. }
  133. }