GregorianCalendar::yearLength1()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 2
nc 2
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
// +---------------------------------------------------------------------------+
4
// | This file is part of the Agavi package.                                   |
5
// | Copyright (c) 2005-2011 the Agavi Project.                                |
6
// |                                                                           |
7
// | For the full copyright and license information, please view the LICENSE   |
8
// | file that was distributed with this source code. You can also view the    |
9
// | LICENSE file online at http://www.agavi.org/LICENSE.txt                   |
10
// |   vi: set noexpandtab:                                                    |
11
// |   Local Variables:                                                        |
12
// |   indent-tabs-mode: t                                                     |
13
// |   End:                                                                    |
14
// +---------------------------------------------------------------------------+
15
16
namespace Agavi\Date;
17
18
use Agavi\Translation\Locale;
19
use Agavi\Translation\TranslationManager;
20
use Agavi\Util\Toolkit;
21
22
/**
23
 * Ported from ICU:
24
 *  icu/trunk/source/i18n/gregocal.cpp        r22379
25
 *  icu/trunk/source/i18n/unicode/gregocal.h  r19003
26
 *
27
 * @package    agavi
28
 * @subpackage date
29
 *
30
 * @author     Dominik del Bondio <[email protected]>
31
 * @author     The ICU Project
32
 * @copyright  Authors
33
 * @copyright  The Agavi Project
34
 *
35
 * @since      0.11.0
36
 *
37
 * @version    $Id$
38
 */
39
class GregorianCalendar extends Calendar
40
{
41
    const CUTOVER_JULIAN_DAY = 2299161;
42
    //const kPapalCutover = (2299161.0 - kEpochStartAsJulianDay) * AgaviDateDefinitions::MILLIS_PER_DAY;
43
    const PAPAL_CUTOVER      = -12219292800000.0;
44
45
    /**
46
     * Overloaded.
47
     *
48
     * @see        AgaviGregorianCalendar::constructorO()
49
     * @see        AgaviGregorianCalendar::constructorOO()
50
     * @see        AgaviGregorianCalendar::constructorOIII()
51
     * @see        AgaviGregorianCalendar::constructorOIIIII()
52
     * @see        AgaviGregorianCalendar::constructorOIIIIII()
53
     *
54
     * @author     Dominik del Bondio <[email protected]>
55
     * @author     The ICU Project
56
     * @since      0.11.0
57
     */
58
    public function __construct()
59
    {
60
        $this->initVariables();
61
62
        $arguments = func_get_args();
63
64
        $fName = Toolkit::overloadHelper(
65
            array(
66
                array(
67
                    'name' => 'constructorO',
68
                    'parameters' => array('object')
69
                ),
70
                array(
71
                    'name' => 'constructorOO',
72
                    'parameters' => array('object', 'object')
73
                    ),
74
                array(
75
                    'name' => 'constructorOIII',
76
                    'parameters' => array('object', 'int', 'int', 'int')
77
                ),
78
                array(
79
                    'name' => 'constructorOIIIII',
80
                    'parameters' => array('object', 'int', 'int', 'int', 'int', 'int')
81
                ),
82
                array(
83
                    'name' => 'constructorOIIIIII',
84
                    'parameters' => array('object', 'int', 'int', 'int', 'int', 'int', 'int')
85
                ),
86
            ),
87
            $arguments
88
        );
89
        call_user_func_array(array($this, $fName), $arguments);
90
    }
91
92
    /**
93
     * Initialize all variables to default values.
94
     *
95
     * @author     Dominik del Bondio <[email protected]>
96
     * @author     The ICU Project
97
     * @since      0.11.0
98
     */
99
    protected function initVariables()
100
    {
101
        parent::initVariables();
102
103
        $this->fGregorianCutover = self::PAPAL_CUTOVER;
0 ignored issues
show
Documentation Bug introduced by
The property $fGregorianCutover was declared of type double, but self::PAPAL_CUTOVER is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
104
        $this->fCutoverJulianDay = self::CUTOVER_JULIAN_DAY;
105
        $this->fNormalizedGregorianCutover = $this->fGregorianCutover;
0 ignored issues
show
Documentation Bug introduced by
The property $fNormalizedGregorianCutover was declared of type double, but $this->fGregorianCutover is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
106
        $this->fGregorianCutoverYear = 1582;
107
        $this->fIsGregorian = true;
108
        $this->fInvertGregorian = false;
109
    }
110
111
    /**
112
     * Constructor.
113
     *
114
     * @param      TimeZone|Locale|TranslationManager
115
     *
116
     * @author     Dominik del Bondio <[email protected]>
117
     * @author     The ICU Project
118
     * @since      0.11.0
119
     */
120
    protected function constructorO($zoneOrLocale)
121
    {
122
        $zone = null;
0 ignored issues
show
Unused Code introduced by
$zone 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...
123
        $locale = null;
0 ignored issues
show
Unused Code introduced by
$locale 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...
124
125
        if ($zoneOrLocale instanceof TimeZone) {
126
            $this->translationManager = $zoneOrLocale->getTranslationManager();
127
            $zone = $zoneOrLocale;
128
            $locale = $this->translationManager->getCurrentLocale();
129
        } elseif ($zoneOrLocale instanceof Locale) {
130
            $this->translationManager = $zoneOrLocale->getTranslationManager();
0 ignored issues
show
Bug introduced by
The method getTranslationManager() does not seem to exist on object<Agavi\Translation\Locale>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
131
            $zone = $this->translationManager->getDefaultTimeZone();
132
            $locale = $zoneOrLocale;
133
        } elseif ($zoneOrLocale instanceof TranslationManager) {
134
            $this->translationManager = $zoneOrLocale;
135
            $zone = $this->translationManager->getDefaultTimeZone();
136
            $locale = $this->translationManager->getCurrentLocale();
137
        } else {
138
            throw new \InvalidArgumentException('Object of type ' . get_class($zoneOrLocale) . ' was not expected');
139
        }
140
        parent::constructorOO($zone, $locale);
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (constructorOO() instead of constructorO()). Are you sure this is correct? If so, you might want to change this to $this->constructorOO().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
141
142
        $this->setTimeInMillis(self::getNow());
143
        $this->set(DateDefinitions::ERA, self::AD);
144
    }
145
146
    /**
147
     * Constructor.
148
     *
149
     * @param      TimeZone $zone
150
     * @param      Locale   $locale
151
     *
152
     * @author     Dominik del Bondio <[email protected]>
153
     * @author     The ICU Project
154
     * @since      0.11.0
155
     */
156
    protected function constructorOO(TimeZone $zone, Locale $locale)
157
    {
158
        parent::constructorOO($zone, $locale);
159
        $this->setTimeInMillis(self::getNow());
160
        $this->set(DateDefinitions::ERA, self::AD);
161
    }
162
163
    /**
164
     * Constructor.
165
     *
166
     * @param      TranslationManager $tm
167
     * @param      int $year
168
     * @param      int $month
169
     * @param      int $date
170
     *
171
     * @author     Dominik del Bondio <[email protected]>
172
     * @author     The ICU Project
173
     * @since      0.11.0
174
     */
175
    protected function constructorOIII(TranslationManager $tm, $year, $month, $date)
176
    {
177
        parent::constructorOO($tm->getDefaultTimeZone(), $tm->getCurrentLocale());
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (constructorOO() instead of constructorOIII()). Are you sure this is correct? If so, you might want to change this to $this->constructorOO().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
178
        $this->set(DateDefinitions::ERA, self::AD);
179
        $this->set(DateDefinitions::YEAR, $year);
180
        $this->set(DateDefinitions::MONTH, $month);
181
        $this->set(DateDefinitions::DATE, $date);
182
    }
183
184
    /**
185
     * Constructor.
186
     *
187
     * @param      TranslationManager $tm
188
     * @param      int $year
189
     * @param      int $month
190
     * @param      int $date
191
     * @param      int $hour
192
     * @param      int $minute
193
     *
194
     * @author     Dominik del Bondio <[email protected]>
195
     * @author     The ICU Project
196
     * @since      0.11.0
197
     */
198 View Code Duplication
    protected function constructorOIIIII(TranslationManager $tm, $year, $month, $date, $hour, $minute)
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...
199
    {
200
        parent::constructorOO($tm->getDefaultTimeZone(), $tm->getCurrentLocale());
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (constructorOO() instead of constructorOIIIII()). Are you sure this is correct? If so, you might want to change this to $this->constructorOO().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
201
        $this->set(DateDefinitions::ERA, self::AD);
202
        $this->set(DateDefinitions::YEAR, $year);
203
        $this->set(DateDefinitions::MONTH, $month);
204
        $this->set(DateDefinitions::DATE, $date);
205
        $this->set(DateDefinitions::HOUR_OF_DAY, $hour);
206
        $this->set(DateDefinitions::MINUTE, $minute);
207
    }
208
209
    /**
210
     * Constructor.
211
     *
212
     * @param      TranslationManager $tm
213
     * @param      int $year
214
     * @param      int $month
215
     * @param      int $date
216
     * @param      int $hour
217
     * @param      int $minute
218
     * @param      int $second
219
     *
220
     * @author     Dominik del Bondio <[email protected]>
221
     * @author     The ICU Project
222
     * @since      0.11.0
223
     */
224 View Code Duplication
    protected function constructorOIIIIII(TranslationManager $tm, $year, $month, $date, $hour, $minute, $second)
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...
225
    {
226
        parent::constructorOO($tm->getDefaultTimeZone(), $tm->getCurrentLocale());
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (constructorOO() instead of constructorOIIIIII()). Are you sure this is correct? If so, you might want to change this to $this->constructorOO().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
227
        $this->set(DateDefinitions::ERA, self::AD);
228
        $this->set(DateDefinitions::YEAR, $year);
229
        $this->set(DateDefinitions::MONTH, $month);
230
        $this->set(DateDefinitions::DATE, $date);
231
        $this->set(DateDefinitions::HOUR_OF_DAY, $hour);
232
        $this->set(DateDefinitions::MINUTE, $minute);
233
        $this->set(DateDefinitions::SECOND, $second);
234
    }
235
236
    /**
237
     * Useful constants for GregorianCalendar and TimeZone.
238
     */
239
     const BC = 0;
240
     const AD = 1;
241
242
    /**
243
     * Sets the GregorianCalendar change date. This is the point when the switch
244
     * from Julian dates to Gregorian dates occurred. Default is 00:00:00 local
245
     * time, October 15, 1582. Previous to this time and date will be Julian
246
     * dates.
247
     *
248
     * @param      float $date The given Gregorian cutover date.
249
     *
250
     * @author     Dominik del Bondio <[email protected]>
251
     * @author     The ICU Project
252
     * @since      0.11.0
253
     */
254
    public function setGregorianChange($date)
255
    {
256
        $this->fGregorianCutover = $date;
257
258
        // Precompute two internal variables which we use to do the actual
259
        // cutover computations.  These are the normalized cutover, which is the
260
        // midnight at or before the cutover, and the cutover year.  The
261
        // normalized cutover is in pure date milliseconds; it contains no time
262
        // of day or timezone component, and it used to compare against other
263
        // pure date values.
264
        $cutoverDay = (int) floor($this->fGregorianCutover / DateDefinitions::MILLIS_PER_DAY);
265
        $this->fNormalizedGregorianCutover = $cutoverDay * DateDefinitions::MILLIS_PER_DAY;
0 ignored issues
show
Documentation Bug introduced by
It seems like $cutoverDay * \Agavi\Dat...nitions::MILLIS_PER_DAY can also be of type integer. However, the property $fNormalizedGregorianCutover is declared as type double. 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...
266
267
        // Handle the rare case of numeric overflow.  If the user specifies a
268
        // change of UDate(Long.MIN_VALUE), in order to get a pure Gregorian
269
        // calendar, then the epoch day is -106751991168, which when multiplied
270
        // by ONE_DAY gives 9223372036794351616 -- the negative value is too
271
        // large for 64 bits, and overflows into a positive value.  We correct
272
        // this by using the next day, which for all intents is semantically
273
        // equivalent.
274
        if ($cutoverDay < 0 && $this->fNormalizedGregorianCutover > 0) {
275
            $this->fNormalizedGregorianCutover = ($cutoverDay + 1) * DateDefinitions::MILLIS_PER_DAY;
276
        }
277
278
        // Normalize the year so BC values are represented as 0 and negative
279
        // values.
280
        $cal = new GregorianCalendar($this->getTimeZone());
281
282
        $cal->setTime($date);
283
        $this->fGregorianCutoverYear = $cal->get(DateDefinitions::YEAR);
284
        if ($cal->get(DateDefinitions::ERA) == self::BC) {
285
            $this->fGregorianCutoverYear = 1 - $this->fGregorianCutoverYear;
286
        }
287
        $this->fCutoverJulianDay = $cutoverDay;
0 ignored issues
show
Documentation Bug introduced by
It seems like $cutoverDay can also be of type double. However, the property $fCutoverJulianDay is declared as type 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...
288
    }
289
290
    /**
291
     * Gets the Gregorian Calendar change date. This is the point when the switch
292
     * from Julian dates to Gregorian dates occurred. Default is 00:00:00 local
293
     * time, October 15, 1582. Previous to this time and date will be Julian
294
     * dates.
295
     *
296
     * @return     float The Gregorian cutover time for this calendar.
297
     *
298
     * @author     Dominik del Bondio <[email protected]>
299
     * @author     The ICU Project
300
     * @since      0.11.0
301
     */
302
    public function getGregorianChange()
303
    {
304
        return $this->fGregorianCutover;
305
    }
306
307
    /**
308
     * Return true if the given year is a leap year. Determination of whether a
309
     * year is a leap year is actually very complicated. We do something crude
310
     * and mostly correct here, but for a real determination you need a lot of
311
     * contextual information. For example, in Sweden, the change from Julian to
312
     * Gregorian happened in a complex way resulting in missed leap years and
313
     * double leap years between 1700 and 1753. Another example is that after the
314
     * start of the Julian calendar in 45 B.C., the leap years did not regularize
315
     * until 8 A.D. This method ignores these quirks, and pays attention only to
316
     * the Julian onset date and the Gregorian cutover (which can be changed).
317
     *
318
     * @param      int  $year The given year.
319
     *
320
     * @return     bool if the given year is a leap year; false otherwise.
321
     *
322
     * @author     Dominik del Bondio <[email protected]>
323
     * @author     The ICU Project
324
     * @since      0.11.0
325
     */
326
    public function isLeapYear($year)
327
    {
328
        // MSVC complains bitterly if we try to use Grego::isLeapYear here
329
        // NOTE: year&0x3 == year%4
330
        return ($year >= $this->fGregorianCutoverYear
331
                        ? CalendarGrego::isLeapYear($year) // Gregorian
332
                        : (($year & 0x3) == 0)); // Julian
333
    }
334
335
    /**
336
     * Returns true if the given Calendar object is equivalent to this
337
     * one.  Calendar override.
338
     *
339
     * @param      Calendar $other the Calendar to be compared with this Calendar
340
     *
341
     * @return     bool
342
     *
343
     * @author     Dominik del Bondio <[email protected]>
344
     * @author     The ICU Project
345
     * @since      0.11.0
346
     */
347
    public function isEquivalentTo(Calendar $other)
348
    {
349
        // Calendar override.
350
        return Calendar::isEquivalentTo($other) && $this->getGregorianChange() == $other->getGregorianChange();
351
    }
352
353
    /**
354
     * @see        AgaviCalendar::getActualMinimum()
355
     *
356
     * @author     Dominik del Bondio <[email protected]>
357
     * @author     The ICU Project
358
     * @since      0.11.0
359
     */
360
    public function getActualMinimum($field)
361
    {
362
        return $this->getMinimum($field);
363
    }
364
365
    /**
366
     * @see        AgaviCalendar::getActualMaximum()
367
     *
368
     * @author     Dominik del Bondio <[email protected]>
369
     * @author     The ICU Project
370
     * @since      0.11.0
371
     */
372
    public function getActualMaximum($field)
373
    {
374
        /* It is a known limitation that the code here (and in getActualMinimum)
375
		* won't behave properly at the extreme limits of GregorianCalendar's
376
		* representable range (except for the code that handles the YEAR
377
		* field).  That's because the ends of the representable range are at
378
		* odd spots in the year.  For calendars with the default Gregorian
379
		* cutover, these limits are Sun Dec 02 16:47:04 GMT 292269055 BC to Sun
380
		* Aug 17 07:12:55 GMT 292278994 AD, somewhat different for non-GMT
381
		* zones.  As a result, if the calendar is set to Aug 1 292278994 AD,
382
		* the actual maximum of DAY_OF_MONTH is 17, not 30.  If the date is Mar
383
		* 31 in that year, the actual maximum month might be Jul, whereas is
384
		* the date is Mar 15, the actual maximum might be Aug -- depending on
385
		* the precise semantics that are desired.  Similar considerations
386
		* affect all fields.  Nonetheless, this effect is sufficiently arcane
387
		* that we permit it, rather than complicating the code to handle such
388
		* intricacies. - liu 8/20/98
389
390
		* UPDATE: No longer true, since we have pulled in the limit values on
391
		* the year. - Liu 11/6/00 */
392
393
        switch ($field) {
394
            case DateDefinitions::YEAR:
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
395
                /* The year computation is no different, in principle, from the
396
				* others, however, the range of possible maxima is large.  In
397
				* addition, the way we know we've exceeded the range is different.
398
				* For these reasons, we use the special case code below to handle
399
				* this field.
400
				*
401
				* The actual maxima for YEAR depend on the type of calendar:
402
				*
403
				*     Gregorian = May 17, 292275056 BC - Aug 17, 292278994 AD
404
				*     Julian    = Dec  2, 292269055 BC - Jan  3, 292272993 AD
405
				*     Hybrid    = Dec  2, 292269055 BC - Aug 17, 292278994 AD
406
				*
407
				* We know we've exceeded the maximum when either the month, date,
408
				* time, or era changes in response to setting the year.  We don't
409
				* check for month, date, and time here because the year and era are
410
				* sufficient to detect an invalid year setting.  NOTE: If code is
411
				* added to check the month and date in the future for some reason,
412
				* Feb 29 must be allowed to shift to Mar 1 when setting the year.
413
				*/
414
                {
415
                    $cal = clone $this;
416
417
                    $cal->setLenient(true);
418
419
                    $era = $cal->get(DateDefinitions::ERA);
420
                    $d = $cal->getTime();
421
422
                    /* Perform a binary search, with the invariant that lowGood is a
423
					* valid year, and highBad is an out of range year.
424
					*/
425
                    $lowGood = self::$kGregorianCalendarLimits[DateDefinitions::YEAR][1];
426
                    $highBad = self::$kGregorianCalendarLimits[DateDefinitions::YEAR][2] + 1;
427
                while (($lowGood + 1) < $highBad) {
428
                    $y = (int)(($lowGood + $highBad) / 2);
429
                    $cal->set(DateDefinitions::YEAR, $y);
430
                    if ($cal->get(DateDefinitions::YEAR) == $y && $cal->get(DateDefinitions::ERA) == $era) {
431
                        $lowGood = $y;
432
                    } else {
433
                        $highBad = $y;
434
                        $cal->setTime($d); // Restore original fields
435
                    }
436
                }
437
438
                return $lowGood;
439
            }
440
441
            default:
442
                return parent::getActualMaximum($field);
443
        }
444
    }
445
446
    /**
447
     * @see        AgaviCalendar::inDaylightTime
448
     *
449
     * @author     Dominik del Bondio <[email protected]>
450
     * @author     The ICU Project
451
     * @since      0.11.0
452
     */
453
    public function inDaylightTime()
454
    {
455
        if (!$this->getTimeZone()->useDaylightTime()) {
456
            return false;
457
        }
458
459
        // Force an update of the state of the Calendar.
460
        $this->complete(); // cast away const
461
462
        return ($this->internalGet(DateDefinitions::DST_OFFSET) != 0);
463
    }
464
465
    /**
466
     * Return the ERA.  We need a special method for this because the
467
     * default ERA is AD, but a zero (unset) ERA is BC.
468
     *
469
     * @return     int The ERA.
470
     *
471
     * @author     Dominik del Bondio <[email protected]>
472
     * @author     The ICU Project
473
     * @since      0.11.0
474
     */
475
    protected function internalGetEra()
476
    {
477
        return $this->_isSet(DateDefinitions::ERA) ? $this->internalGet(DateDefinitions::ERA) : self::AD;
478
    }
479
480
    /**
481
     * @see        AgaviCalendar::handleComputeMonthStart
482
     *
483
     * @author     Dominik del Bondio <[email protected]>
484
     * @author     The ICU Project
485
     * @since      0.11.0
486
     */
487
    protected function handleComputeMonthStart($eyear, $month, $useMonth)
488
    {
489
        // If the month is out of range, adjust it into range, and
490
        // modify the extended year value accordingly.
491
        if ($month < 0 || $month > 11) {
492
            $eyear += Toolkit::floorDivide($month, 12, $month);
493
        }
494
495
        $isLeap = ($eyear % 4 == 0);
496
        $y = $eyear - 1;
497
        $julianDay = 365 * $y + floor($y / 4) + (DateDefinitions::JAN_1_1_JULIAN_DAY - 3);
498
499
        $this->fIsGregorian = ($eyear >= $this->fGregorianCutoverYear);
500
501
        if ($this->fInvertGregorian) {
502
            $this->fIsGregorian = !$this->fIsGregorian;
503
        }
504 View Code Duplication
        if ($this->fIsGregorian) {
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...
505
            $isLeap = $isLeap && (($eyear % 100 != 0) || ($eyear % 400 == 0));
506
            // Add 2 because Gregorian calendar starts 2 days after
507
            // Julian calendar
508
            $gregShift = CalendarGrego::gregorianShift($eyear);
509
510
            $julianDay += $gregShift;
511
        }
512
513
        // At this point julianDay indicates the day BEFORE the first
514
        // day of January 1, <eyear> of either the Julian or Gregorian
515
        // calendar.
516
517
        if ($month != 0) {
518
            $julianDay += $isLeap ? self::$kLeapNumDays[$month] : self::$kNumDays[$month];
519
        }
520
521
        return $julianDay;
522
    }
523
524
    /**
525
     * @see        AgaviCalendar::handleComputeJulianDay
526
     *
527
     * @author     Dominik del Bondio <[email protected]>
528
     * @author     The ICU Project
529
     * @since      0.11.0
530
     */
531
    protected function handleComputeJulianDay($bestField)
532
    {
533
        $this->fInvertGregorian = false;
534
535
        $jd = parent::handleComputeJulianDay($bestField);
536
537
        if (($bestField == DateDefinitions::WEEK_OF_YEAR) &&  // if we are doing WOY calculations, we are counting relative to Jan 1 *julian*
538
                ($this->internalGet(DateDefinitions::EXTENDED_YEAR) == $this->fGregorianCutoverYear) && $jd >= $this->fCutoverJulianDay) {
539
            $this->fInvertGregorian = true;  // So that the Julian Jan 1 will be used in handleComputeMonthStart
540
            return parent::handleComputeJulianDay($bestField);
541
        }
542
543
        // The following check handles portions of the cutover year BEFORE the
544
        // cutover itself happens.
545
        //if((fIsGregorian==true) != (jd >= fCutoverJulianDay)) {  /*  cutoverJulianDay)) { */
0 ignored issues
show
Unused Code Comprehensibility introduced by
42% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
546
        if (($this->fIsGregorian == true) != ($jd >= $this->fCutoverJulianDay)) {  /*  cutoverJulianDay)) { */
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
547
            $this->fInvertGregorian = true;
548
            $jd = parent::handleComputeJulianDay($bestField);
549
        } else {
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

This check looks for the else branches of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These else branches can be removed.

if (rand(1, 6) > 3) {
print "Check failed";
} else {
    //print "Check succeeded";
}

could be turned into

if (rand(1, 6) > 3) {
    print "Check failed";
}

This is much more concise to read.

Loading history...
550
        }
551
552
        if ($this->fIsGregorian && ($this->internalGet(DateDefinitions::EXTENDED_YEAR) == $this->fGregorianCutoverYear)) {
553
            $gregShift = CalendarGrego::gregorianShift($this->internalGet(DateDefinitions::EXTENDED_YEAR));
554
            if ($bestField == DateDefinitions::DAY_OF_YEAR) {
555
                $jd -= $gregShift;
556
            } elseif ($bestField == DateDefinitions::WEEK_OF_MONTH) {
557
                $weekShift = 14;
558
                $jd += $weekShift; // shift by weeks for week based fields.
559
            }
560
        }
561
562
        return $jd;
563
    }
564
565
    /**
566
     * @see        AgaviCalendar::handleGetMonthLength
567
     *
568
     * @author     Dominik del Bondio <[email protected]>
569
     * @author     The ICU Project
570
     * @since      0.11.0
571
     */
572
    protected function handleGetMonthLength($extendedYear, $month)
573
    {
574
        // If the month is out of range, adjust it into range, and
575
        // modify the extended year value accordingly.
576
        if ($month < 0 || $month > 11) {
577
            $extendedYear += Toolkit::floorDivide($month, 12, $month);
578
        }
579
        
580
        return $this->isLeapYear($extendedYear) ? self::$kLeapMonthLength[$month] : self::$kMonthLength[$month];
581
    }
582
583
    /**
584
     * @see        AgaviCalendar::handleGetYearLength
585
     *
586
     * @author     Dominik del Bondio <[email protected]>
587
     * @author     The ICU Project
588
     * @since      0.11.0
589
     */
590
    protected function handleGetYearLength($eyear)
591
    {
592
        return $this->isLeapYear($eyear) ? 366 : 365;
593
    }
594
595
    /**
596
     * Return the length of the given month.
597
     *
598
     * @param      int $month The given month.
599
     *
600
     * @return     int The length of the given month.
601
     *
602
     * @author     Dominik del Bondio <[email protected]>
603
     * @author     The ICU Project
604
     * @since      0.11.0
605
     */
606
    protected function monthLength($month)
607
    {
608
        $year = $this->internalGet(DateDefinitions::EXTENDED_YEAR);
609
        return $this->handleGetMonthLength($year, $month);
610
    }
611
612
    /**
613
     * Return the length of the month according to the given year.
614
     *
615
     * @param      int $month The given month.
616
     * @param      int $year  The given year.
617
     *
618
     * @return     int The length of the month.
619
     *
620
     * @author     Dominik del Bondio <[email protected]>
621
     * @author     The ICU Project
622
     * @since      0.11.0
623
     */
624
    protected function monthLength1($month, $year)
625
    {
626
        return $this->isLeapYear($year) ? self::$kLeapMonthLength[$month] : self::$kMonthLength[$month];
627
    }
628
    
629
    /**
630
     * Return the length of the given year.
631
     *
632
     * @param      int $year The given year.
633
     * @return     int The length of the given year.
634
     *
635
     * @author     Dominik del Bondio <[email protected]>
636
     * @author     The ICU Project
637
     * @since      0.11.0
638
     */
639
    protected function yearLength1($year)
640
    {
641
        return $this->isLeapYear($year) ? 366 : 365;
642
    }
643
    
644
    /**
645
     * Return the length of the year field.
646
     *
647
     * @return     int The length of the year field
648
     *
649
     * @author     Dominik del Bondio <[email protected]>
650
     * @author     The ICU Project
651
     * @since      0.11.0
652
     */
653
    protected function yearLength()
654
    {
655
        return $this->isLeapYear($this->internalGet(DateDefinitions::YEAR)) ? 366 : 365;
656
    }
657
658
    /**
659
     * After adjustments such as add(MONTH), add(YEAR), we don't want the
660
     * month to jump around.  E.g., we don't want Jan 31 + 1 month to go to Mar
661
     * 3, we want it to go to Feb 28.  Adjustments which might run into this
662
     * problem call this method to retain the proper month.
663
     *
664
     * @author     Dominik del Bondio <[email protected]>
665
     * @author     The ICU Project
666
     * @since      0.11.0
667
     */
668
    protected function pinDayOfMonth()
669
    {
670
        $monthLen = $this->monthLength($this->internalGet(DateDefinitions::MONTH));
671
        $dom = $this->internalGet(DateDefinitions::DATE);
672
        if ($dom > $monthLen) {
673
            $this->set(DateDefinitions::DATE, $monthLen);
674
        }
675
    }
676
677
    /**
678
     * Return the day number with respect to the epoch.
679
     * January 1, 1970 (Gregorian) is day zero.
680
     *
681
     * @return     float the day number with respect to the epoch.
682
     *
683
     * @author     Dominik del Bondio <[email protected]>
684
     * @author     The ICU Project
685
     * @since      0.11.0
686
     */
687
    protected function getEpochDay()
688
    {
689
        $this->complete();
690
        // Divide by 1000 (convert to seconds) in order to prevent overflow when
691
        // dealing with UDate(Long.MIN_VALUE) and UDate(Long.MAX_VALUE).
692
        $wallSec = $this->internalGetTime() / 1000.0 + ($this->internalGet(DateDefinitions::ZONE_OFFSET) + $this->internalGet(DateDefinitions::DST_OFFSET)) / 1000.0;
693
694
        return floor($wallSec / (DateDefinitions::MILLIS_PER_DAY / 1000.0));
695
    }
696
697
    /**
698
     * @see        AgaviCalendar::handleGetLimit
699
     *
700
     * @author     Dominik del Bondio <[email protected]>
701
     * @author     The ICU Project
702
     * @since      0.11.0
703
     */
704
    protected function handleGetLimit($field, $limitType)
705
    {
706
        return self::$kGregorianCalendarLimits[$field][$limitType];
707
    }
708
709
    /**
710
     * @see        AgaviCalendar::handleGetExtendedYear
711
     *
712
     * @author     Dominik del Bondio <[email protected]>
713
     * @author     The ICU Project
714
     * @since      0.11.0
715
     */
716
    protected function handleGetExtendedYear()
717
    {
718
        $year = DateDefinitions::EPOCH_YEAR;
0 ignored issues
show
Unused Code introduced by
$year 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...
719
720
        switch ($this->resolveFields(self::$kYearPrecedence)) {
721
            case DateDefinitions::EXTENDED_YEAR:
722
                $year = $this->internalGet(DateDefinitions::EXTENDED_YEAR, DateDefinitions::EPOCH_YEAR);
723
                break;
724
725
            case DateDefinitions::YEAR:
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
726
                {
727
                    // The year defaults to the epoch start, the era to AD
728
                    $era = $this->internalGet(DateDefinitions::ERA, self::AD);
729
                if ($era == self::BC) {
730
                    $year = 1 - $this->internalGet(DateDefinitions::YEAR, 1); // Convert to extended year
731
                } else {
732
                    $year = $this->internalGet(DateDefinitions::YEAR, DateDefinitions::EPOCH_YEAR);
733
                }
734
            }
735
            break;
736
737
            case DateDefinitions::YEAR_WOY:
738
                $year = $this->handleGetExtendedYearFromWeekFields($this->internalGet(DateDefinitions::YEAR_WOY), $this->internalGet(DateDefinitions::WEEK_OF_YEAR));
739
                break;
740
741
            default:
742
                $year = DateDefinitions::EPOCH_YEAR;
743
        }
744
        return $year;
745
    }
746
747
    /**
748
     * @see        AgaviCalendar::handleGetExtendedYearFromWeekFields
749
     *
750
     * @author     Dominik del Bondio <[email protected]>
751
     * @author     The ICU Project
752
     * @since      0.11.0
753
     */
754
    protected function handleGetExtendedYearFromWeekFields($yearWoy, $woy)
755
    {
756
        // convert year to extended form
757
        $era = $this->internalGet(DateDefinitions::ERA, self::AD);
758
        if ($era == self::BC) {
759
            $yearWoy = 1 - $yearWoy;
760
        }
761
        return parent::handleGetExtendedYearFromWeekFields($yearWoy, $woy);
762
    }
763
764
    /**
765
     * @see        AgaviCalendar::handleComputeFields
766
     *
767
     * @author     Dominik del Bondio <[email protected]>
768
     * @author     The ICU Project
769
     * @since      0.11.0
770
     */
771
    protected function handleComputeFields($julianDay)
772
    {
773
        if ($julianDay >= $this->fCutoverJulianDay) {
774
            $month = $this->getGregorianMonth();
775
            $dayOfMonth = $this->getGregorianDayOfMonth();
776
            $dayOfYear = $this->getGregorianDayOfYear();
777
            $eyear = $this->getGregorianYear();
778
        } else {
779
            // The Julian epoch day (not the same as Julian Day)
780
            // is zero on Saturday December 30, 0 (Gregorian).
781
            $julianEpochDay = $julianDay - (DateDefinitions::JAN_1_1_JULIAN_DAY - 2);
782
            $eyear = (int) floor((4 * $julianEpochDay + 1464) / 1461);
783
784
            // Compute the Julian calendar day number for January 1, eyear
785
            $january1 = 365 * ($eyear - 1) + floor(($eyear -1 ) / 4);
786
            $dayOfYear = ($julianEpochDay - $january1); // 0-based
787
788
            // Julian leap years occurred historically every 4 years starting
789
            // with 8 AD.  Before 8 AD the spacing is irregular; every 3 years
790
            // from 45 BC to 9 BC, and then none until 8 AD.  However, we don't
791
            // implement this historical detail; instead, we implement the
792
            // computationally cleaner proleptic calendar, which assumes
793
            // consistent 4-year cycles throughout time.
794
            $isLeap = (($eyear & 0x3) == 0); // equiv. to (eyear%4 == 0)
795
796
            // Common Julian/Gregorian calculation
797
            $correction = 0;
798
            $march1 = $isLeap ? 60 : 59; // zero-based DOY for March 1
799
            if ($dayOfYear >= $march1) {
800
                $correction = $isLeap ? 1 : 2;
801
            }
802
            $month = (int) ((12 * ($dayOfYear + $correction) + 6) / 367); // zero-based month
803
            $dayOfMonth = $dayOfYear - ($isLeap ? self::$kLeapNumDays[$month] : self::$kNumDays[$month]) + 1; // one-based DOM
804
            ++$dayOfYear;
805
        }
806
807
        // [j81] if we are after the cutover in its year, shift the day of the year
808
        if (($eyear == $this->fGregorianCutoverYear) && ($julianDay >= $this->fCutoverJulianDay)) {
809
            //from handleComputeMonthStart
810
            $gregShift = CalendarGrego::gregorianShift($eyear);
811
            $dayOfYear += $gregShift;
812
        }
813
814
        $this->internalSet(DateDefinitions::MONTH, $month);
815
        $this->internalSet(DateDefinitions::DAY_OF_MONTH, $dayOfMonth);
816
        $this->internalSet(DateDefinitions::DAY_OF_YEAR, $dayOfYear);
817
        $this->internalSet(DateDefinitions::EXTENDED_YEAR, $eyear);
818
        $era = self::AD;
819
        if ($eyear < 1) {
820
            $era = self::BC;
821
            $eyear = 1 - $eyear;
822
        }
823
        $this->internalSet(DateDefinitions::ERA, $era);
824
        $this->internalSet(DateDefinitions::YEAR, $eyear);
825
    }
826
827
    /**
828
     * Compute the julian day number of the given year.
829
     *
830
     * @param      bool   $isGregorian If true, using Gregorian calendar, otherwise using
831
     *                    Julian calendar.
832
     * @param      int    $year The given year.
833
     * @param      bool   $isLeap True if the year is a leap year.
834
     *
835
     * @return     double
836
     *
837
     * @author     Dominik del Bondio <[email protected]>
838
     * @author     The ICU Project
839
     * @since      0.11.0
840
     */
841
    private static function computeJulianDayOfYear($isGregorian, $year, &$isLeap)
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
842
    {
843
        $isLeap = ($year % 4 == 0);
844
        $y = $year - 1;
845
        $julianDay = 365.0 * $y + floor($y / 4) + (DateDefinitions::JAN_1_1_JULIAN_DAY - 3);
846
847 View Code Duplication
        if ($isGregorian) {
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...
848
            $isLeap = $isLeap && (($year % 100 != 0) || ($year % 400 == 0));
849
            // Add 2 because Gregorian calendar starts 2 days after Julian calendar
850
            $julianDay += CalendarGrego::gregorianShift($year);
851
        }
852
853
        return $julianDay;
854
    }
855
    
856
    /**
857
     * Validates the values of the set time fields.  True if they're all valid.
858
     *
859
     * @return     bool True if the set time fields are all valid.
860
     *
861
     * @author     Dominik del Bondio <[email protected]>
862
     * @author     The ICU Project
863
     * @since      0.11.0
864
     */
865
    private function validateFields()
0 ignored issues
show
Bug introduced by
Consider using a different method name as you override a private method of the parent class.

Overwriting private methods is generally fine as long as you also use private visibility. It might still be preferable for understandability to use a different method name.

Loading history...
866
    {
867
        for ($field = 0; $field < DateDefinitions::FIELD_COUNT; ++$field) {
868
            // Ignore DATE and DAY_OF_YEAR which are handled below
869
            if ($field != DateDefinitions::DATE &&
870
                    $field != DateDefinitions::DAY_OF_YEAR &&
871
                    $this->_isSet($field) &&
872
                    ! $this->boundsCheck($this->internalGet($field), $field)) {
873
                return false;
874
            }
875
        }
876
877
        // Values differ in Least-Maximum and Maximum should be handled
878
        // specially.
879
        if ($this->_isSet(DateDefinitions::DATE)) {
880
            $date = $this->internalGet(DateDefinitions::DATE);
881
            if ($date < $this->getMinimum(DateDefinitions::DATE) ||
882
                    $date > $this->monthLength($this->internalGet(DateDefinitions::MONTH))) {
883
                return false;
884
            }
885
        }
886
887
        if ($this->_isSet(DateDefinitions::DAY_OF_YEAR)) {
888
            $days = $this->internalGet(DateDefinitions::DAY_OF_YEAR);
889
            if ($days < 1 || $days > $this->yearLength()) {
890
                return false;
891
            }
892
        }
893
894
        // Handle DAY_OF_WEEK_IN_MONTH, which must not have the value zero.
895
        // We've checked against minimum and maximum above already.
896
        if ($this->_isSet(DateDefinitions::DAY_OF_WEEK_IN_MONTH) &&
897
            0 == $this->internalGet(DateDefinitions::DAY_OF_WEEK_IN_MONTH)) {
898
            return false;
899
        }
900
901
        return true;
902
    }
903
904
    /**
905
     * Validates the value of the given time field.  True if it's valid.
906
     *
907
     * @param      int $value
908
     * @param      int $field
909
     *
910
     * @return     bool
911
     *
912
     * @author     Dominik del Bondio <[email protected]>
913
     * @author     The ICU Project
914
     * @since      0.11.0
915
     */
916
    private function boundsCheck($value, $field)
917
    {
918
        return $value >= $this->getMinimum($field) && $value <= $this->getMaximum($field);
919
    }
920
921
    /**
922
     * Return the pseudo-time-stamp for two fields, given their
923
     * individual pseudo-time-stamps.  If either of the fields
924
     * is unset, then the aggregate is unset.  Otherwise, the
925
     * aggregate is the later of the two stamps.
926
     *
927
     * @param      int $stamp_a One given field.
928
     * @param      int $stamp_b Another given field.
929
     * @return     int The pseudo-time-stamp for two fields.
930
     *
931
     * @author     Dominik del Bondio <[email protected]>
932
     * @author     The ICU Project
933
     * @since      0.11.0
934
     */
935
    private function aggregateStamp($stamp_a, $stamp_b)
936
    {
937
        return ((($stamp_a != Calendar::kUnset && $stamp_b != Calendar::kUnset)
938
            ? max($stamp_a, $stamp_b)
939
            : Calendar::kUnset
940
        ));
941
    }
942
943
    /**
944
     * @var        float The point at which the Gregorian calendar rules are used,
945
     *                   measured in milliseconds from the standard epoch. Default
946
     *                   is October 15, 1582 (Gregorian) 00:00:00 UTC, that is,
947
     *                   October 4, 1582 (Julian) is followed by October 15, 1582
948
     *                   (Gregorian).  This corresponds to Julian day number
949
     *                   2299161. This is measured from the standard epoch, not in
950
     *                   Julian Days.
951
     */
952
    private $fGregorianCutover;
953
954
    /**
955
     * @var        int Julian day number of the Gregorian cutover
956
     */
957
    private $fCutoverJulianDay;
958
959
    /**
960
     * @var        float Midnight, local time (using this Calendar's TimeZone)
961
     *                   at or before the gregorianCutover. This is a pure
962
     *                   date value with no time of day or timezone component.
963
     */
964
    private $fNormalizedGregorianCutover;// = gregorianCutover;
965
966
    /**
967
     * @var        int The year of the gregorianCutover, with 0 representing
968
     *                 1 BC, -1 representing 2 BC, etc.
969
     */
970
    private $fGregorianCutoverYear;// = 1582;
971
972
    /**
973
     * @var        int The year of the gregorianCutover, with 0 representing
974
     *                 1 BC, -1 representing 2 BC, etc.
975
     */
976
    private $fGregorianCutoverJulianDay;// = 2299161;
0 ignored issues
show
Unused Code introduced by
The property $fGregorianCutoverJulianDay is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
977
978
    /**
979
     * Converts time as milliseconds to Julian date. The Julian date used here is
980
     * not a true Julian date, since it is measured from midnight, not noon.
981
     *
982
     * @param      float $millis The given milliseconds.
983
     *
984
     * @return     float The Julian date number.
985
     *
986
     * @author     Dominik del Bondio <[email protected]>
987
     * @author     The ICU Project
988
     * @since      0.11.0
989
     */
990
    private static function millisToJulianDay($millis)
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
991
    {
992
        return DateDefinitions::EPOCH_START_AS_JULIAN_DAY + floor($millis / DateDefinitions::MILLIS_PER_DAY);
993
    }
994
995
    /**
996
     * Converts Julian date to time as milliseconds. The Julian date used here is
997
     * not a true Julian date, since it is measured from midnight, not noon.
998
     *
999
     * @param      float $julian The given Julian date number.
1000
     *
1001
     * @return     float Time as milliseconds.
1002
     *
1003
     * @author     Dominik del Bondio <[email protected]>
1004
     * @author     The ICU Project
1005
     * @since      0.11.0
1006
     */
1007
    private static function julianDayToMillis($julian)
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
1008
    {
1009
        return (($julian - DateDefinitions::EPOCH_START_AS_JULIAN_DAY) * DateDefinitions::MILLIS_PER_DAY);
1010
    }
1011
1012
    /**
1013
     * @var        bool Used by handleComputeJulianDay() and
1014
     *                  handleComputeMonthStart(). Temporary field indicating
1015
     *                  whether the calendar is currently Gregorian as opposed to
1016
     *                  Julian.
1017
     */
1018
    private $fIsGregorian;
1019
1020
    /**
1021
     * @var        bool Used by handleComputeJulianDay() and
1022
     *                  handleComputeMonthStart(). Temporary field indicating that
1023
     *                  the sense of the gregorian cutover should be inverted to
1024
     *                  handle certain calculations on and around the cutover
1025
     *                  date.
1026
     */
1027
    private $fInvertGregorian;
1028
1029
    /**
1030
     * @see        AgaviCalendar::haveDefaultCentury
1031
     *
1032
     * @author     Dominik del Bondio <[email protected]>
1033
     * @author     The ICU Project
1034
     * @since      0.11.0
1035
     */
1036
    public function haveDefaultCentury()
1037
    {
1038
        return true;
1039
    }
1040
1041
    /**
1042
     * @see        AgaviCalendar::defaultCenturyStart
1043
     *
1044
     * @author     Dominik del Bondio <[email protected]>
1045
     * @author     The ICU Project
1046
     * @since      0.11.0
1047
     */
1048
    public function defaultCenturyStart()
1049
    {
1050
        return $this->internalGetDefaultCenturyStart();
1051
    }
1052
1053
    /**
1054
     * @see        AgaviCalendar::defaultCenturyStartYear
1055
     *
1056
     * @author     Dominik del Bondio <[email protected]>
1057
     * @author     The ICU Project
1058
     * @since      0.11.0
1059
     */
1060
    public function defaultCenturyStartYear()
1061
    {
1062
        return $this->internalGetDefaultCenturyStartYear();
1063
    }
1064
1065
    /**
1066
     * @see        AgaviCalendar::getType
1067
     *
1068
     * @author     Dominik del Bondio <[email protected]>
1069
     * @since      0.11.0
1070
     */
1071
    public function getType()
1072
    {
1073
        return Calendar::GREGORIAN;
1074
    }
1075
1076
    /**
1077
     * @var        float The system maintains a static default century start date.
1078
     *                   This is initialized the first time it is used.
1079
     *                   Before then, it is set to SYSTEM_DEFAULT_CENTURY to
1080
     *                   indicate an uninitialized state.  Once the system default
1081
     *                   century date and year are set, they do not change.
1082
     */
1083
    private static $fgSystemDefaultCenturyStart;
1084
1085
    /**
1086
     * @var        int See documentation for systemDefaultCenturyStart.
1087
     */
1088
    private static $fgSystemDefaultCenturyStartYear = -1;
1089
1090
    /**
1091
     * @var        int Default value that indicates the defaultCenturyStartYear is
1092
     *                 uninitialized
1093
     */
1094
    private static $fgSystemDefaultCenturyYear = -1;
0 ignored issues
show
Unused Code introduced by
The property $fgSystemDefaultCenturyYear is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
1095
1096
    /**
1097
     * @var        float Default value that indicates the UDate of the beginning
1098
     *                   of the system default century
1099
     */
1100
    private static $fgSystemDefaultCentury;
1101
1102
    /**
1103
     * Returns the beginning date of the 100-year window that dates with 2-digit
1104
     * years are considered to fall within.
1105
     *
1106
     * @return     float The beginning date of the 100-year window that dates
1107
     *                   with 2-digit years are considered to fall within.
1108
     *
1109
     * @author     Dominik del Bondio <[email protected]>
1110
     * @author     The ICU Project
1111
     * @since      0.11.0
1112
     */
1113
    private function internalGetDefaultCenturyStart()
1114
    {
1115
        // lazy-evaluate systemDefaultCenturyStart
1116
        $needsUpdate = (self::$fgSystemDefaultCenturyStart == self::$fgSystemDefaultCentury);
1117
1118
        if ($needsUpdate) {
1119
            $this->initializeSystemDefaultCentury();
1120
        }
1121
1122
        // use defaultCenturyStart unless it's the flag value;
1123
        // then use systemDefaultCenturyStart
1124
1125
        return self::$fgSystemDefaultCenturyStart;
1126
    }
1127
1128
    /**
1129
     * Returns the first year of the 100-year window that dates with 2-digit years
1130
     * are considered to fall within.
1131
     *
1132
     * @return     int The first year of the 100-year window that dates with
1133
     *                 2-digit years are considered to fall within.
1134
     *
1135
     * @author     Dominik del Bondio <[email protected]>
1136
     * @author     The ICU Project
1137
     * @since      0.11.0
1138
     */
1139
    private function internalGetDefaultCenturyStartYear()
1140
    {
1141
        // lazy-evaluate systemDefaultCenturyStartYear
1142
        $needsUpdate = (self::$fgSystemDefaultCenturyStart == self::$fgSystemDefaultCentury);
1143
1144
        if ($needsUpdate) {
1145
            $this->initializeSystemDefaultCentury();
1146
        }
1147
1148
        // use defaultCenturyStart unless it's the flag value;
1149
        // then use systemDefaultCenturyStartYear
1150
1151
        return self::$fgSystemDefaultCenturyStartYear;
1152
    }
1153
1154
    /**
1155
     * Initializes the 100-year window that dates with 2-digit years are
1156
     * considered to fall within so that its start date is 80 years before the
1157
     * current time.
1158
     *
1159
     * @author     Dominik del Bondio <[email protected]>
1160
     * @author     The ICU Project
1161
     * @since      0.11.0
1162
     */
1163
    private static function initializeSystemDefaultCentury()
1164
    {
1165
        // initialize systemDefaultCentury and systemDefaultCenturyYear based
1166
        // on the current time.  They'll be set to 80 years before
1167
        // the current time.
1168
        // No point in locking as it should be idempotent.
1169
        if (self::$fgSystemDefaultCenturyStart == self::$fgSystemDefaultCentury) {
1170
            $calendar = new GregorianCalendar();
1171
            $calendar->setTime(Calendar::getNow());
1172
            $calendar->add(DateDefinitions::YEAR, -80);
1173
1174
            $newStart = $calendar->getTime();
1175
            $newYear  = $calendar->get(DateDefinitions::YEAR);
1176
            self::$fgSystemDefaultCenturyStart = $newStart;
1177
            self::$fgSystemDefaultCenturyStartYear = $newYear;
1178
        }
1179
    }
1180
1181
    protected static $kNumDays         = array(0,  31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334); // 0-based, for day-in-year
1182
    protected static $kLeapNumDays     = array(0,  31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335); // 0-based, for day-in-year
1183
    protected static $kMonthLength     = array(31, 28, 31, 30,  31,  30,  31,  31,  30,  31,  30,  31); // 0-based
1184
    protected static $kLeapMonthLength = array(31, 29, 31, 30,  31,  30,  31,  31,  30,  31,  30,  31); // 0-based
1185
    protected static $kGregorianCalendarLimits = array(
1186
        //     Minimum  Greatest   Least      Maximum
1187
        //                Minimum   Maximum
1188
        array(        0,        0,        1,        1 ), // ERA
1189
        array(        1,        1,   140742,   144683 ), // YEAR
1190
        array(        0,        0,       11,       11 ), // MONTH
1191
        array(        1,        1,       52,       53 ), // WEEK_OF_YEAR
1192
        array(        0,        0,        4,        6 ), // WEEK_OF_MONTH
1193
        array(        1,        1,       28,       31 ), // DAY_OF_MONTH
1194
        array(        1,        1,      365,      366 ), // DAY_OF_YEAR
1195
        array(/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1 ), // DAY_OF_WEEK
1196
        array(       -1,       -1,        4,        5 ), // DAY_OF_WEEK_IN_MONTH
1197
        array(/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1 ), // AM_PM
1198
        array(/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1 ), // HOUR
1199
        array(/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1 ), // HOUR_OF_DAY
1200
        array(/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1 ), // MINUTE
1201
        array(/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1 ), // SECOND
1202
        array(/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1 ), // MILLISECOND
1203
        array(/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1 ), // ZONE_OFFSET
1204
        array(/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1 ), // DST_OFFSET
1205
        array(  -140742,  -140742,   140742,   144683 ), // YEAR_WOY
1206
        array(/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1 ), // DOW_LOCAL
1207
        array(  -140742,  -140742,   140742,   144683 ), // EXTENDED_YEAR
1208
        array(/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1 ), // JULIAN_DAY
1209
        array(/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1 ), // MILLISECONDS_IN_DAY
1210
    );
1211
}
1212