DayOfWeekField.php 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. <?php
  2. namespace Cron;
  3. use DateTime;
  4. use InvalidArgumentException;
  5. /**
  6. * Day of week field. Allows: * / , - ? L #
  7. *
  8. * Days of the week can be represented as a number 0-7 (0|7 = Sunday)
  9. * or as a three letter string: SUN, MON, TUE, WED, THU, FRI, SAT.
  10. *
  11. * 'L' stands for "last". It allows you to specify constructs such as
  12. * "the last Friday" of a given month.
  13. *
  14. * '#' is allowed for the day-of-week field, and must be followed by a
  15. * number between one and five. It allows you to specify constructs such as
  16. * "the second Friday" of a given month.
  17. */
  18. class DayOfWeekField extends AbstractField
  19. {
  20. protected $rangeStart = 0;
  21. protected $rangeEnd = 7;
  22. protected $nthRange;
  23. protected $literals = [1 => 'MON', 2 => 'TUE', 3 => 'WED', 4 => 'THU', 5 => 'FRI', 6 => 'SAT', 7 => 'SUN'];
  24. public function __construct()
  25. {
  26. $this->nthRange = range(1, 5);
  27. parent::__construct();
  28. }
  29. public function isSatisfiedBy(DateTime $date, $value)
  30. {
  31. if ($value == '?') {
  32. return true;
  33. }
  34. // Convert text day of the week values to integers
  35. $value = $this->convertLiterals($value);
  36. $currentYear = $date->format('Y');
  37. $currentMonth = $date->format('m');
  38. $lastDayOfMonth = $date->format('t');
  39. // Find out if this is the last specific weekday of the month
  40. if (strpos($value, 'L')) {
  41. $weekday = str_replace('7', '0', substr($value, 0, strpos($value, 'L')));
  42. $tdate = clone $date;
  43. $tdate->setDate($currentYear, $currentMonth, $lastDayOfMonth);
  44. while ($tdate->format('w') != $weekday) {
  45. $tdateClone = new DateTime();
  46. $tdate = $tdateClone
  47. ->setTimezone($tdate->getTimezone())
  48. ->setDate($currentYear, $currentMonth, --$lastDayOfMonth);
  49. }
  50. return $date->format('j') == $lastDayOfMonth;
  51. }
  52. // Handle # hash tokens
  53. if (strpos($value, '#')) {
  54. list($weekday, $nth) = explode('#', $value);
  55. if (!is_numeric($nth)) {
  56. throw new InvalidArgumentException("Hashed weekdays must be numeric, {$nth} given");
  57. } else {
  58. $nth = (int) $nth;
  59. }
  60. // 0 and 7 are both Sunday, however 7 matches date('N') format ISO-8601
  61. if ($weekday === '0') {
  62. $weekday = 7;
  63. }
  64. $weekday = $this->convertLiterals($weekday);
  65. // Validate the hash fields
  66. if ($weekday < 0 || $weekday > 7) {
  67. throw new InvalidArgumentException("Weekday must be a value between 0 and 7. {$weekday} given");
  68. }
  69. if (!in_array($nth, $this->nthRange)) {
  70. throw new InvalidArgumentException("There are never more than 5 or less than 1 of a given weekday in a month, {$nth} given");
  71. }
  72. // The current weekday must match the targeted weekday to proceed
  73. if ($date->format('N') != $weekday) {
  74. return false;
  75. }
  76. $tdate = clone $date;
  77. $tdate->setDate($currentYear, $currentMonth, 1);
  78. $dayCount = 0;
  79. $currentDay = 1;
  80. while ($currentDay < $lastDayOfMonth + 1) {
  81. if ($tdate->format('N') == $weekday) {
  82. if (++$dayCount >= $nth) {
  83. break;
  84. }
  85. }
  86. $tdate->setDate($currentYear, $currentMonth, ++$currentDay);
  87. }
  88. return $date->format('j') == $currentDay;
  89. }
  90. // Handle day of the week values
  91. if (strpos($value, '-')) {
  92. $parts = explode('-', $value);
  93. if ($parts[0] == '7') {
  94. $parts[0] = '0';
  95. } elseif ($parts[1] == '0') {
  96. $parts[1] = '7';
  97. }
  98. $value = implode('-', $parts);
  99. }
  100. // Test to see which Sunday to use -- 0 == 7 == Sunday
  101. $format = in_array(7, str_split($value)) ? 'N' : 'w';
  102. $fieldValue = $date->format($format);
  103. return $this->isSatisfied($fieldValue, $value);
  104. }
  105. public function increment(DateTime $date, $invert = false)
  106. {
  107. if ($invert) {
  108. $date->modify('-1 day');
  109. $date->setTime(23, 59, 0);
  110. } else {
  111. $date->modify('+1 day');
  112. $date->setTime(0, 0, 0);
  113. }
  114. return $this;
  115. }
  116. /**
  117. * @inheritDoc
  118. */
  119. public function validate($value)
  120. {
  121. $basicChecks = parent::validate($value);
  122. if (!$basicChecks) {
  123. // Handle the # value
  124. if (strpos($value, '#') !== false) {
  125. $chunks = explode('#', $value);
  126. $chunks[0] = $this->convertLiterals($chunks[0]);
  127. if (parent::validate($chunks[0]) && is_numeric($chunks[1]) && in_array($chunks[1], $this->nthRange)) {
  128. return true;
  129. }
  130. }
  131. if (preg_match('/^(.*)L$/', $value, $matches)) {
  132. return $this->validate($matches[1]);
  133. }
  134. return false;
  135. }
  136. return $basicChecks;
  137. }
  138. }