Date   C
last analyzed

Complexity

Total Complexity 72

Size/Duplication

Total Lines 575
Duplicated Lines 14.78 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 7
Bugs 2 Features 2
Metric Value
wmc 72
c 7
b 2
f 2
lcom 1
cbo 0
dl 85
loc 575
rs 5.5667

21 Methods

Rating   Name   Duplication   Size   Complexity  
A getLastErrors() 0 4 1
A defaultTimezone() 9 14 3
A __set_state() 0 10 2
C __construct() 10 69 12
A __wakeup() 0 4 1
A __toString() 0 4 1
C createFromFormat() 11 71 14
B add() 22 22 5
B sub() 22 22 5
A modify() 0 9 2
A setDate() 0 9 2
A setISODate() 0 9 2
A setTime() 0 9 2
A setTimestamp() 0 9 2
A setTimezone() 11 18 3
A diff() 0 11 2
C format() 0 69 9
A getDateTime() 0 4 1
A getOffset() 0 4 1
A getTimestamp() 0 4 1
A getTimezone() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Date often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Date, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @package    Fuel\Common
4
 * @version    2.0
5
 * @author     Fuel Development Team
6
 * @license    MIT License
7
 * @copyright  2010 - 2015 Fuel Development Team
8
 * @link       http://fuelphp.com
9
 */
10
11
namespace Fuel\Common;
12
13
use DateInterval;
14
use DateTimeZone;
15
use DateTime;
16
use DateTimeInterface;
17
18
/**
19
 * Date Class
20
 *
21
 * DateTime drop-in replacement that supports internationalization and does
22
 * correction to GMT when your webserver isn't configured correctly.
23
 *
24
 * @package Fuel\Common
25
 *
26
 * @since 1.0
27
 *
28
 * Notes:
29
 * - Always returns Date objects, will accept both Date objects and UNIX timestamps
30
 * - Uses either strptime() or DateTime to create a date from a format, depending on the format
31
 * - Uses strftime formatting for dates www.php.net/manual/en/function.strftime.php
32
 */
33
class Date implements DateTimeInterface
34
{
35
	/**
36
	 * @var  DateTime object warnings and errors
37
	 */
38
	protected static $lastErrors = array();
39
40
	/**
41
	 * @var  DateTimeZone  default timezone
42
	 */
43
	protected static $defaultTimezone;
44
45
	/**
46
	 * @var  string  encoding to use for strftime()
47
	 */
48
	protected static $encoding = null;
49
50
	/**
51
	 * @var  int  any GMT offset for ill configured servers (application timezone !== server timezone)
52
	 */
53
	protected static $gmtOffset = 0;
54
55
	/**
56
	 * @var  array  custom defined datetime formats
57
	 */
58
	protected static $patterns = array(
59
	);
60
61
	/**
62
	 * Returns the warnings and errors from the last parsing operation
63
	 *
64
	 * @return  array  array of warnings and errors found while parsing a date/time string.
65
	 */
66
	public static function getLastErrors()
67
	{
68
		return static::$lastErrors;
69
	}
70
71
	/**
72
	 * Sets the default timezone, the current display timezone of the application
73
	 *
74
	 * @throws  Exception  if the timezone passed is not valid
75
	 * @return  DateTimeZone
76
	 */
77
	public static function defaultTimezone($timezone = null)
78
	{
79 View Code Duplication
		if ($timezone !== null)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
80
		{
81
			if ( ! $timezone instanceOf DateTimeZone)
82
			{
83
				$timezone = new DateTimeZone($timezone);
84
			}
85
86
			static::$defaultTimezone = $timezone;
87
		}
88
89
		return static::$defaultTimezone;
90
	}
91
92
	/**
93
	 * Magic method that handles running code generated by var_export()
94
	 *
95
	 */
96
	public static function __set_state(Array $array)
97
	{
98
		$instance = new static;
99
		foreach ($array as $key => $value)
100
		{
101
			$instance->$key = $value;
102
		}
103
104
		return $instance;
105
	}
106
107
	/**
108
	 * @var  DateTime  object used internally to store the datetime
109
	 */
110
	protected $datetime;
111
112
	/**
113
	 * Constructor, create a new Date object using the information passed
114
	 *
115
	 * @param  string|int           $time      date/time as a UNIX timestamp or a string
116
	 * @param  string|DateTimeZone  $timezone  timezone $time is in
117
	 * @param  array                $config    any custom configuration for this object
118
	 *
119
	 * @throws  Exception  if the time passed is not valid
120
	 */
121
	public function __construct($time = "now", $timezone = null, Array $config = array())
122
	{
123
		// any GMT offset defined?
124
		if (isset($config['gmtOffset']))
125
		{
126
			static::$gmtOffset = $config['gmtOffset'];
127
		}
128
129
		// any encoding defined?
130
		if (isset($config['encoding']))
131
		{
132
			static::$encoding = strtoupper($config['encoding']);
133
		}
134
135
		// do we have a default timezone?
136
		if (isset($config['defaultTimezone']))
137
		{
138
			static::defaultTimezone($config['defaultTimezone']);
139
		}
140
		else
141
		{
142
			static::defaultTimezone(date_default_timezone_get());
143
		}
144
145
		// any custom format patterns?
146
		if (isset($config['patterns']) and is_array($config['patterns']))
147
		{
148
			static::$patterns = $config['patterns'];
149
		}
150
151
		// default to current time if none is given
152
		if ($time === null or $time === 'now')
153
		{
154
			$time = '@'.strval(time() + static::$gmtOffset);
155
		}
156
157
		// deal with timestamps passed
158
		elseif (is_numeric($time))
159
		{
160
			$time = '@'.$time;
161
		}
162
163
		// construct the datetime object
164
		if ($time instanceOf DateTime)
165
		{
166
			$this->datetime = $time;
167
		}
168
		else
169
		{
170
			// if timezone is given as a string, convert it
171 View Code Duplication
			if (is_string($timezone))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
172
			{
173
				$timezone = new DateTimeZone($timezone);
174
			}
175
176
			// default to default timezone if none is given
177
			elseif ($timezone === null)
178
			{
179
				$timezone = static::defaultTimezone();
180
			}
181
182
			$this->datetime = new DateTime($time, $timezone);
183
			static::$lastErrors = DateTime::getLastErrors();
184
185
			// make sure the objects timezone is ok,
186
			// some formats default to UTC, no matter what timezone is passed
187
			$this->datetime->setTimezone($timezone);
188
		}
189
	}
190
191
	/**
192
	 */
193
	public function __wakeup()
194
	{
195
		return $this->datetime->__wakeup();
196
	}
197
198
	/**
199
	 * Allows you to just put the object in a string and get it inserted in the default pattern
200
	 *
201
	 * @return  bool|string  the formatted date string on success or false on failure.
202
	 */
203
	public function __toString()
204
	{
205
		return $this->format();
206
	}
207
208
	/**
209
	 * Returns new Date object formatted according to the specified format. The
210
	 * method supports both strptime() formats and DateTime formats
211
	 *
212
	 * @param  string               $format  any format supported by DateTime, or a format name configured
213
	 * @param  string               $time    string representing the time.
214
	 * @param  string|DateTimeZone  $time    timezone, if null the default timezone will be used
215
	 *
216
	 * @throws  Exception             if the timezone passed is not valid
217
	 * @throws  OutOfBoundsException  if the input time passed is not valid
218
	 *
219
	 * @return  bool|Date  new Date instance, or false on failure
220
	 */
221
	public function createFromFormat($format = 'local', $time, $timezone = null)
222
	{
223
		// deal with the timezone passed
224 View Code Duplication
		if ($timezone !== null)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
225
		{
226
			if ( ! $timezone instanceOf DateTimeZone)
227
			{
228
				$timezone = new DateTimeZone($timezone);
229
			}
230
		}
231
		else
232
		{
233
			$timezone = static::defaultTimezone();
234
		}
235
236
		// custom format pattern lookup
237
		if (array_key_exists($format, static::$patterns))
238
		{
239
			$format = static::$patterns[$format];
240
		}
241
242
		// do we have a possible strptime() format to work with?
243
		if (strpos($format, '%') !== false and $timestamp = strptime($time, $format))
244
		{
245
			// convert it into a timestamp
246
			$timestamp = mktime($timestamp['tm_hour'], $timestamp['tm_min'], $timestamp['tm_sec'],
247
							$timestamp['tm_mon'] + 1, $timestamp['tm_mday'], $timestamp['tm_year'] + 1900);
248
249
			if ($timestamp === false or $timestamp['unparsed'])
250
			{
251
				throw new \OutOfBoundsException('Input was invalid.'.(PHP_INT_SIZE == 4?' A 32-bit system only supports dates between 1901 and 2038.':''));
252
			}
253
254
			// coversion was made in UTC, and strptime() does timezones but no daylight savings, so we might need a correction here
255
			if (($delta = static::defaultTimezone()->getOffset(new DateTime("2013-01-01 12:00:00", $timezone))) !== 0)
256
			{
257
				$time += $delta;
258
			}
259
			$format = 'U';
260
		}
261
262
		if ($datetime = DateTime::createFromFormat($format, $time, $timezone))
263
		{
264
			// if we had a delta, we're working with UTC, so we have to do some adjusting again
265
			if (isset($delta))
266
			{
267
				$delta = $timezone->getOffset($datetime);
268
				if ($delta < 0)
269
				{
270
					$delta = new DateInterval('PT'.abs($delta).'S');
271
					$delta->invert = 1;
272
				}
273
				else
274
				{
275
					$delta = new DateInterval('PT'.$delta.'S');
276
				}
277
				$datetime->sub($delta);
278
			}
279
			$datetime = new static('@'.$datetime->getTimestamp(), $timezone);
280
		}
281
282
		// update any errors found in the last DateTime static call
283
		static::$lastErrors = DateTime::getLastErrors();
284
285
		if ( ! $datetime)
286
		{
287
			return false;
288
		}
289
290
		return $datetime;
291
	}
292
293
	/**
294
	 * Adds an amount of days, months, years, hours, minutes and seconds to the object
295
	 *
296
	 * @param  int|DateInterval  $interval  either a number of seconds, or a DateInterval object
297
	 *
298
	 * @return  bool|Date  this object for chaining, or false on failure
299
	 */
300 View Code Duplication
	public function add($interval = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
301
	{
302
		if ( ! $interval instanceOf DateInterval)
303
		{
304
			if (is_numeric($interval))
305
			{
306
				$delta = intval($interval);
307
				$interval = new DateInterval('PT'.strval(abs($delta)).'S');
308
				if ($delta < 0)
309
				{
310
					$interval->invert = 1;
311
				}
312
			}
313
		}
314
315
		if ($this->datetime->add($interval))
316
		{
317
			return $this;
318
		}
319
320
		return false;
321
	}
322
323
	/**
324
	 * Subtracts an amount of days, months, years, hours, minutes and seconds to the object
325
	 *
326
	 * @param  int|DateInterval  $interval  either a number of seconds, or a DateInterval object
327
	 *
328
	 * @return  bool|Date  this object for chaining, or false on failure
329
	 */
330 View Code Duplication
	public function sub($interval = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
331
	{
332
		if ( ! $interval instanceOf DateInterval)
333
		{
334
			if (is_numeric($interval))
335
			{
336
				$delta = intval($interval);
337
				$interval = new DateInterval('PT'.strval(abs($delta)).'S');
338
				if ($delta < 0)
339
				{
340
					$interval->invert = 1;
341
				}
342
			}
343
		}
344
345
		if ($this->datetime->sub($interval))
346
		{
347
			return $this;
348
		}
349
350
		return false;
351
	}
352
353
	/**
354
	 * Alter the timestamp of a Date object by incrementing or decrementing
355
	 *
356
	 * @param  string  $modify  any format accepted by strtotime().
357
	 *
358
	 * @return bool|Date  this object, or false on failure
359
	 */
360
	public function modify($modify)
361
	{
362
		if ($this->datetime->modify($modify))
363
		{
364
			return $this;
365
		}
366
367
		return false;
368
	}
369
370
	/**
371
	 * (re)sets the date
372
	 *
373
	 * @param  int  $year   Year
374
	 * @param  int  $month  Month
375
	 * @param  int  $day    Day
376
	 *
377
	 * @return bool|Date  this object, or false on failure
378
	 */
379
	public function setDate($year, $month, $day)
380
	{
381
		if ($this->datetime->setDate($year, $month, $day))
382
		{
383
			return $this;
384
		}
385
386
		return false;
387
	}
388
389
	/**
390
	 * (re)sets the ISO date
391
	 *
392
	 * @param  int  $year   Year
393
	 * @param  int  $week   Week
394
	 * @param  int  $day    Offset of the first day of the week, base 1
395
	 *
396
	 * @return bool|Date  this object, or false on failure
397
	 */
398
	public function setISODate($year, $week, $day = 1)
399
	{
400
		if ($this->datetime->setISODate($year, $week, $day))
401
		{
402
			return $this;
403
		}
404
405
		return false;
406
	}
407
408
	/**
409
	 * (re)sets the time
410
	 *
411
	 * @param  int  $hour    Hour
412
	 * @param  int  $minute  Minute
413
	 * @param  int  $second  Second
414
	 *
415
	 * @return bool|Date  this object, or false on failure
416
	 */
417
	public function setTime($hour, $minute, $second = 0)
418
	{
419
		if ($this->datetime->setTime($hour, $minute, $second))
420
		{
421
			return $this;
422
		}
423
424
		return false;
425
	}
426
427
	/**
428
	 * (re)sets the date and time based on an Unix timestamp
429
	 *
430
	 * @param  int  unixTimestamp    unix timestamp
431
	 *
432
	 * @return bool|Date  this object, or false on failure
433
	 */
434
	public function setTimestamp($unixTimestamp)
435
	{
436
		if ($this->datetime->setTimestamp($unixTimestamp))
437
		{
438
			return $this;
439
		}
440
441
		return false;
442
	}
443
444
	/**
445
	 * (re)sets the time zone for the DateTime object
446
	 *
447
	 * @param  string|DateTimeZone  $timezone    New timezone, or null for default timezone
448
	 *
449
	 * @throws  Exception  if the timezone string passed is not valid
450
	 * @return Date  this object
451
	 */
452
	public function setTimezone($timezone)
453
	{
454 View Code Duplication
		if ( ! $timezone instanceOf DateTimeZone)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
455
		{
456
			if ($timezone === null)
457
			{
458
				$timezone = static::defaultTimezone();
459
			}
460
			else
461
			{
462
				$timezone = new DateTimeZone($timezone);
463
			}
464
		}
465
466
		$this->datetime->setTimezone($timezone);
467
468
		return $this;
469
	}
470
471
	/**
472
	 * Returns the difference between this objects time and anothers
473
	 *
474
	 * @param  DateTimeInterface  $datetime2  Other DateTimeInterface object to compare against
475
	 * @param  bool               $absolute   true if the interval must be forced to be positive
476
	 *
477
	 * @return  bool|DateInterval  object defining the difference between the two, or false on failure
478
	 */
479
	public function diff($datetime2, $absolute = false)
480
	{
481
		if ($datetime2 instanceOf Date)
482
		{
483
			return $this->datetime->diff($datetime2->datetime, $absolute);
484
		}
485
		else
486
		{
487
			return $this->datetime->diff($datetime2, $absolute);
488
		}
489
	}
490
491
	/**
492
	 * Returns date formatted according to given format
493
	 *
494
	 * @param  string  $format  any format supported by DateTime, or a format name configured
495
	 *
496
	 * @return  bool|string  the formatted date string on success or false on failure.
497
	 */
498
	public function format($format = 'local', $timezone = null)
499
	{
500
		// custom format pattern lookup
501
		if (array_key_exists($format, static::$patterns))
502
		{
503
			$format = static::$patterns[$format];
504
		}
505
506
		// save the objects timezone
507
		$orginalTimezone = $this->datetime->getTimezone();
508
509
		// default GMT offset
510
		$offset = 0;
0 ignored issues
show
Unused Code introduced by
$offset is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
511
512
		// want to output in the application default timezone?
513
		if ($timezone === true)
514
		{
515
			$timezone = static::defaultTimezone();
516
		}
517
518
		// output timezone specified?
519
		elseif ($timezone)
520
		{
521
			if ( ! $timezone instanceOf DateTimeZone)
522
			{
523
				$this->datetime->setTimezone($timezone = new DateTimeZone($timezone));
524
			}
525
		}
526
527
		// output in the current timezone
528
		else
529
		{
530
			$timezone = $orginalTimezone;
531
		}
532
533
534
		// strftime will always do UTC to local time, and
535
		// strptime() does timezones but no daylight savings,
536
		// so we might need a correction here
537
538
		// offsets are to UTC, so first, UTC -> given timezone, second, UTC -> current object timezone
539
		$testdate1 = new DateTime('@'.$this->datetime->getTimestamp(), $orginalTimezone);
540
		$testdate2 = new DateTime('@'.$this->datetime->getTimestamp(), $timezone);
541
542
		// offset is the delta between both
543
		$offset = $timezone->getOffset($testdate1) - $orginalTimezone->getOffset($testdate2);
544
545
		// Create output
546
		if (strpos($format, '%') === false)
547
		{
548
			$result = $this->datetime->format($format);
549
		}
550
		else
551
		{
552
			$result = strftime($format, $this->datetime->getTimestamp() + $offset);
553
			if (static::$encoding)
554
			{
555
				$result = static::$encoding == 'UTF8' ? utf8_encode($result) : iconv('ISO8859-1', static::$encoding, $result);
556
			}
557
		}
558
559
		// restore the timezone if needed
560
		if ($timezone !== null)
561
		{
562
			$this->datetime->setTimezone($orginalTimezone);
563
		}
564
565
		return $result;
566
	}
567
568
	/**
569
	 * Returns the internal DateTime object
570
	 *
571
	 * @return  DateTime  the internal DateTime object
572
	 */
573
	public function getDateTime()
574
	{
575
		return $this->datetime;
576
	}
577
578
	/**
579
	 * Returns the timezone offset in seconds from UTC
580
	 *
581
	 * @return  bool|int  the timezone offset in seconds from UTC on success or false on failure.
582
	 */
583
	public function getOffset()
584
	{
585
		return $this->datetime->getOffset();
586
	}
587
588
	/**
589
	 * Returns the Unix timestamp
590
	 *
591
	 * @return  int  the Unix timestamp representing the date.
592
	 */
593
	public function getTimestamp()
594
	{
595
		return $this->datetime->getTimestamp();
596
	}
597
598
	/**
599
	 * Return the time zone of this object
600
	 *
601
	 * @return  DateTimeZone  the defined timezone
602
	 */
603
	public function getTimezone()
604
	{
605
		return $this->datetime->getTimezone();
606
	}
607
}
608