Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
declare(strict_types=1);
4
 
5
namespace Cron;
6
 
7
use DateTime;
8
use DateTimeInterface;
9
 
10
/**
11
 * Day of month field.  Allows: * , / - ? L W.
12
 *
13
 * 'L' stands for "last" and specifies the last day of the month.
14
 *
15
 * The 'W' character is used to specify the weekday (Monday-Friday) nearest the
16
 * given day. As an example, if you were to specify "15W" as the value for the
17
 * day-of-month field, the meaning is: "the nearest weekday to the 15th of the
18
 * month". So if the 15th is a Saturday, the trigger will fire on Friday the
19
 * 14th. If the 15th is a Sunday, the trigger will fire on Monday the 16th. If
20
 * the 15th is a Tuesday, then it will fire on Tuesday the 15th. However if you
21
 * specify "1W" as the value for day-of-month, and the 1st is a Saturday, the
22
 * trigger will fire on Monday the 3rd, as it will not 'jump' over the boundary
23
 * of a month's days. The 'W' character can only be specified when the
24
 * day-of-month is a single day, not a range or list of days.
25
 *
26
 * @author Michael Dowling <mtdowling@gmail.com>
27
 */
28
class DayOfMonthField extends AbstractField
29
{
30
    /**
31
     * {@inheritdoc}
32
     */
33
    protected $rangeStart = 1;
34
 
35
    /**
36
     * {@inheritdoc}
37
     */
38
    protected $rangeEnd = 31;
39
 
40
    /**
41
     * Get the nearest day of the week for a given day in a month.
42
     *
43
     * @param int $currentYear Current year
44
     * @param int $currentMonth Current month
45
     * @param int $targetDay Target day of the month
46
     *
47
     * @return \DateTime|null Returns the nearest date
48
     */
49
    private static function getNearestWeekday(int $currentYear, int $currentMonth, int $targetDay): ?DateTime
50
    {
51
        $tday = str_pad((string) $targetDay, 2, '0', STR_PAD_LEFT);
52
        $target = DateTime::createFromFormat('Y-m-d', "{$currentYear}-{$currentMonth}-{$tday}");
53
 
54
        if ($target === false) {
55
            return null;
56
        }
57
 
58
        $currentWeekday = (int) $target->format('N');
59
 
60
        if ($currentWeekday < 6) {
61
            return $target;
62
        }
63
 
64
        $lastDayOfMonth = $target->format('t');
65
        foreach ([-1, 1, -2, 2] as $i) {
66
            $adjusted = $targetDay + $i;
67
            if ($adjusted > 0 && $adjusted <= $lastDayOfMonth) {
68
                $target->setDate($currentYear, $currentMonth, $adjusted);
69
 
70
                if ((int) $target->format('N') < 6 && (int) $target->format('m') === $currentMonth) {
71
                    return $target;
72
                }
73
            }
74
        }
75
 
76
        return null;
77
    }
78
 
79
    /**
80
     * {@inheritdoc}
81
     */
82
    public function isSatisfiedBy(DateTimeInterface $date, $value, bool $invert): bool
83
    {
84
        // ? states that the field value is to be skipped
85
        if ('?' === $value) {
86
            return true;
87
        }
88
 
89
        $fieldValue = $date->format('d');
90
 
91
        // Check to see if this is the last day of the month
92
        if ('L' === $value) {
93
            return $fieldValue === $date->format('t');
94
        }
95
 
96
        // Check to see if this is the nearest weekday to a particular value
97
        if ($wPosition = strpos($value, 'W')) {
98
            // Parse the target day
99
            $targetDay = (int) substr($value, 0, $wPosition);
100
            // Find out if the current day is the nearest day of the week
101
            $nearest = self::getNearestWeekday(
102
                (int) $date->format('Y'),
103
                (int) $date->format('m'),
104
                $targetDay
105
            );
106
            if ($nearest) {
107
                return $date->format('j') === $nearest->format('j');
108
            }
109
 
110
            throw new \RuntimeException('Unable to return nearest weekday');
111
        }
112
 
113
        return $this->isSatisfied((int) $date->format('d'), $value);
114
    }
115
 
116
    /**
117
     * @inheritDoc
118
     *
119
     * @param \DateTime|\DateTimeImmutable $date
120
     */
121
    public function increment(DateTimeInterface &$date, $invert = false, $parts = null): FieldInterface
122
    {
123
        if (! $invert) {
124
            $date = $date->add(new \DateInterval('P1D'));
125
            $date = $date->setTime(0, 0);
126
        } else {
127
            $date = $date->sub(new \DateInterval('P1D'));
128
            $date = $date->setTime(23, 59);
129
        }
130
 
131
        return $this;
132
    }
133
 
134
    /**
135
     * {@inheritdoc}
136
     */
137
    public function validate(string $value): bool
138
    {
139
        $basicChecks = parent::validate($value);
140
 
141
        // Validate that a list don't have W or L
142
        if (false !== strpos($value, ',') && (false !== strpos($value, 'W') || false !== strpos($value, 'L'))) {
143
            return false;
144
        }
145
 
146
        if (!$basicChecks) {
147
            if ('?' === $value) {
148
                return true;
149
            }
150
 
151
            if ('L' === $value) {
152
                return true;
153
            }
154
 
155
            if (preg_match('/^(.*)W$/', $value, $matches)) {
156
                return $this->validate($matches[1]);
157
            }
158
 
159
            return false;
160
        }
161
 
162
        return $basicChecks;
163
    }
164
}