date_Core   C
last analyzed

Complexity

Total Complexity 57

Size/Duplication

Total Lines 382
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 0

Importance

Changes 2
Bugs 1 Features 0
Metric Value
c 2
b 1
f 0
dl 0
loc 382
rs 5.1724
wmc 57
lcom 0
cbo 0

13 Methods

Rating   Name   Duplication   Size   Complexity  
A unix2dos() 0 16 3
A dos2unix() 0 11 1
B offset() 0 26 3
A seconds() 0 13 3
A minutes() 0 8 1
B hours() 0 23 5
A ampm() 0 7 2
B adjust() 0 20 5
B days() 0 25 4
A months() 0 4 1
A years() 0 17 4
F timespan() 0 72 15
D timespan_string() 0 32 10

How to fix   Complexity   

Complex Class

Complex classes like date_Core 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_Core, and based on these observations, apply Extract Interface, too.

1
<?php defined('SYSPATH') or die('No direct access allowed.');
2
/**
3
 * Date helper class.
4
 *
5
 * $Id: date.php 4316 2009-05-04 01:03:54Z kiall $
6
 *
7
 * @package    Core
8
 * @author     Kohana Team
9
 * @copyright  (c) 2007-2008 Kohana Team
10
 * @license    http://kohanaphp.com/license.html
11
 */
12
class date_Core
13
{
14
15
    /**
16
     * Converts a UNIX timestamp to DOS format.
17
     *
18
     * @param   integer  UNIX timestamp
19
     * @return  integer
20
     */
21
    public static function unix2dos($timestamp = false)
22
    {
23
        $timestamp = ($timestamp === false) ? getdate() : getdate($timestamp);
24
25
        if ($timestamp['year'] < 1980) {
26
            return (1 << 21 | 1 << 16);
27
        }
28
29
        $timestamp['year'] -= 1980;
30
31
        // What voodoo is this? I have no idea... Geert can explain it though,
32
        // and that's good enough for me.
33
        return ($timestamp['year']    << 25 | $timestamp['mon']     << 21 |
34
                $timestamp['mday']    << 16 | $timestamp['hours']   << 11 |
35
                $timestamp['minutes'] << 5  | $timestamp['seconds'] >> 1);
36
    }
37
38
    /**
39
     * Converts a DOS timestamp to UNIX format.
40
     *
41
     * @param   integer  DOS timestamp
42
     * @return  integer
43
     */
44
    public static function dos2unix($timestamp = false)
45
    {
46
        $sec  = 2 * ($timestamp & 0x1f);
47
        $min  = ($timestamp >>  5) & 0x3f;
48
        $hrs  = ($timestamp >> 11) & 0x1f;
49
        $day  = ($timestamp >> 16) & 0x1f;
50
        $mon  = ($timestamp >> 21) & 0x0f;
51
        $year = ($timestamp >> 25) & 0x7f;
52
53
        return mktime($hrs, $min, $sec, $mon, $day, $year + 1980);
54
    }
55
56
    /**
57
     * Returns the offset (in seconds) between two time zones.
58
     * @see     http://php.net/timezones
59
     *
60
     * @param   string          timezone that to find the offset of
61
     * @param   string|boolean  timezone used as the baseline
62
     * @return  integer
0 ignored issues
show
Documentation introduced by
Should the return type not be integer|double?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
63
     */
64
    public static function offset($remote, $local = true)
65
    {
66
        static $offsets;
67
68
        // Default values
69
        $remote = (string) $remote;
70
        $local  = ($local === true) ? date_default_timezone_get() : (string) $local;
71
72
        // Cache key name
73
        $cache = $remote.$local;
74
75
        if (empty($offsets[$cache])) {
76
            // Create timezone objects
77
            $remote = new DateTimeZone($remote);
78
            $local  = new DateTimeZone($local);
79
80
            // Create date objects from timezones
81
            $time_there = new DateTime('now', $remote);
82
            $time_here  = new DateTime('now', $local);
83
84
            // Find the offset
85
            $offsets[$cache] = $remote->getOffset($time_there) - $local->getOffset($time_here);
86
        }
87
88
        return $offsets[$cache];
89
    }
90
91
    /**
92
     * Number of seconds in a minute, incrementing by a step.
93
     *
94
     * @param   integer  amount to increment each step by, 1 to 30
95
     * @param   integer  start value
96
     * @param   integer  end value
97
     * @return  array    A mirrored (foo => foo) array from 1-60.
98
     */
99
    public static function seconds($step = 1, $start = 0, $end = 60)
100
    {
101
        // Always integer
102
        $step = (int) $step;
103
104
        $seconds = array();
105
106
        for ($i = $start; $i < $end; $i += $step) {
107
            $seconds[$i] = ($i < 10) ? '0'.$i : $i;
108
        }
109
110
        return $seconds;
111
    }
112
113
    /**
114
     * Number of minutes in an hour, incrementing by a step.
115
     *
116
     * @param   integer  amount to increment each step by, 1 to 30
117
     * @return  array    A mirrored (foo => foo) array from 1-60.
118
     */
119
    public static function minutes($step = 5)
120
    {
121
        // Because there are the same number of minutes as seconds in this set,
122
        // we choose to re-use seconds(), rather than creating an entirely new
123
        // function. Shhhh, it's cheating! ;) There are several more of these
124
        // in the following methods.
125
        return date::seconds($step);
126
    }
127
128
    /**
129
     * Number of hours in a day.
130
     *
131
     * @param   integer  amount to increment each step by
132
     * @param   boolean  use 24-hour time
133
     * @param   integer  the hour to start at
134
     * @return  array    A mirrored (foo => foo) array from start-12 or start-23.
135
     */
136
    public static function hours($step = 1, $long = false, $start = null)
137
    {
138
        // Default values
139
        $step = (int) $step;
140
        $long = (bool) $long;
141
        $hours = array();
0 ignored issues
show
Unused Code introduced by
$hours 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...
142
143
        // Set the default start if none was specified.
144
        if ($start === null) {
145
            $start = ($long === false) ? 1 : 0;
146
        }
147
148
        $hours = array();
149
150
        // 24-hour time has 24 hours, instead of 12
151
        $size = ($long === true) ? 23 : 12;
152
153
        for ($i = $start; $i <= $size; $i += $step) {
154
            $hours[$i] = $i;
155
        }
156
157
        return $hours;
158
    }
159
160
    /**
161
     * Returns AM or PM, based on a given hour.
162
     *
163
     * @param   integer  number of the hour
164
     * @return  string
165
     */
166
    public static function ampm($hour)
167
    {
168
        // Always integer
169
        $hour = (int) $hour;
170
171
        return ($hour > 11) ? 'PM' : 'AM';
172
    }
173
174
    /**
175
     * Adjusts a non-24-hour number into a 24-hour number.
176
     *
177
     * @param   integer  hour to adjust
178
     * @param   string   AM or PM
179
     * @return  string
180
     */
181
    public static function adjust($hour, $ampm)
182
    {
183
        $hour = (int) $hour;
184
        $ampm = strtolower($ampm);
185
186
        switch ($ampm) {
187
            case 'am':
188
                if ($hour == 12) {
189
                    $hour = 0;
190
                }
191
            break;
192
            case 'pm':
193
                if ($hour < 12) {
194
                    $hour += 12;
195
                }
196
            break;
197
        }
198
199
        return sprintf('%02s', $hour);
200
    }
201
202
    /**
203
     * Number of days in month.
204
     *
205
     * @param   integer  number of month
206
     * @param   integer  number of year to check month, defaults to the current year
207
     * @return  array    A mirrored (foo => foo) array of the days.
208
     */
209
    public static function days($month, $year = false)
210
    {
211
        static $months;
212
213
        // Always integers
214
        $month = (int) $month;
215
        $year  = (int) $year;
216
217
        // Use the current year by default
218
        $year  = ($year == false) ? date('Y') : $year;
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $year of type integer to the boolean false. If you are specifically checking for 0, consider using something more explicit like === 0 instead.
Loading history...
219
220
        // We use caching for months, because time functions are used
221
        if (empty($months[$year][$month])) {
222
            $months[$year][$month] = array();
223
224
            // Use date to find the number of days in the given month
225
            $total = date('t', mktime(1, 0, 0, $month, 1, $year)) + 1;
226
227
            for ($i = 1; $i < $total; $i++) {
228
                $months[$year][$month][$i] = $i;
229
            }
230
        }
231
232
        return $months[$year][$month];
233
    }
234
235
    /**
236
     * Number of months in a year
237
     *
238
     * @return  array  A mirrored (foo => foo) array from 1-12.
239
     */
240
    public static function months()
241
    {
242
        return date::hours();
243
    }
244
245
    /**
246
     * Returns an array of years between a starting and ending year.
247
     * Uses the current year +/- 5 as the max/min.
248
     *
249
     * @param   integer  starting year
250
     * @param   integer  ending year
251
     * @return  array
252
     */
253
    public static function years($start = false, $end = false)
254
    {
255
        // Default values
256
        $start = ($start === false) ? date('Y') - 5 : (int) $start;
257
        $end   = ($end   === false) ? date('Y') + 5 : (int) $end;
258
259
        $years = array();
260
261
        // Add one, so that "less than" works
262
        $end += 1;
263
264
        for ($i = $start; $i < $end; $i++) {
265
            $years[$i] = $i;
266
        }
267
268
        return $years;
269
    }
270
271
    /**
272
     * Returns time difference between two timestamps, in human readable format.
273
     *
274
     * @param   integer       timestamp
275
     * @param   integer       timestamp, defaults to the current time
276
     * @param   string        formatting string
277
     * @return  string|array
278
     */
279
    public static function timespan($time1, $time2 = null, $output = 'years,months,weeks,days,hours,minutes,seconds')
280
    {
281
        // Array with the output formats
282
        $output = preg_split('/[^a-z]+/', strtolower((string) $output));
283
284
        // Invalid output
285
        if (empty($output)) {
286
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by date_Core::timespan of type string|array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
287
        }
288
289
        // Make the output values into keys
290
        extract(array_flip($output), EXTR_SKIP);
0 ignored issues
show
Bug introduced by
array_flip($output) cannot be passed to extract() as the parameter $var_array expects a reference.
Loading history...
291
292
        // Default values
293
        $time1  = max(0, (int) $time1);
294
        $time2  = empty($time2) ? time() : max(0, (int) $time2);
295
296
        // Calculate timespan (seconds)
297
        $timespan = abs($time1 - $time2);
298
299
        // All values found using Google Calculator.
300
        // Years and months do not match the formula exactly, due to leap years.
301
302
        // Years ago, 60 * 60 * 24 * 365
303
        isset($years) and $timespan -= 31556926 * ($years = (int) floor($timespan / 31556926));
304
305
        // Months ago, 60 * 60 * 24 * 30
306
        isset($months) and $timespan -= 2629744 * ($months = (int) floor($timespan / 2629743.83));
307
308
        // Weeks ago, 60 * 60 * 24 * 7
309
        isset($weeks) and $timespan -= 604800 * ($weeks = (int) floor($timespan / 604800));
310
311
        // Days ago, 60 * 60 * 24
312
        isset($days) and $timespan -= 86400 * ($days = (int) floor($timespan / 86400));
313
314
        // Hours ago, 60 * 60
315
        isset($hours) and $timespan -= 3600 * ($hours = (int) floor($timespan / 3600));
316
317
        // Minutes ago, 60
318
        isset($minutes) and $timespan -= 60 * ($minutes = (int) floor($timespan / 60));
319
320
        // Seconds ago, 1
321
        isset($seconds) and $seconds = $timespan;
322
323
        // Remove the variables that cannot be accessed
324
        unset($timespan, $time1, $time2);
325
326
        // Deny access to these variables
327
        $deny = array_flip(array('deny', 'key', 'difference', 'output'));
328
329
        // Return the difference
330
        $difference = array();
331
        foreach ($output as $key) {
332
            if (isset($$key) and ! isset($deny[$key])) {
333
                // Add requested key to the output
334
                $difference[$key] = $$key;
335
            }
336
        }
337
338
        // Invalid output formats string
339
        if (empty($difference)) {
340
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by date_Core::timespan of type string|array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
341
        }
342
343
        // If only one output format was asked, don't put it in an array
344
        if (count($difference) === 1) {
345
            return current($difference);
346
        }
347
348
        // Return array
349
        return $difference;
350
    }
351
352
    /**
353
     * Returns time difference between two timestamps, in the format:
354
     * N year, N months, N weeks, N days, N hours, N minutes, and N seconds ago
355
     *
356
     * @param   integer       timestamp
357
     * @param   integer       timestamp, defaults to the current time
358
     * @param   string        formatting string
359
     * @return  string
360
     */
361
    public static function timespan_string($time1, $time2 = null, $output = 'years,months,weeks,days,hours,minutes,seconds')
362
    {
363
        if ($difference = date::timespan($time1, $time2, $output) and is_array($difference)) {
364
            // Determine the key of the last item in the array
365
            $last = end($difference);
0 ignored issues
show
Unused Code introduced by
$last 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...
366
            $last = key($difference);
367
368
            $span = array();
369
            foreach ($difference as $name => $amount) {
370
                if ($amount === 0) {
371
                    // Skip empty amounts
372
                    continue;
373
                }
374
375
                // Add the amount to the span
376
                $span[] = ($name === $last ? ' and ' : ', ').$amount.' '.($amount === 1 ? inflector::singular($name) : $name);
377
            }
378
379
            // If the difference is less than 60 seconds, remove the preceding and.
380
            if (count($span) === 1) {
381
                $span[0] = ltrim($span[0], 'and ');
382
            }
383
384
            // Replace difference by making the span into a string
385
            $difference = trim(implode('', $span), ',');
386
        } elseif (is_int($difference)) {
387
            // Single-value return
388
            $difference = $difference.' '.($difference === 1 ? inflector::singular($output) : $output);
389
        }
390
391
        return $difference;
392
    }
393
} // End date
394