Completed
Push — master ( 6ca4bd...61c879 )
by mw
06:26
created

IcalTimezoneFormatter::setLocalTimezones()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4.016

Importance

Changes 0
Metric Value
dl 0
loc 14
ccs 9
cts 10
cp 0.9
rs 9.2
c 0
b 0
f 0
cc 4
eloc 10
nc 4
nop 1
crap 4.016
1
<?php
2
3
namespace SRF\iCalendar;
4
5
use DateTimeZone;
6
use Exception;
7
8
/**
9
 * Create the iCalendar's vTimezone component
10
 *
11
 * @license GNU GPL v2+
12
 * @since 3.0
13
 *
14
 * @author HgO
15
 */
16
class IcalTimezoneFormatter {
17
18
	/**
19
	 * @var array
20
	 */
21
	private $localTimezones = [];
22
23
	/**
24
	 * @var array
25
	 */
26
	private $transitions = [];
27
28
	/**
29
	 * @var array
30
	 */
31
	private $offsets = [];
32
33
	/**
34
	 * @since 3.0
35
	 */
36 5
	public function __construct() {
0 ignored issues
show
Coding Style introduced by
__construct uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
37 5
		$this->localTimezones = [ $GLOBALS['wgLocaltimezone'] ];
38 5
	}
39
40
	/**
41
	 * Set a list of local timezones.
42
	 *
43
	 * @since 3.0
44
	 *
45
	 * @param array|string $localTimezones
46
	 */
47 4
	public function setLocalTimezones( $localTimezones ) {
48
49 4
		if ( is_array( $localTimezones ) ) {
50
			$localTimezones = $localTimezones;
0 ignored issues
show
Bug introduced by
Why assign $localTimezones to itself?

This checks looks for cases where a variable has been assigned to itself.

This assignement can be removed without consequences.

Loading history...
51 4
		} elseif ( strpos( $localTimezones, ',' ) !== false ) {
52 1
			$localTimezones = explode( ',', $localTimezones );
53 4
		} elseif ( $localTimezones !== '' ) {
54 2
			$localTimezones = [ $localTimezones ];
55
		} else {
56 2
			$localTimezones = [];
57
		}
58
59 4
		$this->localTimezones = $localTimezones;
60 4
	}
61
62
	/**
63
	 * Calculate transitions for each timezone.
64
	 *
65
	 * @since 3.0
66
	 *
67
	 * @param integer $from Timestamp from which transitions are generated.
68
	 * @param integer $to Timestamp until which transitions are generated.
69
	 *
70
	 * @return boolean
71
	 */
72 4
	public function calcTransitions( $from = null, $to = null ) {
73
74 4
		if ( $this->localTimezones === [] ) {
75 2
			return false;
76
		}
77
78 2
		if ( $from === null || $to === null ){
79
			return false;
80
		}
81
82 2
		foreach ( $this->localTimezones as $timezone ) {
83
			try {
84 2
				$dateTimezone = new DateTimeZone( $timezone );
85 1
			} catch( Exception $e ) {
86 1
				continue;
87
			}
88
89 2
			$transitions = $dateTimezone->getTransitions();
90
91 2
			if ( $transitions === false ) {
92
				continue;
93
			}
94
95 2
			$min = 0;
96 2
			$max = 1;
97
98 2
			foreach ( $transitions as $i => $transition ) {
99 2
				if ( $transition['ts'] < $from ) {
100 2
					$min = $i;
101 2
					continue;
102
				}
103
104 1
				if ( $transition['ts'] > $to ) {
105 1
					$max = $i;
106 1
					break;
107
				}
108
			}
109
110 2
			$this->offsets[$timezone] = $transitions[max( $min-1, 0 )]['offset'];
111 2
			$this->transitions[$timezone] = array_slice( $transitions, $min, $max - $min );
112
		}
113
114 2
		return true;
115
	}
116
117
	/**
118
	 * Generate the transitions for a given range, for each timezones, in the
119
	 * iCalendar format.
120
	 *
121
	 * @since 3.0
122
	 *
123
	 * @return string
124
	 */
125 4
	public function getTransitions() {
126
127 4
		$result = '';
128
129 4
		if ( $this->transitions === null || $this->transitions === [] ) {
130 2
			return $result;
131
		}
132
133 2
		foreach ( $this->transitions as $timezone => $transitions ) {
134
			// cf. http://www.kanzaki.com/docs/ical/vtimezone.html
135 2
			$result .= "BEGIN:VTIMEZONE\r\n";
136 2
			$result .= "TZID:$timezone\r\n";
137
138 2
			$tzfrom = $this->offsets[$timezone] / 3600;
139 2
			foreach ( $transitions as $transition ) {
140 2
				$dst = ( $transition['isdst'] ) ? "DAYLIGHT" : "STANDARD";
141 2
				$result .= "BEGIN:$dst\r\n";
142
143 2
				$start_date = date( 'Ymd\THis', $transition['ts'] );
144 2
				$result .= "DTSTART:$start_date\r\n";
145
146 2
				$offset = $transition['offset'] / 3600;
147
148 2
				$offset_from = $this->formatTimezoneOffset( $tzfrom );
149 2
				$result .= "TZOFFSETFROM:$offset_from\r\n";
150
151 2
				$offset_to = $this->formatTimezoneOffset( $offset );
152 2
				$result .= "TZOFFSETTO:$offset_to\r\n";
153
154 2
				if ( !empty( $transition['abbr'] ) )
155 2
					$result .= "TZNAME:{$transition['abbr']}\r\n";
156
157 2
				$result .= "END:$dst\r\n";
158
159 2
				$tzfrom = $offset;
160
			}
161
162 2
			$result .= "END:VTIMEZONE\r\n";
163
		}
164
165
		// Clear the calculation
166 2
		$this->transitions = [];
167
168 2
		return $result;
169
	}
170
171
	/**
172
	 * Format an integer offset to '+hhii', where hh are the hours, and ii the
173
	 * minutes
174
	 *
175
	 * @param int $offset
176
	 */
177 2
	private function formatTimezoneOffset( $offset ) {
178 2
		return sprintf('%s%02d%02d', $offset >= 0 ? '+' : '', floor($offset), ($offset - floor($offset)) * 60);
179
	}
180
181
}
182