<?php /** * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; under version 2 * of the License (non-upgradable). * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Copyright (c) 2008-2010 (original work) Deutsche Institut für Internationale Pädagogische Forschung (under the project TAO-TRANSFER); * 2009-2012 (update and modification) Public Research Centre Henri Tudor (under the project TAO-SUSTAIN & TAO-DEV); * */ use oat\tao\helpers\dateFormatter\EuropeanFormatter; use oat\tao\helpers\dateFormatter\DateFormatterInterface; /** * Utility to display dates. * * @author Joel Bout, <joel@taotesting.com> * @package tao * */ class tao_helpers_Date { const CONFIG_KEY = 'dateService'; const FORMAT_LONG = 0; const FORMAT_VERBOSE = 1; const FORMAT_DATEPICKER = 2; const FORMAT_ISO8601 = 3; const FORMAT_LONG_MICROSECONDS = 4; const FORMAT_INTERVAL_LONG = 100; const FORMAT_INTERVAL_SHORT = 101; const FORMAT_FALLBACK = -1; private static $service; /** * Returns configured date formatter. * * @return DateFormatterInterface */ public static function getDateFormatter() { if (is_null(self::$service)) { $ext = common_ext_ExtensionsManager::singleton()->getExtensionById('tao'); $service = $ext->getConfig(self::CONFIG_KEY); self::$service = $service instanceof DateFormatterInterface ? $service : new EuropeanFormatter(); } return self::$service; } /** * Displays a date/time * Should in theory be dependant on the users locale and timezone * @param mixed $timestamp * @param int $format The date format. See tao_helpers_Date's constants. * @param DateTimeZone $timeZone user timezone * @return string The formatted date. * @throws common_Exception when timestamp is not recognized */ public static function displayeDate($timestamp, $format = self::FORMAT_LONG, DateTimeZone $timeZone = null) { if (is_object($timestamp) && $timestamp instanceof core_kernel_classes_Literal) { $ts = $timestamp->__toString(); } elseif (is_object($timestamp) && $timestamp instanceof DateTimeInterface) { $ts = self::getTimeStampWithMicroseconds($timestamp); } elseif (is_numeric($timestamp)) { $ts = $timestamp; } elseif (is_string($timestamp) && preg_match('/.\+0000$/', $timestamp)) { $ts = self::getTimeStampWithMicroseconds(new DateTime($timestamp, new DateTimeZone('UTC'))); } elseif (is_string($timestamp) && preg_match('/0\.[\d]+\s[\d]+/', $timestamp)) { $ts = self::getTimeStamp($timestamp, true); } else { throw new common_Exception('Unexpected timestamp'); } return self::getDateFormatter()->format($ts, $format, $timeZone); } /** * * @author Lionel Lecaque, lionel@taotesting.com * @param unknown $interval * @param integer $format * @return string|Ambigous <string, string> */ public static function displayInterval($interval, $format = self::FORMAT_INTERVAL_LONG) { if (is_object($interval)) { $intervalObj = $interval; } else { $intervalObj = new DateTime(); $intervalObj->setTimestamp($interval); } $newDate = new \DateTime(); $intervalObj = $intervalObj instanceof DateTimeInterface ? $newDate->diff($intervalObj, true) : $intervalObj; if (! $intervalObj instanceof DateInterval) { common_Logger::w('Unknown interval format ' . get_class($interval) . ' for ' . __FUNCTION__); return ''; } $formatStrings = self::getNonNullIntervalFormats($intervalObj); if (empty($formatStrings)) { $returnValue = __("less than a minute"); } else { $returnValue = ''; switch ($format) { case self::FORMAT_INTERVAL_SHORT: $returnValue = $intervalObj->format(array_shift($formatStrings)); break; case self::FORMAT_INTERVAL_LONG: $returnValue = self::formatElapsed($intervalObj, $formatStrings); break; default: common_Logger::w('Unknown date format ' . $format . ' for ' . __FUNCTION__); } } return $returnValue; } /** * * @author Lionel Lecaque, lionel@taotesting.com * @param DateInterval $interval * @param unknown $formatStrings * @return string */ protected static function formatElapsed(DateInterval $interval, $formatStrings) { $string = ''; while (! empty($formatStrings)) { $string .= $interval->format(array_shift($formatStrings)) . (count($formatStrings) == 0 ? '' : (count($formatStrings) == 1 ? __(' and ') : ' ')); } return $string; } /** * * @author Lionel Lecaque, lionel@taotesting.com * @param DateInterval $interval * @return multitype:string Ambigous <string, string> */ private static function getNonNullIntervalFormats(DateInterval $interval) { $formats = []; if ($interval->y > 0) { $formats[] = $interval->y == 1 ? __("%y year") : __("%y years"); } if ($interval->m > 0) { $formats[] = $interval->m == 1 ? __("%m month") : __("%m months"); } if ($interval->d > 0) { $formats[] = $interval->d == 1 ? __("%d day") : __("%d days"); } if ($interval->h > 0) { $formats[] = $interval->h == 1 ? __("%h hour") : __("%h hours"); } if ($interval->i > 0) { $formats[] = $interval->i == 1 ? __("%i minute") : __("%i minutes"); } return $formats; } /** * * @author Lionel Lecaque, lionel@taotesting.com * @param unknown $microtime * @return number */ static function getTimeStamp($microtime, $microseconds = false) { $parts = array_reverse(explode(" ", $microtime)); if ($microseconds && isset($parts[1])) { $round = sprintf('%0.6f', $parts[1]); if ($round === '1.000000') { // Edge case -> rounded up to the second. $timestamp = '' . (intval($parts[0]) + 1) . '.000000'; } else { $timestamp = $parts[0] . '.' . str_replace('0.', '', $round); } } else { $timestamp = $parts[0]; } return $timestamp; } static function getTimeStampWithMicroseconds(DateTime $dt) { return join('.', [$dt->getTimestamp(), $dt->format('u')]); } /** * Get array of DateTime objects build from $date (or current time if not given) $amount times back with given interval * Example: * $timeKeys = $service->getTimeKeys(new \DateInterval('PT1H'), new \DateTime('now'), 24); * * array ( * 0 => * DateTime::__set_state(array( * 'date' => '2017-04-24 08:00:00.000000', * 'timezone_type' => 1, * 'timezone' => '+00:00', * )), * 1 => * DateTime::__set_state(array( * 'date' => '2017-04-24 07:00:00.000000', * 'timezone_type' => 1, * 'timezone' => '+00:00', * )), * 2 => * DateTime::__set_state(array( * 'date' => '2017-04-24 06:00:00.000000', * 'timezone_type' => 1, * 'timezone' => '+00:00', * )), * ... * ) * * @param \DateInterval $interval * @param \DateTimeInterface|null $date * @param null $amount * @return \DateTime[] */ public static function getTimeKeys(\DateInterval $interval, \DateTimeInterface $date = null, $amount = null) { $timeKeys = []; if ($date === null) { $date = new \DateTime('now', new \DateTimeZone('UTC')); } if ($interval->format('%i') > 0) { $date->setTime($date->format('H'), $date->format('i') + 1, 0); $amount = $amount === null ? 60 : $amount; } if ($interval->format('%h') > 0) { $date->setTime($date->format('H') + 1, 0, 0); $amount = $amount === null ? 24 : $amount; } if ($interval->format('%d') > 0) { $date->setTime(0, 0, 0); $date->setDate($date->format('Y'), $date->format('m'), $date->format('d') + 1); $amount = $amount === null ? cal_days_in_month(CAL_GREGORIAN, $date->format('m'), $date->format('Y')) : $amount; } if ($interval->format('%m') > 0) { $date->setTime(0, 0, 0); $date->setDate($date->format('Y'), $date->format('m') + 1, 1); $amount = $amount === null ? 12 : $amount; } while ($amount > 0) { $timeKeys[] = new \DateTime($date->format(\DateTime::ISO8601), new \DateTimeZone('UTC')); $date->sub($interval); $amount--; } return $timeKeys; } }