Passed
Push — master ( 003970...601449 )
by y
01:50
created

DateTimeModifyTrait::subSecond()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
eloc 1
c 1
b 0
f 1
nc 1
nop 0
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
     * @return DateTime
53
     */
54
    public function addMinute()
55
    {
56
        return $this->addMinutes(1);
57
    }
58
59
    /**
60
     * @param int $minutes
61
     * @return DateTime
62
     */
63
    public function addMinutes(int $minutes)
64
    {
65
        return $this->modify(0, $minutes);
66
    }
67
68
    /**
69
     * @return DateTime
70
     */
71
    public function addMonth()
72
    {
73
        return $this->addMonths(1);
74
    }
75
76
    /**
77
     * @param int $months
78
     * @return DateTime
79
     */
80
    public function addMonths(int $months)
81
    {
82
        return $this->modify(0, 0, 0, 0, $months);
83
    }
84
85
    /**
86
     * @return DateTime
87
     */
88
    public function addSecond()
89
    {
90
        return $this->addSeconds(1);
91
    }
92
93
    /**
94
     * @param int $seconds
95
     * @return DateTime
96
     */
97
    public function addSeconds(int $seconds)
98
    {
99
        return $this->modify($seconds);
100
    }
101
102
    /**
103
     * @return DateTime
104
     */
105
    public function addYear()
106
    {
107
        return $this->addYears(1);
108
    }
109
110
    /**
111
     * @param int $years
112
     * @return DateTime
113
     */
114
    public function addYears(int $years)
115
    {
116
        return $this->modify(0, 0, 0, 0, 0, $years);
117
    }
118
119
    /**
120
     * `YYYY-MM-01`
121
     *
122
     * @return DateTime
123
     */
124
    public function firstDayOfMonth()
125
    {
126
        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

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