Issues (13)

src/Converter/UnixTimeConverter/TimeOffset.php (2 issues)

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
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...
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
The condition false === $offsets can never be true.
Loading history...
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