Passed
Push — master ( 6b6e47...003970 )
by y
01:34
created

DateTimeModifyTrait::subHours()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
1
<?php
2
3
namespace Helix\DB\Fluent\DateTime;
4
5
use DateInterval;
6
use Helix\DB\Fluent\AbstractTrait;
7
use Helix\DB\Fluent\DateTime;
8
9
/**
10
 * Date-time modifiers.
11
 */
12
trait DateTimeModifyTrait
13
{
14
15
    use AbstractTrait;
16
17
    /**
18
     * @return DateTime
19
     */
20
    public function addDay()
21
    {
22
        return $this->addDays(1);
23
    }
24
25
    /**
26
     * @param int $days
27
     * @return DateTime
28
     */
29
    public function addDays(int $days)
30
    {
31
        return $this->modify(0, 0, 0, $days);
32
    }
33
34
    /**
35
     * @return DateTime
36
     */
37
    public function addHour()
38
    {
39
        return $this->addHours(1);
40
    }
41
42
    /**
43
     * @param int $hours
44
     * @return DateTime
45
     */
46
    public function addHours(int $hours)
47
    {
48
        return $this->modify(0, 0, $hours);
49
    }
50
51
    /**
52
     * @param int $minutes
53
     * @return DateTime
54
     */
55
    public function addMinutes(int $minutes)
56
    {
57
        return $this->modify(0, $minutes);
58
    }
59
60
    /**
61
     * @return DateTime
62
     */
63
    public function addMonth()
64
    {
65
        return $this->addMonths(1);
66
    }
67
68
    /**
69
     * @param int $months
70
     * @return DateTime
71
     */
72
    public function addMonths(int $months)
73
    {
74
        return $this->modify(0, 0, 0, 0, $months);
75
    }
76
77
    /**
78
     * @param int $seconds
79
     * @return DateTime
80
     */
81
    public function addSeconds(int $seconds)
82
    {
83
        return $this->modify($seconds);
84
    }
85
86
    /**
87
     * @return DateTime
88
     */
89
    public function addYear()
90
    {
91
        return $this->addYears(1);
92
    }
93
94
    /**
95
     * @param int $years
96
     * @return DateTime
97
     */
98
    public function addYears(int $years)
99
    {
100
        return $this->modify(0, 0, 0, 0, 0, $years);
101
    }
102
103
    /**
104
     * `YYYY-MM-01`
105
     *
106
     * @return DateTime
107
     */
108
    public function firstDayOfMonth()
109
    {
110
        return DateTime::factory($this->db, $this->dateFormat('%Y-%m-01'));
0 ignored issues
show
Bug introduced by
It seems like dateFormat() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

110
        return DateTime::factory($this->db, $this->/** @scrutinizer ignore-call */ dateFormat('%Y-%m-01'));
Loading history...
111
    }
112
113
    /**
114
     * `YYYY-01-01`
115
     *
116
     * @return DateTime
117
     */
118
    public function firstDayOfYear()
119
    {
120
        return DateTime::factory($this->db, $this->dateFormat('%Y-01-01'));
121
    }
122
123
    /**
124
     * Applies date-time modifiers.
125
     *
126
     * `$s` can be a `DateInterval` or `DateInterval` description (e.g. `"+1 day"`).
127
     * If so, the rest of the arguments are ignored.
128
     *
129
     * @param int|string|DateInterval $s Seconds, or `DateInterval` related
130
     * @param int $m Minutes
131
     * @param int $h Hours
132
     * @param int $D Days
133
     * @param int $M Months
134
     * @param int $Y Years
135
     * @return DateTime
136
     */
137
    public function modify($s, int $m = 0, int $h = 0, int $D = 0, int $M = 0, int $Y = 0)
138
    {
139
        // interval units. process larger intervals first.
140
        static $units = ['YEAR', 'MONTH', 'DAY', 'HOUR', 'MINUTE', 'SECOND'];
141
        if (is_string($s)) {
142
            $s = DateInterval::createFromDateString($s);
143
            assert($s instanceof DateInterval);
144
        }
145
        if ($s instanceof DateInterval) {
146
            $ints = [$s->y, $s->m, $s->d, $s->h, $s->i, $s->s];
147
        } else {
148
            $ints = [$Y, $M, $D, $h, $m, $s];
149
        }
150
151
        // key by units and remove zeroes
152
        $ints = array_filter(array_combine($units, $ints));
153
154
        if ($this->db->isSQLite()) {
155
            return $this->modify_sqlite($ints);
156
        }
157
        return $this->modify_mysql($ints);
158
    }
159
160
    /**
161
     * MySQL requires nesting.
162
     *
163
     * @param int[] $ints
164
     * @return DateTime
165
     * @internal
166
     */
167
    protected function modify_mysql(array $ints)
168
    {
169
        $spec = $this;
170
        foreach ($ints as $unit => $int) {
171
            $spec = sprintf('DATE_%s(%s, INTERVAL %s %s)', $int > 0 ? 'ADD' : 'SUB', $spec, abs($int), $unit);
172
        }
173
        return DateTime::factory($this->db, $spec);
174
    }
175
176
    /**
177
     * SQLite allows variadic modifiers.
178
     *
179
     * @param int[] $ints
180
     * @return DateTime
181
     * @internal
182
     */
183
    protected function modify_sqlite(array $ints)
184
    {
185
        $spec = [$this];
186
        foreach ($ints as $unit => $int) {
187
            $spec[] = sprintf("'%s %s'", $int > 0 ? "+{$int}" : $int, $unit);
188
        }
189
        return DateTime::factory($this->db, sprintf('DATETIME(%s)', implode(',', $spec)));
190
    }
191
192
    /**
193
     * @return DateTime
194
     */
195
    public function subDay()
196
    {
197
        return $this->subDays(1);
198
    }
199
200
    /**
201
     * @param int $days
202
     * @return DateTime
203
     */
204
    public function subDays(int $days)
205
    {
206
        return $this->modify(0, 0, 0, $days * -1);
207
    }
208
209
    /**
210
     * @return DateTime
211
     */
212
    public function subHour()
213
    {
214
        return $this->subHours(1);
215
    }
216
217
    /**
218
     * @param int $hours
219
     * @return DateTime
220
     */
221
    public function subHours(int $hours)
222
    {
223
        return $this->modify(0, 0, $hours * -1);
224
    }
225
226
    /**
227
     * @param int $minutes
228
     * @return DateTime
229
     */
230
    public function subMinutes(int $minutes)
231
    {
232
        return $this->modify(0, $minutes * -1);
233
    }
234
235
    /**
236
     * @return DateTime
237
     */
238
    public function subMonth()
239
    {
240
        return $this->subMonths(1);
241
    }
242
243
    /**
244
     * @param int $months
245
     * @return DateTime
246
     */
247
    public function subMonths(int $months)
248
    {
249
        return $this->modify(0, 0, 0, 0, $months * -1);
250
    }
251
252
    /**
253
     * @param int $seconds
254
     * @return DateTime
255
     */
256
    public function subSeconds(int $seconds)
257
    {
258
        return $this->modify($seconds * -1);
259
    }
260
261
    /**
262
     * @return DateTime
263
     */
264
    public function subYear()
265
    {
266
        return $this->subYears(1);
267
    }
268
269
    /**
270
     * @param int $years
271
     * @return DateTime
272
     */
273
    public function subYears(int $years)
274
    {
275
        return $this->modify(0, 0, 0, 0, 0, $years * -1);
276
    }
277
278
    /**
279
     * Changes the timezone from local to UTC.
280
     *
281
     * SQLite uses the system's timezone as the "local" timezone,
282
     * whereas MySQL allows you to specify it.
283
     *
284
     * > Warning: Datetimes are already stored and retrieved as UTC.
285
     * > Only use this if you know the expression is in the local timezone.
286
     *
287
     * > Warning: Chaining this multiple times will further change the timezone offset.
288
     *
289
     * @param null|string $mysqlLocalTz The "local" timezone name or offset given to MySQL. Defaults to PHP's current timezone.
290
     * @return DateTime
291
     */
292
    public function toUTC(string $mysqlLocalTz = null)
293
    {
294
        return DateTime::fromFormat($this->db, [
295
            'mysql' => "CONVERT_TZ(%s,'%s','UTC')",
296
            'sqlite' => "DATETIME(%s,'utc')"
297
        ], $this, $mysqlLocalTz ?? date_default_timezone_get());
298
    }
299
}
300