2 /*=======================================================================
3 // File: JPGRAPH_DATE.PHP
4 // Description: Classes to handle Date scaling
6 // Author: Johan Persson (johanp@aditus.nu)
7 // Ver: $Id: jpgraph_date.php,v 1.1 2006/07/07 13:37:14 powles Exp $
9 // Copyright (c) Aditus Consulting. All rights reserved.
10 //========================================================================
13 DEFINE('HOURADJ_1',0+
30);
14 DEFINE('HOURADJ_2',1+
30);
15 DEFINE('HOURADJ_3',2+
30);
16 DEFINE('HOURADJ_4',3+
30);
17 DEFINE('HOURADJ_6',4+
30);
18 DEFINE('HOURADJ_12',5+
30);
20 DEFINE('MINADJ_1',0+
20);
21 DEFINE('MINADJ_5',1+
20);
22 DEFINE('MINADJ_10',2+
20);
23 DEFINE('MINADJ_15',3+
20);
24 DEFINE('MINADJ_30',4+
20);
28 DEFINE('SECADJ_10',2);
29 DEFINE('SECADJ_15',3);
30 DEFINE('SECADJ_30',4);
33 DEFINE('YEARADJ_1',0+
30);
34 DEFINE('YEARADJ_2',1+
30);
35 DEFINE('YEARADJ_5',2+
30);
37 DEFINE('MONTHADJ_1',0+
20);
38 DEFINE('MONTHADJ_6',1+
20);
41 DEFINE('DAYADJ_WEEK',1);
44 DEFINE('SECPERYEAR',31536000);
45 DEFINE('SECPERDAY',86400);
46 DEFINE('SECPERHOUR',3600);
47 DEFINE('SECPERMIN',60);
50 class DateScale
extends LinearScale
{
51 var $date_format = '';
52 var $iStartAlign = false, $iEndAlign = false;
53 var $iStartTimeAlign = false, $iEndTimeAlign = false;
57 function DateScale($aMin=0,$aMax=0,$aType='x') {
62 $this->scale
=array($aMin,$aMax);
63 $this->world_size
=$aMax-$aMin;
64 $this->ticks
= new LinearTicks();
69 //------------------------------------------------------------------------------------------
70 // Utility Function AdjDate()
71 // Description: Will round a given time stamp to an even year, month or day
73 //------------------------------------------------------------------------------------------
75 function AdjDate($aTime,$aRound=0,$aYearType=false,$aMonthType=false,$aDayType=false) {
76 $y = (int)date('Y',$aTime); $m = (int)date('m',$aTime); $d = (int)date('d',$aTime);
78 if( $aYearType !== false ) {
79 $yearAdj = array(0=>1, 1=>2, 2=>5);
81 $y = floor($y/$yearAdj[$aYearType])*$yearAdj[$aYearType];
85 $y = ceil($y/$yearAdj[$aYearType])*$yearAdj[$aYearType];
89 elseif( $aMonthType !== false ) {
90 $monthAdj = array(0=>1, 1=>6);
92 $m = floor($m/$monthAdj[$aMonthType])*$monthAdj[$aMonthType];
97 $m = ceil($m/$monthAdj[$aMonthType])*$monthAdj[$aMonthType];
101 elseif( $aDayType !== false ) {
102 if( $aDayType == 0 ) {
109 // Adjust to an even week boundary.
110 $w = (int)date('w',$aTime); // Day of week 0=Sun, 6=Sat
111 if( true ) { // Adjust to start on Mon
124 return mktime($h,$i,$s,$m,$d,$y);
128 //------------------------------------------------------------------------------------------
129 // Wrapper for AdjDate that will round a timestamp to an even date rounding
131 //------------------------------------------------------------------------------------------
132 function AdjStartDate($aTime,$aYearType=false,$aMonthType=false,$aDayType=false) {
133 return $this->AdjDate($aTime,0,$aYearType,$aMonthType,$aDayType);
136 //------------------------------------------------------------------------------------------
137 // Wrapper for AdjDate that will round a timestamp to an even date rounding
139 //------------------------------------------------------------------------------------------
140 function AdjEndDate($aTime,$aYearType=false,$aMonthType=false,$aDayType=false) {
141 return $this->AdjDate($aTime,1,$aYearType,$aMonthType,$aDayType);
144 //------------------------------------------------------------------------------------------
145 // Utility Function AdjTime()
146 // Description: Will round a given time stamp to an even time according to
148 //------------------------------------------------------------------------------------------
150 function AdjTime($aTime,$aRound=0,$aHourType=false,$aMinType=false,$aSecType=false) {
151 $y = (int)date('Y',$aTime); $m = (int)date('m',$aTime); $d = (int)date('d',$aTime);
152 $h = (int)date('H',$aTime); $i = (int)date('i',$aTime); $s = (int)date('s',$aTime);
153 if( $aHourType !== false ) {
155 $hourAdj = array(0=>1, 1=>2, 2=>3, 3=>4, 4=>6, 5=>12);
157 $h = floor($h/$hourAdj[$aHourType])*$hourAdj[$aHourType];
159 if( ($h %
$hourAdj[$aHourType]==0) && ($i > 0 ||
$s > 0) ) {
162 $h = ceil($h/$hourAdj[$aHourType])*$hourAdj[$aHourType];
165 $y = (int)date('Y',$aTime); $m = (int)date('m',$aTime); $d = (int)date('d',$aTime);
171 elseif( $aMinType !== false ) {
173 $minAdj = array(0=>1, 1=>5, 2=>10, 3=>15, 4=>30);
175 $i = floor($i/$minAdj[$aMinType])*$minAdj[$aMinType];
178 if( ($i %
$minAdj[$aMinType]==0) && $s > 0 ) {
181 $i = ceil($i/$minAdj[$aMinType])*$minAdj[$aMinType];
184 $y = (int)date('Y',$aTime); $m = (int)date('m',$aTime); $d = (int)date('d',$aTime);
185 $h = (int)date('H',$aTime); $i = 0;
190 elseif( $aSecType !== false ) {
192 $secAdj = array(0=>1, 1=>5, 2=>10, 3=>15, 4=>30);
194 $s = floor($s/$secAdj[$aSecType])*$secAdj[$aSecType];
197 $s = ceil($s/$secAdj[$aSecType]*1.0)*$secAdj[$aSecType];
201 $y = (int)date('Y',$aTime); $m = (int)date('m',$aTime); $d = (int)date('d',$aTime);
202 $h = (int)date('H',$aTime); $i = (int)date('i',$aTime);
206 return mktime($h,$i,$s,$m,$d,$y);
209 //------------------------------------------------------------------------------------------
210 // Wrapper for AdjTime that will round a timestamp to an even time rounding
212 // Example: AdjStartTime(mktime(18,27,13,2,22,2005),false,2) => 18:20
213 //------------------------------------------------------------------------------------------
214 function AdjStartTime($aTime,$aHourType=false,$aMinType=false,$aSecType=false) {
215 return $this->AdjTime($aTime,0,$aHourType,$aMinType,$aSecType);
218 //------------------------------------------------------------------------------------------
219 // Wrapper for AdjTime that will round a timestamp to an even time rounding
221 // Example: AdjEndTime(mktime(18,27,13,2,22,2005),false,2) => 18:30
222 //------------------------------------------------------------------------------------------
223 function AdjEndTime($aTime,$aHourType=false,$aMinType=false,$aSecType=false) {
224 return $this->AdjTime($aTime,1,$aHourType,$aMinType,$aSecType);
227 //------------------------------------------------------------------------------------------
229 // Autoscale a date axis given start and end time
230 // Returns an array ($start,$end,$major,$minor,$format)
231 //------------------------------------------------------------------------------------------
232 function DoDateAutoScale($aStartTime,$aEndTime,$aDensity=0,$aAdjust=true) {
234 // array ( Decision point, array( array( Major-scale-step-array ),
235 // array( Minor-scale-step-array ),
236 // array( 0=date-adjust, 1=time-adjust, adjustment-alignment) )
240 /* Intervall larger than 10 years */
241 SECPERYEAR
*10,array(array(SECPERYEAR
*5,SECPERYEAR
*2),
243 array(0,YEARADJ_1
, 0,YEARADJ_1
) ),
245 /* Intervall larger than 2 years */
246 SECPERYEAR
*2,array(array(SECPERYEAR
),array(SECPERYEAR
),
247 array(0,YEARADJ_1
) ),
249 /* Intervall larger than 90 days (approx 3 month) */
250 SECPERDAY
*90,array(array(SECPERDAY
*30,SECPERDAY
*14,SECPERDAY
*7,SECPERDAY
),
251 array(SECPERDAY
*5,SECPERDAY
*7,SECPERDAY
,SECPERDAY
),
252 array(0,MONTHADJ_1
, 0,DAYADJ_WEEK
, 0,DAYADJ_1
, 0,DAYADJ_1
)),
254 /* Intervall larger than 30 days (approx 1 month) */
255 SECPERDAY
*30,array(array(SECPERDAY
*14,SECPERDAY
*7,SECPERDAY
*2, SECPERDAY
),
256 array(SECPERDAY
,SECPERDAY
.SECPERDAY
,SECPERDAY
),
257 array(0,DAYADJ_WEEK
, 0,DAYADJ_1
, 0,DAYADJ_1
, 0,DAYADJ_1
)),
259 /* Intervall larger than 7 days */
260 SECPERDAY
*7,array(array(SECPERDAY
,SECPERHOUR
*12,SECPERHOUR
*6,SECPERHOUR
*2),
261 array(SECPERHOUR
*6,SECPERHOUR
*3,SECPERHOUR
,SECPERHOUR
),
262 array(0,DAYADJ_1
, 1,HOURADJ_12
, 1,HOURADJ_6
, 1,HOURADJ_1
)),
264 /* Intervall larger than 1 day */
265 SECPERDAY
,array(array(SECPERDAY
,SECPERHOUR
*12,SECPERHOUR
*6,SECPERHOUR
*2,SECPERHOUR
),
266 array(SECPERHOUR
*6,SECPERHOUR
*2,SECPERHOUR
,SECPERHOUR
,SECPERHOUR
),
267 array(1,HOURADJ_12
, 1,HOURADJ_6
, 1,HOURADJ_1
, 1,HOURADJ_1
)),
269 /* Intervall larger than 12 hours */
270 SECPERHOUR
*12,array(array(SECPERHOUR
*2,SECPERHOUR
,SECPERMIN
*30,900,600),
271 array(1800,1800,900,300,300),
272 array(1,HOURADJ_1
, 1,MINADJ_30
, 1,MINADJ_15
, 1,MINADJ_10
, 1,MINADJ_5
) ),
274 /* Intervall larger than 2 hours */
275 SECPERHOUR
*2,array(array(SECPERHOUR
,SECPERMIN
*30,900,600,300),
276 array(1800,900,300,120,60),
277 array(1,HOURADJ_1
, 1,MINADJ_30
, 1,MINADJ_15
, 1,MINADJ_10
, 1,MINADJ_5
) ),
279 /* Intervall larger than 1 hours */
280 SECPERHOUR
,array(array(SECPERMIN
*30,900,600,300),array(900,300,120,60),
281 array(1,MINADJ_30
, 1,MINADJ_15
, 1,MINADJ_10
, 1,MINADJ_5
) ),
283 /* Intervall larger than 30 min */
284 SECPERMIN
*30,array(array(SECPERMIN
*15,SECPERMIN
*10,SECPERMIN
*5,SECPERMIN
),
285 array(300,300,60,10),
286 array(1,MINADJ_15
, 1,MINADJ_10
, 1,MINADJ_5
, 1,MINADJ_1
)),
288 /* Intervall larger than 1 min */
289 SECPERMIN
,array(array(SECPERMIN
,15,10,5),
291 array(1,MINADJ_1
, 1,SECADJ_15
, 1,SECADJ_10
, 1,SECADJ_5
)),
293 /* Intervall larger than 10 sec */
296 array(1,SECADJ_5
, 1,SECADJ_1
)),
298 /* Intervall larger than 1 sec */
304 $ns = count($scalePoints);
305 // Establish major and minor scale units for the date scale
306 $diff = $aEndTime - $aStartTime;
307 if( $diff < 1 ) return false;
311 if( $diff > $scalePoints[2*$i] ) {
312 // Get major and minor scale for this intervall
313 $scaleSteps = $scalePoints[2*$i+
1];
314 $major = $scaleSteps[0][min($aDensity,count($scaleSteps[0])-1)];
315 // Try to find out which minor step looks best
316 $minor = $scaleSteps[1][min($aDensity,count($scaleSteps[1])-1)];
318 // Find out how we should align the start and end timestamps
319 $idx = 2*min($aDensity,floor(count($scaleSteps[2])/2)-1);
320 if( $scaleSteps[2][$idx] === 0 ) {
321 // Use date adjustment
322 $adj = $scaleSteps[2][$idx+
1];
324 $start = $this->AdjStartDate($aStartTime,$adj-30);
325 $end = $this->AdjEndDate($aEndTime,$adj-30);
327 elseif( $adj >= 20 ) {
328 $start = $this->AdjStartDate($aStartTime,false,$adj-20);
329 $end = $this->AdjEndDate($aEndTime,false,$adj-20);
332 $start = $this->AdjStartDate($aStartTime,false,false,$adj);
333 $end = $this->AdjEndDate($aEndTime,false,false,$adj);
334 // We add 1 second for date adjustment to make sure we end on 00:00 the following day
335 // This makes the final major tick be srawn when we step day-by-day instead of ending
336 // on xx:59:59 which would not draw the final major tick
341 // Use time adjustment
342 $adj = $scaleSteps[2][$idx+
1];
344 $start = $this->AdjStartTime($aStartTime,$adj-30);
345 $end = $this->AdjEndTime($aEndTime,$adj-30);
347 elseif( $adj >= 20 ) {
348 $start = $this->AdjStartTime($aStartTime,false,$adj-20);
349 $end = $this->AdjEndTime($aEndTime,false,$adj-20);
352 $start = $this->AdjStartTime($aStartTime,false,false,$adj);
353 $end = $this->AdjEndTime($aEndTime,false,false,$adj);
357 // If the overall date span is larger than 1 day ten we show date
359 if( ($end-$start) > SECPERDAY
) {
362 // If the major step is less than 1 day we need to whow hours + min
363 if( $major < SECPERDAY
) {
366 // If the major step is less than 1 min we need to show sec
374 return array($start,$end,$major,$minor,$format);
377 // Overrides the automatic determined date format. Must be a valid date() format string
378 function SetDateFormat($aFormat) {
379 $this->date_format
= $aFormat;
382 function SetDateAlign($aStartAlign,$aEndAlign=false) {
383 if( $aEndAlign === false ) {
384 $aEndAlign=$aStartAlign;
386 $this->iStartAlign
= $aStartAlign;
387 $this->iEndAlign
= $aEndAlign;
390 function SetTimeAlign($aStartAlign,$aEndAlign=false) {
391 if( $aEndAlign === false ) {
392 $aEndAlign=$aStartAlign;
394 $this->iStartTimeAlign
= $aStartAlign;
395 $this->iEndTimeAlign
= $aEndAlign;
399 function AutoScale(&$img,$aStartTime,$aEndTime,$aNumSteps) {
400 if( $aStartTime == $aEndTime ) {
401 // Special case when we only have one data point.
402 // Create a small artifical intervall to do the autoscaling
408 while( ! $done && $i < 5) {
409 list($adjstart,$adjend,$maj,$min,$format) = $this->DoDateAutoScale($aStartTime,$aEndTime,$i);
410 $n = floor(($adjend-$adjstart)/$maj);
411 if( $n * 1.7 > $aNumSteps ) {
419 echo " Start =".date("Y-m-d H:i:s",$aStartTime)."<br>";
420 echo " End =".date("Y-m-d H:i:s",$aEndTime)."<br>";
421 echo "Adj Start =".date("Y-m-d H:i:s",$adjstart)."<br>";
422 echo "Adj End =".date("Y-m-d H:i:s",$adjend)."<p>";
423 echo "Major = $maj s, ".floor($maj/60)."min, ".floor($maj/3600)."h, ".floor($maj/86400)."day<br>";
424 echo "Min = $min s, ".floor($min/60)."min, ".floor($min/3600)."h, ".floor($min/86400)."day<br>";
425 echo "Format=$format<p>";
429 if( $this->iStartTimeAlign
!== false && $this->iStartAlign
!== false ) {
430 JpGraphError
::RaiseL(3001);
431 //('It is only possible to use either SetDateAlign() or SetTimeAlign() but not both');
434 if( $this->iStartTimeAlign
!== false ) {
435 if( $this->iStartTimeAlign
>= 30 ) {
436 $adjstart = $this->AdjStartTime($aStartTime,$this->iStartTimeAlign
-30);
438 elseif( $this->iStartTimeAlign
>= 20 ) {
439 $adjstart = $this->AdjStartTime($aStartTime,false,$this->iStartTimeAlign
-20);
442 $adjstart = $this->AdjStartTime($aStartTime,false,false,$this->iStartTimeAlign
);
445 if( $this->iEndTimeAlign
!== false ) {
446 if( $this->iEndTimeAlign
>= 30 ) {
447 $adjend = $this->AdjEndTime($aEndTime,$this->iEndTimeAlign
-30);
449 elseif( $this->iEndTimeAlign
>= 20 ) {
450 $adjend = $this->AdjEndTime($aEndTime,false,$this->iEndTimeAlign
-20);
453 $adjend = $this->AdjEndTime($aEndTime,false,false,$this->iEndTimeAlign
);
459 if( $this->iStartAlign
!== false ) {
460 if( $this->iStartAlign
>= 30 ) {
461 $adjstart = $this->AdjStartDate($aStartTime,$this->iStartAlign
-30);
463 elseif( $this->iStartAlign
>= 20 ) {
464 $adjstart = $this->AdjStartDate($aStartTime,false,$this->iStartAlign
-20);
467 $adjstart = $this->AdjStartDate($aStartTime,false,false,$this->iStartAlign
);
470 if( $this->iEndAlign
!== false ) {
471 if( $this->iEndAlign
>= 30 ) {
472 $adjend = $this->AdjEndDate($aEndTime,$this->iEndAlign
-30);
474 elseif( $this->iEndAlign
>= 20 ) {
475 $adjend = $this->AdjEndDate($aEndTime,false,$this->iEndAlign
-20);
478 $adjend = $this->AdjEndDate($aEndTime,false,false,$this->iEndAlign
);
481 $this->Update($img,$adjstart,$adjend);
482 if( ! $this->ticks
->IsSpecified() )
483 $this->ticks
->Set($maj,$min);
484 if( $this->date_format
== '' )
485 $this->ticks
->SetLabelDateFormat($format);
487 $this->ticks
->SetLabelDateFormat($this->date_format
);