DayOfWeekField.php 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  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. public function isSatisfiedBy(DateTime $date, $value)
  21. {
  22. if ($value == '?') {
  23. return true;
  24. }
  25. // Convert text day of the week values to integers
  26. $value = $this->convertLiterals($value);
  27. $currentYear = $date->format('Y');
  28. $currentMonth = $date->format('m');
  29. $lastDayOfMonth = $date->format('t');
  30. // Find out if this is the last specific weekday of the month
  31. if (strpos($value, 'L')) {
  32. $weekday = str_replace('7', '0', substr($value, 0, strpos($value, 'L')));
  33. $tdate = clone $date;
  34. $tdate->setDate($currentYear, $currentMonth, $lastDayOfMonth);
  35. while ($tdate->format('w') != $weekday) {
  36. $tdateClone = new DateTime();
  37. $tdate = $tdateClone
  38. ->setTimezone($tdate->getTimezone())
  39. ->setDate($currentYear, $currentMonth, --$lastDayOfMonth);
  40. }
  41. return $date->format('j') == $lastDayOfMonth;
  42. }
  43. // Handle # hash tokens
  44. if (strpos($value, '#')) {
  45. list($weekday, $nth) = explode('#', $value);
  46. // 0 and 7 are both Sunday, however 7 matches date('N') format ISO-8601
  47. if ($weekday === '0') {
  48. $weekday = 7;
  49. }
  50. // Validate the hash fields
  51. if ($weekday < 0 || $weekday > 7) {
  52. throw new InvalidArgumentException("Weekday must be a value between 0 and 7. {$weekday} given");
  53. }
  54. if ($nth > 5) {
  55. throw new InvalidArgumentException('There are never more than 5 of a given weekday in a month');
  56. }
  57. // The current weekday must match the targeted weekday to proceed
  58. if ($date->format('N') != $weekday) {
  59. return false;
  60. }
  61. $tdate = clone $date;
  62. $tdate->setDate($currentYear, $currentMonth, 1);
  63. $dayCount = 0;
  64. $currentDay = 1;
  65. while ($currentDay < $lastDayOfMonth + 1) {
  66. if ($tdate->format('N') == $weekday) {
  67. if (++$dayCount >= $nth) {
  68. break;
  69. }
  70. }
  71. $tdate->setDate($currentYear, $currentMonth, ++$currentDay);
  72. }
  73. return $date->format('j') == $currentDay;
  74. }
  75. // Handle day of the week values
  76. if (strpos($value, '-')) {
  77. $parts = explode('-', $value);
  78. if ($parts[0] == '7') {
  79. $parts[0] = '0';
  80. } elseif ($parts[1] == '0') {
  81. $parts[1] = '7';
  82. }
  83. $value = implode('-', $parts);
  84. }
  85. // Test to see which Sunday to use -- 0 == 7 == Sunday
  86. $format = in_array(7, str_split($value)) ? 'N' : 'w';
  87. $fieldValue = $date->format($format);
  88. return $this->isSatisfied($fieldValue, $value);
  89. }
  90. public function increment(DateTime $date, $invert = false)
  91. {
  92. if ($invert) {
  93. $date->modify('-1 day');
  94. $date->setTime(23, 59, 0);
  95. } else {
  96. $date->modify('+1 day');
  97. $date->setTime(0, 0, 0);
  98. }
  99. return $this;
  100. }
  101. public function validate($value)
  102. {
  103. $value = $this->convertLiterals($value);
  104. foreach (explode(',', $value) as $expr) {
  105. if (!preg_match('/^(\*|[0-7](L?|#[1-5]))([\/\,\-][0-7]+)*$/', $expr)) {
  106. return false;
  107. }
  108. }
  109. return true;
  110. }
  111. private function convertLiterals($string)
  112. {
  113. return str_ireplace(
  114. array('SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT'),
  115. range(0, 6),
  116. $string
  117. );
  118. }
  119. }