1
|
|
|
<?php |
2
|
|
|
namespace Amenadiel\JpGraph\Util; |
3
|
|
|
|
4
|
|
|
//============================================================================= |
5
|
|
|
// CLASS DateScaleUtils |
6
|
|
|
// Description: Help to create a manual date scale |
7
|
|
|
//============================================================================= |
8
|
|
|
define('DSUTILS_MONTH', 1); // Major and minor ticks on a monthly basis |
9
|
|
|
define('DSUTILS_MONTH1', 1); // Major and minor ticks on a monthly basis |
10
|
|
|
define('DSUTILS_MONTH2', 2); // Major ticks on a bi-monthly basis |
11
|
|
|
define('DSUTILS_MONTH3', 3); // Major icks on a tri-monthly basis |
12
|
|
|
define('DSUTILS_MONTH6', 4); // Major on a six-monthly basis |
13
|
|
|
define('DSUTILS_WEEK1', 5); // Major ticks on a weekly basis |
14
|
|
|
define('DSUTILS_WEEK2', 6); // Major ticks on a bi-weekly basis |
15
|
|
|
define('DSUTILS_WEEK4', 7); // Major ticks on a quod-weekly basis |
16
|
|
|
define('DSUTILS_DAY1', 8); // Major ticks on a daily basis |
17
|
|
|
define('DSUTILS_DAY2', 9); // Major ticks on a bi-daily basis |
18
|
|
|
define('DSUTILS_DAY4', 10); // Major ticks on a qoud-daily basis |
19
|
|
|
define('DSUTILS_YEAR1', 11); // Major ticks on a yearly basis |
20
|
|
|
define('DSUTILS_YEAR2', 12); // Major ticks on a bi-yearly basis |
21
|
|
|
define('DSUTILS_YEAR5', 13); // Major ticks on a five-yearly basis |
22
|
|
|
|
23
|
|
|
class DateScaleUtils |
24
|
|
|
{ |
25
|
|
|
public static $iMin = 0; |
26
|
|
|
public static $iMax = 0; |
27
|
|
|
|
28
|
|
|
private static $starthour; |
29
|
|
|
private static $startmonth; |
30
|
|
|
private static $startday; |
31
|
|
|
private static $startyear; |
32
|
|
|
private static $endmonth; |
33
|
|
|
private static $endyear; |
34
|
|
|
private static $endday; |
35
|
|
|
private static $tickPositions = array(); |
36
|
|
|
private static $minTickPositions = array(); |
37
|
|
|
private static $iUseWeeks = true; |
38
|
|
|
|
39
|
|
|
public static function UseWeekFormat($aFlg) |
40
|
|
|
{ |
41
|
|
|
self::$iUseWeeks = $aFlg; |
42
|
|
|
} |
43
|
|
|
|
44
|
|
|
public static function doYearly($aType, $aMinor = false) |
45
|
|
|
{ |
46
|
|
|
$i = 0; |
47
|
|
|
$j = 0; |
48
|
|
|
$m = self::$startmonth; |
49
|
|
|
$y = self::$startyear; |
50
|
|
|
|
51
|
|
|
if (self::$startday == 1) { |
52
|
|
|
self::$tickPositions[$i++] = mktime(0, 0, 0, $m, 1, $y); |
53
|
|
|
} |
54
|
|
|
++$m; |
55
|
|
|
|
56
|
|
|
switch ($aType) { |
57
|
|
|
case DSUTILS_YEAR1: |
58
|
|
|
for ($y = self::$startyear; $y <= self::$endyear; ++$y) { |
59
|
|
View Code Duplication |
if ($aMinor) { |
|
|
|
|
60
|
|
|
while ($m <= 12) { |
61
|
|
|
if (!($y == self::$endyear && $m > self::$endmonth)) { |
62
|
|
|
self::$minTickPositions[$j++] = mktime(0, 0, 0, $m, 1, $y); |
63
|
|
|
} |
64
|
|
|
++$m; |
65
|
|
|
} |
66
|
|
|
$m = 1; |
67
|
|
|
} |
68
|
|
|
self::$tickPositions[$i++] = mktime(0, 0, 0, 1, 1, $y); |
69
|
|
|
} |
70
|
|
|
break; |
71
|
|
View Code Duplication |
case DSUTILS_YEAR2: |
|
|
|
|
72
|
|
|
$y = self::$startyear; |
73
|
|
|
while ($y <= self::$endyear) { |
74
|
|
|
self::$tickPositions[$i++] = mktime(0, 0, 0, 1, 1, $y); |
75
|
|
|
for ($k = 0; $k < 1; ++$k) { |
76
|
|
|
++$y; |
77
|
|
|
if ($aMinor) { |
78
|
|
|
self::$minTickPositions[$j++] = mktime(0, 0, 0, 1, 1, $y); |
79
|
|
|
} |
80
|
|
|
} |
81
|
|
|
++$y; |
82
|
|
|
} |
83
|
|
|
break; |
84
|
|
View Code Duplication |
case DSUTILS_YEAR5: |
|
|
|
|
85
|
|
|
$y = self::$startyear; |
86
|
|
|
while ($y <= self::$endyear) { |
87
|
|
|
self::$tickPositions[$i++] = mktime(0, 0, 0, 1, 1, $y); |
88
|
|
|
for ($k = 0; $k < 4; ++$k) { |
89
|
|
|
++$y; |
90
|
|
|
if ($aMinor) { |
91
|
|
|
self::$minTickPositions[$j++] = mktime(0, 0, 0, 1, 1, $y); |
92
|
|
|
} |
93
|
|
|
} |
94
|
|
|
++$y; |
95
|
|
|
} |
96
|
|
|
break; |
97
|
|
|
} |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
public static function doDaily($aType, $aMinor = false) |
101
|
|
|
{ |
102
|
|
|
$m = self::$startmonth; |
103
|
|
|
$y = self::$startyear; |
104
|
|
|
$d = self::$startday; |
105
|
|
|
$h = self::$starthour; |
106
|
|
|
$i = 0; |
107
|
|
|
$j = 0; |
108
|
|
|
|
109
|
|
|
if ($h == 0) { |
110
|
|
|
self::$tickPositions[$i++] = mktime(0, 0, 0, $m, $d, $y); |
111
|
|
|
} |
112
|
|
|
$t = mktime(0, 0, 0, $m, $d, $y); |
113
|
|
|
|
114
|
|
|
switch ($aType) { |
115
|
|
View Code Duplication |
case DSUTILS_DAY1: |
|
|
|
|
116
|
|
|
while ($t <= self::$iMax) { |
117
|
|
|
$t = strtotime('+1 day', $t); |
118
|
|
|
self::$tickPositions[$i++] = $t; |
119
|
|
|
if ($aMinor) { |
120
|
|
|
self::$minTickPositions[$j++] = strtotime('+12 hours', $t); |
121
|
|
|
} |
122
|
|
|
} |
123
|
|
|
break; |
124
|
|
View Code Duplication |
case DSUTILS_DAY2: |
|
|
|
|
125
|
|
|
while ($t <= self::$iMax) { |
126
|
|
|
$t = strtotime('+1 day', $t); |
127
|
|
|
if ($aMinor) { |
128
|
|
|
self::$minTickPositions[$j++] = $t; |
129
|
|
|
} |
130
|
|
|
$t = strtotime('+1 day', $t); |
131
|
|
|
self::$tickPositions[$i++] = $t; |
132
|
|
|
} |
133
|
|
|
break; |
134
|
|
|
case DSUTILS_DAY4: |
135
|
|
|
while ($t <= self::$iMax) { |
136
|
|
View Code Duplication |
for ($k = 0; $k < 3; ++$k) { |
|
|
|
|
137
|
|
|
$t = strtotime('+1 day', $t); |
138
|
|
|
if ($aMinor) { |
139
|
|
|
self::$minTickPositions[$j++] = $t; |
140
|
|
|
} |
141
|
|
|
} |
142
|
|
|
$t = strtotime('+1 day', $t); |
143
|
|
|
self::$tickPositions[$i++] = $t; |
144
|
|
|
} |
145
|
|
|
break; |
146
|
|
|
} |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
public static function doWeekly($aType, $aMinor = false) |
150
|
|
|
{ |
151
|
|
|
$hpd = 3600 * 24; |
152
|
|
|
$hpw = 3600 * 24 * 7; |
153
|
|
|
// Find out week number of min date |
154
|
|
|
$thursday = self::$iMin + $hpd * (3 - (date('w', self::$iMin) + 6) % 7); |
155
|
|
|
$week = 1 + (date('z', $thursday) - (11 - date('w', mktime(0, 0, 0, 1, 1, date('Y', $thursday)))) % 7) / 7; |
|
|
|
|
156
|
|
|
$daynumber = date('w', self::$iMin); |
157
|
|
|
if ($daynumber == 0) { |
158
|
|
|
$daynumber = 7; |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
$m = self::$startmonth; |
162
|
|
|
$y = self::$startyear; |
163
|
|
|
$d = self::$startday; |
164
|
|
|
$i = 0; |
165
|
|
|
$j = 0; |
166
|
|
|
// The assumption is that the weeks start on Monday. If the first day |
167
|
|
|
// is later in the week then the first week tick has to be on the following |
168
|
|
|
// week. |
169
|
|
|
if ($daynumber == 1) { |
170
|
|
|
self::$tickPositions[$i++] = mktime(0, 0, 0, $m, $d, $y); |
171
|
|
|
$t = mktime(0, 0, 0, $m, $d, $y) + $hpw; |
172
|
|
|
} else { |
173
|
|
|
$t = mktime(0, 0, 0, $m, $d, $y) + $hpd * (8 - $daynumber); |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
switch ($aType) { |
177
|
|
|
case DSUTILS_WEEK1: |
178
|
|
|
$cnt = 0; |
179
|
|
|
break; |
180
|
|
|
case DSUTILS_WEEK2: |
181
|
|
|
$cnt = 1; |
182
|
|
|
break; |
183
|
|
|
case DSUTILS_WEEK4: |
184
|
|
|
$cnt = 3; |
185
|
|
|
break; |
186
|
|
|
} |
187
|
|
|
while ($t <= self::$iMax) { |
188
|
|
|
self::$tickPositions[$i++] = $t; |
189
|
|
View Code Duplication |
for ($k = 0; $k < $cnt; ++$k) { |
|
|
|
|
190
|
|
|
$t += $hpw; |
191
|
|
|
if ($aMinor) { |
192
|
|
|
self::$minTickPositions[$j++] = $t; |
193
|
|
|
} |
194
|
|
|
} |
195
|
|
|
$t += $hpw; |
196
|
|
|
} |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
public static function doMonthly($aType, $aMinor = false) |
200
|
|
|
{ |
201
|
|
|
$monthcount = 0; |
202
|
|
|
$m = self::$startmonth; |
203
|
|
|
$y = self::$startyear; |
204
|
|
|
$i = 0; |
205
|
|
|
$j = 0; |
206
|
|
|
|
207
|
|
|
// Skip the first month label if it is before the startdate |
208
|
|
|
if (self::$startday == 1) { |
209
|
|
|
self::$tickPositions[$i++] = mktime(0, 0, 0, $m, 1, $y); |
210
|
|
|
$monthcount = 1; |
211
|
|
|
} |
212
|
|
|
if ($aType == 1) { |
213
|
|
|
if (self::$startday < 15) { |
214
|
|
|
self::$minTickPositions[$j++] = mktime(0, 0, 0, $m, 15, $y); |
215
|
|
|
} |
216
|
|
|
} |
217
|
|
|
++$m; |
218
|
|
|
|
219
|
|
|
// Loop through all the years included in the scale |
220
|
|
|
for ($y = self::$startyear; $y <= self::$endyear; ++$y) { |
221
|
|
|
// Loop through all the months. There are three cases to consider: |
222
|
|
|
// 1. We are in the first year and must start with the startmonth |
223
|
|
|
// 2. We are in the end year and we must stop at last month of the scale |
224
|
|
|
// 3. A year in between where we run through all the 12 months |
225
|
|
|
$stopmonth = $y == self::$endyear ? self::$endmonth : 12; |
226
|
|
|
while ($m <= $stopmonth) { |
227
|
|
|
switch ($aType) { |
228
|
|
|
case DSUTILS_MONTH1: |
229
|
|
|
// Set minor tick at the middle of the month |
230
|
|
View Code Duplication |
if ($aMinor) { |
|
|
|
|
231
|
|
|
if ($m <= $stopmonth) { |
232
|
|
|
if (!($y == self::$endyear && $m == $stopmonth && self::$endday < 15)) { |
233
|
|
|
self::$minTickPositions[$j++] = mktime(0, 0, 0, $m, 15, $y); |
234
|
|
|
} |
235
|
|
|
} |
236
|
|
|
} |
237
|
|
|
// Major at month |
238
|
|
|
// Get timestamp of first hour of first day in each month |
239
|
|
|
self::$tickPositions[$i++] = mktime(0, 0, 0, $m, 1, $y); |
240
|
|
|
|
241
|
|
|
break; |
242
|
|
View Code Duplication |
case DSUTILS_MONTH2: |
|
|
|
|
243
|
|
|
if ($aMinor) { |
244
|
|
|
// Set minor tick at start of each month |
245
|
|
|
self::$minTickPositions[$j++] = mktime(0, 0, 0, $m, 1, $y); |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
// Major at every second month |
249
|
|
|
// Get timestamp of first hour of first day in each month |
250
|
|
|
if ($monthcount % 2 == 0) { |
251
|
|
|
self::$tickPositions[$i++] = mktime(0, 0, 0, $m, 1, $y); |
252
|
|
|
} |
253
|
|
|
break; |
254
|
|
View Code Duplication |
case DSUTILS_MONTH3: |
|
|
|
|
255
|
|
|
if ($aMinor) { |
256
|
|
|
// Set minor tick at start of each month |
257
|
|
|
self::$minTickPositions[$j++] = mktime(0, 0, 0, $m, 1, $y); |
258
|
|
|
} |
259
|
|
|
// Major at every third month |
260
|
|
|
// Get timestamp of first hour of first day in each month |
261
|
|
|
if ($monthcount % 3 == 0) { |
262
|
|
|
self::$tickPositions[$i++] = mktime(0, 0, 0, $m, 1, $y); |
263
|
|
|
} |
264
|
|
|
break; |
265
|
|
View Code Duplication |
case DSUTILS_MONTH6: |
|
|
|
|
266
|
|
|
if ($aMinor) { |
267
|
|
|
// Set minor tick at start of each month |
268
|
|
|
self::$minTickPositions[$j++] = mktime(0, 0, 0, $m, 1, $y); |
269
|
|
|
} |
270
|
|
|
// Major at every third month |
271
|
|
|
// Get timestamp of first hour of first day in each month |
272
|
|
|
if ($monthcount % 6 == 0) { |
273
|
|
|
self::$tickPositions[$i++] = mktime(0, 0, 0, $m, 1, $y); |
274
|
|
|
} |
275
|
|
|
break; |
276
|
|
|
} |
277
|
|
|
++$m; |
278
|
|
|
++$monthcount; |
279
|
|
|
} |
280
|
|
|
$m = 1; |
281
|
|
|
} |
282
|
|
|
|
283
|
|
|
// For the case where all dates are within the same month |
284
|
|
|
// we want to make sure we have at least two ticks on the scale |
285
|
|
|
// since the scale want work properly otherwise |
286
|
|
|
if (self::$startmonth == self::$endmonth && self::$startyear == self::$endyear && $aType == 1) { |
287
|
|
|
self::$tickPositions[$i++] = mktime(0, 0, 0, self::$startmonth + 1, 1, self::$startyear); |
288
|
|
|
} |
289
|
|
|
|
290
|
|
|
return array(self::$tickPositions, self::$minTickPositions); |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
public static function GetTicks($aData, $aType = 1, $aMinor = false, $aEndPoints = false) |
294
|
|
|
{ |
295
|
|
|
$n = count($aData); |
296
|
|
|
return self::GetTicksFromMinMax($aData[0], $aData[$n - 1], $aType, $aMinor, $aEndPoints); |
297
|
|
|
} |
298
|
|
|
|
299
|
|
|
public static function GetAutoTicks($aMin, $aMax, $aMaxTicks = 10, $aMinor = false) |
300
|
|
|
{ |
301
|
|
|
$diff = $aMax - $aMin; |
302
|
|
|
$spd = 3600 * 24; |
303
|
|
|
$spw = $spd * 7; |
304
|
|
|
$spm = $spd * 30; |
305
|
|
|
$spy = $spd * 352; |
306
|
|
|
|
307
|
|
|
if (self::$iUseWeeks) { |
308
|
|
|
$w = 'W'; |
309
|
|
|
} else { |
310
|
|
|
$w = 'd M'; |
311
|
|
|
} |
312
|
|
|
|
313
|
|
|
// Decision table for suitable scales |
314
|
|
|
// First value: Main decision point |
315
|
|
|
// Second value: Array of formatting depending on divisor for wanted max number of ticks. <divisor><formatting><format-string>,.. |
316
|
|
|
$tt = array( |
317
|
|
|
array($spw, array(1, DSUTILS_DAY1, 'd M', 2, DSUTILS_DAY2, 'd M', -1, DSUTILS_DAY4, 'd M')), |
318
|
|
|
array($spm, array(1, DSUTILS_DAY1, 'd M', 2, DSUTILS_DAY2, 'd M', 4, DSUTILS_DAY4, 'd M', 7, DSUTILS_WEEK1, $w, -1, DSUTILS_WEEK2, $w)), |
319
|
|
|
array($spy, array(1, DSUTILS_DAY1, 'd M', 2, DSUTILS_DAY2, 'd M', 4, DSUTILS_DAY4, 'd M', 7, DSUTILS_WEEK1, $w, 14, DSUTILS_WEEK2, $w, 30, DSUTILS_MONTH1, 'M', 60, DSUTILS_MONTH2, 'M', -1, DSUTILS_MONTH3, 'M')), |
320
|
|
|
array(-1, array(30, DSUTILS_MONTH1, 'M-Y', 60, DSUTILS_MONTH2, 'M-Y', 90, DSUTILS_MONTH3, 'M-Y', 180, DSUTILS_MONTH6, 'M-Y', 352, DSUTILS_YEAR1, 'Y', 704, DSUTILS_YEAR2, 'Y', -1, DSUTILS_YEAR5, 'Y'))); |
321
|
|
|
|
322
|
|
|
$ntt = count($tt); |
323
|
|
|
$nd = floor($diff / $spd); |
324
|
|
|
for ($i = 0; $i < $ntt; ++$i) { |
325
|
|
|
if ($diff <= $tt[$i][0] || $i == $ntt - 1) { |
326
|
|
|
$t = $tt[$i][1]; |
327
|
|
|
$n = count($t) / 3; |
328
|
|
|
for ($j = 0; $j < $n; ++$j) { |
329
|
|
|
if ($nd / $t[3 * $j] <= $aMaxTicks || $j == $n - 1) { |
330
|
|
|
$type = $t[3 * $j + 1]; |
331
|
|
|
$fs = $t[3 * $j + 2]; |
332
|
|
|
list($tickPositions, $minTickPositions) = self::GetTicksFromMinMax($aMin, $aMax, $type, $aMinor); |
333
|
|
|
return array($fs, $tickPositions, $minTickPositions, $type); |
334
|
|
|
} |
335
|
|
|
} |
336
|
|
|
} |
337
|
|
|
} |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
public static function GetTicksFromMinMax($aMin, $aMax, $aType, $aMinor = false, $aEndPoints = false) |
341
|
|
|
{ |
342
|
|
|
self::$starthour = date('G', $aMin); |
343
|
|
|
self::$startmonth = date('n', $aMin); |
344
|
|
|
self::$startday = date('j', $aMin); |
345
|
|
|
self::$startyear = date('Y', $aMin); |
346
|
|
|
self::$endmonth = date('n', $aMax); |
347
|
|
|
self::$endyear = date('Y', $aMax); |
348
|
|
|
self::$endday = date('j', $aMax); |
349
|
|
|
self::$iMin = $aMin; |
350
|
|
|
self::$iMax = $aMax; |
351
|
|
|
|
352
|
|
|
if ($aType <= DSUTILS_MONTH6) { |
353
|
|
|
self::doMonthly($aType, $aMinor); |
354
|
|
|
} elseif ($aType <= DSUTILS_WEEK4) { |
355
|
|
|
self::doWeekly($aType, $aMinor); |
356
|
|
|
} elseif ($aType <= DSUTILS_DAY4) { |
357
|
|
|
self::doDaily($aType, $aMinor); |
358
|
|
|
} elseif ($aType <= DSUTILS_YEAR5) { |
359
|
|
|
self::doYearly($aType, $aMinor); |
360
|
|
|
} else { |
361
|
|
|
JpGraphError::RaiseL(24003); |
362
|
|
|
} |
363
|
|
|
// put a label at the very left data pos |
364
|
|
|
if ($aEndPoints) { |
365
|
|
|
$tickPositions[$i++] = $aData[0]; |
|
|
|
|
366
|
|
|
} |
367
|
|
|
|
368
|
|
|
// put a label at the very right data pos |
369
|
|
|
if ($aEndPoints) { |
370
|
|
|
$tickPositions[$i] = $aData[$n - 1]; |
|
|
|
|
371
|
|
|
} |
372
|
|
|
|
373
|
|
|
return array(self::$tickPositions, self::$minTickPositions); |
374
|
|
|
} |
375
|
|
|
} |
376
|
|
|
|
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.