Completed
Push — master ( 92bd35...fb5c6c )
by Tom
04:10
created

TimeElapsed::diff()   B

Complexity

Conditions 5
Paths 10

Size

Total Lines 31
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 31
c 0
b 0
f 0
rs 8.439
cc 5
eloc 17
nc 10
nop 2
1
<?php
2
/*
3
 * @author Tom Klingenberg <https://github.com/ktomk>
4
 */
5
6
namespace N98\Util;
7
8
use BadMethodCallException;
9
use DateInterval;
10
11
/**
12
 * Class TimeElapsed
13
 *
14
 * Diff a datetime against now and return human readable output (english) like
15
 *   1 year 40 weeks 2 days 1 hour 5 minutes 7 seconds ago
16
 * or
17
 *   just now
18
 *
19
 * @note borrowed from <http://stackoverflow.com/questions/1416697/>
20
 *
21
 * echo time_elapsed_string('2013-05-01 00:22:35');
22
 * echo time_elapsed_string('@1367367755'); # timestamp input
23
 * echo time_elapsed_string('2013-05-01 00:22:35', true);
24
 *
25
 * with changes:
26
 *  - extraced into static class methods
27
 *  - it is possible to provide the time of now (instead of time()
28
 *  - pass integers as seconds (relative to now)
29
 *
30
 * @package N98\Util
31
 */
32
class TimeElapsed
33
{
34
    /**
35
     * Full format, e.g. "85 years, 10 months, 3 weeks, 1 day, 3 hours, 29 minutes, 21 seconds ago"
36
     *
37
     * @param int|string $datetimeOrSeconds numbers of seconds (int) or \DateTime time (string)
38
     * @param int|null $now [optional] time()
39
     * @return string
40
     */
41 View Code Duplication
    public static function full($datetimeOrSeconds, $now = 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...
42
    {
43
        $diff = self::diff($datetimeOrSeconds, $now);
44
45
        $pieces = self::pieces($diff);
46
47
        $buffer = $pieces ? implode(', ', $pieces) . ' ago' : 'just now';
48
49
        return $buffer;
50
    }
51
52
    /**
53
     * Short format, e.g. "85 years ago"
54
     *
55
     * @param int|string $datetimeOrSeconds numbers of seconds (int) or \DateTime time (string)
56
     * @param int|null $now [optional] time()
57
     * @return string
58
     */
59 View Code Duplication
    public static function short($datetimeOrSeconds, $now = 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...
60
    {
61
        $diff = self::diff($datetimeOrSeconds, $now);
62
63
        $pieces = self::pieces($diff);
64
65
        $buffer = $pieces ? $pieces[0] . ' ago' : 'just now';
66
67
        return $buffer;
68
    }
69
70
    /**
71
     * @param int|string $datetimeOrSeconds numbers of seconds (int) or \DateTime time (string)
72
     * @param int|null $now [optional] time()
73
     * @return DateInterval
74
     */
75
    private static function diff($datetimeOrSeconds, $now = null)
76
    {
77
        $tsNow = $now === null ? time() : $now;
78
79
        if (is_numeric($datetimeOrSeconds)) {
80
            // seconds
81
            $timestampAgo = $tsNow - $datetimeOrSeconds;
82
            if ($timestampAgo < 0) {
83
                throw new BadMethodCallException(
84
                    sprintf('Negative unix timestamp with "%s" for now @ "%s"', $datetimeOrSeconds, $now)
85
                );
86
            }
87
            $dtStringAgo = "@$timestampAgo";
88
        } else {
89
            // datetime
90
            $dtStringAgo = $datetimeOrSeconds;
91
        }
92
93
        $now = new \DateTime("@$tsNow");
94
        $ago = new \DateTime($dtStringAgo);
95
96
        $diff = $now->diff($ago);
97
98
        if ($diff === false) {
99
            throw new BadMethodCallException(
100
                sprintf('Diff failed with "%s" for now @ "%s"', $datetimeOrSeconds, $now)
101
            );
102
        }
103
104
        return $diff;
105
    }
106
107
    /**
108
     * @param DateInterval $diff
109
     * @return array
110
     */
111
    private static function pieces(DateInterval $diff)
112
    {
113
        $keys = array(
114
            'y' => 'year',
115
            'm' => 'month',
116
            'w' => 'week',
117
            'd' => 'day',
118
            'h' => 'hour',
119
            'i' => 'minute',
120
            's' => 'second',
121
        );
122
123
        // map diff
124
        $diffArray = array();
125
        $diffVars = get_object_vars($diff);
126
        foreach ($keys as $unit => $name) {
127
            $diffArray[$unit] = isset($diffVars[$unit]) ? $diffVars[$unit] : null;
128
        }
129
130
        // shorten days by weeks (note: ignoring months and years)
131
        $weeks = floor($diffArray['d'] / 7);
132
        $diffArray['w'] = $weeks;
133
        $diffArray['d'] -= $weeks * 7;
134
135
        // fill string buffer array
136
        $pieces = array();
137
        foreach ($keys as $unit => $name) {
138
            if ($value = $diffArray[$unit]) {
139
                $pieces[] = sprintf('%s %s%s', $value, $name, $value > 1 ? 's' : '');
140
            }
141
        }
142
143
        return $pieces;
144
    }
145
}
146