Time::convertTimeToMicroseconds()   C
last analyzed

Complexity

Conditions 7
Paths 6

Size

Total Lines 32
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 14
nc 6
nop 1
dl 0
loc 32
rs 6.7272
c 0
b 0
f 0
1
<?php
2
3
namespace Popy\Calendar\Converter\UnixTimeConverter;
4
5
use Popy\Calendar\ValueObject\Time as TimeObject;
6
use Popy\Calendar\Converter\Conversion;
7
use Popy\Calendar\Converter\UnixTimeConverterInterface;
8
use Popy\Calendar\ValueObject\DateTimeRepresentationInterface;
9
10
/**
11
 * Time of the day representation.
12
 *
13
 * Fragment units are, usually, named as, and used as :
14
 * 0 => hours (format symbols ghGH)
15
 * 1 => minutes (format symbol i)
16
 * 2 => seconds (format symbol s)
17
 * 3 => milliseconds (format symbol v)
18
 * 4 => microseconds (format symbol µ)
19
 *
20
 * Transversal units are, usually, named as and used as :
21
 * 0 => Internet swatch time (format symbol B).
22
 */
23
class Time implements UnixTimeConverterInterface
24
{
25
    /**
26
     * Day length in seconds.
27
     *
28
     * @var integer
29
     */
30
    protected $dayLengthInSeconds = 24 * 3600;
31
32
    /**
33
     * Time format ranges.
34
     *
35
     * @var array<int>
36
     */
37
    protected $ranges = [24, 60, 60, 1000, 1000];
38
39
    /**
40
     * Day length in microseconds.
41
     *
42
     * @var integer
43
     */
44
    protected $dayLengthInMicroSeconds;
45
46
    /**
47
     * Class constructor.
48
     *
49
     * @param array|null   $ranges             Time segments ranges/sizes.
50
     * @param integer|null $dayLengthInSeconds Day length.
51
     */
52
    public function __construct(array $ranges = null, $dayLengthInSeconds = null)
53
    {
54
        if (null !== $ranges) {
55
            $this->ranges = $ranges;
56
        }
57
58
        if (null !== $dayLengthInSeconds) {
59
            $this->dayLengthInSeconds = $dayLengthInSeconds;
60
        }
61
62
        $this->dayLengthInMicroSeconds = $this->dayLengthInSeconds * 1000000;
63
    }
64
65
66
    /**
67
     * @inheritDoc
68
     */
69
    public function fromUnixTime(Conversion $conversion)
70
    {
71
        $input = $conversion->getTo();
72
73
        if (!$input instanceof DateTimeRepresentationInterface) {
74
            return;
75
        }
76
77
        $unixTime = $conversion->getUnixTime();
78
        $microSec = $conversion->getUnixMicroTime()
79
            + ($unixTime % $this->dayLengthInSeconds) * 1000000
80
        ;
81
82
        $time = $this->convertMicrosecondsToTime($microSec);
83
84
        // Swatch time calculation. It's a fixed earth-day ratio, starting at a
85
        // fixed +01:00 time offset.
86
        $time = $time->withTransversal(
87
            0,
88
            intval(1000 * ($input->getUnixTime() + 3600) / 86400) % 1000
89
        );
90
91
        // Removing the consumed seconds
92
        $unixTime -= $unixTime % $this->dayLengthInSeconds;
93
94
        $conversion
95
            ->setUnixTime($unixTime)
96
            ->setUnixMicroTime(0)
97
            ->setTo($input->withTime($time))
98
        ;
99
    }
100
101
    /**
102
     * @inheritDoc
103
     */
104
    public function toUnixTime(Conversion $conversion)
105
    {
106
        $input = $conversion->getTo();
107
108
        if (!$input instanceof DateTimeRepresentationInterface) {
109
            return;
110
        }
111
112
        $time = $input->getTime()->withSizes($this->ranges);
113
114
        $microsec = $this->convertTimeToMicroseconds($time);
115
116
        $conversion
117
            ->setTo($input->withTime($time))
118
            ->setUnixTime($conversion->getUnixTime() + intval($microsec / 1000000))
119
            // Swatch time can return negative time. Shield against that.
120
            ->setUnixMicroTime(max(0, $microsec % 1000000))
121
        ;
122
    }
123
124
    /**
125
     * Convert a time expressed in microseconds, into a Time object.
126
     *
127
     * @param integer $time
128
     *
129
     * @return TimeObject
130
     */
131
    public function convertMicrosecondsToTime($time)
132
    {
133
        // Calculating day-ratio;
134
        // Dividing microseconds per seconds results in the intended value
135
        $ratio = intval($time / $this->dayLengthInSeconds);
136
137
        // If using a different time format, apply ratio
138
        $timeScale = array_product($this->ranges);
139
140
        if ($this->dayLengthInMicroSeconds !== $timeScale) {
141
            $time = intval(
142
                ($time * $timeScale) / $this->dayLengthInMicroSeconds
143
            );
144
        }
145
146
        $len = count($this->ranges);
147
        $res = array_fill(0, $len, 0);
148
149
        for ($i=$len - 1; $i > -1; $i--) {
150
            $res[$i] = $time % $this->ranges[$i];
151
            $time = intval($time / $this->ranges[$i]);
152
        }
153
154
        return new TimeObject($res, $this->ranges, $ratio);
155
    }
156
157
    /**
158
     * Converts a "Time" into its value in its lower unit.
159
     *
160
     * @param TimeObject $time
161
     *
162
     * @return integer
163
     */
164
    public function convertTimeToMicroseconds(TimeObject $time)
165
    {
166
        $meaningfull = $time->countMeaningfull() > 0;
167
168
        // IF time had no meaningfull informations, fallback to a day ratio.
169
        if (!$meaningfull && null !== $ratio = $time->getRatio()) {
170
            return $ratio * $this->dayLengthInSeconds;
171
        }
172
173
        // IF time had no meaningfull informations, use internel swatch time.
174
        // May not work properly on non-earth systems. Whatever, who uses this
175
        // anyway ? ANd who will use this on other planets ?
176
        if (!$meaningfull && null !== $swatch = $time->getTransversal(0)) {
177
            return $swatch * 1000 * 86400
178
                - 3600 * 1000000 // minus fixed time offset
179
            ;
180
        }
181
182
        $len = count($this->ranges);
183
        $res = 0;
184
   
185
        for ($i=0; $i < $len; $i++) {
186
            $res = $res * $this->ranges[$i] + (int)$time->get($i);
187
        }
188
189
        // If using a different time format, apply ratio
190
        $timeScale = array_product($this->ranges);
191
        if ($this->dayLengthInMicroSeconds !== $timeScale) {
192
            return intval(($res * $this->dayLengthInMicroSeconds) / $timeScale);
193
        }
194
195
        return $res;
196
    }
197
}
198