1 | <?php |
||
2 | |||
3 | namespace Popy\Calendar\Converter\UnixTimeConverter; |
||
4 | |||
5 | use DateTimeZone; |
||
6 | use DateTimeImmutable; |
||
7 | use Popy\Calendar\Converter\Conversion; |
||
8 | use Popy\Calendar\ValueObject\TimeOffset as TimeOffsetValue; |
||
9 | use Popy\Calendar\Converter\UnixTimeConverterInterface; |
||
10 | use Popy\Calendar\ValueObject\DateRepresentationInterface; |
||
11 | |||
12 | /** |
||
13 | * Determines dates offset and applies it to unixTime. |
||
14 | */ |
||
15 | class TimeOffset implements UnixTimeConverterInterface |
||
16 | { |
||
17 | /** |
||
18 | * Day length in seconds. |
||
19 | * |
||
20 | * @var integer |
||
21 | */ |
||
22 | protected $dayLengthInSeconds = 24 * 3600; |
||
23 | |||
24 | /** |
||
25 | * @inheritDoc |
||
26 | */ |
||
27 | public function fromUnixTime(Conversion $conversion) |
||
28 | { |
||
29 | $offset = $conversion->getFrom()->getOffset(); |
||
30 | |||
31 | $conversion->setUnixTime( |
||
32 | $conversion->getUnixTime() + $offset->getValue() |
||
33 | ); |
||
34 | |||
35 | $conversion->setTo( |
||
36 | $conversion->getTo()->withOffset($offset) |
||
37 | ); |
||
38 | } |
||
39 | |||
40 | /** |
||
41 | * @inheritDoc |
||
42 | */ |
||
43 | public function toUnixTime(Conversion $conversion) |
||
44 | { |
||
45 | $input = $conversion->getTo() ?: $conversion->getFrom(); |
||
46 | |||
47 | $unixTime = $conversion->getUnixTime(); |
||
48 | |||
49 | $input = $this->getOffsetFor($input, $unixTime); |
||
50 | |||
51 | $conversion |
||
52 | ->setUnixTime($unixTime - $input->getOffset()->getValue()) |
||
53 | ->setTo($input) |
||
54 | ; |
||
55 | } |
||
56 | |||
57 | /** |
||
58 | * Search for the offset that have (or might) have been used for the input |
||
59 | * date representation, and updates input date. |
||
60 | * |
||
61 | * @param DateRepresentationInterface $input |
||
62 | * @param integer $timestamp Calculated offsetted timestamp |
||
63 | * |
||
64 | * @return DateRepresentationInterface |
||
65 | */ |
||
66 | protected function getOffsetFor(DateRepresentationInterface $input, $timestamp) |
||
67 | { |
||
68 | $input = $this->extractAbbreviation($input); |
||
69 | $offset = $input->getOffset(); |
||
70 | |||
71 | if (null !== $offset->getValue()) { |
||
72 | return $input; |
||
73 | } |
||
74 | |||
75 | // Looking for timezone offset matching the incomplete timestamp. |
||
76 | // The LMT transition is skipped to mirror the behaviour of |
||
77 | // DateTimeZone->getOffset() |
||
0 ignored issues
–
show
|
|||
78 | $previous = null; |
||
79 | |||
80 | $offsets = $input->getTimezone()->getTransitions( |
||
81 | $timestamp - $this->dayLengthInSeconds, |
||
82 | // Usually, $timestamp + $this->dayLengthInSeconds should be enougth, |
||
83 | // but for dates before 1900-01-01 timezones fallback to LMT that |
||
84 | // we are trying to skip. |
||
85 | max(0, $timestamp + $this->dayLengthInSeconds) |
||
86 | ); |
||
87 | |||
88 | // DateTimeZone can return a false $offsets value for unsupported ranges. |
||
89 | if (false === $offsets) { |
||
0 ignored issues
–
show
|
|||
90 | return $input->withOffset( |
||
91 | $offset |
||
92 | ->withValue( |
||
93 | $input->getTimezone()->getOffset( |
||
94 | new DateTimeImmutable() |
||
95 | ) |
||
96 | ) |
||
97 | ) |
||
98 | ; |
||
99 | } |
||
100 | |||
101 | foreach ($offsets as $info) { |
||
102 | if ( |
||
103 | (!$previous || $previous['abbr'] !== 'LMT') |
||
104 | && $timestamp - $info['offset'] < $info['ts'] |
||
105 | ) { |
||
106 | break; |
||
107 | } |
||
108 | |||
109 | $previous = $info; |
||
110 | } |
||
111 | |||
112 | if ($previous === null) { |
||
113 | return $input; |
||
114 | } |
||
115 | |||
116 | return $input->withOffset(new TimeOffsetValue( |
||
117 | $previous['offset'], |
||
118 | $previous['isdst'], |
||
119 | $previous['abbr'] |
||
120 | )); |
||
121 | } |
||
122 | |||
123 | /** |
||
124 | * Extract informations from timezone abbreviation. |
||
125 | * |
||
126 | * @param DateRepresentationInterface $input |
||
127 | * |
||
128 | * @return DateRepresentationInterface |
||
129 | */ |
||
130 | protected function extractAbbreviation(DateRepresentationInterface $input) |
||
131 | { |
||
132 | $offset = $input->getOffset(); |
||
133 | |||
134 | if (null === $abbr = $offset->getAbbreviation()) { |
||
135 | return $input; |
||
136 | } |
||
137 | |||
138 | $abbr = strtolower($abbr); |
||
139 | $list = DateTimeZone::listAbbreviations(); |
||
140 | |||
141 | if (!isset($list[$abbr]) || empty($list[$abbr])) { |
||
142 | return $input; |
||
143 | } |
||
144 | |||
145 | $list = $list[$abbr]; |
||
146 | |||
147 | $criterias = [ |
||
148 | 'offset' => $offset->getValue(), |
||
149 | 'timezone_id' => $input->getTimezone()->getName(), |
||
150 | 'dst' => $offset->isDst(), |
||
151 | ]; |
||
152 | |||
153 | foreach ($criterias as $key => $value) { |
||
154 | if (null === $value) { |
||
155 | continue; |
||
156 | } |
||
157 | $previous = $list; |
||
158 | |||
159 | $list = array_filter($list, function ($infos) use ($key, $value) { |
||
160 | return $value === $infos[$key]; |
||
161 | }); |
||
162 | |||
163 | if (empty($list)) { |
||
164 | $list = $previous; |
||
165 | } |
||
166 | } |
||
167 | |||
168 | $infos = reset($list); |
||
169 | |||
170 | if (null === $offset->getValue()) { |
||
171 | $offset = $offset->withValue($infos['offset']); |
||
172 | } |
||
173 | |||
174 | return $input |
||
175 | ->withOffset($offset->withDst($infos['dst'])) |
||
176 | ->withTimezone(new DateTimeZone($infos['timezone_id'])) |
||
177 | ; |
||
178 | } |
||
179 | } |
||
180 |
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.