Skip to content

Instantly share code, notes, and snippets.

@kylefarris
Created August 26, 2011 14:10
Show Gist options
  • Save kylefarris/1173470 to your computer and use it in GitHub Desktop.
Save kylefarris/1173470 to your computer and use it in GitHub Desktop.
Codeigniter Date Library
<?php
class CI_Date {
public $sec_in_min, $min_in_hour, $hr_in_day, $day_in_wk, $sec_in_wk, $sec_in_day, $min_in_wk, $min_in_day, $hr_in_wk;
public function __construct() {
$this->sec_in_min = 60;
$this->min_in_hr = 60;
$this->hr_in_day = 24;
$this->day_in_wk = 7;
$this->day_in_yr = 365;
$this->sec_in_hr = $this->sec_in_min * $this->min_in_hr;
$this->sec_in_wk = $this->sec_in_min * $this->min_in_hr * $this->hr_in_day * $this->day_in_wk;
$this->sec_in_day = $this->sec_in_min * $this->min_in_hr * $this->hr_in_day;
$this->sec_in_yr = $this->sec_in_day * $this->day_in_yr;
$this->sec_in_mo = $this->sec_in_yr / 12;
$this->min_in_wk = $this->min_in_hr * $this->hr_in_day * $this->day_in_wk;
$this->min_in_day = $this->min_in_hr * $this->hr_in_day;
$this->hr_in_wk = $this->hr_in_day * $this->day_in_wk;
}
// *****************************************************************************
// convert_datetime_to_epoch( $datetime )
// -- takes datetime and converts it to seconds since epoch
// *****************************************************************************
public function convert_datetime_to_epoch( $datetime = '1971-01-01 12:00:00' ) {
list( $date, $time ) = explode( ' ', $datetime );
list( $year, $month, $day) = explode( '-', $date );
list( $hour, $min, $sec) = explode( ':', $time );
return mktime( $hour, $min, $sec, $month, $day, $year );
}
// *****************************************************************************
// Takes time in seconds since epoch and converts it to a datetime (Y-m-d h:i:s)
// If not time is given, the current time is used.
// -----
// @param Integer $epoch The number of seconds since Jan 1 1971 @ 00:00:00.
// @return String Datetime formatted from epoch time.
// *****************************************************************************
public function convert_epoch_to_datetime($epoch=false) {
if(!$epoch) { $epoch = time(); }
return date('Y-m-d h:i:s',$epoch);
}
// *****************************************************************************
// Takes one of those wierd time values from AD/LDAP found in pwdLastSet and
// stuff like that and converts it to a unix epoch timestamp.
// -----
// @param Integer $ldap_date Crazy time like this: 129095083680625000
// @return Integer Unix seconds since epoch.
// *****************************************************************************
public function convert_ldap_to_epoch($ldap_date=false) {
if(empty($ldap_date)) return 0;
$years_from_1601_to_1970 = 1970 - 1601;
$days_from_1601_to_1970 = $years_from_1601_to_1970 * 365;
$days_from_1601_to_1970 += ($years_from_1601_to_1970 / 4); // leap years
$days_from_1601_to_1970 -= 3; // non-leap centuries (1700,1800,1900). 2000 is a leap century
$seconds_from_1601_to_1970 = $days_from_1601_to_1970 * 24 * 60 * 60;
$total_seconds_since_1601 = ($ldap_date / 10000000);
$total_seconds_since_1970 = $total_seconds_since_1601 - $seconds_from_1601_to_1970;
return $total_seconds_since_1970;
}
// *****************************************************************************
// Converts a given date/time string and converts it to the ISO 8601 datetime
// standard. The format is as such: 2004-02-12T15:19:21+00:00.
// If `$datetime` is empty, the current time is used.
// -----
// @param String $datetime The date/time in some 'valid' format to convert.
// @return String ISO 8601 date/time string.
// *****************************************************************************
public function convert_to_iso_8601($datetime) {
if(empty($datetime))
$datetime = time();
else
$datetime = strtotime($datetime);
if($this->is_valid_date($datetime))
return date('c',$datetime);
return date('c',0);
}
// *****************************************************************************
// convert_google_to_epoch( $google_time )
// -- Converts google date/time format (2009-04-28T10:00:00.000-04:00) to unix time
// *****************************************************************************
public function convert_google_to_epoch($google_time='1971-01-01T12:00:00.000-05:00') {
if(strpos($google_time,'T') === false) {
// Assume only date is given (yyyy-mm-dd)
list( $year, $month, $day) = explode( '-', $google_time );
return mktime( 0,0,0, $month, $day, $year );
}
list($date,$time) = explode('T',$google_time);
list($time,$timezone) = explode('.',$time);
list( $year, $month, $day) = explode( '-', $date );
list( $hour, $min, $sec) = explode( ':', $time );
return mktime( $hour, $min, $sec, $month, $day, $year );
}
// *****************************************************************************
// convert_to_time_ago( $datefrom, $dateto )
// -- Tells how long ago a time (in seconds since epoch) occured.
// http://www.sajithmr.com/php-time-ago-calculation/
// *****************************************************************************
public function convert_to_time_ago( $datefrom, $dateto=-1 ) {
return $this->convert_to_easy_time( $datefrom, $dateto );
}
public function convert_to_easy_time( $datefrom, $dateto=-1,$easy_measure='ago' ) {
if( $datefrom<=0 ) return "before the 70's... wow...!";
if( $dateto==-1 ) $dateto = time();
$difference = $dateto - $datefrom;
// If difference is less than 60 seconds, seconds is a good interval of choice
if($difference < 60) {
$interval = "s";
}
// If difference is between 60 seconds and 60 minutes, minutes is a good interval
elseif($difference >= 60 && $difference<60*60) {
$interval = "n";
}
// If difference is between 1 hour and 24 hours hours is a good interval
elseif($difference >= 60*60 && $difference<60*60*24) {
$interval = "h";
}
// If difference is between 1 day and 7 days days is a good interval
elseif($difference >= 60*60*24 && $difference<60*60*24*7) {
$interval = "d";
}
// If difference is between 1 week and 30 days weeks is a good interval
elseif($difference >= 60*60*24*7 && $difference < 60*60*24*30) {
$interval = "ww";
}
// If difference is between 30 days and 365 days months is a good interval, again, the same thing
// applies, if the 29th February happens to exist between your 2 dates, the function will return
// the 'incorrect' value for a day
elseif($difference >= 60*60*24*30 && $difference < 60*60*24*365) {
$interval = "m";
}
// If difference is greater than or equal to 365days, return year. This will be incorrect if
// for example, you call the function on the 28th April 2008 passing in 29th April 2007. It will return
// 1 year ago when in actual fact (yawn!) not quite a year has gone by
elseif($difference >= 60*60*24*365) {
$interval = "y";
}
// Based on the interval, determine the number of units between the two dates
// From this point on, you would be hard pushed telling the difference between
// this function and DateDiff. If the $datediff returned is 1, be sure to return the singular
// of the unit, e.g. 'day' rather 'days'
switch($interval) {
case "m":
$months_difference = floor($difference / 60 / 60 / 24 /
29);
while (mktime(date("H", $datefrom), date("i", $datefrom),
date("s", $datefrom), date("n", $datefrom)+($months_difference),
date("j", $dateto), date("Y", $datefrom)) < $dateto) {
$months_difference++;
}
$datediff = $months_difference;
// We need this in here because it is possible
// to have an 'm' interval and a months
// difference of 12 because we are using 29 days
// in a month
if($datediff==12) {
$datediff--;
}
$res = ($datediff==1) ? "$datediff month $easy_measure" : "$datediff months $easy_measure";
break;
case "y":
$datediff = floor($difference / 60 / 60 / 24 / 365);
$res = ($datediff==1) ? "$datediff year $easy_measure" : "$datediff years $easy_measure";
break;
case "d":
$datediff = floor($difference / 60 / 60 / 24);
$res = ($datediff==1) ? "$datediff day $easy_measure" : "$datediff days $easy_measure";
break;
case "ww":
$datediff = floor($difference / 60 / 60 / 24 / 7);
$res = ($datediff==1) ? "$datediff week $easy_measure" : "$datediff weeks $easy_measure";
break;
case "h":
$datediff = floor($difference / 60 / 60);
$res = ($datediff==1) ? "$datediff hour $easy_measure" : "$datediff hours $easy_measure";
break;
case "n":
$datediff = floor($difference / 60);
$res = ($datediff==1) ? "$datediff minute $easy_measure" : "$datediff minutes $easy_measure";
break;
case "s":
$datediff = $difference;
$res = ($datediff==1) ? "$datediff second $easy_measure" : "$datediff seconds $easy_measure";
break;
}
return $res;
}
// *****************************************************************************
// Deducts the given number of seconds from a given datetime and returns the modified
// MySQL datetime string.
// -----
// @param String $datetime - the valid MySQL datetime to be modified
// @param Integer $seconds - the number of seconds to be deducted from datetime
// @hint - use this format for seconds: 2*60*60 (2 hrs * 60 mins * 60 secs), for instance.
// @Return String - the new datetime with deducted seconds
// *****************************************************************************
public function deduct_time_from_datetime($datetime,$seconds) {
if(is_valid_datetime($datetime)) {
$time = (int)$this->convert_datetime_to_epoch($datetime)-(int)$seconds;
$time = $this->convert_epoch_to_datetime($time);
return $time;
}
return false;
}
// ***************************************************************************************
// Establishes a date range from the most recent previous Sunday to the next-most Saturday.
// -----
// @return Array - an array containin the start date and end date described above.
// ***************************************************************************************
public function get_current_week_range($current_date=false) {
if($current_date === false) { $current_date = time(); }
// figure out date range info
$one_day = 24*60*60;
$today = getdate($current_date);
// Use 1 am so DST spring forward doesn't mess up our date calculation below
$ts = mktime(1, 0, 0, $today['mon'], $today['mday'], $today['year']);
// adjust the timestamp to Sunday
if($today['wday'] != 0) {
$ts -= $today['wday'] * $one_day;
}
$start_ts = $ts; // keep track of the starting timestamp
$start_date = date("Y-m-d", $ts);
$end_date = date("Y-m-d", ( $ts + (6 * $one_day)) );
$date_range = array('start_date'=>$start_date,'end_date'=>$end_date);
return $date_range;
}
// *****************************************************************************
// Formats a timestamp (seconds since epoch) to its wordy cousin:
// "Monday, the 27th of October 2008"
// -----
// @param Unix Timestamp $when Either 'now' or a valid unix timestamp.
// *****************************************************************************
public function get_formatted_date( $when = 'now' ) {
if( $when == 'now' ) {
return date( 'l, \t\h\e jS \of F Y' );
}
return date( 'l, \t\h\e jS \of F Y', $when );
}
// *****************************************************************************
// Takes two times (24-hour range, formatted HH:MM) and returns an array
// of half-hour increments within the two given times
// -----
// @param String $start_time The time to tart giving 30 minute increments
// @param String $end_time The time to stop creating 30 minute increments
// *****************************************************************************
public function get_half_hour_time_range( $start_time='8:00', $end_time='18:00' ) {
list( $sh,$sm ) = explode( ':', $start_time );
list( $eh,$em ) = explode( ':', $end_time );
$timeRangeArray = array();
//return ;
while((int)$sh.$sm < (int)$eh.$em) {
if($sm == "00") {
$sm = "30";
} else {
$sm = "00";
$sh = (int)($sh+1);
}
$fullTime = date("h:ia",mktime((int)$sh,(int)$sm,0));
array_push($timeRangeArray,$fullTime);
if($sh < 10) {
$sh = "0"+$sh;
}
}
return $timeRangeArray;
}
// *****************************************************************************
// Returns the number of work days within the range of a provided start and end
// date. If no date is provided for either or both dates, defaults will be:
// -----
// @default Date $date1 today -1 week
// @default Date $date2 today
// -----
// @param Date $date1 - should be the start of the date range
// @param Date $date2 - should be the end of the date range.
// @return array
// *****************************************************************************
public function get_num_work_days($date1='',$date2='') {
if(empty($date1)) {
$date1 = strtotime(date('Y-m-d', strtotime('-1 week')) . " 00:00:00");
} else {
$date1 = strtotime($date1 . " 00:00:00");
}
if(empty($date2)) {
$date2 = strtotime(date('Y-m-d', time()) . " 23:59:59");
} else {
$date2 = strtotime($date2 . " 23:59:59");
}
if($date1 >= $date2) { $end_date = $date1; $start_date = $date2; }
if($date2 > $date1) { $end_date = $date2; $start_date = $date1; }
//echo "Start Date: {$start_date} (".date('Y-m-d H:i:s',$start_date).") | End Date: {$end_date} (".date('Y-m-d H:i:s',$end_date).")<br />";
$num_days = abs(($end_date - $start_date)) / $this->sec_in_day;
$num_weekdays = 0;
for($i=0;$i<=$num_days;$i++) {
$the_date = date('N',($start_date + ($this->sec_in_day * $i)));
if($the_date >= 1 && $the_date <= 5)
$num_weekdays++;
}
return $num_weekdays;
}
// ***************************************************************************************
// Returns the number of seconds in between two dates. Dates provided could be in any
// parsable date format. There is no inherent order in which the params need to be provided.
// -----
// @param String $t1 One of the two times in the span
// @param String $t2 One of the two times in the span
// @return Integer The number of seconds between the two times given.
// ***************************************************************************************
public function get_time_between($t1=0,$t2=0) {
$t1 = (preg_match('/^\d{6,}$/',$t1) ? $t1 : strtotime($t1));
$t2 = (preg_match('/^\d{6,}$/',$t2) ? $t2 : strtotime($t2));
if($t1 == 0 || $t2 == 0) {
$t1 = $this->nice_date($t1,'U');
$t2 = $this->nice_date($t2,'U');
//error_log("T1: $t1, T2: $t2");
if(empty($t2) || empty($t1) || $t2 == 'Unknown' || $t1 == 'Unknown')
return 'Unknown';
}
if($t2 >= $t1)
return ($t2 - $t1);
return ($t1 - $t2);
}
// *****************************************************************************
// Returns in a human-readable format, the amount of time left between the given
// time ($time) and now (time()). If time has already passed, let the user know.
// -----
// @param Integer $time - the timestamp that we will compare to now.
// @Return array
// *****************************************************************************
public function get_time_left($time,$day=true,$hr=true,$min=true,$sec=true,$week=false,$month=false,$year=false) {
$time = $time - time();
if($time > 0) {
$years = ($year ? floor($time/$this->sec_in_year) : 0);
$months = (($month || $year == 0) ? floor(($time-($years*$this->sec_in_mo))): 0);
$weeks = 0;
$days = ($day ? floor($time/$this->sec_in_day) : 0);
$hours = (($hr || $days == 0) ? floor(($time-($days*86400))/3600) : 0);
$mins = (($min || ($days == 0 && $hours == 0)) ? floor (($time-($days*86400)-($hours*3600))/60) : 0);
$secs = (($sec || ($days == 0 && $hours == 0 && $mins == 0)) ? floor ($time-($days*86400)-($hours*3600)-($mins*60)) : 0);
$expires_in = ($days > 0?"$days days, ":'').($hours > 0?"$hours hours, ":'').($mins > 0?"$mins mins, ":'').($secs > 0?"$secs secs. ":'');
$expires_in = rtrim($expires_in,',. ').'.';
return $expires_in;
} else {
return 'Time is up!';
}
}
// *****************************************************************************
// A more advanced/elaborate version of get_time_left method.
// -----
// @param Integer $future The timestamp that we will compare to now.
// @return String Legible amount of time until the $future date.
// *****************************************************************************
// $original should be the future date and time in unix format
public function time_to($future,$options=array(),$bad_wrap=array('<span class="error" style="display:inline">Your item is ',' old!</span>')) {
$defaults = array(
'year' => false,
'month' => false,
'week' => false,
'day' => false,
'hour' => false,
'min' => false,
'sec' => true,
);
$options = array_merge($defaults,$options);
if(!isset($this->time_left)) $this->time_left = '';
// Common time periods as an array of arrays
if(!isset($this->periods)) {
$this->periods = array(
($options['year'] ? array('year', $this->sec_in_yr) : false),
($options['month'] ? array('month', $this->sec_in_mo) : false),
($options['week'] ? array('week', $this->sec_in_wk) : false),
($options['day'] ? array('day', $this->sec_in_day): false),
($options['hour'] ? array('hour', $this->sec_in_hr) : false),
($options['min'] ? array('minute', $this->sec_in_min): false),
($options['sec'] ? array('second', 1): false),
);
}
$today = strtotime(date('Y-m-d',time()) . '00:00:00');
$since = $future - $today; // Find the difference of time between now and the future
if($since < 0) return $bad_wrap[0] . $this->convert_to_easy_time($future,-1,'') . $bad_wrap[1];
// Loop around the periods, starting with the biggest
if(count($this->periods) > 0) {
$chunk = array_shift($this->periods);
if(empty($chunk))
return $this->time_to($future,$options);
list($period,$seconds) = $chunk;
// Find the biggest whole period
$count = floor($since / $seconds);
if($count == 0) {
return $this->time_to($future,$options);
} else {
$this->time_left .= ($count == 1 ? "1 {$period}" : "{$count} {$period}s") . ', ';
return $this->time_to($future - ($count * $seconds),$options);
}
} else {
$time_left = $this->time_left;
unset($this->time_left);
unset($this->periods);
return rtrim($time_left,' ,');
}
}
// ****************************************************************************
// Basic Description
// -----
// @param
// @return
// ****************************************************************************
public function get_weekdays_in_timerange($date1=false,$date2=false) {
if(!empty($date1) && !empty($date2)) {
$seconds = $this->get_time_between($date1,$date2);
} else {
$seconds = $date1;
}
if($this->sec_in_day >= $seconds) {
if($this->sec_in_hr > $seconds) {
return 'Less than one work hour';
} else {
return round($seconds / $this->sec_in_hr) . ' work hours';
}
} else {
$final = '';
$days = $seconds / $this->sec_in_day;
$work_days = $days - (2 * floor($days / 7));
$remainder = $seconds - (($work_days * $this->sec_in_day) + (2 * floor($days / 7)));
if($this->sec_in_hr > $remainder) {
$final = "{$work_days} work days and less than one work hour";
} else {
$hours = round($remainder / $this->sec_in_hr) . ' work hours';
$final = "{$work_days} work days and {$hours} work hours";
}
}
}
// ****************************************************************************
// Uses Regex to determine if a given string is a valid MySQL datetime stamp.
// @param String $datetime - the MySQL datetime to be verified
// @hint - it should be yyyy-mm-dd hh:mm:ss
// @Return Boolean - true if valid, false if not.
// *****************************************************************************
public function is_valid_datetime($datetime) {
if ( !isset($datetime) ) return false;
if ( strlen($datetime) != 19 ) return false;
$array = explode( ' ', $datetime );
if ( count($array) != 2 ) return false;
if ( preg_match( '/^(\d\d\d\d)-(\d\d)-(\d\d)$/', $array[0], $matches ) ) {
if ( !checkdate($matches[2], $matches[3], $matches[1]) ) return false;
}
else {
return false;
}
if ( !preg_match( '/^(?:0\d|1\d|2[0-4]):(?:0\d|[1-5]\d):(?:0\d|[1-5]\d)$/', $array[1] ) )
return false;
return true;
}
// *****************************************************************************
// Tests to see if a time is a valid time (and not the epoch).
// -----
// @param String $datetime the MySQL datetime to be verified
// @return Boolean true if valid, false if not.
// *****************************************************************************
public function is_valid_date($datetime) {
if(!isset($datetime)) return false;
if(!date('U', strtotime($datetime)) == '0')
return false;
return true;
}
// ***************************************************************************************
// Converts crappy, fake date formats into whatever format you want...
// ***************************************************************************************
function nice_date($bad_date='',$format='Y-m-d') {
if(empty($bad_date))
return 'Unknown';
// Date like: YYYYMM
if(preg_match('/^\d{6}$/',$bad_date)) {
//echo $bad_date." ";
if(in_array(substr($bad_date,0,2),array('19','20'))) {
$year = substr($bad_date,0,4);
$month = substr($bad_date,4,2);
} else {
$month = substr($bad_date,0,2);
$year = substr($bad_date,2,4);
}
return date($format,strtotime($year . '-' . $month . '-01'));
}
// Date Like: YYYYMMDD
if(preg_match('/^\d{8}$/',$bad_date)) {
$month = substr($bad_date,0,2);
$day = substr($bad_date,2,2);
$year = substr($bad_date,4,4);
return date($format,strtotime($month . '/01/' . $year));
}
// Date Like: MM-DD-YYYY __or__ M-D-YYYY (or anything in between)
if(preg_match('/^\d{1,2}-\d{1,2}-\d{4}$/',$bad_date)) {
list($m,$d,$y) = explode('-',$bad_date);
return date($format,strtotime("{$y}-{$m}-{$d}"));
}
// Any other kind of string, when converted into UNIX time,
// produces "0 seconds after epoc..." is probably bad...
// return "Invalid Date".
if(!date('U', strtotime($bad_date)) == '0') {
return "Invalid Date"; //date($format,strtotime($bad_date));
}
// It's probably a valid-ish date format already
if($format != 'U')
return date($format,strtotime($bad_date));
return strtotime($bad_date);
}
}
@maikeldaloo
Copy link

very nice :)

@kylefarris
Copy link
Author

Wow, kinda forgot I had this up here. Glad you like it! It's just a compilation of date functions I've written over the years. Sorry if it has any bugs :D.

@maikeldaloo
Copy link

It looks useful. I haven't tested it, but I'll use it for any upcoming project that may require it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment