1<?php defined('SYSPATH') OR die('No direct access allowed.'); 2/** 3 * Date helper class. 4 * 5 * $Id: date.php 4316 2009-05-04 01:03:54Z kiall $ 6 * 7 * @package Core 8 * @author Kohana Team 9 * @copyright (c) 2007-2008 Kohana Team 10 * @license http://kohanaphp.com/license.html 11 */ 12class date_Core { 13 14 /** 15 * Converts a UNIX timestamp to DOS format. 16 * 17 * @param integer UNIX timestamp 18 * @return integer 19 */ 20 public static function unix2dos($timestamp = FALSE) 21 { 22 $timestamp = ($timestamp === FALSE) ? getdate() : getdate($timestamp); 23 24 if ($timestamp['year'] < 1980) 25 { 26 return (1 << 21 | 1 << 16); 27 } 28 29 $timestamp['year'] -= 1980; 30 31 // What voodoo is this? I have no idea... Geert can explain it though, 32 // and that's good enough for me. 33 return ($timestamp['year'] << 25 | $timestamp['mon'] << 21 | 34 $timestamp['mday'] << 16 | $timestamp['hours'] << 11 | 35 $timestamp['minutes'] << 5 | $timestamp['seconds'] >> 1); 36 } 37 38 /** 39 * Converts a DOS timestamp to UNIX format. 40 * 41 * @param integer DOS timestamp 42 * @return integer 43 */ 44 public static function dos2unix($timestamp = FALSE) 45 { 46 $sec = 2 * ($timestamp & 0x1f); 47 $min = ($timestamp >> 5) & 0x3f; 48 $hrs = ($timestamp >> 11) & 0x1f; 49 $day = ($timestamp >> 16) & 0x1f; 50 $mon = ($timestamp >> 21) & 0x0f; 51 $year = ($timestamp >> 25) & 0x7f; 52 53 return mktime($hrs, $min, $sec, $mon, $day, $year + 1980); 54 } 55 56 /** 57 * Returns the offset (in seconds) between two time zones. 58 * @see http://php.net/timezones 59 * 60 * @param string timezone that to find the offset of 61 * @param string|boolean timezone used as the baseline 62 * @return integer 63 */ 64 public static function offset($remote, $local = TRUE) 65 { 66 static $offsets; 67 68 // Default values 69 $remote = (string) $remote; 70 $local = ($local === TRUE) ? date_default_timezone_get() : (string) $local; 71 72 // Cache key name 73 $cache = $remote.$local; 74 75 if (empty($offsets[$cache])) 76 { 77 // Create timezone objects 78 $remote = new DateTimeZone($remote); 79 $local = new DateTimeZone($local); 80 81 // Create date objects from timezones 82 $time_there = new DateTime('now', $remote); 83 $time_here = new DateTime('now', $local); 84 85 // Find the offset 86 $offsets[$cache] = $remote->getOffset($time_there) - $local->getOffset($time_here); 87 } 88 89 return $offsets[$cache]; 90 } 91 92 /** 93 * Number of seconds in a minute, incrementing by a step. 94 * 95 * @param integer amount to increment each step by, 1 to 30 96 * @param integer start value 97 * @param integer end value 98 * @return array A mirrored (foo => foo) array from 1-60. 99 */ 100 public static function seconds($step = 1, $start = 0, $end = 60) 101 { 102 // Always integer 103 $step = (int) $step; 104 105 $seconds = array(); 106 107 for ($i = $start; $i < $end; $i += $step) 108 { 109 $seconds[$i] = ($i < 10) ? '0'.$i : $i; 110 } 111 112 return $seconds; 113 } 114 115 /** 116 * Number of minutes in an hour, incrementing by a step. 117 * 118 * @param integer amount to increment each step by, 1 to 30 119 * @return array A mirrored (foo => foo) array from 1-60. 120 */ 121 public static function minutes($step = 5) 122 { 123 // Because there are the same number of minutes as seconds in this set, 124 // we choose to re-use seconds(), rather than creating an entirely new 125 // function. Shhhh, it's cheating! ;) There are several more of these 126 // in the following methods. 127 return date::seconds($step); 128 } 129 130 /** 131 * Number of hours in a day. 132 * 133 * @param integer amount to increment each step by 134 * @param boolean use 24-hour time 135 * @param integer the hour to start at 136 * @return array A mirrored (foo => foo) array from start-12 or start-23. 137 */ 138 public static function hours($step = 1, $long = FALSE, $start = NULL) 139 { 140 // Default values 141 $step = (int) $step; 142 $long = (bool) $long; 143 $hours = array(); 144 145 // Set the default start if none was specified. 146 if ($start === NULL) 147 { 148 $start = ($long === FALSE) ? 1 : 0; 149 } 150 151 $hours = array(); 152 153 // 24-hour time has 24 hours, instead of 12 154 $size = ($long === TRUE) ? 23 : 12; 155 156 for ($i = $start; $i <= $size; $i += $step) 157 { 158 $hours[$i] = $i; 159 } 160 161 return $hours; 162 } 163 164 /** 165 * Returns AM or PM, based on a given hour. 166 * 167 * @param integer number of the hour 168 * @return string 169 */ 170 public static function ampm($hour) 171 { 172 // Always integer 173 $hour = (int) $hour; 174 175 return ($hour > 11) ? 'PM' : 'AM'; 176 } 177 178 /** 179 * Adjusts a non-24-hour number into a 24-hour number. 180 * 181 * @param integer hour to adjust 182 * @param string AM or PM 183 * @return string 184 */ 185 public static function adjust($hour, $ampm) 186 { 187 $hour = (int) $hour; 188 $ampm = strtolower($ampm); 189 190 switch ($ampm) 191 { 192 case 'am': 193 if ($hour == 12) 194 $hour = 0; 195 break; 196 case 'pm': 197 if ($hour < 12) 198 $hour += 12; 199 break; 200 } 201 202 return sprintf('%02s', $hour); 203 } 204 205 /** 206 * Number of days in month. 207 * 208 * @param integer number of month 209 * @param integer number of year to check month, defaults to the current year 210 * @return array A mirrored (foo => foo) array of the days. 211 */ 212 public static function days($month, $year = FALSE) 213 { 214 static $months; 215 216 // Always integers 217 $month = (int) $month; 218 $year = (int) $year; 219 220 // Use the current year by default 221 $year = ($year == FALSE) ? date('Y') : $year; 222 223 // We use caching for months, because time functions are used 224 if (empty($months[$year][$month])) 225 { 226 $months[$year][$month] = array(); 227 228 // Use date to find the number of days in the given month 229 $total = date('t', mktime(1, 0, 0, $month, 1, $year)) + 1; 230 231 for ($i = 1; $i < $total; $i++) 232 { 233 $months[$year][$month][$i] = $i; 234 } 235 } 236 237 return $months[$year][$month]; 238 } 239 240 /** 241 * Number of months in a year 242 * 243 * @return array A mirrored (foo => foo) array from 1-12. 244 */ 245 public static function months() 246 { 247 return date::hours(); 248 } 249 250 /** 251 * Returns an array of years between a starting and ending year. 252 * Uses the current year +/- 5 as the max/min. 253 * 254 * @param integer starting year 255 * @param integer ending year 256 * @return array 257 */ 258 public static function years($start = FALSE, $end = FALSE) 259 { 260 // Default values 261 $start = ($start === FALSE) ? date('Y') - 5 : (int) $start; 262 $end = ($end === FALSE) ? date('Y') + 5 : (int) $end; 263 264 $years = array(); 265 266 // Add one, so that "less than" works 267 $end += 1; 268 269 for ($i = $start; $i < $end; $i++) 270 { 271 $years[$i] = $i; 272 } 273 274 return $years; 275 } 276 277 /** 278 * Returns time difference between two timestamps, in human readable format. 279 * 280 * @param integer timestamp 281 * @param integer timestamp, defaults to the current time 282 * @param string formatting string 283 * @return string|array 284 */ 285 public static function timespan($time1, $time2 = NULL, $output = 'years,months,weeks,days,hours,minutes,seconds') 286 { 287 // Array with the output formats 288 $output = preg_split('/[^a-z]+/', strtolower((string) $output)); 289 290 // Invalid output 291 if (empty($output)) 292 return FALSE; 293 294 // Make the output values into keys 295 extract(array_flip($output), EXTR_SKIP); 296 297 // Default values 298 $time1 = max(0, (int) $time1); 299 $time2 = empty($time2) ? time() : max(0, (int) $time2); 300 301 // Calculate timespan (seconds) 302 $timespan = abs($time1 - $time2); 303 304 // All values found using Google Calculator. 305 // Years and months do not match the formula exactly, due to leap years. 306 307 // Years ago, 60 * 60 * 24 * 365 308 isset($years) and $timespan -= 31556926 * ($years = (int) floor($timespan / 31556926)); 309 310 // Months ago, 60 * 60 * 24 * 30 311 isset($months) and $timespan -= 2629744 * ($months = (int) floor($timespan / 2629743.83)); 312 313 // Weeks ago, 60 * 60 * 24 * 7 314 isset($weeks) and $timespan -= 604800 * ($weeks = (int) floor($timespan / 604800)); 315 316 // Days ago, 60 * 60 * 24 317 isset($days) and $timespan -= 86400 * ($days = (int) floor($timespan / 86400)); 318 319 // Hours ago, 60 * 60 320 isset($hours) and $timespan -= 3600 * ($hours = (int) floor($timespan / 3600)); 321 322 // Minutes ago, 60 323 isset($minutes) and $timespan -= 60 * ($minutes = (int) floor($timespan / 60)); 324 325 // Seconds ago, 1 326 isset($seconds) and $seconds = $timespan; 327 328 // Remove the variables that cannot be accessed 329 unset($timespan, $time1, $time2); 330 331 // Deny access to these variables 332 $deny = array_flip(array('deny', 'key', 'difference', 'output')); 333 334 // Return the difference 335 $difference = array(); 336 foreach ($output as $key) 337 { 338 if (isset($$key) AND ! isset($deny[$key])) 339 { 340 // Add requested key to the output 341 $difference[$key] = $$key; 342 } 343 } 344 345 // Invalid output formats string 346 if (empty($difference)) 347 return FALSE; 348 349 // If only one output format was asked, don't put it in an array 350 if (count($difference) === 1) 351 return current($difference); 352 353 // Return array 354 return $difference; 355 } 356 357 /** 358 * Returns time difference between two timestamps, in the format: 359 * N year, N months, N weeks, N days, N hours, N minutes, and N seconds ago 360 * 361 * @param integer timestamp 362 * @param integer timestamp, defaults to the current time 363 * @param string formatting string 364 * @return string 365 */ 366 public static function timespan_string($time1, $time2 = NULL, $output = 'years,months,weeks,days,hours,minutes,seconds') 367 { 368 if ($difference = date::timespan($time1, $time2, $output) AND is_array($difference)) 369 { 370 // Determine the key of the last item in the array 371 $last = end($difference); 372 $last = key($difference); 373 374 $span = array(); 375 foreach ($difference as $name => $amount) 376 { 377 if ($amount === 0) 378 { 379 // Skip empty amounts 380 continue; 381 } 382 383 // Add the amount to the span 384 $span[] = ($name === $last ? ' and ' : ', ').$amount.' '.($amount === 1 ? inflector::singular($name) : $name); 385 } 386 387 // If the difference is less than 60 seconds, remove the preceding and. 388 if (count($span) === 1) 389 { 390 $span[0] = ltrim($span[0], 'and '); 391 } 392 393 // Replace difference by making the span into a string 394 $difference = trim(implode('', $span), ','); 395 } 396 elseif (is_int($difference)) 397 { 398 // Single-value return 399 $difference = $difference.' '.($difference === 1 ? inflector::singular($output) : $output); 400 } 401 402 return $difference; 403 } 404 405} // End date