Issues (10)

src/DateTimeImmutable.php (9 issues)

1
<?php
2
3
namespace Gearbox\DateTime;
4
5
use DateTime as PhpDateTime;
6
use Exception as PhpException;
7
8
/**
9
 * Represents a specific moment in time and cannot be changed.
10
 */
11
class DateTimeImmutable implements DateTimeInterface
12
{
13
    const DATETIME_FOR_FILE_NAME    = 'Y-m-d_H-i-s';
14
    const GERMAN_DATETIME           = 'd.m.Y H:i:s';
15
    const GERMAN_DATE               = 'd.m.Y';
16
    const MYSQL_DATETIME            = 'Y-m-d H:i:s';
17
    const MYSQL_DATE                = 'Y-m-d';
18
19
    /** @var \DateTime */
20
    protected $phpDateTime = null;
21
22
23
24
    /**
25
     * May be called with any date string the php version of DateTime accepts
26
     * and additionally with a unix timestamp also.
27
     *
28
     * @param string $dateString Defaults to 'now'.
29
     */
30
    public function __construct($dateString = 'now')
31
    {
32
        try {
33
            $this->phpDateTime = new PhpDateTime($dateString);
34
        }
35
        catch (PhpException $exception) {
36
            // Suspect it is a unixtime that could not be parsed.
37
            $this->phpDateTime = new PhpDateTime();
38
            $this->phpDateTime->setTimestamp($dateString);
0 ignored issues
show
$dateString of type string is incompatible with the type integer expected by parameter $timestamp of DateTime::setTimestamp(). ( Ignorable by Annotation )

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

38
            $this->phpDateTime->setTimestamp(/** @scrutinizer ignore-type */ $dateString);
Loading history...
39
        }
40
    }
41
42
43
44
    public function __clone()
45
    {
46
        // Force a clone of phpDateTime, otherwise the cloned DateTime object
47
        // would point to the same phpDateTime instance.
48
        $this->phpDateTime = clone $this->phpDateTime;
49
    }
50
51
52
53
    /**
54
     * Returns numeric representation of a day, without leading zeros.
55
     *
56
     * @return integer
57
     */
58
    public function getDayAsNumber()
59
    {
60
        $day = $this->phpDateTime->format('j');
61
62
        return $day;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $day returns the type string which is incompatible with the documented return type integer.
Loading history...
63
    }
64
65
66
67
    /**
68
     * Returns instance of MonthImmutable
69
     *
70
     * @return \Gearbox\DateTime\MonthImmutable
71
     */
72
    public function getMonth()
73
    {
74
        $month = new MonthImmutable($this->phpDateTime->format('Y-m'));
75
76
        return $month;
77
    }
78
79
80
81
    /**
82
     * Returns numeric representation of a month, without leading zeros.
83
     *
84
     * @return integer
85
     */
86
    public function getMonthAsNumber()
87
    {
88
        $month = $this->phpDateTime->format('n');
89
90
        return $month;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $month returns the type string which is incompatible with the documented return type integer.
Loading history...
91
    }
92
93
94
95
    /**
96
     * Returns year with four digits
97
     *
98
     * @return integer
99
     */
100
    public function getYearAsNumber()
101
    {
102
        $year = $this->phpDateTime->format('Y');
103
104
        return $year;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $year returns the type string which is incompatible with the documented return type integer.
Loading history...
105
    }
106
107
108
109
    /**
110
     * @param integer $hours
111
     * @param integer $minutes
112
     * @param integer $seconds
113
     * @return \Gearbox\DateTime\DateTimeImmutable
114
     */
115
    public function setTime($hours, $minutes, $seconds = 0)
116
    {
117
        $phpDateTime = clone $this->phpDateTime;
118
        $phpDateTime->setTime($hours, $minutes, $seconds);
119
        $newDateTime = $this->createInstance($phpDateTime);
120
121
        return $newDateTime;
122
    }
123
124
125
126
    /**
127
     * Returns true if the DateTime object and the date to compare with represent the same date.
128
     *
129
     * @param \Gearbox\DateTime\DateTimeInterface $dateToCompareWith
130
     * @return boolean
131
     */
132
    public function equals(DateTimeInterface $dateToCompareWith)
133
    {
134
        $equals = $this->asUnixTimestamp() == $dateToCompareWith->asUnixTimestamp();
135
136
        return $equals;
137
    }
138
139
140
141
    /**
142
     * Returns true if the DateTime object is earlier than the date to compare with.
143
     *
144
     * @param \Gearbox\DateTime\DateTimeInterface $dateToCompareWith
145
     * @return boolean
146
     */
147
    public function isEarlier(DateTimeInterface $dateToCompareWith)
148
    {
149
        $isEarlier = $this->asUnixTimestamp() < $dateToCompareWith->asUnixTimestamp();
150
151
        return $isEarlier;
152
    }
153
154
155
156
    /**
157
     * Returns true if the DateTime object is later than the date to compare with.
158
     *
159
     * @param \Gearbox\DateTime\DateTimeInterface $dateToCompareWith
160
     * @return boolean
161
     */
162
    public function isLater(DateTimeInterface $dateToCompareWith)
163
    {
164
        $isLater = $this->asUnixTimestamp() > $dateToCompareWith->asUnixTimestamp();
165
166
        return $isLater;
167
    }
168
169
170
171
    /**
172
     * @param \Gearbox\DateTime\DateTimeInterface $dateToCompareWith
173
     * @return boolean
174
     */
175
    public function isLaterOrEquals(DateTimeInterface $dateToCompareWith)
176
    {
177
        $equals = ($this->isLater($dateToCompareWith) || $this->equals($dateToCompareWith));
178
179
        return $equals;
180
    }
181
182
183
184
    /**
185
     * @param \Gearbox\DateTime\DateTimeInterface $dateToCompareWith
186
     * @return boolean
187
     */
188
    public function isEarlierOrEquals(DateTimeInterface $dateToCompareWith)
189
    {
190
        $equals = ($this->isEarlier($dateToCompareWith) || $this->equals($dateToCompareWith));
191
192
        return $equals;
193
    }
194
195
196
197
    /**
198
     * @param \Gearbox\DateTime\DateTimeInterface $dateTimeToCompareWith
199
     * @return boolean
200
     */
201
    public function isSameMonth(DateTimeInterface $dateTimeToCompareWith)
202
    {
203
        $sameMonth = $this->getMonthAsNumber() == $dateTimeToCompareWith->getMonthAsNumber() && $this->getYearAsNumber() == $dateTimeToCompareWith->getYearAsNumber();
204
205
        return $sameMonth;
206
    }
207
208
209
210
    /**
211
     * @return boolean
212
     */
213
    public function isInThePast()
214
    {
215
        $isInThePast = $this->asUnixTimestamp() < time();
216
217
        return $isInThePast;
218
    }
219
220
221
222
    /**
223
     * @return boolean
224
     */
225
    public function isInTheFuture()
226
    {
227
        $isInTheFuture = $this->asUnixTimestamp() > time();
228
229
        return $isInTheFuture;
230
    }
231
232
233
234
    /**
235
     * @param \Gearbox\DateTime\DateIntervalInterface $interval
236
     * @return \Gearbox\DateTime\DateTimeImmutable
237
     */
238
    public function add(DateIntervalInterface $interval)
239
    {
240
        $phpDateTime = clone $this->phpDateTime;
241
        $phpDateTime->add($interval->getAsPhpDateInterval());
242
        $newDateTime = $this->createInstance($phpDateTime);
243
244
        return $newDateTime;
245
    }
246
247
248
249
    /**
250
     * @param integer $numberOfDays
251
     * @return \Gearbox\DateTime\DateTimeImmutable
252
     */
253
    public function addDays($numberOfDays)
254
    {
255
        $daysInterval = new DateInterval($numberOfDays . ' days');
256
        $newDateTime = $this->add($daysInterval);
257
258
        return $newDateTime;
259
    }
260
261
262
263
    /**
264
     * @param integer $numberOfMonths
265
     * @return \Gearbox\DateTime\DateTimeImmutable
266
     */
267
    public function addMonths($numberOfMonths)
268
    {
269
        $monthsInterval = new DateInterval($numberOfMonths . ' months');
270
        $newDateTime = $this->add($monthsInterval);
271
272
        return $newDateTime;
273
    }
274
275
276
277
    /**
278
     * @param \Gearbox\DateTime\DateTimeInterface $dateTime
279
     * @return \Gearbox\DateTime\DateIntervalInterface
280
     */
281
    public function diff(DateTimeInterface $dateTime)
282
    {
283
        $phpDateInterval = $this->phpDateTime->diff($dateTime->phpDateTime);
0 ignored issues
show
Accessing phpDateTime on the interface Gearbox\DateTime\DateTimeInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
284
        $dateIntervalAsString = $phpDateInterval->format(DateIntervalInterface::FORMAT_ISO_8601);
285
        $interval = new DateInterval($dateIntervalAsString);
286
287
        return $interval;
288
    }
289
290
291
292
293
    /**
294
     * @param \Gearbox\DateTime\DateTimeInterface $dateTime
295
     * @return integer
296
     */
297
    public function diffInDays(DateTimeInterface $dateTime)
298
    {
299
        $phpDateInterval = $this->phpDateTime->diff($dateTime->phpDateTime);
0 ignored issues
show
Accessing phpDateTime on the interface Gearbox\DateTime\DateTimeInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
300
        $dateIntervalInDays = $phpDateInterval->days;
0 ignored issues
show
Documentation Bug introduced by
It seems like $phpDateInterval->days can also be of type boolean. However, the property $days is declared as type false|integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
301
302
        return $dateIntervalInDays;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $dateIntervalInDays also could return the type boolean which is incompatible with the documented return type integer.
Loading history...
303
    }
304
305
306
307
    /**
308
     * @param \Gearbox\DateTime\DateIntervalInterface $interval
309
     * @return \Gearbox\DateTime\DateTimeImmutable
310
     */
311
    public function sub(DateIntervalInterface $interval)
312
    {
313
        $phpDateTime = clone $this->phpDateTime;
314
        $phpDateTime->sub($interval->getAsPhpDateInterval());
315
        $newDateTime = $this->createInstance($phpDateTime);
316
317
        return $newDateTime;
318
    }
319
320
321
322
    /**
323
     * @param string $formatString
324
     * @return string
325
     */
326
    public function format($formatString)
327
    {
328
        return $this->phpDateTime->format($formatString);
329
    }
330
331
332
333
    /**
334
     * @return string 'Y-m-d H:i:s'
335
     */
336
    public function asMySqlDateTime()
337
    {
338
        return $this->phpDateTime->format(self::MYSQL_DATETIME);
339
    }
340
341
342
343
    /**
344
     * @return string 'Y-m-d'
345
     */
346
    public function asMySqlDate()
347
    {
348
        return $this->phpDateTime->format(self::MYSQL_DATE);
349
    }
350
351
352
353
    /**
354
     * @return string
355
     */
356
    public function asUnixTimestamp()
357
    {
358
        return $this->phpDateTime->format('U');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->phpDateTime->format('U') returns the type string which is incompatible with the return type mandated by Gearbox\DateTime\DateTim...face::asUnixTimestamp() of integer.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
359
    }
360
361
362
363
    /**
364
     * @return string 'd.m.Y'
365
     */
366
    public function asGermanDate()
367
    {
368
        return $this->phpDateTime->format(self::GERMAN_DATE);
369
    }
370
371
372
373
    /**
374
     * @return string 'd.m.Y H:i:s'
375
     */
376
    public function asGermanDateTime()
377
    {
378
        return $this->phpDateTime->format(self::GERMAN_DATETIME);
379
    }
380
381
382
383
    /**
384
     * @return string 'Y-m-d_H-i-s'
385
     */
386
    public function asDateTimeForFilename()
387
    {
388
        return $this->phpDateTime->format(self::DATETIME_FOR_FILE_NAME);
389
    }
390
391
392
393
    /**
394
     * @return string
395
     */
396
    public function __toString()
397
    {
398
        return $this->phpDateTime->format(self::GERMAN_DATETIME);
399
    }
400
401
402
403
    /**
404
     * @param $phpDateTime
405
     * @return \Gearbox\DateTime\DateTimeImmutable
406
     */
407
    private function createInstance($phpDateTime)
408
    {
409
        $newDateTime = new self($phpDateTime->format('Y-m-d H:i:s'));
410
411
        return $newDateTime;
412
    }
413
}
414