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