Passed
Push — master ( f802e2...a1b288 )
by Sebastian
02:28
created

handle_localeChanged()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 4
rs 10
1
<?php
2
/**
3
 * File containing the {@see AppUtils\ConvertHelper_DurationConverter} class.
4
 *
5
 * @package Application Utils
6
 * @subpackage ConvertHelper
7
 * @see ConvertHelper_DurationConverter
8
 */
9
10
declare(strict_types=1);
11
12
namespace AppUtils;
13
14
/**
15
 * Converts a timespan to a human readable duration string,
16
 * e.g. "2 months", "4 minutes".
17
 * 
18
 * @package Application Utils
19
 * @subpackage ConvertHelper
20
 * @author Sebastian Mordziol <[email protected]>
21
 * 
22
 * @link http://www.sajithmr.com/php-time-ago-calculation/
23
 */
24
class ConvertHelper_DurationConverter
25
{
26
    const ERROR_NO_DATE_FROM_SET = 43401;
27
    
28
    
29
   /**
30
    * @var int
31
    */
32
    protected $dateFrom;
33
    
34
   /**
35
    * @var int
36
    */
37
    protected $dateTo;
38
    
39
   /**
40
    * @var bool
41
    */
42
    protected $future = false;
43
    
44
   /**
45
    * @var string
46
    */
47
    protected $interval = '';
48
    
49
   /**
50
    * @var int
51
    */
52
    protected $difference = 0;
53
    
54
   /**
55
    * @var int
56
    */
57
    protected $dateDiff = 0;
58
    
59
   /**
60
    * @var array
61
    */
62
    protected static $texts;
63
    
64
    public function __construct()
65
    {
66
        if(class_exists('\AppLocalize\Localization')) {
67
            \AppLocalize\Localization::onLocaleChanged(array($this, 'handle_localeChanged'));
0 ignored issues
show
Bug introduced by
The type AppLocalize\Localization was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
68
        }
69
    }
70
    
71
    /**
72
     * Called whenever the application locale has changed,
73
     * to reset the internal translation cache.
74
     */
75
    public function handle_localeChanged()
76
    {
77
        // force the texts to be refreshed when needed.
78
        unset(self::$texts);
79
    }
80
    
81
   /**
82
    * Sets the origin date to calculate from.
83
    * 
84
    * NOTE: if this is further in the future than
85
    * the to: date, it will be considered as a 
86
    * calculation for something to come, i.e. 
87
    * "In two days".
88
    *  
89
    * @param \DateTime $date
90
    * @return ConvertHelper_DurationConverter
91
    */
92
    public function setDateFrom(\DateTime $date) : ConvertHelper_DurationConverter
93
    {
94
        $this->dateFrom = ConvertHelper::date2timestamp($date);
95
        
96
        return $this;
97
    }
98
    
99
   /**
100
    * Sets the date to calculate to. Defaults to 
101
    * the current time if not set.
102
    * 
103
    * @param \DateTime $date
104
    * @return ConvertHelper_DurationConverter
105
    */
106
    public function setDateTo(\DateTime $date) : ConvertHelper_DurationConverter
107
    {
108
        $this->dateTo = ConvertHelper::date2timestamp($date);
109
        
110
        return $this;
111
    }
112
    
113
   /**
114
    * Converts the specified dates to a human readable string.
115
    * 
116
    * @throws ConvertHelper_Exception
117
    * @return string
118
    * 
119
    * @see ConvertHelper_DurationConverter::ERROR_NO_DATE_FROM_SET
120
    */
121
    public function convert() : string
122
    {
123
        $this->initTexts();
124
        $this->resolveCalculations();
125
        
126
        $epoch = 'past';
127
        $key = 'singular';
128
        if($this->dateDiff > 1) {
129
            $key = 'plural';
130
        }
131
        
132
        if($this->future) {
133
            $epoch = 'future'; 
134
        }
135
        
136
        $key .= '-'.$epoch;
137
        
138
        $text = self::$texts[$this->interval][$key];
139
        
140
        return str_replace('$value', $this->dateDiff, $text);
141
    }
142
    
143
    protected function initTexts()
144
    {
145
        if(isset(self::$texts)) {
146
            return;
147
        }
148
        
149
        self::$texts = array(
150
            'y' => array(
151
                'singular-future' => t('In one year'),
152
                'plural-future' => t('In %1s years', '$value'),
153
                'singular-past' => t('One year ago'),
154
                'plural-past' => t('%1s years ago', '$value')
155
            ),
156
            'm' => array(
157
                'singular-future' => t('In one month'),
158
                'plural-future' => t('In %1s months', '$value'),
159
                'singular-past' => t('One month ago'),
160
                'plural-past' => t('%1s months ago', '$value')
161
            ),
162
            'ww' => array(
163
                'singular-future' => t('In one week'),
164
                'plural-future' => t('In %1s weeks', '$value'),
165
                'singular-past' => t('One week ago'),
166
                'plural-past' => t('%1s weeks ago', '$value')
167
            ),
168
            'd' => array(
169
                'singular-future' => t('In one day'),
170
                'plural-future' => t('In %1s days', '$value'),
171
                'singular-past' => t('One day ago'),
172
                'plural-past' => t('%1s days ago', '$value')
173
            ),
174
            'h' => array(
175
                'singular-future' => t('In one hour'),
176
                'plural-future' => t('In %1s hours', '$value'),
177
                'singular-past' => t('One hour ago'),
178
                'plural-past' => t('%1s hours ago', '$value')
179
            ),
180
            'n' => array(
181
                'singular-future' => t('In one minute'),
182
                'plural-future' => t('In %1s minutes', '$value'),
183
                'singular-past' => t('One minute ago'),
184
                'plural-past' => t('%1s minutes ago', '$value')
185
            ),
186
            's' => array(
187
                'singular-future' => t('In one second'),
188
                'plural-future' => t('In %1s seconds', '$value'),
189
                'singular-past' => t('One second ago'),
190
                'plural-past' => t('%1s seconds ago', '$value')
191
            )
192
        );
193
    }
194
    
195
    protected function convert_minute() : int
196
    {
197
        return (int)floor($this->difference / 60);
198
    }
199
    
200
    protected function convert_hour() : int
201
    {
202
        return (int)floor($this->difference / 60 / 60);
203
    }
204
    
205
    protected function convert_week() : int
206
    {
207
        return (int)floor($this->difference / 60 / 60 / 24 / 7);
208
    }
209
    
210
    protected function convert_day() : int
211
    {
212
        return (int)floor($this->difference / 60 / 60 / 24);
213
    }
214
    
215
    protected function convert_year() : int
216
    {
217
        return (int)floor($this->difference / 60 / 60 / 24 / 365);
218
    }
219
    
220
    protected function convert_month() : int
221
    {
222
        $months_difference = (int)floor($this->difference / 60 / 60 / 24 / 29);
223
        
224
        $hour = (int)date("H", $this->dateFrom);
225
        $min = (int)date("i", $this->dateFrom);
226
        $sec = (int)date("s", $this->dateFrom);
227
        $month = (int)date("n", $this->dateFrom);
228
        $day = (int)date("j", $this->dateTo);
229
        $year = (int)date("Y", $this->dateFrom);
230
        
231
        while(mktime($hour, $min, $sec, $month + ($months_difference), $day, $year) < $this->dateTo)
232
        {
233
            $months_difference++;
234
        }
235
        
236
        $datediff = $months_difference;
237
        
238
        // We need this in here because it is possible
239
        // to have an 'm' interval and a months
240
        // difference of 12 because we are using 29 days
241
        // in a month
242
        if ($datediff == 12) {
243
            $datediff--;
244
        }
245
        
246
        return $datediff;
247
    }
248
    
249
    protected function resolveCalculations() : void
250
    {
251
        if(!isset($this->dateFrom))
252
        {
253
            throw new ConvertHelper_Exception(
254
                'No date from has been specified.',
255
                null,
256
                self::ERROR_NO_DATE_FROM_SET
257
            );
258
        }
259
        
260
        // no date to set? Assume we want to use today.
261
        if(!isset($this->dateTo))
262
        {
263
            $this->dateTo = time();
264
        }
265
        
266
        $this->difference = $this->resolveDifference();
267
        $this->interval = $this->resolveInterval();
268
        $this->dateDiff = $this->resolveDateDiff();
269
    }
270
    
271
    protected function resolveInterval() : string
272
    {
273
        // If difference is less than 60 seconds,
274
        // seconds is a good interval of choice
275
        
276
        if ($this->difference < 60) 
277
        {
278
            return "s";
279
        }
280
        
281
        // If difference is between 60 seconds and
282
        // 60 minutes, minutes is a good interval
283
        if ($this->difference >= 60 && $this->difference < 60 * 60) 
284
        {
285
            return "n";
286
        }
287
        
288
        // If difference is between 1 hour and 24 hours
289
        // hours is a good interval
290
        if ($this->difference >= 60 * 60 && $this->difference < 60 * 60 * 24) 
291
        {
292
            return "h";
293
        }
294
        
295
        // If difference is between 1 day and 7 days
296
        // days is a good interval
297
        if ($this->difference >= 60 * 60 * 24 && $this->difference < 60 * 60 * 24 * 7) 
298
        {
299
            return "d";
300
        }
301
        
302
        // If difference is between 1 week and 30 days
303
        // weeks is a good interval
304
        if ($this->difference >= 60 * 60 * 24 * 7 && $this->difference < 60 * 60 * 24 * 30) 
305
        {
306
            return "ww";
307
        }
308
        
309
        // If difference is between 30 days and 365 days
310
        // months is a good interval, again, the same thing
311
        // applies, if the 29th February happens to exist
312
        // between your 2 dates, the function will return
313
        // the 'incorrect' value for a day
314
        if ($this->difference >= 60 * 60 * 24 * 30 && $this->difference < 60 * 60 * 24 * 365) 
315
        {
316
            return "m";
317
        }
318
        
319
        // If difference is greater than or equal to 365
320
        // days, return year. This will be incorrect if
321
        // for example, you call the function on the 28th April
322
        // 2008 passing in 29th April 2007. It will return
323
        // 1 year ago when in actual fact (yawn!) not quite
324
        // a year has gone by
325
        if ($this->difference >= 60 * 60 * 24 * 365) 
326
        {
327
            return "y";
328
        }
329
        
330
        return ""; 
331
    }
332
    
333
    protected function resolveDifference() : int
334
    {
335
        // Calculate the difference in seconds betweeen
336
        // the two timestamps
337
        
338
        $difference = $this->dateTo - $this->dateFrom;
339
        
340
        if($difference < 0)
341
        {
342
            $difference = $difference * -1;
343
            $this->future = true;
344
        }
345
        
346
        return $difference;
347
    }
348
    
349
    protected function resolveDateDiff() : int
350
    {
351
        // Based on the interval, determine the
352
        // number of units between the two dates
353
        // From this point on, you would be hard
354
        // pushed telling the difference between
355
        // this function and DateDiff. If the $datediff
356
        // returned is 1, be sure to return the singular
357
        // of the unit, e.g. 'day' rather 'days'
358
        switch ($this->interval)
359
        {
360
            case "m":
361
                return $this->convert_month();
362
                
363
            case "y":
364
                return $this->convert_year();
365
                
366
            case "d":
367
                return $this->convert_day();
368
                
369
            case "ww":
370
                return $this->convert_week();
371
                
372
            case "h":
373
                return $this->convert_hour();
374
                
375
            case "n":
376
                return $this->convert_minute();
377
        }
378
        
379
        // seconds
380
        return $this->difference;
381
    }
382
}
383