1<?php 2/*======================================================================= 3// File: JPGRAPH_DATE.PHP 4// Description: Classes to handle Date scaling 5// Created: 2005-05-02 6// Author: Johan Persson (johanp@aditus.nu) 7// Ver: $Id: jpgraph_date.php,v 1.1.1.1 2005/11/30 23:01:51 gth2 Exp $ 8// 9// Copyright (c) Aditus Consulting. All rights reserved. 10//======================================================================== 11*/ 12 13DEFINE('HOURADJ_1',0+30); 14DEFINE('HOURADJ_2',1+30); 15DEFINE('HOURADJ_3',2+30); 16DEFINE('HOURADJ_4',3+30); 17DEFINE('HOURADJ_6',4+30); 18DEFINE('HOURADJ_12',5+30); 19 20DEFINE('MINADJ_1',0+20); 21DEFINE('MINADJ_5',1+20); 22DEFINE('MINADJ_10',2+20); 23DEFINE('MINADJ_15',3+20); 24DEFINE('MINADJ_30',4+20); 25 26DEFINE('SECADJ_1',0); 27DEFINE('SECADJ_5',1); 28DEFINE('SECADJ_10',2); 29DEFINE('SECADJ_15',3); 30DEFINE('SECADJ_30',4); 31 32 33DEFINE('YEARADJ_1',0+30); 34DEFINE('YEARADJ_2',1+30); 35DEFINE('YEARADJ_5',2+30); 36 37DEFINE('MONTHADJ_1',0+20); 38DEFINE('MONTHADJ_6',1+20); 39 40DEFINE('DAYADJ_1',0); 41DEFINE('DAYADJ_WEEK',1); 42DEFINE('DAYADJ_7',1); 43 44DEFINE('SECPERYEAR',31536000); 45DEFINE('SECPERDAY',86400); 46DEFINE('SECPERHOUR',3600); 47DEFINE('SECPERMIN',60); 48 49 50class DateScale extends LinearScale { 51 var $date_format = ''; 52 var $iStartAlign = false, $iEndAlign = false; 53 var $iStartTimeAlign = false, $iEndTimeAlign = false; 54 55//--------------- 56// CONSTRUCTOR 57 function DateScale($aMin=0,$aMax=0,$aType='x') { 58 assert($aType=="x"); 59 assert($aMin<=$aMax); 60 61 $this->type=$aType; 62 $this->scale=array($aMin,$aMax); 63 $this->world_size=$aMax-$aMin; 64 $this->ticks = new LinearTicks(); 65 $this->intscale=true; 66 } 67 68 69//------------------------------------------------------------------------------------------ 70// Utility Function AdjDate() 71// Description: Will round a given time stamp to an even year, month or day 72// argument. 73//------------------------------------------------------------------------------------------ 74 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); 77 $h=0;$i=0;$s=0; 78 if( $aYearType !== false ) { 79 $yearAdj = array(0=>1, 1=>2, 2=>5); 80 if( $aRound == 0 ) { 81 $y = floor($y/$yearAdj[$aYearType])*$yearAdj[$aYearType]; 82 } 83 else { 84 ++$y; 85 $y = ceil($y/$yearAdj[$aYearType])*$yearAdj[$aYearType]; 86 } 87 $m=1;$d=1; 88 } 89 elseif( $aMonthType !== false ) { 90 $monthAdj = array(0=>1, 1=>6); 91 if( $aRound == 0 ) { 92 $m = floor($m/$monthAdj[$aMonthType])*$monthAdj[$aMonthType]; 93 $d=1; 94 } 95 else { 96 ++$m; 97 $m = ceil($m/$monthAdj[$aMonthType])*$monthAdj[$aMonthType]; 98 $d=1; 99 } 100 } 101 elseif( $aDayType !== false ) { 102 if( $aDayType == 0 ) { 103 if( $aRound == 1 ) { 104 //++$d; 105 $h=23;$i=59;$s=59; 106 } 107 } 108 else { 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 112 if( $w==0 ) $w=6; 113 else --$w; 114 } 115 if( $aRound == 0 ) { 116 $d -= $w; 117 } 118 else { 119 $d += (7-$w); 120 $h=23;$i=59;$s=59; 121 } 122 } 123 } 124 return mktime($h,$i,$s,$m,$d,$y); 125 126 } 127 128//------------------------------------------------------------------------------------------ 129// Wrapper for AdjDate that will round a timestamp to an even date rounding 130// it downwards. 131//------------------------------------------------------------------------------------------ 132 function AdjStartDate($aTime,$aYearType=false,$aMonthType=false,$aDayType=false) { 133 return $this->AdjDate($aTime,0,$aYearType,$aMonthType,$aDayType); 134 } 135 136//------------------------------------------------------------------------------------------ 137// Wrapper for AdjDate that will round a timestamp to an even date rounding 138// it upwards 139//------------------------------------------------------------------------------------------ 140 function AdjEndDate($aTime,$aYearType=false,$aMonthType=false,$aDayType=false) { 141 return $this->AdjDate($aTime,1,$aYearType,$aMonthType,$aDayType); 142 } 143 144//------------------------------------------------------------------------------------------ 145// Utility Function AdjTime() 146// Description: Will round a given time stamp to an even time according to 147// argument. 148//------------------------------------------------------------------------------------------ 149 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 ) { 154 $aHourType %= 6; 155 $hourAdj = array(0=>1, 1=>2, 2=>3, 3=>4, 4=>6, 5=>12); 156 if( $aRound == 0 ) 157 $h = floor($h/$hourAdj[$aHourType])*$hourAdj[$aHourType]; 158 else { 159 if( ($h % $hourAdj[$aHourType]==0) && ($i > 0 || $s > 0) ) { 160 $h++; 161 } 162 $h = ceil($h/$hourAdj[$aHourType])*$hourAdj[$aHourType]; 163 if( $h >= 24 ) { 164 $aTime += 86400; 165 $y = (int)date('Y',$aTime); $m = (int)date('m',$aTime); $d = (int)date('d',$aTime); 166 $h -= 24; 167 } 168 } 169 $i=0;$s=0; 170 } 171 elseif( $aMinType !== false ) { 172 $aMinType %= 5; 173 $minAdj = array(0=>1, 1=>5, 2=>10, 3=>15, 4=>30); 174 if( $aRound == 0 ) { 175 $i = floor($i/$minAdj[$aMinType])*$minAdj[$aMinType]; 176 } 177 else { 178 if( ($i % $minAdj[$aMinType]==0) && $s > 0 ) { 179 $i++; 180 } 181 $i = ceil($i/$minAdj[$aMinType])*$minAdj[$aMinType]; 182 if( $i >= 60) { 183 $aTime += 3600; 184 $y = (int)date('Y',$aTime); $m = (int)date('m',$aTime); $d = (int)date('d',$aTime); 185 $h = (int)date('H',$aTime); $i = 0; 186 } 187 } 188 $s=0; 189 } 190 elseif( $aSecType !== false ) { 191 $aSecType %= 5; 192 $secAdj = array(0=>1, 1=>5, 2=>10, 3=>15, 4=>30); 193 if( $aRound == 0 ) { 194 $s = floor($s/$secAdj[$aSecType])*$secAdj[$aSecType]; 195 } 196 else { 197 $s = ceil($s/$secAdj[$aSecType]*1.0)*$secAdj[$aSecType]; 198 if( $s >= 60) { 199 $s=0; 200 $aTime += 60; 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); 203 } 204 } 205 } 206 return mktime($h,$i,$s,$m,$d,$y); 207 } 208 209//------------------------------------------------------------------------------------------ 210// Wrapper for AdjTime that will round a timestamp to an even time rounding 211// it downwards. 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); 216 } 217 218//------------------------------------------------------------------------------------------ 219// Wrapper for AdjTime that will round a timestamp to an even time rounding 220// it upwards 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); 225 } 226 227//------------------------------------------------------------------------------------------ 228// DateAutoScale 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) { 233 $scalePoints = 234 array( 235 /* Intervall larger than 10 years */ 236 SECPERYEAR*10,array(array(SECPERYEAR*5,SECPERYEAR*2),array(SECPERYEAR), 237 array(0,YEARADJ_1, 0,YEARADJ_1) ), 238 239 /* Intervall larger than 2 years */ 240 SECPERYEAR*2,array(array(SECPERYEAR),array(SECPERYEAR), 241 array(0,YEARADJ_1) ), 242 243 /* Intervall larger than 90 days (approx 3 month) */ 244 SECPERDAY*90,array(array(SECPERDAY*30,SECPERDAY*14,SECPERDAY*7,SECPERDAY),array(SECPERDAY*5,SECPERDAY*7,SECPERDAY,SECPERDAY), 245 array(0,MONTHADJ_1, 0,DAYADJ_WEEK, 0,DAYADJ_1, 0,DAYADJ_1)), 246 247 /* Intervall larger than 30 days (approx 1 month) */ 248 SECPERDAY*30,array(array(SECPERDAY*14,SECPERDAY*7,SECPERDAY*2, SECPERDAY),array(SECPERDAY,SECPERDAY.SECPERDAY,SECPERDAY), 249 array(0,DAYADJ_WEEK, 0,DAYADJ_1, 0,DAYADJ_1, 0,DAYADJ_1)), 250 251 /* Intervall larger than 7 days */ 252 SECPERDAY*7,array(array(SECPERDAY,SECPERHOUR*12,SECPERHOUR*6,SECPERHOUR*2),array(SECPERHOUR*6,SECPERHOUR*3,SECPERHOUR,SECPERHOUR), 253 array(0,DAYADJ_1, 1,HOURADJ_12, 1,HOURADJ_6, 1,HOURADJ_1)), 254 255 /* Intervall larger than 1 day */ 256 SECPERDAY,array(array(SECPERDAY,SECPERHOUR*12,SECPERHOUR*6,SECPERHOUR*2,SECPERHOUR),array(SECPERHOUR*6,SECPERHOUR*2,SECPERHOUR,SECPERHOUR,SECPERHOUR), 257 array(1,HOURADJ_12, 1,HOURADJ_6, 1,HOURADJ_1, 1,HOURADJ_1)), 258 259 /* Intervall larger than 12 hours */ 260 SECPERHOUR*12,array(array(SECPERHOUR*2,SECPERHOUR,SECPERMIN*30,900,600),array(1800,1800,900,300,300), 261 array(1,HOURADJ_1, 1,MINADJ_30, 1,MINADJ_15, 1,MINADJ_10, 1,MINADJ_5) ), 262 263 /* Intervall larger than 2 hours */ 264 SECPERHOUR*2,array(array(SECPERHOUR,SECPERMIN*30,900,600,300),array(1800,900,300,120,60), 265 array(1,HOURADJ_1, 1,MINADJ_30, 1,MINADJ_15, 1,MINADJ_10, 1,MINADJ_5) ), 266 267 /* Intervall larger than 1 hours */ 268 SECPERHOUR,array(array(SECPERMIN*30,900,600,300),array(900,300,120,60), 269 array(1,MINADJ_30, 1,MINADJ_15, 1,MINADJ_10, 1,MINADJ_5) ), 270 271 /* Intervall larger than 30 min */ 272 SECPERMIN*30,array(array(SECPERMIN*15,SECPERMIN*10,SECPERMIN*5,SECPERMIN),array(300,300,60,10), 273 array(1,MINADJ_15, 1,MINADJ_10, 1,MINADJ_5, 1,MINADJ_1)), 274 275 /* Intervall larger than 1 min */ 276 SECPERMIN,array(array(SECPERMIN,15,10,5),array(15,5,2,1), 277 array(1,MINADJ_1, 1,SECADJ_15, 1,SECADJ_10, 1,SECADJ_5)), 278 279 /* Intervall larger than 10 sec */ 280 10,array(array(5,2),array(1,1),array(1,SECADJ_5, 1,SECADJ_1)), 281 282 /* Intervall larger than 1 sec */ 283 1,array(array(1),array(1),array(1,SECADJ_1)), 284 ); 285 286 $ns = count($scalePoints); 287 // Establish major and minor scale units for the date scale 288 $diff = $aEndTime - $aStartTime; 289 if( $diff < 1 ) return false; 290 $done=false; 291 $i=0; 292 while( ! $done ) { 293 if( $diff > $scalePoints[2*$i] ) { 294 // Get major and minor scale for this intervall 295 $scaleSteps = $scalePoints[2*$i+1]; 296 $major = $scaleSteps[0][min($aDensity,count($scaleSteps[0])-1)]; 297 // Try to find out which minor step looks best 298 $minor = $scaleSteps[1][min($aDensity,count($scaleSteps[1])-1)]; 299 if( $aAdjust ) { 300 // Find out how we should align the start and end timestamps 301 $idx = 2*min($aDensity,floor(count($scaleSteps[2])/2)-1); 302 if( $scaleSteps[2][$idx] === 0 ) { 303 // Use date adjustment 304 $adj = $scaleSteps[2][$idx+1]; 305 if( $adj >= 30 ) { 306 $start = $this->AdjStartDate($aStartTime,$adj-30); 307 $end = $this->AdjEndDate($aEndTime,$adj-30); 308 } 309 elseif( $adj >= 20 ) { 310 $start = $this->AdjStartDate($aStartTime,false,$adj-20); 311 $end = $this->AdjEndDate($aEndTime,false,$adj-20); 312 } 313 else { 314 $start = $this->AdjStartDate($aStartTime,false,false,$adj); 315 $end = $this->AdjEndDate($aEndTime,false,false,$adj); 316 // We add 1 second for date adjustment to make sure we end on 00:00 the following day 317 // This makes the final major tick be srawn when we step day-by-day instead of ending 318 // on xx:59:59 which would not draw the final major tick 319 $end++; 320 } 321 } 322 else { 323 // Use time adjustment 324 $adj = $scaleSteps[2][$idx+1]; 325 if( $adj >= 30 ) { 326 $start = $this->AdjStartTime($aStartTime,$adj-30); 327 $end = $this->AdjEndTime($aEndTime,$adj-30); 328 } 329 elseif( $adj >= 20 ) { 330 $start = $this->AdjStartTime($aStartTime,false,$adj-20); 331 $end = $this->AdjEndTime($aEndTime,false,$adj-20); 332 } 333 else { 334 $start = $this->AdjStartTime($aStartTime,false,false,$adj); 335 $end = $this->AdjEndTime($aEndTime,false,false,$adj); 336 } 337 } 338 } 339 // If the overall date span is larger than 1 day ten we show date 340 $format = ''; 341 if( ($end-$start) > SECPERDAY ) { 342 $format = 'Y-m-d '; 343 } 344 // If the major step is less than 1 day we need to whow hours + min 345 if( $major < SECPERDAY ) { 346 $format .= 'H:i'; 347 } 348 // If the major step is less than 1 min we need to show sec 349 if( $major < 60 ) { 350 $format .= ':s'; 351 } 352 $done=true; 353 } 354 ++$i; 355 } 356 return array($start,$end,$major,$minor,$format); 357 } 358 359 // Overrides the automatic determined date format. Must be a valid date() format string 360 function SetDateFormat($aFormat) { 361 $this->date_format = $aFormat; 362 } 363 364 function SetDateAlign($aStartAlign,$aEndAlign=false) { 365 if( $aEndAlign === false ) { 366 $aEndAlign=$aStartAlign; 367 } 368 $this->iStartAlign = $aStartAlign; 369 $this->iEndAlign = $aEndAlign; 370 } 371 372 function SetTimeAlign($aStartAlign,$aEndAlign=false) { 373 if( $aEndAlign === false ) { 374 $aEndAlign=$aStartAlign; 375 } 376 $this->iStartTimeAlign = $aStartAlign; 377 $this->iEndTimeAlign = $aEndAlign; 378 } 379 380 381 function AutoScale(&$img,$aStartTime,$aEndTime,$aNumSteps) { 382 assert($aStartTime < $aEndTime); 383 $done=false; 384 $i=0; 385 while( ! $done && $i < 5) { 386 list($adjstart,$adjend,$maj,$min,$format) = $this->DoDateAutoScale($aStartTime,$aEndTime,$i); 387 $n = floor(($adjend-$adjstart)/$maj); 388 if( $n * 1.7 > $aNumSteps ) { 389 $done=true; 390 } 391 $i++; 392 } 393 394 /* 395 if( 0 ) { // DEBUG 396 echo " Start =".date("Y-m-d H:i:s",$aStartTime)."<br>"; 397 echo " End =".date("Y-m-d H:i:s",$aEndTime)."<br>"; 398 echo "Adj Start =".date("Y-m-d H:i:s",$adjstart)."<br>"; 399 echo "Adj End =".date("Y-m-d H:i:s",$adjend)."<p>"; 400 echo "Major = $maj s, ".floor($maj/60)."min, ".floor($maj/3600)."h, ".floor($maj/86400)."day<br>"; 401 echo "Min = $min s, ".floor($min/60)."min, ".floor($min/3600)."h, ".floor($min/86400)."day<br>"; 402 echo "Format=$format<p>"; 403 } 404 */ 405 406 if( $this->iStartTimeAlign !== false && $this->iStartAlign !== false ) { 407 JpGraphError::Raise('It is only possible to use either SetDateAlign() or SetTimeAlign() but not both'); 408 } 409 410 if( $this->iStartTimeAlign !== false ) { 411 if( $this->iStartTimeAlign >= 30 ) { 412 $adjstart = $this->AdjStartTime($aStartTime,$this->iStartTimeAlign-30); 413 } 414 elseif( $this->iStartTimeAlign >= 20 ) { 415 $adjstart = $this->AdjStartTime($aStartTime,false,$this->iStartTimeAlign-20); 416 } 417 else { 418 $adjstart = $this->AdjStartTime($aStartTime,false,false,$this->iStartTimeAlign); 419 } 420 } 421 if( $this->iEndTimeAlign !== false ) { 422 if( $this->iEndTimeAlign >= 30 ) { 423 $adjend = $this->AdjEndTime($aEndTime,$this->iEndTimeAlign-30); 424 } 425 elseif( $this->iEndTimeAlign >= 20 ) { 426 $adjend = $this->AdjEndTime($aEndTime,false,$this->iEndTimeAlign-20); 427 } 428 else { 429 $adjend = $this->AdjEndTime($aEndTime,false,false,$this->iEndTimeAlign); 430 } 431 } 432 433 434 435 if( $this->iStartAlign !== false ) { 436 if( $this->iStartAlign >= 30 ) { 437 $adjstart = $this->AdjStartDate($aStartTime,$this->iStartAlign-30); 438 } 439 elseif( $this->iStartAlign >= 20 ) { 440 $adjstart = $this->AdjStartDate($aStartTime,false,$this->iStartAlign-20); 441 } 442 else { 443 $adjstart = $this->AdjStartDate($aStartTime,false,false,$this->iStartAlign); 444 } 445 } 446 if( $this->iEndAlign !== false ) { 447 if( $this->iEndAlign >= 30 ) { 448 $adjend = $this->AdjEndDate($aEndTime,$this->iEndAlign-30); 449 } 450 elseif( $this->iEndAlign >= 20 ) { 451 $adjend = $this->AdjEndDate($aEndTime,false,$this->iEndAlign-20); 452 } 453 else { 454 $adjend = $this->AdjEndDate($aEndTime,false,false,$this->iEndAlign); 455 } 456 } 457 $this->Update($img,$adjstart,$adjend); 458 if( ! $this->ticks->IsSpecified() ) 459 $this->ticks->Set($maj,$min); 460 if( $this->date_format == '' ) 461 $this->ticks->SetLabelDateFormat($format); 462 else 463 $this->ticks->SetLabelDateFormat($this->date_format); 464 } 465} 466 467 468?> 469