getOffset(new DateTime('@' . mktime(0, 0, 0, 2, 1, date('Y')))); $offset = $offset / HOUR; # a date inside of DST $offset2 = $timezone->getOffset(new DateTime('@' . mktime(0, 0, 0, 8, 1, date('Y')))); $offset2 = $offset2 / HOUR; return abs($offset2 - $offset) > 0; } /** * Calculate the current GMT offset from a timezone string (respecting DST) * * @param string|DateTimeZone $timezone User's timezone string or DateTimeZone object * @return int Offset in hours * 2012-05-20 ms */ public function getGmtOffset($timezone = null) { $timezone = self::timezone($timezone); $offset = $timezone->getOffset(new DateTime('@' . time())); $offset = $offset / HOUR; return $offset; } /** * gets the timezone that is closest to the given coordinates * @return DateTimeZone Timezone object * 2012-05-20 ms */ public function timezoneByCoordinates($lat, $lng) { $current = array('timezone' => null, 'distance' => 0); $identifiers = DateTimeZone::listIdentifiers(); foreach ($identifiers as $identifier) { //TODO } return $current['timezone']; } /** * Calculate the difference between two dates * * TODO: deprecate in favor of DateTime::diff() etc which will be more precise * * should only be used for < month (due to the different month lenghts it gets fuzzy) * @param mixed $start (db format or timestamp) * @param mixex §end (db format or timestamp) * @return int: the distance in seconds * 2011-03-03 ms */ public static function difference($startTime = null, $endTime = null, $options = array()) { if (!is_int($startTime)) { $startTime = strtotime($startTime); } if (!is_int($endTime)) { $endTime = strtotime($endTime); } //@FIXME: make it work for > month return abs($endTime - $startTime); } /** * @param start date (if empty, use today) * @param end date (if empty, use today) * start and end cannot be both empty! * @param accuracy (year only = 0, incl months/days = 2) * if > 0, returns array!!! ('days'=>x,'months'=>y,'years'=>z) * * does this work too? $now = mktime(0,0,0,date("m"),date("d"),date("Y")); $birth = mktime(0,0,0, $monat, $tag, $jahr); $age = intval(($now - $birth) / (3600 * 24 * 365)); * @return int age (0 if both timestamps are equal or empty, -1 on invalid dates) * 2009-03-12 ms */ public static function age($start = null, $end = null, $accuracy = 0) { $age = 0; if (empty($start) && empty($end) || $start == $end) { return 0; } if (empty($start)) { list($yearS, $monthS, $dayS) = explode('-', date(FORMAT_DB_DATE)); } else { $startDate = self::fromString($start); $yearS = date('Y', $startDate); $monthS = date('m', $startDate); $dayS = date('d', $startDate); if (!checkdate($monthS, $dayS, $yearS)) { return -1; } } if (empty($end)) { list($yearE, $monthE, $dayE) = explode('-', date(FORMAT_DB_DATE)); } else { $endDate = self::fromString($end); $yearE = date('Y', $endDate); $monthE = date('m', $endDate); $dayE = date('d', $endDate); if (!checkdate($monthE, $dayE, $yearE)) { return -1; } } //$startDate = mktime(0,0,0, $monthS, $dayS, $yearS); //$endDate = mktime(0,0,0, $monthE, $dayE, $yearE); //$age = intval(($endDate - $startDate) / (3600 * 24 * 365)); //$age = self::timef($endDate-$startDate, 'Y'); # !!! timef function $n_tag = $dayE; $n_monat = $monthE; $n_jahr = $yearE; $g_tag = $dayS; $g_monat = $monthS; $g_jahr = $yearS; $g_date = mktime(0, 0, 0, $g_tag, $g_monat, $g_jahr); if (($n_monat>$g_monat)||(($n_monat == $g_monat)&&($n_tag>$g_tag))||(($n_monat == $g_monat)&&($n_tag==$g_tag))) { $age = $n_jahr-$g_jahr; // is correct if one already had his birthday this year } else { $age = $n_jahr-$g_jahr-1; // is correct if one didnt have his birthday yet in this year } return $age; //TODO: test this short method //return (date("Y",time()) - $val); } /** * try to return the age only with the year available * can be e.g. 22/23 * @param integer $year * @param integer $month (optional) * 2011-03-11 ms */ public static function ageByYear($year, $month = null) { if ($month === null) { $maxAge = self::age(mktime(0,0,0,1,1, $year)); $minAge = self::age(mktime(23,59,59,12,31, $year)); $ages = array_unique(array($minAge, $maxAge)); return implode('/', $ages); } if (date('n') == $month) { $maxAge = self::age(mktime(0, 0, 0, $month, 1, $year)); $minAge = self::age(mktime(23, 59, 59, $month, self::daysInMonth($year, $month), $year)); $ages = array_unique(array($minAge, $maxAge)); return implode('/', $ages); } return self::age(mktime(0, 0, 0, $month, 1, $year)); } /** * 2011-11-22 lb * @return mixed */ public static function ageByHoroscope($year, $sign) { App::uses('ZodiacLib', 'Tools.Misc'); $Zodiac = new ZodiacLib(); $range = $Zodiac->getRange($sign); if ($sign == ZodiacLib::SIGN_CAPRICORN) { // undefined return array(date('Y') - $year - 1, date('Y') - $year); } if($range[0][0] > date('m') || ($range[0][0] == date('m') && $range[0][1] > date('d'))) { // not over return date('Y') - $year - 1; } if ($range[1][0] < date('m') || ($range[1][0] == date('m') && $range[1][1] <= date('d'))) { // over return date('Y') - $year; } return array(date('Y') - $year - 1, date('Y') - $year); } /** * rounded age depended on steps (e.g. age 16 with steps = 10 => "11-20") * @FIXME * TODO: move to helper? * 2011-04-07 ms */ public static function ageRange($year, $month = null, $day = null, $steps = 1) { if ($month == null && $day == null) { $age = date('Y') - $year - 1; } elseif ($day == null) { if ($month >= date('m')) $age = date('Y') - $year - 1; else $age = date('Y') - $year; } else { if ($month > date('m') || ($month == date('m') && $day > date('d'))) $age = date('Y') - $year - 1; else $age = date('Y') - $year; } if ($age % $steps == 0) { $lowerRange = $age - $steps + 1; $upperRange = $age; } else { $lowerRange = $age - ($age % $steps) + 1; $upperRange = $age - ($age % $steps) + $steps; } if ($lowerRange == $upperRange) { return $upperRange; } return array($lowerRange, $upperRange); } /** * return the days of a given month * @param integer $year * @param integer $month * 2011-11-03 ms */ public static function daysInMonth($year, $month) { return date("t", mktime(0, 0, 0, $month, 1, $year)); } /** * Calendar Week (current week of the year) * @param date in DB format - if none is passed, current day is used * @param int relative - weeks relative to the date (+1 next, -1 previous etc) * @TODO: use timestamp - or make the function able to use timestamps at least (besides dateString) * * Mit date('W', $time) (großes W!) bekommst die ISO6801-Wochennummer des angegebenen Zeitpunkts, das entspricht der Europäischen Kalenderwoche - mit einer Ausnahme: Daten die zur letzten Kalenderwoche des vorherigen Jahres gehören, liefern die 0 zurück; in dem Fall solltest du dann die KW des 31.12. des Vorjahres ermitteln. */ public static function cweek($dateString = null, $relative = 0) { //$time = self::fromString($dateString); if (!empty($dateString)) { $date = explode(' ', $dateString); list ($y, $m, $d) = explode('-', $date[0]); $t = mktime(0, 0, 0, $m, $d, $y); } else { $d = date('d'); $m = date('m'); $y = date('Y'); $t = time(); } $relative = (int)$relative; if ($relative != 0) { $t += WEEK*$relative; // 1day * 7 * relativeWeeks } if (($kw = date('W', $t)) === 0) { $kw = 1+date($t-DAY*date('w', $t), 'W'); $y--; } //echo "Der $d.$m.$y liegt in der Kalenderwoche $kw/$y"; return $kw.'/'.$y; } /** * return the timestamp to a day in a specific cweek * 0=sunday to 7=saturday (default) * @return timestamp of the weekDay * @FIXME: offset * not needed, use localDate! */ public static function cweekDay($cweek, $year, $day, $offset = 0) { $cweekBeginning = self::cweekBeginning($year, $cweek); return $cweekBeginning + $day * DAY; } /** * @FIXME ??? * Get number of days since the start of the week. * 1 = monday, 7 = sunday ? should be 0=sunday to 7=saturday (default) * @param integer $num Number of day. * @return int Days since the start of the week. */ public static function cweekMod($num, $offset = 0) { $base = 7; return ($num - $base*floor($num/$base)); } /** * calculate the beginning of a calenderweek * if no cweek is given get the beginning of the first week of the year * @param year (format xxxx) * @param cweek (optional, defaults to first, range 1...52/53) * 2011-03-07 ms */ public static function cweekBeginning($year, $cweek = null) { if ((int)$cweek <= 1 || (int)$cweek > self::cweeks($year)) { $first = mktime(0,0,0,1,1, $year); $wtag = date('w', $first); if ($wtag<=4) { /*Donnerstag oder kleiner: auf den Montag zurückrechnen.*/ $firstmonday = mktime(0,0,0,1,1-($wtag-1), $year); } elseif ($wtag!=1) { /*auf den Montag nach vorne rechnen.*/ $firstmonday = mktime(0,0,0,1,1+(7-$wtag+1), $year); } else { $firstmonday = $first; } return $firstmonday; } $monday = strtotime($year.'W'.str_pad($cweek, 2, '0', STR_PAD_LEFT).'1'); return $monday; } /** * calculate the ending of a calenderweek * if no cweek is given get the ending of the last week of the year * @param year (format xxxx) * @param cweek (optional, defaults to last, range 1...52/53) * 2011-03-07 ms */ public static function cweekEnding($year, $cweek = null) { if ((int)$cweek < 1 || (int)$cweek >= self::cweeks($year)) { return self::cweekBeginning($year+1)-1; } return self::cweekBeginning($year, intval($cweek)+1)-1; } /** * calculate the amount of calender weeks in a year * @param year (format xxxx, defaults to current year) * @return int: 52 or 53 * 2011-03-07 ms */ public static function cweeks($year = null) { if ($year === null) { $year = date('Y'); } return date('W', mktime(23, 59, 59, 12, 28, $year)); } /** * @param year (format xxxx, defaults to current year) * @return bool $success * 2012-02-17 ms */ public static function isLeapYear($year) { if ($year % 4 != 0) { return false; } if ($year % 400 == 0) { return true; } if ($year > 1582 && $year % 100 == 0) { # if gregorian calendar (>1582), century not-divisible by 400 is not leap return false; } return true; } /** * Handles month/year increment calculations in a safe way, avoiding the pitfall of "fuzzy" month units. * * @param mixed $startDate Either a date string or a DateTime object * @param integer $years Years to increment/decrement * @param integer $months Months to increment/decrement * @param string|DateTimeZone $timezone Timezone string or DateTimeZone object * @return object DateTime with incremented/decremented month/year values. */ public static function incrementDate($startDate, $years = 0, $months = 0, $days = 0, $timezone = null) { if (!is_object($startDate)) { $startDate = new DateTime($startDate); $startDate->setTimezone($timezone ? new DateTimeZone($timezone) : self::timezone()); } $startingTimeStamp = $startDate->getTimestamp(); // Get the month value of the given date: $monthString = date('Y-m', $startingTimeStamp); // Create a date string corresponding to the 1st of the give month, // making it safe for monthly/yearly calculations: $safeDateString = "first day of $monthString"; // offset is wrong $months++; // Increment date by given month/year increments: $incrementedDateString = "$safeDateString $months month $years year"; $newTimeStamp = strtotime($incrementedDateString) + $days * DAY; $newDate = DateTime::createFromFormat('U', $newTimeStamp); return $newDate; } /** * get the age bounds (min, max) as timestamp that would result in the given age(s) * note: expects valid age (> 0 and < 120) * @param $firstAge * @param $secondAge (defaults to first one if not specified) * @return array('min'=>$min, 'max'=>$max); * 2011-07-06 ms */ public static function ageBounds($firstAge, $secondAge = null, $returnAsString = false, $relativeTime = null) { if ($secondAge === null) { $secondAge = $firstAge; } //TODO: other relative time then today should work as well $Date = new DateTime($relativeTime !== null ? $relativeTime : 'now'); $max = mktime(23, 23, 59, $Date->format('m'), $Date->format('d'), $Date->format('Y')-$firstAge); $min = mktime(0, 0, 1, $Date->format('m'), $Date->format('d')+1, $Date->format('Y')-$secondAge-1); if ($returnAsString) { $max = date(FORMAT_DB_DATE, $max); $min = date(FORMAT_DB_DATE, $min); } return array('min' => $min, 'max' => $max); } /** * for birthdays etc * @param date * @param string days with +- * @param options * 2011-03-03 ms */ public static function isInRange($dateString, $seconds, $options = array()) { //$newDate = is_int($dateString) ? $dateString : strtotime($dateString); //$newDate += $seconds; $newDate = time(); return self::difference($dateString, $newDate) <= $seconds; } /** * outputs Date(time) Sting nicely formatted (+ localized!) * @param string $dateString, * @param string $format (YYYY-MM-DD, DD.MM.YYYY) * @param array $options * - timezone: User's timezone * - default (defaults to "-----") * 2009-03-31 ms */ public static function localDate($dateString = null, $format = null, $options = array()) { $defaults = array('default' => '-----', 'timezone' => null); $options = array_merge($defaults, $options); if ($options['timezone'] === null && strlen($dateString) === 10) { $options['timezone'] = date_default_timezone_get(); } if ($dateString === null) { $dateString = time(); } $date = self::fromString($dateString, $options['timezone']); if ($date === null || $date === false || $date <= 0) { return $options['default']; } if ($format === null) { if (is_int($dateString) || strpos($dateString, ' ') !== false) { $format = FORMAT_LOCAL_YMDHM; } else { $format = FORMAT_LOCAL_YMD; } } return parent::_strftime($format, $date); } /** * outputs Date(time) Sting nicely formatted * @param string $dateString, * @param string $format (YYYY-MM-DD, DD.MM.YYYY) * @param array $options * - timezone: User's timezone * - default (defaults to "-----") * 2009-03-31 ms */ public static function niceDate($dateString = null, $format = null, $options = array()) { $defaults = array('default' => '-----', 'timezone' => null); $options = array_merge($defaults, $options); if ($options['timezone'] === null && strlen($dateString) === 10) { $options['timezone'] = date_default_timezone_get(); } if ($dateString === null) { $dateString = time(); } $date = self::fromString($dateString, $options['timezone']); if ($date === null || $date === false || $date <= 0) { return $options['default']; } if ($format === null) { if (is_int($dateString) || strpos($dateString, ' ') !== false) { $format = FORMAT_NICE_YMDHM; } else { $format = FORMAT_NICE_YMD; } } $ret = date($format, $date); if (!empty($options['oclock']) && $options['oclock']) { switch ($format) { case FORMAT_NICE_YMDHM: case FORMAT_NICE_YMDHMS: case FORMAT_NICE_YMDHM: case FORMAT_NICE_HM: case FORMAT_NICE_HMS: $ret .= ' '.__('o\'clock'); break; } } return $ret; } /** * return the translation to a specific week day * @param integer $day: * 0=sunday to 7=saturday (default numbers) * @param boolean $abbr (if abbreviation should be returned) * @param offset: 0-6 (defaults to 0) [1 => 1=monday to 7=sunday] * @return string $translatedText * 2011-12-07 ms */ public static function day($day, $abbr = false, $offset = 0) { $days = array( 'long' => array( 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ), 'short' => array( 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ) ); $day = (int) $day; pr($day); if ($offset) { $day = ($day + $offset) % 7; } pr($day); if ($abbr) { return __($days['short'][$day]); } return __($days['long'][$day]); } /** * return the translation to a specific week day * @param integer $month: * 1..12 * @param boolean $abbr (if abbreviation should be returned) * @param array $options * - appendDot (only for 3 letter abbr; defaults to false) * @return string $translatedText * 2011-12-07 ms */ public static function month($month, $abbr = false, $options = array()) { $months = array( 'long' => array( 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ), 'short' => array( 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ), ); $month = (int) ($month - 1); if (!$abbr) { return __($months['long'][$month]); } $monthName = __($months['short'][$month]); if (!empty($options['appendDot']) && strlen(__($months['long'][$month])) > 3) { $monthName .= '.'; } return $monthName; } /** * @return array (for forms etc) */ public static function months($monthKeys = array(), $options = array()) { if (!$monthKeys) { $monthKeys = range(1, 12); } $res = array(); $abbr = isset($options['abbr']) ? $options['abbr'] : false; foreach ($monthKeys as $key) { $res[str_pad($key, 2, '0', STR_PAD_LEFT)] = self::month($key, $abbr, $options); } return $res; } /** * weekdays * @return array (for forms etc) */ public static function days($dayKeys = array(), $options = array()) { if (!$dayKeys) { $dayKeys = range(0, 6); } $res = array(); $abbr = isset($options['abbr']) ? $options['abbr'] : false; $offset = isset($options['offset']) ? $options['offset'] : 0; foreach ($dayKeys as $key) { $res[$key] = self::day($key, $abbr, $offset); } return $res; } /** * can convert time from one unit to another * @param int INT | time * @param from CHAR * @param to CHAR * @param options: acc=>INT [accuracy], showZero=>BOOL, returnArray=>BOOL * 2008-11-06 ms */ public static function convertTime($int, $from, $to, $options = array()) { $accuracy = 0; # 0 = only the "to"-element, 1..n = higher accurancy $showZero = false; # show only the non-zero elements $returnArray = false; # return as array instead of as string if (!empty($options)) { if (isset($options['acc'])) { $accuracy = (int)$options['acc']; } if (isset($options['showZero'])) { $showZero = (int)$options['showZero']; } if (isset($options['returnArray'])) { $return = ($options['returnArray']===true?true:false); } } $times = array( 's'=>'0', 'm'=>'1', 'h'=>'2', 'd'=>'3', 'w'=>'4', 'm'=>'5', 'y'=>'6', ); $options = array( '0'=>array( 'steps'=>array('1'=>60,'2'=>3600,'3'=>86400,'4'=>86400*7,'5'=>86400*30,'6'=>86400*365), 'down'=>0, 'up'=>60, 'short'=>'s', 'long'=>'seconds' ), '1'=>array( 'steps'=>array('0'=>60,'2'=>60,'3'=>60*24,'4'=>60*24*7,'5'=>60*24*30,'6'=>60*24*365), 'down'=>60, 'up'=>60, 'short'=>'m', 'long'=>'minutes' ), '2'=>array( 'steps'=>array('0'=>3600,'1'=>60,'3'=>24,'4'=>24*7,'5'=>24*30,'6'=>24*365), 'down'=>60, 'up'=>24, 'short'=>'h', 'long'=>'hours' ), '3'=>array( 'steps'=>array('0'=>86400,'1'=>3600,'2'=>24,'4'=>7,'5'=>30,'6'=>365), 'down'=>24, 'up'=>7, 'short'=>'d', 'long'=>'days' ), '4'=>array( 'steps'=>array('0'=>86400*7,'1'=>60*24*7,'2'=>24*7,'3'=>7,'5'=>4.2,'6'=>52), 'down'=>7, 'up'=>4.2, 'short'=>'w', 'long'=>'weeks' ), '5'=>array( 'steps'=>array('0'=>86400*30,'1'=>60*24*30,'2'=>24*30,'3'=>30,'4'=>4.2,'6'=>12), 'down'=>4.2, 'up'=>12, 'short'=>'m', 'long'=>'months' ), '6'=>array( 'steps'=>array('0'=>86400*365,'1'=>60*24*365,'2'=>24*365,'3'=>365,'4'=>52,'5'=>12), 'down'=>12, 'up'=>0, 'short'=>'y', 'long'=>'years' ), ); if (array_key_exists($from, $times) && array_key_exists($to, $times)) { $begin = $times[$from]; $end = $times[$to]; } $minutes = $int; if ($minutes < 60) { return $minutes . 'min'; } $calculated = floor($minutes / 60) . "h " . ($minutes % 60) . "min"; if ($returnArray) { // return as array } else { // convert to the desired string } return $calculated; } /** * Returns the difference between a time and now in a "fuzzy" way. * Note that unlike [span], the "local" timestamp will always be the * current time. Displaying a fuzzy time instead of a date is usually * faster to read and understand. * * $span = fuzzy(time() - 10); // "moments ago" * $span = fuzzy(time() + 20); // "in moments" * * @param integer "remote" timestamp * @return string */ public static function fuzzy($timestamp) { // Determine the difference in seconds $offset = abs(time() - $timestamp); return self::fuzzyFromOffset($offset, $timestamp <= time()); } /** * @param integer $offset in seconds * @param boolean $past (defaults to null: return plain text) * - new: if not boolean but a string use this as translating text * @return string $text (i18n!) * 2011-03-06 ms */ public static function fuzzyFromOffset($offset, $past = null) { if ($offset <= MINUTE) { $span = 'moments'; } elseif ($offset < (MINUTE * 20)) { $span = 'a few minutes'; } elseif ($offset < HOUR) { $span = 'less than an hour'; } elseif ($offset < (HOUR * 4)) { $span = 'a couple of hours'; } elseif ($offset < DAY) { $span = 'less than a day'; } elseif ($offset < (DAY * 2)) { $span = 'about a day'; } elseif ($offset < (DAY * 4)) { $span = 'a couple of days'; } elseif ($offset < WEEK) { $span = 'less than a week'; } elseif ($offset < (WEEK * 2)) { $span = 'about a week'; } elseif ($offset < MONTH) { $span = 'less than a month'; } elseif ($offset < (MONTH * 2)) { $span = 'about a month'; } elseif ($offset < (MONTH * 4)) { $span = 'a couple of months'; } elseif ($offset < YEAR) { $span = 'less than a year'; } elseif ($offset < (YEAR * 2)) { $span = 'about a year'; } elseif ($offset < (YEAR * 4)) { $span = 'a couple of years'; } elseif ($offset < (YEAR * 8)) { $span = 'a few years'; } elseif ($offset < (YEAR * 12)) { $span = 'about a decade'; } elseif ($offset < (YEAR * 24)) { $span = 'a couple of decades'; } elseif ($offset < (YEAR * 64)) { $span = 'several decades'; } else { $span = 'a long time'; } if ($past === true) { // This is in the past return __('%s ago', __($span)); } if ($past === false) { // This in the future return __('in %s', __($span)); } if ($past !== null) { // Custom translation return __($past, __($span)); } return __($span); } /** * time length to human readable format * @param integer $seconds * @param string format: format * @param options * - boolean v: verbose * - boolean zero: if false: 0 days 5 hours => 5 hours etc. * - int: accuracy (how many sub-formats displayed?) //TODO * 2009-11-21 ms * @see timeAgoInWords() */ public static function lengthOfTime($seconds, $format = null, $options = array()) { $defaults = array('verbose'=>true, 'zero'=>false, 'separator'=>', ', 'default'=>''); $ret = ''; $j = 0; $options = array_merge($defaults, $options); if (!$options['verbose']) { $s = array( 'm' => 'mth', 'd' => 'd', 'h' => 'h', 'i' => 'm', 's' => 's' ); $p = $s; } else { $s = array( 'm' => ' ' . __('Month'), # translated 'd' => ' ' .__('Day'), 'h' => ' ' .__('Hour'), 'i' => ' ' .__('Minute'), 's' => ' ' .__('Second'), ); $p = array( 'm' => ' ' . __('Months'), # translated 'd' => ' ' . __('Days'), 'h' => ' ' . __('Hours'), 'i' => ' ' . __('Minutes'), 's' => ' ' . __('Seconds'), ); } if (!isset($format)) { //if (floor($seconds / MONTH) > 0) $format = 'Md'; if (floor($seconds / DAY) > 0) $format = 'Dh'; elseif (floor($seconds / 3600) > 0) $format = 'Hi'; elseif (floor($seconds / 60) > 0) $format = 'Is'; else $format = 'S'; } for ($i = 0; $i < mb_strlen($format); $i++) { switch (mb_substr($format, $i, 1)) { case 'D': $str = floor($seconds / 86400); break; case 'd': $str = floor($seconds / 86400 % 30); break; case 'H': $str = floor($seconds / 3600); break; case 'h': $str = floor($seconds / 3600 % 24); break; case 'I': $str = floor($seconds / 60); break; case 'i': $str = floor($seconds / 60 % 60); break; case 'S': $str = $seconds; break; case 's': $str = floor($seconds % 60); break; default: return ''; break; } if ($str > 0 || $j > 0 || $options['zero'] || $i == mb_strlen($format) - 1) { if ($j>0) { $ret .= $options['separator']; } $j++; $x = mb_strtolower(mb_substr($format, $i, 1)); if ($str == 1) { $ret .= $str . $s[$x]; } else { $title = $p[$x]; if (!empty($options['plural'])) { if (mb_substr($title, -1, 1) === 'e') { $title .= $options['plural']; } } $ret .= $str . $title; } } } return $ret; } /** * time relative to NOW in human readable format - absolute (negative as well as positive) * //TODO: make "now" adjustable * @param mixed $datestring * @param string format: format * @param options * - default, separator * - boolean zero: if false: 0 days 5 hours => 5 hours etc. * - verbose/past/future: string with %s or boolean true/false * 2009-11-21 ms */ public static function relLengthOfTime($dateString, $format = null, $options = array()) { if ($dateString !== null) { $timezone = null; $sec = time() - self::fromString($dateString, $timezone); $type = ($sec > 0)?-1:(($sec < 0)?1:0); $sec = abs($sec); } else { $sec = 0; $type = 0; } $defaults = array( 'verbose' => __('justNow'), 'zero' => false, 'separator' => ', ', 'future' => __('In %s'), 'past' => __('%s ago'), 'default' => ''); $options = array_merge($defaults, $options); $ret = self::lengthOfTime($sec, $format, $options); if ($type == 1) { if ($options['future'] !== false) { return sprintf($options['future'], $ret); } return array('future'=>$ret); } if ($type == -1) { if ($options['past'] !== false) { return sprintf($options['past'], $ret); } return array('past'=>$ret); } if ($options['verbose'] !== false) { return $options['verbose']; } return $options['default']; } /** * Convenience method to convert a given date * * @param string * @param string * @param integer $timezone User's timezone * @return string Formatted date */ public static function convertDate($oldDateString, $newDateFormatString, $timezone = null) { $Date = new DateTime($oldDateString, $timezone); return $Date->format($newDateFormatString); } /** * Returns true if given datetime string was day before yesterday. * * @param string $dateString Datetime string or Unix timestamp * @param integer $timezone User's timezone * @return boolean True if datetime string was day before yesterday */ public static function wasDayBeforeYesterday($dateString, $timezone = null) { $date = self::fromString($dateString, $timezone); return date(FORMAT_DB_DATE, $date) == date(FORMAT_DB_DATE, time()-2*DAY); } /** * Returns true if given datetime string is the day after tomorrow. * * @param string $dateString Datetime string or Unix timestamp * @param integer $timezone User's timezone * @return boolean True if datetime string is day after tomorrow */ public static function isDayAfterTomorrow($dateString, $timezone = null) { $date = self::fromString($dateString, $timezone); return date(FORMAT_DB_DATE, $date) == date(FORMAT_DB_DATE, time()+2*DAY); } /** * Returns true if given datetime string is not today AND is in the future. * * @param string $dateString Datetime string or Unix timestamp * @param integer $timezone User's timezone * @return boolean True if datetime is not today AND is in the future */ public static function isNotTodayAndInTheFuture($dateString, $timezone = null) { $date = self::fromString($dateString, $timezone); return date(FORMAT_DB_DATE, $date) > date(FORMAT_DB_DATE, time()); } /** * Returns true if given datetime string is not now AND is in the future. * * @param string $dateString Datetime string or Unix timestamp * @param integer $timezone User's timezone * @return boolean True if datetime is not today AND is in the future */ public static function isInTheFuture($dateString, $timezone = null) { $date = self::fromString($dateString, $timezone); return date(FORMAT_DB_DATETIME, $date) > date(FORMAT_DB_DATETIME, time()); } /** * try to parse date from various input formats * - DD.MM.YYYY, DD/MM/YYYY, YYYY-MM-DD, YYYY, YYYY-MM, ... * - i18n: Today, Yesterday, Tomorrow * @param string $date to parse * @param format to parse (null = auto) * @param type * - start: first second of this interval * - end: last second of this interval * @return int timestamp * 2011-11-19 ms */ public static function parseLocalizedDate($date, $format = null, $type = 'start') { $date = trim($date); $i18n = array( strtolower(__('Today')) => array('start'=>date(FORMAT_DB_DATETIME, mktime(0, 0, 0, date('m'), date('d'), date('Y'))), 'end'=>date(FORMAT_DB_DATETIME, mktime(23, 59, 59, date('m'), date('d'), date('Y')))), strtolower(__('Tomorrow')) => array('start'=>date(FORMAT_DB_DATETIME, mktime(0, 0, 0, date('m'), date('d'), date('Y'))+DAY), 'end'=>date(FORMAT_DB_DATETIME, mktime(23, 59, 59, date('m'), date('d'), date('Y'))+DAY)), strtolower(__('Yesterday')) => array('start'=>date(FORMAT_DB_DATETIME, mktime(0, 0, 0, date('m'), date('d'), date('Y'))-DAY), 'end'=>date(FORMAT_DB_DATETIME, mktime(23, 59, 59, date('m'), date('d'), date('Y'))-DAY)), strtolower(__('The day after tomorrow')) => array('start'=>date(FORMAT_DB_DATETIME, mktime(0, 0, 0, date('m'), date('d'), date('Y'))+2*DAY), 'end'=>date(FORMAT_DB_DATETIME, mktime(23, 59, 59, date('m'), date('d'), date('Y'))+2*DAY)), strtolower(__('The day before yesterday')) => array('start'=>date(FORMAT_DB_DATETIME, mktime(0, 0, 0, date('m'), date('d'), date('Y'))-2*DAY), 'end'=>date(FORMAT_DB_DATETIME, mktime(23, 59, 59, date('m'), date('d'), date('Y'))-2*DAY)), ); if (isset($i18n[strtolower($date)])) { return $i18n[strtolower($date)][$type]; } if ($format) { $res = DateTime::createFromFormat($format, $date); $res = $res->format(FORMAT_DB_DATE).' '.($type === 'end'?'23:59:59':'00:00:00'); return $res; } if (strpos($date, '.') !== false) { $explode = explode('.', $date, 3); $explode = array_reverse($explode); } elseif (strpos($date, '/') !== false) { $explode = explode('/', $date, 3); $explode = array_reverse($explode); } elseif (strpos($date, '-') !== false) { $explode = explode('-', $date, 3); } else { $explode = array($date); } if (isset($explode)) { for ($i = 0; $i < count($explode); $i++) { $explode[$i] = str_pad($explode[$i], 2, '0', STR_PAD_LEFT); } $explode[0] = str_pad($explode[0], 4, '20', STR_PAD_LEFT); if (count($explode) === 3) { return implode('-', $explode).' '.($type === 'end'?'23:59:59':'00:00:00'); } if (count($explode) === 2) { return implode('-', $explode).'-'.($type === 'end'?self::daysInMonth($explode[0], $explode[1]):'01').' '.($type === 'end' ? '23:59:59':'00:00:00'); } return $explode[0].'-'.($type === 'end'?'12':'01').'-'.($type === 'end'?'31':'01').' '.($type === 'end' ? '23:59:59':'00:00:00'); } return false; } /** * Parse a period (from ... to) * * @param string $searchString to parse * @param array $options * - separator (defaults to space [ ]) * - format (defaults to Y-m-d H:i:s) * @return array $period [0=>min, 1=>max] * 2011-11-18 ms */ public static function period($string, $options = array()) { if (strpos($string, ' ') !== false) { $filters = explode(' ', $string); $filters = array(array_shift($filters), array_pop($filters)); } else { $filters = array($string, $string); } $min = $filters[0]; $max = $filters[1]; //$x = preg_match('/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/', $date, $date_parts); //$x = Datetime::createFromFormat('Y-m-d', $string); //die(returns($x)); //$actualDateTime = new DateTime($min); //$actualDateTime->add(new DateInterval('P1M')); $min = self::parseLocalizedDate($min); $max = self::parseLocalizedDate($max, null, 'end'); //die($actualDateTime->format('Y-m-d')); //$searchParameters['conditions']['Coupon.date'] = $actualDateTime->format('Y-m-d'); /* if ($min == $max) { if (strlen($max) > 8) { $max = date(FORMAT_DB_DATE, strtotime($max)+DAY); } elseif (strlen($max) > 5) { $max = date(FORMAT_DB_DATE, strtotime($max)+MONTH); } else { $max = date(FORMAT_DB_DATE, strtotime($max)+YEAR+MONTH); } } $min = date(FORMAT_DB_DATE, strtotime($min)); $max = date(FORMAT_DB_DATE, strtotime($max)); */ return array($min, $max); } /** * @param string $searchString to parse * @param string $fieldname (Model.field) * @param array $options (see TimeLib::period) * @return string $query SQL Query * 2011-11-18 ms */ public static function periodAsSql($string, $fieldName, $options = array()) { $period = self::period($string, $options); return self::daysAsSql($period[0], $period[1], $fieldName); } /** * hours, minutes * e.g. 9.3 => 9.5 * 2010-11-03 ms */ public static function standardToDecimalTime($value) { $base = (int)$value; $tmp = $value-$base; $tmp *= 100; $tmp *= 1 / 60; $value = $base+$tmp; return $value; } /** * hours, minutes * e.g. 9.5 => 9.3 * with pad=2: 9.30 * 2010-11-03 ms */ public static function decimalToStandardTime($value, $pad = null, $decPoint = '.') { $base = (int)$value; $tmp = $value - $base; $tmp /= 1 / 60; $tmp /= 100; $value = $base + $tmp; if ($pad === null) { return $value; } return number_format($value, $pad, $decPoint, ''); } /** * parse 2,5 - 2.5 2:30 2:31:58 or even 2011-11-12 10:10:10 * now supports negative values like -2,5 -2,5 -2:30 -:30 or -4 * @param string * @return int: seconds * 2011-03-06 ms */ public static function parseTime($duration, $allowed = array(':', '.', ',')) { if (empty($duration)) { return 0; } $parts = explode(' ', $duration); $duration = array_pop($parts); if (strpos($duration, '.') !== false && in_array('.', $allowed)) { $duration = self::decimalToStandardTime($duration, 2, ':'); } elseif (strpos($duration, ',') !== false && in_array(',', $allowed)) { $duration = str_replace(',', '.', $duration); $duration = self::decimalToStandardTime($duration, 2, ':'); } # now there is only the time schema left... $pieces = explode(':', $duration, 3); $res = 0; $hours = abs((int)$pieces[0])*HOUR; //echo pre($hours); $isNegative = (strpos((string)$pieces[0], '-') !== false ? true : false); if (count($pieces) === 3) { $res += $hours + ((int)$pieces[1])*MINUTE + ((int)$pieces[2])*SECOND; } elseif (count($pieces) === 2) { $res += $hours + ((int)$pieces[1])*MINUTE; } else { $res += $hours; } if ($isNegative) { return -$res; } return $res; } /** * parse 2022-11-12 or 12.11.2022 or even 12.11.22 * @param string $date * @return int: seconds * 2011-03-09 ms */ public static function parseDate($date, $allowed = array('.', '-')) { $datePieces = explode(' ', $date, 2); $date = array_shift($datePieces); if (strpos($date, '.') !== false) { $pieces = explode('.', $date); $year = $pieces[2]; if (strlen($year) === 2) { if ($year < 50) { $year = '20' . $year; } else { $year = '19' . $year; } } $date = mktime(0, 0, 0, $pieces[1], $pieces[0], $year); } elseif (strpos($date, '-') !== false) { //$pieces = explode('-', $date); $date = strtotime($date); } else { return 0; } return $date; } /** * return strings like 2:30 (later //TODO: or 2:33:99) from seconds etc * @param int: seconds * @return string * 2011-03-06 ms */ public static function buildTime($duration, $mode = 'H:MM') { if ($duration < 0) { $duration = abs($duration); $isNegative = true; } $minutes = $duration % HOUR; $hours = ($duration - $minutes) / HOUR; $res = (int)$hours . ':' . str_pad(intval($minutes/MINUTE), 2, '0', STR_PAD_LEFT); if (strpos($mode, 'SS') !== false) { //TODO } if (!empty($isNegative)) { $res = '-' . $res; } return $res; } /** * return strings like 2:33:99 from seconds etc * @param int: seconds * @return string * 2011-03-09 ms */ public static function buildDefaultTime($duration) { $minutes = $duration % HOUR; $duration = $duration - $minutes; $hours = $duration / HOUR; $seconds = $minutes % MINUTE; return self::pad($hours) . ':' . self::pad($minutes / MINUTE) . ':' . self::pad($seconds / SECOND); } public static function pad($value, $length = 2) { return str_pad(intval($value), $length, '0', STR_PAD_LEFT); } }