DatetimeLib.php 54 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854
  1. <?php
  2. /**
  3. * TODO: include all helper methods
  4. *
  5. *
  6. * 2011-03-03 ms
  7. */
  8. class DatetimeLib {
  9. protected $userOffset = null;
  10. protected $daylightSavings = false;
  11. public function __construct() {
  12. $i18n = Configure::read('Localization');
  13. if (!empty($i18n['time_offset'])) {
  14. $this->userOffset = (int)$i18n['time_offset'];
  15. }
  16. if (!empty($i18n['daylight_savings'])) {
  17. $this->daylightSavings = (bool)$i18n['daylight_savings'];
  18. }
  19. }
  20. /** custom stuff **/
  21. /**
  22. * calculate the difference between two dates
  23. * should only be used for < month (due to the different month lenghts it gets fuzzy)
  24. * @param mixed $start (db format or timestamp)
  25. * @param mixex §end (db format or timestamp)
  26. * @return int: the distance in seconds
  27. * 2011-03-03 ms
  28. */
  29. public function difference($startTime = null, $endTime = null, $options = array()) {
  30. if (!is_int($startTime)) {
  31. $startTime = strtotime($startTime);
  32. }
  33. if (!is_int($endTime)) {
  34. $endTime = strtotime($endTime);
  35. }
  36. //@FIXME: make it work for > month
  37. return abs($endTime - $startTime);
  38. }
  39. /**
  40. * @param start date (if empty, use today)
  41. * @param end date (if empty, use today)
  42. * start and end cannot be both empty!
  43. * @param accuracy (year only = 0, incl months/days = 2)
  44. * if > 0, returns array!!! ('days'=>x,'months'=>y,'years'=>z)
  45. *
  46. * does this work too?
  47. $now = mktime(0,0,0,date("m"),date("d"),date("Y"));
  48. $birth = mktime(0,0,0,$monat,$tag,$jahr);
  49. $age = intval(($now - $birth) / (3600 * 24 * 365));
  50. * @return int age (0 if both timestamps are equal or empty, -1 on invalid dates)
  51. * 2009-03-12 ms
  52. */
  53. public function age($start = null, $end = null, $accuracy = 0) {
  54. $age = 0;
  55. if (empty($start) && empty($end) || $start == $end) {
  56. return 0;
  57. }
  58. if (empty($start)) {
  59. list($yearS, $monthS, $dayS) = explode('-', date(FORMAT_DB_DATE));
  60. } else {
  61. $startDate = $this->fromString($start);
  62. $yearS = date('Y', $startDate);
  63. $monthS = date('m', $startDate);
  64. $dayS = date('d', $startDate);
  65. if (!checkdate($monthS, $dayS, $yearS)) {
  66. return -1;
  67. }
  68. }
  69. if (empty($end)) {
  70. list($yearE, $monthE, $dayE) = explode('-', date(FORMAT_DB_DATE));
  71. } else {
  72. $endDate = $this->fromString($end);
  73. $yearE = date('Y', $endDate);
  74. $monthE = date('m', $endDate);
  75. $dayE = date('d', $endDate);
  76. if (!checkdate($monthE, $dayE, $yearE)) {
  77. return -1;
  78. }
  79. }
  80. //$startDate = mktime(0,0,0,$monthS,$dayS,$yearS);
  81. //$endDate = mktime(0,0,0,$monthE,$dayE,$yearE);
  82. //$age = intval(($endDate - $startDate) / (3600 * 24 * 365));
  83. //$age = $this->timef($endDate-$startDate, 'Y'); # !!! timef function
  84. $n_tag = $dayE;
  85. $n_monat = $monthE;
  86. $n_jahr = $yearE;
  87. $g_tag = $dayS;
  88. $g_monat = $monthS;
  89. $g_jahr = $yearS;
  90. $g_date = mktime(0, 0, 0, $g_tag, $g_monat, $g_jahr);
  91. if (($n_monat>$g_monat)||(($n_monat == $g_monat)&&($n_tag>$g_tag))||(($n_monat == $g_monat)&&($n_tag==$g_tag))) {
  92. $age = $n_jahr-$g_jahr; // is correct if one already had his birthday this year
  93. } else {
  94. $age = $n_jahr-$g_jahr-1; // is correct if one didnt have his birthday yet in this year
  95. }
  96. return $age;
  97. //TODO: test this short method
  98. //return (date("Y",time()) - $val);
  99. }
  100. /**
  101. * try to return the age only with the year available
  102. * can be e.g. 22/23
  103. * @param int $year
  104. * @param int $month (optional)
  105. * 2011-03-11 ms
  106. */
  107. public function ageByYear($year, $month = null) {
  108. if ($month === null) {
  109. $maxAge = $this->age(mktime(0,0,0,1,1,$year));
  110. $minAge = $this->age(mktime(23,59,59,12,31,$year));
  111. $ages = array_unique(array($minAge, $maxAge));
  112. return implode('/', $ages);
  113. }
  114. if (date('n') == $month) {
  115. $maxAge = $this->age(mktime(0, 0, 0, $month, 1, $year));
  116. $minAge = $this->age(mktime(23, 59, 59, $month, $this->daysInMonth($year, $month), $year));
  117. $ages = array_unique(array($minAge, $maxAge));
  118. return implode('/', $ages);
  119. }
  120. return $this->age(mktime(0, 0, 0, $month, 1, $year));
  121. }
  122. /**
  123. * 2011-11-22 lb
  124. */
  125. public function ageByHoroscope($year, $sign) {
  126. App::uses('ZodiacLib', 'Tools.Misc');
  127. $Zodiac = new ZodiacLib();
  128. $range = $Zodiac->getRange($sign);
  129. // undefined
  130. if ($sign == ZodiacLib::SIGN_CAPRICORN) {
  131. return array(date('Y') - $year - 1, date('Y') - $year);
  132. }
  133. // not over
  134. elseif($range[0][0] > date('m') || ($range[0][0] == date('m') && $range[0][1] > date('d'))) {
  135. return date('Y') - $year - 1;
  136. }
  137. // over
  138. elseif ($range[1][0] < date('m') || ($range[1][0] == date('m') && $range[1][1] <= date('d'))) {
  139. return date('Y') - $year;
  140. } else {
  141. return array(date('Y') - $year - 1, date('Y') - $year);
  142. }
  143. }
  144. /**
  145. * rounded age depended on steps (e.g. age 16 with steps = 10 => "11-20")
  146. * @FIXME
  147. * TODO: move to helper?
  148. * 2011-04-07 ms
  149. */
  150. public function ageRange($year, $month = null, $day = null, $steps = 1) {
  151. if ($month == null && $day == null) {
  152. $age = date('Y') - $year - 1;
  153. } elseif ($day == null) {
  154. if ($month >= date('m'))
  155. $age = date('Y') - $year - 1;
  156. else
  157. $age = date('Y') - $year;
  158. } else {
  159. if ($month > date('m') || ($month == date('m') && $day > date('d')))
  160. $age = date('Y') - $year - 1;
  161. else
  162. $age = date('Y') - $year;
  163. }
  164. if ($age % $steps == 0) {
  165. $lowerRange = $age - $steps + 1;
  166. $upperRange = $age;
  167. } else {
  168. $lowerRange = $age - ($age % $steps) + 1;
  169. $upperRange = $age - ($age % $steps) + $steps;
  170. }
  171. if ($lowerRange == $upperRange)
  172. return $upperRange;
  173. else
  174. return array($lowerRange, $upperRange);
  175. }
  176. /**
  177. * return the days of a given month
  178. * @param int $year
  179. * @param int $month
  180. * 2011-11-03 ms
  181. */
  182. public static function daysInMonth($year, $month) {
  183. return date("t", mktime(0, 0, 0, $month, 1, $year));
  184. }
  185. /**
  186. * Calendar Week (current week of the year)
  187. * @param date in DB format - if none is passed, current day is used
  188. * @param int relative - weeks relative to the date (+1 next, -1 previous etc)
  189. * @TODO: use timestamp - or make the function able to use timestamps at least (besides dateString)
  190. *
  191. * 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.
  192. */
  193. public function cweek($dateString = null, $relative = 0) {
  194. //$time = $this->fromString($dateString);
  195. if (!empty($dateString)) {
  196. //pr ($dateString);
  197. $date = explode(' ', $dateString);
  198. list ($y, $m, $d) = explode('-', $date[0]);
  199. $t = mktime(0, 0, 0, $m, $d, $y);
  200. } else {
  201. $d = date('d');
  202. $m = date('m');
  203. $y = date('Y');
  204. $t = time();
  205. }
  206. $relative = (int)$relative;
  207. if ($relative != 0) {
  208. $t += WEEK*$relative; // 1day * 7 * relativeWeeks
  209. }
  210. if (0==($kw=date('W', $t))) {
  211. $kw = 1+date($t-DAY*date('w', $t), 'W');
  212. $y--;
  213. }
  214. //echo "Der $d.$m.$y liegt in der Kalenderwoche $kw/$y";
  215. return $kw.'/'.$y;
  216. }
  217. /**
  218. * return the timestamp to a day in a specific cweek
  219. * 0=sunday to 7=saturday (default)
  220. * @return timestamp of the weekDay
  221. * @FIXME: offset
  222. * not needed, use localDate!
  223. */
  224. public function cweekDay($cweek, $year, $day, $offset = 0) {
  225. $cweekBeginning = $this->cweekBeginning($year, $cweek);
  226. return $cweekBeginning + $day * DAY;
  227. }
  228. /**
  229. * @FIXME ???
  230. * Get number of days since the start of the week.
  231. * 1 = monday, 7 = sunday ? should be 0=sunday to 7=saturday (default)
  232. * @param int $num Number of day.
  233. * @return int Days since the start of the week.
  234. */
  235. public function cweekMod($num, $offset = 0) {
  236. $base = 7;
  237. return ($num - $base*floor($num/$base));
  238. }
  239. /**
  240. * calculate the beginning of a calenderweek
  241. * if no cweek is given get the beginning of the first week of the year
  242. * @param year (format xxxx)
  243. * @param cweek (optional, defaults to first, range 1...52/53)
  244. * 2011-03-07 ms
  245. */
  246. public function cweekBeginning($year, $cweek = null) {
  247. if ((int)$cweek <= 1 || (int)$cweek > self::cweeks($year)) {
  248. $first = mktime(0,0,0,1,1, $year);
  249. $wtag = date('w', $first);
  250. if ($wtag<=4) {
  251. /*Donnerstag oder kleiner: auf den Montag zurückrechnen.*/
  252. $firstmonday = mktime(0,0,0,1,1-($wtag-1),$year);
  253. } elseif ($wtag!=1) {
  254. /*auf den Montag nach vorne rechnen.*/
  255. $firstmonday = mktime(0,0,0,1,1+(7-$wtag+1),$year);
  256. } else {
  257. $firstmonday = $first;
  258. }
  259. return $firstmonday;
  260. }
  261. $monday = strtotime($year.'W'.str_pad($cweek, 2, '0', STR_PAD_LEFT).'1');
  262. return $monday;
  263. }
  264. /**
  265. * calculate the ending of a calenderweek
  266. * if no cweek is given get the ending of the last week of the year
  267. * @param year (format xxxx)
  268. * @param cweek (optional, defaults to last, range 1...52/53)
  269. * 2011-03-07 ms
  270. */
  271. public function cweekEnding($year, $cweek = null) {
  272. if ((int)$cweek < 1 || (int)$cweek >= self::cweeks($year)) {
  273. return self::cweekBeginning($year+1)-1;
  274. }
  275. return self::cweekBeginning($year, intval($cweek)+1)-1;
  276. }
  277. /**
  278. * calculate the amount of calender weeks in a year
  279. * @param year (format xxxx, defaults to current year)
  280. * @return int: 52 or 53
  281. * 2011-03-07 ms
  282. */
  283. public function cweeks($year = null) {
  284. if ($year === null) {
  285. $year = date('Y');
  286. }
  287. return date('W', mktime(23, 59, 59, 12, 28, $year));
  288. }
  289. /**
  290. * @param year (format xxxx, defaults to current year)
  291. * @return bool $success
  292. * 2012-02-17 ms
  293. */
  294. public function isLeapYear($year) {
  295. if ($year % 4 != 0) {
  296. return false;
  297. }
  298. if ($year % 400 == 0) {
  299. return true;
  300. }
  301. if ($year > 1582 && $year % 100 == 0) {
  302. # if gregorian calendar (>1582), century not-divisible by 400 is not leap
  303. return false;
  304. }
  305. return true;
  306. }
  307. /**
  308. * get the age bounds (min, max) as timestamp that would result in the given age(s)
  309. * note: expects valid age (> 0 and < 120)
  310. * @param $firstAge
  311. * @param $secondAge (defaults to first one if not specified)
  312. * @return array('min'=>$min, 'max'=>$max);
  313. * 2011-07-06 ms
  314. */
  315. public function ageBounds($firstAge, $secondAge = null, $returnAsString = false, $relativeTime = null) {
  316. if ($secondAge === null) {
  317. $secondAge = $firstAge;
  318. }
  319. //TODO: other relative time then today should work as well
  320. $max = mktime(23, 23, 59, date('m'), date('d'), date('Y')-$firstAge); // time()-($firstAge+1)*YEAR;
  321. $min = mktime(0, 0, 1, date('m'), date('d')+1, date('Y')-$secondAge-1); // time()+DAY-$secondAge*YEAR;
  322. if ($returnAsString) {
  323. $max = date(FORMAT_DB_DATE, $max);
  324. $min = date(FORMAT_DB_DATE, $min);
  325. }
  326. return array('min'=>$min, 'max'=>$max);
  327. }
  328. /**
  329. * for birthdays etc
  330. * @param date
  331. * @param string days with +-
  332. * @param options
  333. * 2011-03-03 ms
  334. */
  335. public function isInRange($dateString, $seconds, $options = array()) {
  336. //$newDate = is_int($dateString) ? $dateString : strtotime($dateString);
  337. //$newDate += $seconds;
  338. $newDate = time();
  339. return $this->difference($dateString, $newDate) <= $seconds;
  340. }
  341. /**
  342. * outputs Date(time) Sting nicely formatted (+ localized!)
  343. * @param string $dateString,
  344. * @param string $format (YYYY-MM-DD, DD.MM.YYYY)
  345. * @param array $options
  346. * - userOffset: User's offset from GMT (in hours)
  347. * - default (defaults to "-----")
  348. * 2009-03-31 ms
  349. */
  350. public function localDate($dateString = null, $format = null, $options = array()) {
  351. $defaults = array('default'=>'-----', 'userOffset'=>$this->userOffset);
  352. $options = array_merge($defaults, $options);
  353. $date = null;
  354. if ($dateString !== null) {
  355. $date = $this->fromString($dateString, $options['userOffset']);
  356. }
  357. if ($date === null || $date === false || $date <= 0) {
  358. return $options['default'];
  359. }
  360. if ($format === null) {
  361. if (is_int($dateString) || strpos($dateString, ' ') !== false) {
  362. $format = FORMAT_LOCAL_YMDHM;
  363. } else {
  364. $format = FORMAT_LOCAL_YMD;
  365. }
  366. }
  367. return strftime($format, $date);
  368. }
  369. /**
  370. * outputs Date(time) Sting nicely formatted
  371. * @param string $dateString,
  372. * @param string $format (YYYY-MM-DD, DD.MM.YYYY)
  373. * @param array $options
  374. * - userOffset: User's offset from GMT (in hours)
  375. * - default (defaults to "-----")
  376. * 2009-03-31 ms
  377. */
  378. public function niceDate($dateString = null, $format = null, $options = array()) {
  379. $defaults = array('default'=>'-----', 'userOffset'=>$this->userOffset);
  380. $options = array_merge($defaults, $options);
  381. $date = null;
  382. if ($dateString !== null) {
  383. $date = $this->fromString($dateString, $options['userOffset']);
  384. }
  385. if ($date === null || $date === false || $date <= 0) {
  386. return $options['default'];
  387. }
  388. if ($format === null) {
  389. if (is_int($dateString) || strpos($dateString, ' ') !== false) {
  390. $format = FORMAT_NICE_YMDHM;
  391. } else {
  392. $format = FORMAT_NICE_YMD;
  393. }
  394. }
  395. $ret = date($format, $date);
  396. if (!empty($options['oclock']) && $options['oclock']) {
  397. switch ($format) {
  398. case FORMAT_NICE_YMDHM:
  399. case FORMAT_NICE_YMDHMS:
  400. case FORMAT_NICE_YMDHM:
  401. case FORMAT_NICE_HM:
  402. case FORMAT_NICE_HMS:
  403. $ret .= ' '.__('o\'clock');
  404. break;
  405. }
  406. }
  407. return $ret;
  408. }
  409. /**
  410. * return the translation to a specific week day
  411. * @param int $day:
  412. * 0=sunday to 7=saturday (default numbers)
  413. * @param bool $abbr (if abbreviation should be returned)
  414. * @param offset: 0-6 (defaults to 0) [1 => 1=monday to 7=sunday]
  415. * @return string $translatedText
  416. * 2011-12-07 ms
  417. */
  418. public function day($day, $abbr = false, $offset = 0) {
  419. $days = array(
  420. 'long' => array(
  421. 'Sunday',
  422. 'Monday',
  423. 'Tuesday',
  424. 'Wednesday',
  425. 'Thursday',
  426. 'Friday',
  427. 'Saturday'
  428. ),
  429. 'short' => array(
  430. 'Sun',
  431. 'Mon',
  432. 'Tue',
  433. 'Wed',
  434. 'Thu',
  435. 'Fri',
  436. 'Sat'
  437. )
  438. );
  439. $day = (int) $day;
  440. pr($day);
  441. if ($offset) {
  442. $day = ($day + $offset) % 7;
  443. }
  444. pr($day);
  445. if ($abbr) {
  446. return __($days['short'][$day]);
  447. }
  448. return __($days['long'][$day]);
  449. }
  450. /**
  451. * return the translation to a specific week day
  452. * @param int $month:
  453. * 1..12
  454. * @param bool $abbr (if abbreviation should be returned)
  455. * @param array $options
  456. * - appendDot (only for 3 letter abbr; defaults to false)
  457. * @return string $translatedText
  458. * 2011-12-07 ms
  459. */
  460. public function month($month, $abbr = false, $options = array()) {
  461. $months = array(
  462. 'long' => array(
  463. 'January',
  464. 'February',
  465. 'March',
  466. 'April',
  467. 'May',
  468. 'June',
  469. 'July',
  470. 'August',
  471. 'September',
  472. 'October',
  473. 'November',
  474. 'December'
  475. ),
  476. 'short' => array(
  477. 'Jan',
  478. 'Feb',
  479. 'Mar',
  480. 'Apr',
  481. 'May',
  482. 'Jun',
  483. 'Jul',
  484. 'Aug',
  485. 'Sep',
  486. 'Oct',
  487. 'Nov',
  488. 'Dec'
  489. ),
  490. );
  491. $month = (int) ($month - 1);
  492. if (!$abbr) {
  493. return __($months['long'][$month]);
  494. }
  495. $monthName = __($months['short'][$month]);
  496. if (!empty($options['appendDot']) && strlen(__($months['long'][$month])) > 3) {
  497. $monthName .= '.';
  498. }
  499. return $monthName;
  500. }
  501. /**
  502. * @return array (for forms etc)
  503. */
  504. public function months($monthKeys = array(), $options = array()) {
  505. if (!$monthKeys) {
  506. $monthKeys = range(1, 12);
  507. }
  508. $res = array();
  509. $abbr = isset($options['abbr']) ? $options['abbr'] : false;
  510. foreach ($monthKeys as $key) {
  511. $res[str_pad($key, 2, '0', STR_PAD_LEFT)] = self::month($key, $abbr, $options);
  512. }
  513. return $res;
  514. }
  515. /**
  516. * weekdays
  517. * @return array (for forms etc)
  518. */
  519. public function days($dayKeys = array(), $options = array()) {
  520. if (!$dayKeys) {
  521. $dayKeys = range(0, 6);
  522. }
  523. $res = array();
  524. $abbr = isset($options['abbr']) ? $options['abbr'] : false;
  525. $offset = isset($options['offset']) ? $options['offset'] : 0;
  526. foreach ($dayKeys as $key) {
  527. $res[$key] = self::day($key, $abbr, $offset);
  528. }
  529. return $res;
  530. }
  531. /**
  532. * can convert time from one unit to another
  533. * @param int INT | time
  534. * @param from CHAR
  535. * @param to CHAR
  536. * @param options: acc=>INT [accuracy], showZero=>BOOL, returnArray=>BOOL
  537. * 2008-11-06 ms
  538. */
  539. public function convertTime($int, $from, $to, $options = array()) {
  540. $accuracy = 0; # 0 = only the "to"-element, 1..n = higher accurancy
  541. $showZero = false; # show only the non-zero elements
  542. $returnArray = false; # return as array instead of as string
  543. if (!empty($options)) {
  544. if (isset($options['acc'])) {
  545. $accuracy = (int)$options['acc'];
  546. }
  547. if (isset($options['showZero'])) {
  548. $showZero = (int)$options['showZero'];
  549. }
  550. if (isset($options['returnArray'])) {
  551. $return = ($options['returnArray']===true?true:false);
  552. }
  553. }
  554. $times = array(
  555. 's'=>'0',
  556. 'm'=>'1',
  557. 'h'=>'2',
  558. 'd'=>'3',
  559. 'w'=>'4',
  560. 'm'=>'5',
  561. 'y'=>'6',
  562. );
  563. $options = array(
  564. '0'=>array(
  565. 'steps'=>array('1'=>60,'2'=>3600,'3'=>86400,'4'=>86400*7,'5'=>86400*30,'6'=>86400*365),
  566. 'down'=>0,
  567. 'up'=>60,
  568. 'short'=>'s',
  569. 'long'=>'seconds'
  570. ),
  571. '1'=>array(
  572. 'steps'=>array('0'=>60,'2'=>60,'3'=>60*24,'4'=>60*24*7,'5'=>60*24*30,'6'=>60*24*365),
  573. 'down'=>60,
  574. 'up'=>60,
  575. 'short'=>'m',
  576. 'long'=>'minutes'
  577. ),
  578. '2'=>array(
  579. 'steps'=>array('0'=>3600,'1'=>60,'3'=>24,'4'=>24*7,'5'=>24*30,'6'=>24*365),
  580. 'down'=>60,
  581. 'up'=>24,
  582. 'short'=>'h',
  583. 'long'=>'hours'
  584. ),
  585. '3'=>array(
  586. 'steps'=>array('0'=>86400,'1'=>3600,'2'=>24,'4'=>7,'5'=>30,'6'=>365),
  587. 'down'=>24,
  588. 'up'=>7,
  589. 'short'=>'d',
  590. 'long'=>'days'
  591. ),
  592. '4'=>array(
  593. 'steps'=>array('0'=>86400*7,'1'=>60*24*7,'2'=>24*7,'3'=>7,'5'=>4.2,'6'=>52),
  594. 'down'=>7,
  595. 'up'=>4.2,
  596. 'short'=>'w',
  597. 'long'=>'weeks'
  598. ),
  599. '5'=>array(
  600. 'steps'=>array('0'=>86400*30,'1'=>60*24*30,'2'=>24*30,'3'=>30,'4'=>4.2,'6'=>12),
  601. 'down'=>4.2,
  602. 'up'=>12,
  603. 'short'=>'m',
  604. 'long'=>'months'
  605. ),
  606. '6'=>array(
  607. 'steps'=>array('0'=>86400*365,'1'=>60*24*365,'2'=>24*365,'3'=>365,'4'=>52,'5'=>12),
  608. 'down'=>12,
  609. 'up'=>0,
  610. 'short'=>'y',
  611. 'long'=>'years'
  612. ),
  613. );
  614. //echo $options[0]['steps']['4'];
  615. if (array_key_exists($from,$times) && array_key_exists($to,$times)) {
  616. $begin = $times[$from];
  617. $end = $times[$to];
  618. //echo $begin-$end.BR;
  619. }
  620. $minuten = $int;
  621. if ($minuten<60) {
  622. return $minuten.'min';
  623. }
  624. $calculated = floor($minuten/60)."h ".($minuten%60)."min";
  625. if ($returnArray) {
  626. // return as array
  627. } else {
  628. // convert to the desired string
  629. }
  630. return $calculated;
  631. }
  632. /**
  633. * @deprecated
  634. * NICHT TESTEN!
  635. */
  636. public function otherOne() {
  637. $day = floor($anz_sekunden/86400);
  638. $hours = floor(($anz_sekunden-(floor($anz_sekunden/86400)*86400))/3600);
  639. $minutes = floor(($anz_sekunden-(floor($anz_sekunden/3600)*3600))/60);
  640. $seconds = floor($anz_sekunden-(floor($anz_sekunden/60))*60);
  641. if ($day < 10) {
  642. $day = '0'.$day;
  643. }
  644. if ($hours < 10) {
  645. $hours = '0'.$hours;
  646. }
  647. if ($minutes < 10) {
  648. $minutes = '0'.$minutes;
  649. }
  650. if ($seconds < 10) {
  651. $seconds = '0'.$seconds;
  652. }
  653. if ($day > 0) {
  654. $zeit_ausgabe = $day.":".$hours.":".$minutes.":".$seconds;
  655. } else {
  656. $zeit_ausgabe = $hours.":".$minutes.":".$seconds." h";
  657. }
  658. }
  659. /**
  660. * Returns the difference between a time and now in a "fuzzy" way.
  661. * Note that unlike [span], the "local" timestamp will always be the
  662. * current time. Displaying a fuzzy time instead of a date is usually
  663. * faster to read and understand.
  664. *
  665. * $span = fuzzy(time() - 10); // "moments ago"
  666. * $span = fuzzy(time() + 20); // "in moments"
  667. *
  668. * @param integer "remote" timestamp
  669. * @return string
  670. */
  671. public function fuzzy($timestamp) {
  672. // Determine the difference in seconds
  673. $offset = abs(time() - $timestamp);
  674. return $this->fuzzyFromOffset($offset, $timestamp <= time());
  675. }
  676. /**
  677. * @param int $offset in seconds
  678. * @param boolean $past (defaults to null: return plain text)
  679. * - new: if not boolean but a string use this as translating text
  680. * @return string $text (i18n!)
  681. * 2011-03-06 ms
  682. */
  683. public function fuzzyFromOffset($offset, $past = null) {
  684. if ($offset <= MINUTE) {
  685. $span = 'moments';
  686. } elseif ($offset < (MINUTE * 20)) {
  687. $span = 'a few minutes';
  688. } elseif ($offset < HOUR) {
  689. $span = 'less than an hour';
  690. } elseif ($offset < (HOUR * 4)) {
  691. $span = 'a couple of hours';
  692. } elseif ($offset < DAY) {
  693. $span = 'less than a day';
  694. } elseif ($offset < (DAY * 2)) {
  695. $span = 'about a day';
  696. } elseif ($offset < (DAY * 4)) {
  697. $span = 'a couple of days';
  698. } elseif ($offset < WEEK) {
  699. $span = 'less than a week';
  700. } elseif ($offset < (WEEK * 2)) {
  701. $span = 'about a week';
  702. } elseif ($offset < MONTH) {
  703. $span = 'less than a month';
  704. } elseif ($offset < (MONTH * 2)) {
  705. $span = 'about a month';
  706. } elseif ($offset < (MONTH * 4)) {
  707. $span = 'a couple of months';
  708. } elseif ($offset < YEAR) {
  709. $span = 'less than a year';
  710. } elseif ($offset < (YEAR * 2)) {
  711. $span = 'about a year';
  712. } elseif ($offset < (YEAR * 4)) {
  713. $span = 'a couple of years';
  714. } elseif ($offset < (YEAR * 8)) {
  715. $span = 'a few years';
  716. } elseif ($offset < (YEAR * 12)) {
  717. $span = 'about a decade';
  718. } elseif ($offset < (YEAR * 24)) {
  719. $span = 'a couple of decades';
  720. } elseif ($offset < (YEAR * 64)) {
  721. $span = 'several decades';
  722. } else {
  723. $span = 'a long time';
  724. }
  725. if ($past === true) {
  726. // This is in the past
  727. return __('%s ago', __($span));
  728. } elseif ($past === false) {
  729. // This in the future
  730. return __('in %s', __($span));
  731. } elseif ($past !== null) {
  732. // Custom translation
  733. return __($past, __($span));
  734. }
  735. return __($span);
  736. }
  737. /**
  738. * time length to human readable format
  739. * @param int $seconds
  740. * @param string format: format
  741. * @param options
  742. * - boolean v: verbose
  743. * - boolean zero: if false: 0 days 5 hours => 5 hours etc.
  744. * - int: accuracy (how many sub-formats displayed?) //TODO
  745. * 2009-11-21 ms
  746. * @see timeAgoInWords()
  747. */
  748. public function lengthOfTime($seconds, $format = null, $options = array()) {
  749. $defaults = array('verbose'=>true, 'zero'=>false, 'separator'=>', ', 'default'=>'');
  750. $ret = '';
  751. $j = 0;
  752. $options = array_merge($defaults, $options);
  753. if (!$options['verbose']) {
  754. $s = array(
  755. 'm' => 'mth',
  756. 'd' => 'd',
  757. 'h' => 'h',
  758. 'i' => 'm',
  759. 's' => 's'
  760. );
  761. $p = $s;
  762. } else {
  763. $s = array(
  764. 'm' => ' '.__('Month'), # translated
  765. 'd' => ' '.__('Day'),
  766. 'h' => ' '.__('Hour'),
  767. 'i' => ' '.__('Minute'),
  768. 's' => ' '.__('Second'),
  769. );
  770. $p = array (
  771. 'm' => ' '.__('Months'), # translated
  772. 'd' => ' '.__('Days'),
  773. 'h' => ' '.__('Hours'),
  774. 'i' => ' '.__('Minutes'),
  775. 's' => ' '.__('Seconds'),
  776. );
  777. }
  778. if (!isset($format)) {
  779. //if (floor($seconds / MONTH) > 0) $format = "Md";
  780. if (floor($seconds / DAY) > 0) $format = "Dh";
  781. elseif (floor($seconds / 3600) > 0) $format = "Hi";
  782. elseif (floor($seconds / 60) > 0) $format = "Is";
  783. else $format = "S";
  784. }
  785. for ($i = 0; $i < mb_strlen($format); $i++) {
  786. switch (mb_substr($format, $i, 1)) {
  787. case 'D':
  788. $str = floor($seconds / 86400);
  789. break;
  790. case 'd':
  791. $str = floor($seconds / 86400 % 30);
  792. break;
  793. case 'H':
  794. $str = floor($seconds / 3600);
  795. break;
  796. case 'h':
  797. $str = floor($seconds / 3600 % 24);
  798. break;
  799. case 'I':
  800. $str = floor($seconds / 60);
  801. break;
  802. case 'i':
  803. $str = floor($seconds / 60 % 60);
  804. break;
  805. case 'S':
  806. $str = $seconds;
  807. break;
  808. case 's':
  809. $str = floor($seconds % 60);
  810. break;
  811. default:
  812. return "";
  813. break;
  814. }
  815. if ($str > 0 || $j > 0 || $options['zero'] || $i == mb_strlen($format) - 1) {
  816. if ($j>0) {
  817. $ret .= $options['separator'];
  818. }
  819. $j++;
  820. $x = mb_strtolower(mb_substr($format, $i, 1));
  821. if ($str == 1) {
  822. $ret .= $str . $s[$x];
  823. } else {
  824. $title = $p[$x];
  825. if (!empty($options['plural'])) {
  826. if (mb_substr($title, -1, 1) == 'e') {
  827. $title .= $options['plural'];
  828. }
  829. }
  830. $ret .= $str . $title;
  831. }
  832. }
  833. }
  834. return $ret;
  835. }
  836. /**
  837. * time relative to NOW in human readable format - absolute (negative as well as positive)
  838. * //TODO: make "now" adjustable
  839. * @param mixed $datestring
  840. * @param string format: format
  841. * @param options
  842. * - default, separator
  843. * - boolean zero: if false: 0 days 5 hours => 5 hours etc.
  844. * - verbose/past/future: string with %s or boolean true/false
  845. * 2009-11-21 ms
  846. */
  847. public function relLengthOfTime($dateString, $format = null, $options = array()) {
  848. if ($dateString != null) {
  849. $userOffset = null;
  850. $sec = time() - $this->fromString($dateString, $userOffset);
  851. $type = ($sec > 0)?-1:(($sec < 0)?1:0);
  852. $sec = abs($sec);
  853. } else {
  854. $sec = 0;
  855. $type = 0;
  856. }
  857. $defaults = array('verbose'=>__('justNow'), 'zero'=>false,'separator'=>', ', 'future'=>__('In %s'), 'past'=>__('%s ago'),'default'=>'');
  858. $options = array_merge($defaults, $options);
  859. $ret = $this->lengthOfTime($sec, $format, $options);
  860. if ($type == 1) {
  861. if ($options['future'] !== false) {
  862. return sprintf($options['future'], $ret);
  863. }
  864. return array('future'=>$ret);
  865. } elseif ($type == -1) {
  866. if ($options['past'] !== false) {
  867. return sprintf($options['past'], $ret);
  868. }
  869. return array('past'=>$ret);
  870. } else {
  871. if ($options['verbose'] !== false) {
  872. return $options['verbose'];
  873. }
  874. //return array('now'=>true);
  875. }
  876. return $options['default'];
  877. }
  878. /**
  879. * Returns true if given datetime string was day before yesterday.
  880. *
  881. * @param string $dateString Datetime string or Unix timestamp
  882. * @param int $userOffset User's offset from GMT (in hours)
  883. * @return boolean True if datetime string was day before yesterday
  884. */
  885. public function wasDayBeforeYesterday($dateString, $userOffset = null) {
  886. $date = $this->fromString($dateString, $userOffset);
  887. return date(FORMAT_DB_DATE, $date) == date(FORMAT_DB_DATE, time()-2*DAY);
  888. }
  889. /**
  890. * Returns true if given datetime string is the day after tomorrow.
  891. *
  892. * @param string $dateString Datetime string or Unix timestamp
  893. * @param int $userOffset User's offset from GMT (in hours)
  894. * @return boolean True if datetime string is day after tomorrow
  895. */
  896. public function isDayAfterTomorrow($dateString, $userOffset = null) {
  897. $date = $this->fromString($dateString, $userOffset);
  898. return date(FORMAT_DB_DATE, $date) == date(FORMAT_DB_DATE, time()+2*DAY);
  899. }
  900. /**
  901. * Returns true if given datetime string is not today AND is in the future.
  902. *
  903. * @param string $dateString Datetime string or Unix timestamp
  904. * @param int $userOffset User's offset from GMT (in hours)
  905. * @return boolean True if datetime is not today AND is in the future
  906. */
  907. public function isNotTodayAndInTheFuture($dateString, $userOffset = null) {
  908. $date = $this->fromString($dateString, $userOffset);
  909. return date(FORMAT_DB_DATE, $date) > date(FORMAT_DB_DATE, time());
  910. }
  911. /**
  912. * Returns true if given datetime string is not now AND is in the future.
  913. *
  914. * @param string $dateString Datetime string or Unix timestamp
  915. * @param int $userOffset User's offset from GMT (in hours)
  916. * @return boolean True if datetime is not today AND is in the future
  917. */
  918. public function isInTheFuture($dateString, $userOffset = null) {
  919. $date = $this->fromString($dateString, $userOffset);
  920. return date(FORMAT_DB_DATETIME, $date) > date(FORMAT_DB_DATETIME, time());
  921. }
  922. /** CORE TIME HELPER **/
  923. /**
  924. * Converts a string representing the format for the function strftime and returns a
  925. * windows safe and i18n aware format.
  926. *
  927. * @param string $format Format with specifiers for strftime function.
  928. * Accepts the special specifier %S which mimics th modifier S for date()
  929. * @param string UNIX timestamp
  930. * @return string windows safe and date() function compatible format for strftime
  931. * @access public
  932. */
  933. public function convertSpecifiers($format, $time = null) {
  934. if (!$time) {
  935. $time = time();
  936. }
  937. $this->__time = $time;
  938. return preg_replace_callback('/\%(\w+)/', array($this, '__translateSpecifier'), $format);
  939. }
  940. /**
  941. * Auxiliary function to translate a matched specifier element from a regular expresion into
  942. * a windows safe and i18n aware specifier
  943. *
  944. * @param array $specifier match from regular expression
  945. * @return string converted element
  946. * @access private
  947. */
  948. public function __translateSpecifier($specifier) {
  949. switch ($specifier[1]) {
  950. case 'a':
  951. $abday = __c('abday', 5);
  952. if (is_array($abday)) {
  953. return $abday[date('w', $this->__time)];
  954. }
  955. break;
  956. case 'A':
  957. $day = __c('day',5);
  958. if (is_array($day)) {
  959. return $day[date('w', $this->__time)];
  960. }
  961. break;
  962. case 'c':
  963. $format = __c('d_t_fmt',5);
  964. if ($format != 'd_t_fmt') {
  965. return $this->convertSpecifiers($format, $this->__time);
  966. }
  967. break;
  968. case 'C':
  969. return sprintf("%02d", date('Y', $this->__time) / 100);
  970. case 'D':
  971. return '%m/%d/%y';
  972. case 'e':
  973. if (DS === '/') {
  974. return '%e';
  975. }
  976. $day = date('j', $this->__time);
  977. if ($day < 10) {
  978. $day = ' ' . $day;
  979. }
  980. return $day;
  981. case 'eS' :
  982. return date('jS', $this->__time);
  983. case 'b':
  984. case 'h':
  985. $months = __c('abmon', 5);
  986. if (is_array($months)) {
  987. return $months[date('n', $this->__time) -1];
  988. }
  989. return '%b';
  990. case 'B':
  991. $months = __c('mon',5);
  992. if (is_array($months)) {
  993. return $months[date('n', $this->__time) -1];
  994. }
  995. break;
  996. case 'n':
  997. return "\n";
  998. case 'p':
  999. case 'P':
  1000. $default = array('am' => 0, 'pm' => 1);
  1001. $meridiem = $default[date('a',$this->__time)];
  1002. $format = __c('am_pm', 5);
  1003. if (is_array($format)) {
  1004. $meridiem = $format[$meridiem];
  1005. return ($specifier[1] == 'P') ? strtolower($meridiem) : strtoupper($meridiem);
  1006. }
  1007. break;
  1008. case 'r':
  1009. $complete = __c('t_fmt_ampm', 5);
  1010. if ($complete != 't_fmt_ampm') {
  1011. return str_replace('%p',$this->__translateSpecifier(array('%p', 'p')),$complete);
  1012. }
  1013. break;
  1014. case 'R':
  1015. return date('H:i', $this->__time);
  1016. case 't':
  1017. return "\t";
  1018. case 'T':
  1019. return '%H:%M:%S';
  1020. case 'u':
  1021. return ($weekDay = date('w', $this->__time)) ? $weekDay : 7;
  1022. case 'x':
  1023. $format = __c('d_fmt', 5);
  1024. if ($format != 'd_fmt') {
  1025. return $this->convertSpecifiers($format, $this->__time);
  1026. }
  1027. break;
  1028. case 'X':
  1029. $format = __c('t_fmt',5);
  1030. if ($format != 't_fmt') {
  1031. return $this->convertSpecifiers($format, $this->__time);
  1032. }
  1033. break;
  1034. }
  1035. return $specifier[0];
  1036. }
  1037. /**
  1038. * Converts given time (in server's time zone) to user's local time, given his/her offset from GMT.
  1039. *
  1040. * @param string $serverTime UNIX timestamp
  1041. * @param int $userOffset User's offset from GMT (in hours)
  1042. * @return string UNIX timestamp
  1043. * @access public
  1044. */
  1045. public function convert($serverTime, $userOffset) {
  1046. $serverOffset = $this->serverOffset();
  1047. $gmtTime = $serverTime - $serverOffset;
  1048. $userTime = $gmtTime + $userOffset * (60*60);
  1049. return $userTime;
  1050. }
  1051. /**
  1052. * Returns server's offset from GMT in seconds.
  1053. *
  1054. * @return int Offset
  1055. * @access public
  1056. */
  1057. public function serverOffset() {
  1058. return date('Z', time());
  1059. }
  1060. /**
  1061. * Returns a UNIX timestamp, given either a UNIX timestamp or a valid strtotime() date string.
  1062. *
  1063. * @param string $dateString Datetime string
  1064. * @param int $userOffset User's offset from GMT (in hours)
  1065. * @return string Parsed timestamp
  1066. * @access public
  1067. * @link http://book.cakephp.org/view/1471/Formatting
  1068. */
  1069. public function fromString($dateString, $userOffset = null) {
  1070. if (empty($dateString)) {
  1071. return false;
  1072. }
  1073. if (is_integer($dateString) || is_numeric($dateString)) {
  1074. $date = intval($dateString);
  1075. } else {
  1076. $date = strtotime($dateString);
  1077. }
  1078. if ($userOffset !== null) {
  1079. return $this->convert($date, $userOffset);
  1080. }
  1081. if ($date === -1) {
  1082. return false;
  1083. }
  1084. return $date;
  1085. }
  1086. /**
  1087. * Returns a nicely formatted date string for given Datetime string.
  1088. *
  1089. * @param string $dateString Datetime string or Unix timestamp
  1090. * @param int $userOffset User's offset from GMT (in hours)
  1091. * @return string Formatted date string
  1092. * @access public
  1093. * @link http://book.cakephp.org/view/1471/Formatting
  1094. */
  1095. public function nice($dateString = null, $userOffset = null) {
  1096. if ($dateString != null) {
  1097. $date = $this->fromString($dateString, $userOffset);
  1098. } else {
  1099. $date = time();
  1100. }
  1101. $format = $this->convertSpecifiers('%a, %b %eS %Y, %H:%M', $date);
  1102. return strftime($format, $date);
  1103. }
  1104. /**
  1105. * Returns a formatted descriptive date string for given datetime string.
  1106. *
  1107. * If the given date is today, the returned string could be "Today, 16:54".
  1108. * If the given date was yesterday, the returned string could be "Yesterday, 16:54".
  1109. * If $dateString's year is the current year, the returned string does not
  1110. * include mention of the year.
  1111. *
  1112. * @param string $dateString Datetime string or Unix timestamp
  1113. * @param int $userOffset User's offset from GMT (in hours)
  1114. * @return string Described, relative date string
  1115. * @access public
  1116. * @link http://book.cakephp.org/view/1471/Formatting
  1117. */
  1118. public function niceShort($dateString = null, $userOffset = null) {
  1119. $date = $dateString ? $this->fromString($dateString, $userOffset) : time();
  1120. $y = $this->isThisYear($date) ? '' : ' %Y';
  1121. if ($this->isToday($dateString, $userOffset)) {
  1122. $ret = __('Today, %s', strftime("%H:%M", $date));
  1123. } elseif ($this->wasYesterday($dateString, $userOffset)) {
  1124. $ret = __('Yesterday, %s', strftime("%H:%M", $date));
  1125. } else {
  1126. $format = $this->convertSpecifiers("%b %eS{$y}, %H:%M", $date);
  1127. $ret = strftime($format, $date);
  1128. }
  1129. return $ret;
  1130. }
  1131. /**
  1132. * Returns a partial SQL string to search for all records between two dates.
  1133. *
  1134. * @param string $dateString Datetime string or Unix timestamp
  1135. * @param string $end Datetime string or Unix timestamp
  1136. * @param string $fieldName Name of database field to compare with
  1137. * @param int $userOffset User's offset from GMT (in hours)
  1138. * @return string Partial SQL string.
  1139. * @access public
  1140. * @link http://book.cakephp.org/view/1471/Formatting
  1141. */
  1142. public function daysAsSql($begin, $end, $fieldName, $userOffset = null) {
  1143. $begin = $this->fromString($begin, $userOffset);
  1144. $end = $this->fromString($end, $userOffset);
  1145. $begin = date('Y-m-d', $begin) . ' 00:00:00';
  1146. $end = date('Y-m-d', $end) . ' 23:59:59';
  1147. return "($fieldName >= '$begin') AND ($fieldName <= '$end')";
  1148. }
  1149. /**
  1150. * Returns a partial SQL string to search for all records between two times
  1151. * occurring on the same day.
  1152. *
  1153. * @param string $dateString Datetime string or Unix timestamp
  1154. * @param string $fieldName Name of database field to compare with
  1155. * @param int $userOffset User's offset from GMT (in hours)
  1156. * @return string Partial SQL string.
  1157. * @access public
  1158. * @link http://book.cakephp.org/view/1471/Formatting
  1159. */
  1160. public function dayAsSql($dateString, $fieldName, $userOffset = null) {
  1161. $date = $this->fromString($dateString, $userOffset);
  1162. return $this->daysAsSql($dateString, $dateString, $fieldName);
  1163. }
  1164. /**
  1165. * try to parse date from various input formats
  1166. * - DD.MM.YYYY, DD/MM/YYYY, YYYY-MM-DD, YYYY, YYYY-MM, ...
  1167. * - i18n: Today, Yesterday, Tomorrow
  1168. * @param string $date to parse
  1169. * @param format to parse (null = auto)
  1170. * @param type
  1171. * - start: first second of this interval
  1172. * - end: last second of this interval
  1173. * @return int timestamp
  1174. * 2011-11-19 ms
  1175. */
  1176. public function parseDate($date, $format = null, $type = 'start') {
  1177. $date = trim($date);
  1178. $i18n = array(
  1179. 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')))),
  1180. 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)),
  1181. 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)),
  1182. 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)),
  1183. 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)),
  1184. );
  1185. if (isset($i18n[strtolower($date)])) {
  1186. return $i18n[strtolower($date)][$type];
  1187. }
  1188. if ($format) {
  1189. $res = DateTime::createFromFormat($format, $date);
  1190. $res = $res->format(FORMAT_DB_DATE).' '.($type=='end'?'23:59:59':'00:00:00');
  1191. return $res;
  1192. }
  1193. if (strpos($date, '.') !== false) {
  1194. $explode = explode('.', $date, 3);
  1195. $explode = array_reverse($explode);
  1196. } elseif (strpos($date, '/') !== false) {
  1197. $explode = explode('/', $date, 3);
  1198. $explode = array_reverse($explode);
  1199. } elseif (strpos($date, '-') !== false) {
  1200. $explode = explode('-', $date, 3);
  1201. } else {
  1202. $explode = array($date);
  1203. }
  1204. if (isset($explode)) {
  1205. for ($i = 0; $i < count($explode); $i++) {
  1206. $explode[$i] = str_pad($explode[$i], 2, '0', STR_PAD_LEFT);
  1207. }
  1208. $explode[0] = str_pad($explode[0], 4, '20', STR_PAD_LEFT);
  1209. if (count($explode) === 3) {
  1210. return implode('-', $explode).' '.($type=='end'?'23:59:59':'00:00:00');
  1211. } elseif (count($explode) === 2) {
  1212. return implode('-', $explode).'-'.($type=='end'?$this->daysInMonth($explode[0], $explode[1]):'01').' '.($type=='end'?'23:59:59':'00:00:00');
  1213. } else {
  1214. return $explode[0].'-'.($type=='end'?'12':'01').'-'.($type=='end'?'31':'01').' '.($type=='end'?'23:59:59':'00:00:00');
  1215. }
  1216. }
  1217. return false;
  1218. }
  1219. /**
  1220. * @param string $searchString to parse
  1221. * @param array $options
  1222. * - separator (defaults to space [ ])
  1223. * - format (defaults to Y-m-d H:i:s)
  1224. * @return array $period [0=>min, 1=>max]
  1225. * 2011-11-18 ms
  1226. */
  1227. public function period($string, $options = array()) {
  1228. if (strpos($string, ' ') !== false) {
  1229. $filters = explode(' ', $string);
  1230. $filters = array(array_shift($filters), array_pop($filters));
  1231. } else {
  1232. $filters = array($string, $string);
  1233. }
  1234. $min = $filters[0];
  1235. $max = $filters[1];
  1236. //$x = preg_match('/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/', $date, $date_parts);
  1237. //$x = Datetime::createFromFormat('Y-m-d', $string);
  1238. //die(returns($x));
  1239. //$actualDateTime = new DateTime($min);
  1240. //$actualDateTime->add(new DateInterval('P1M'));
  1241. $min = $this->parseDate($min);
  1242. $max = $this->parseDate($max, null, 'end');
  1243. //die($actualDateTime->format('Y-m-d'));
  1244. //$searchParameters['conditions']['Coupon.date'] = $actualDateTime->format('Y-m-d');
  1245. /*
  1246. if ($min == $max) {
  1247. if (strlen($max) > 8) {
  1248. $max = date(FORMAT_DB_DATE, strtotime($max)+DAY);
  1249. } elseif (strlen($max) > 5) {
  1250. $max = date(FORMAT_DB_DATE, strtotime($max)+MONTH);
  1251. } else {
  1252. $max = date(FORMAT_DB_DATE, strtotime($max)+YEAR+MONTH);
  1253. }
  1254. }
  1255. $min = date(FORMAT_DB_DATE, strtotime($min));
  1256. $max = date(FORMAT_DB_DATE, strtotime($max));
  1257. */
  1258. return array($min, $max);
  1259. }
  1260. /**
  1261. * @param string $searchString to parse
  1262. * @param string $fieldname (Model.field)
  1263. * @param array $options (see DatetimeLib::period)
  1264. * @return string $query SQL Query
  1265. * 2011-11-18 ms
  1266. */
  1267. public function periodAsSql($string, $fieldName, $options = array()) {
  1268. $period = $this->period($string, $options);
  1269. return $this->daysAsSql($period[0], $period[1], $fieldName);
  1270. }
  1271. /**
  1272. * Returns true if given datetime string is today.
  1273. *
  1274. * @param string $dateString Datetime string or Unix timestamp
  1275. * @param int $userOffset User's offset from GMT (in hours)
  1276. * @return boolean True if datetime string is today
  1277. * @access public
  1278. */
  1279. public function isToday($dateString, $userOffset = null) {
  1280. $date = $this->fromString($dateString, $userOffset);
  1281. return date('Y-m-d', $date) == date('Y-m-d', time());
  1282. }
  1283. /**
  1284. * Returns true if given datetime string is within this week
  1285. * @param string $dateString
  1286. * @param int $userOffset User's offset from GMT (in hours)
  1287. * @return boolean True if datetime string is within current week
  1288. * @access public
  1289. * @link http://book.cakephp.org/view/1472/Testing-Time
  1290. */
  1291. public function isThisWeek($dateString, $userOffset = null) {
  1292. $date = $this->fromString($dateString, $userOffset);
  1293. return date('W Y', $date) == date('W Y', time());
  1294. }
  1295. /**
  1296. * Returns true if given datetime string is within this month
  1297. * @param string $dateString
  1298. * @param int $userOffset User's offset from GMT (in hours)
  1299. * @return boolean True if datetime string is within current month
  1300. * @access public
  1301. * @link http://book.cakephp.org/view/1472/Testing-Time
  1302. */
  1303. public function isThisMonth($dateString, $userOffset = null) {
  1304. $date = $this->fromString($dateString);
  1305. return date('m Y',$date) == date('m Y', time());
  1306. }
  1307. /**
  1308. * Returns true if given datetime string is within current year.
  1309. *
  1310. * @param string $dateString Datetime string or Unix timestamp
  1311. * @return boolean True if datetime string is within current year
  1312. * @access public
  1313. * @link http://book.cakephp.org/view/1472/Testing-Time
  1314. */
  1315. public function isThisYear($dateString, $userOffset = null) {
  1316. $date = $this->fromString($dateString, $userOffset);
  1317. return date('Y', $date) == date('Y', time());
  1318. }
  1319. /**
  1320. * Returns true if given datetime string was yesterday.
  1321. *
  1322. * @param string $dateString Datetime string or Unix timestamp
  1323. * @param int $userOffset User's offset from GMT (in hours)
  1324. * @return boolean True if datetime string was yesterday
  1325. * @access public
  1326. * @link http://book.cakephp.org/view/1472/Testing-Time
  1327. *
  1328. */
  1329. public function wasYesterday($dateString, $userOffset = null) {
  1330. $date = $this->fromString($dateString, $userOffset);
  1331. return date('Y-m-d', $date) == date('Y-m-d', strtotime('yesterday'));
  1332. }
  1333. /**
  1334. * Returns true if given datetime string is tomorrow.
  1335. *
  1336. * @param string $dateString Datetime string or Unix timestamp
  1337. * @param int $userOffset User's offset from GMT (in hours)
  1338. * @return boolean True if datetime string was yesterday
  1339. * @access public
  1340. * @link http://book.cakephp.org/view/1472/Testing-Time
  1341. */
  1342. public function isTomorrow($dateString, $userOffset = null) {
  1343. $date = $this->fromString($dateString, $userOffset);
  1344. return date('Y-m-d', $date) == date('Y-m-d', strtotime('tomorrow'));
  1345. }
  1346. /**
  1347. * Returns the quarter
  1348. *
  1349. * @param string $dateString
  1350. * @param boolean $range if true returns a range in Y-m-d format
  1351. * @return boolean True if datetime string is within current week
  1352. * @access public
  1353. * @link http://book.cakephp.org/view/1471/Formatting
  1354. */
  1355. public function toQuarter($dateString, $range = false) {
  1356. $time = $this->fromString($dateString);
  1357. $date = ceil(date('m', $time) / 3);
  1358. if ($range === true) {
  1359. $range = 'Y-m-d';
  1360. }
  1361. if ($range !== false) {
  1362. $year = date('Y', $time);
  1363. switch ($date) {
  1364. case 1:
  1365. $date = array($year.'-01-01', $year.'-03-31');
  1366. break;
  1367. case 2:
  1368. $date = array($year.'-04-01', $year.'-06-30');
  1369. break;
  1370. case 3:
  1371. $date = array($year.'-07-01', $year.'-09-30');
  1372. break;
  1373. case 4:
  1374. $date = array($year.'-10-01', $year.'-12-31');
  1375. break;
  1376. }
  1377. }
  1378. return $date;
  1379. }
  1380. /**
  1381. * Returns a UNIX timestamp from a textual datetime description. Wrapper for PHP function strtotime().
  1382. *
  1383. * @param string $dateString Datetime string to be represented as a Unix timestamp
  1384. * @param int $userOffset User's offset from GMT (in hours)
  1385. * @return integer Unix timestamp
  1386. * @access public
  1387. * @link http://book.cakephp.org/view/1471/Formatting
  1388. */
  1389. public function toUnix($dateString, $userOffset = null) {
  1390. return $this->fromString($dateString, $userOffset);
  1391. }
  1392. /**
  1393. * Returns a date formatted for Atom RSS feeds.
  1394. *
  1395. * @param string $dateString Datetime string or Unix timestamp
  1396. * @param int $userOffset User's offset from GMT (in hours)
  1397. * @return string Formatted date string
  1398. * @access public
  1399. * @link http://book.cakephp.org/view/1471/Formatting
  1400. */
  1401. public function toAtom($dateString, $userOffset = null) {
  1402. $date = $this->fromString($dateString, $userOffset);
  1403. return date('Y-m-d\TH:i:s\Z', $date);
  1404. }
  1405. /**
  1406. * Formats date for RSS feeds
  1407. *
  1408. * @param string $dateString Datetime string or Unix timestamp
  1409. * @param int $userOffset User's offset from GMT (in hours)
  1410. * @return string Formatted date string
  1411. * @access public
  1412. * @link http://book.cakephp.org/view/1471/Formatting
  1413. */
  1414. public function toRSS($dateString, $userOffset = null) {
  1415. $date = $this->fromString($dateString, $userOffset);
  1416. return date("r", $date);
  1417. }
  1418. /**
  1419. * Returns either a relative date or a formatted date depending
  1420. * on the difference between the current time and given datetime.
  1421. * $datetime should be in a <i>strtotime</i> - parsable format, like MySQL's datetime datatype.
  1422. *
  1423. * ### Options:
  1424. *
  1425. * - `format` => a fall back format if the relative time is longer than the duration specified by end
  1426. * - `end` => The end of relative time telling
  1427. * - `userOffset` => Users offset from GMT (in hours)
  1428. *
  1429. * Relative dates look something like this:
  1430. * 3 weeks, 4 days ago
  1431. * 15 seconds ago
  1432. *
  1433. * Default date formatting is d/m/yy e.g: on 18/2/09
  1434. *
  1435. * The returned string includes 'ago' or 'on' and assumes you'll properly add a word
  1436. * like 'Posted ' before the function output.
  1437. *
  1438. * @param string $dateString Datetime string or Unix timestamp
  1439. * @param array $options Default format if timestamp is used in $dateString
  1440. * @return string Relative time string.
  1441. * @access public
  1442. * @link http://book.cakephp.org/view/1471/Formatting
  1443. */
  1444. public function timeAgoInWords($dateTime, $options = array()) {
  1445. $userOffset = null;
  1446. if (is_array($options) && isset($options['userOffset'])) {
  1447. $userOffset = $options['userOffset'];
  1448. }
  1449. $now = time();
  1450. if (!is_null($userOffset)) {
  1451. $now = $this->convert(time(), $userOffset);
  1452. }
  1453. $inSeconds = $this->fromString($dateTime, $userOffset);
  1454. $backwards = ($inSeconds > $now);
  1455. $format = 'j/n/y';
  1456. $end = '+1 month';
  1457. if (is_array($options)) {
  1458. if (isset($options['format'])) {
  1459. $format = $options['format'];
  1460. unset($options['format']);
  1461. }
  1462. if (isset($options['end'])) {
  1463. $end = $options['end'];
  1464. unset($options['end']);
  1465. }
  1466. } else {
  1467. $format = $options;
  1468. }
  1469. if ($backwards) {
  1470. $futureTime = $inSeconds;
  1471. $pastTime = $now;
  1472. } else {
  1473. $futureTime = $now;
  1474. $pastTime = $inSeconds;
  1475. }
  1476. $diff = $futureTime - $pastTime;
  1477. // If more than a week, then take into account the length of months
  1478. if ($diff >= 604800) {
  1479. $current = array();
  1480. $date = array();
  1481. list($future['H'], $future['i'], $future['s'], $future['d'], $future['m'], $future['Y']) = explode('/', date('H/i/s/d/m/Y', $futureTime));
  1482. list($past['H'], $past['i'], $past['s'], $past['d'], $past['m'], $past['Y']) = explode('/', date('H/i/s/d/m/Y', $pastTime));
  1483. $years = $months = $weeks = $days = $hours = $minutes = $seconds = 0;
  1484. if ($future['Y'] == $past['Y'] && $future['m'] == $past['m']) {
  1485. $months = 0;
  1486. $years = 0;
  1487. } else {
  1488. if ($future['Y'] == $past['Y']) {
  1489. $months = $future['m'] - $past['m'];
  1490. } else {
  1491. $years = $future['Y'] - $past['Y'];
  1492. $months = $future['m'] + ((12 * $years) - $past['m']);
  1493. if ($months >= 12) {
  1494. $years = floor($months / 12);
  1495. $months = $months - ($years * 12);
  1496. }
  1497. if ($future['m'] < $past['m'] && $future['Y'] - $past['Y'] == 1) {
  1498. $years --;
  1499. }
  1500. }
  1501. }
  1502. if ($future['d'] >= $past['d']) {
  1503. $days = $future['d'] - $past['d'];
  1504. } else {
  1505. $daysInPastMonth = date('t', $pastTime);
  1506. $daysInFutureMonth = date('t', mktime(0, 0, 0, $future['m'] - 1, 1, $future['Y']));
  1507. if (!$backwards) {
  1508. $days = ($daysInPastMonth - $past['d']) + $future['d'];
  1509. } else {
  1510. $days = ($daysInFutureMonth - $past['d']) + $future['d'];
  1511. }
  1512. if ($future['m'] != $past['m']) {
  1513. $months --;
  1514. }
  1515. }
  1516. if ($months == 0 && $years >= 1 && $diff < ($years * 31536000)) {
  1517. $months = 11;
  1518. $years --;
  1519. }
  1520. if ($months >= 12) {
  1521. $years = $years + 1;
  1522. $months = $months - 12;
  1523. }
  1524. if ($days >= 7) {
  1525. $weeks = floor($days / 7);
  1526. $days = $days - ($weeks * 7);
  1527. }
  1528. } else {
  1529. $years = $months = $weeks = 0;
  1530. $days = floor($diff / 86400);
  1531. $diff = $diff - ($days * 86400);
  1532. $hours = floor($diff / 3600);
  1533. $diff = $diff - ($hours * 3600);
  1534. $minutes = floor($diff / 60);
  1535. $diff = $diff - ($minutes * 60);
  1536. $seconds = $diff;
  1537. }
  1538. $relativeDate = '';
  1539. $diff = $futureTime - $pastTime;
  1540. if ($diff > abs($now - $this->fromString($end))) {
  1541. $relativeDate = __('on %s', date($format, $inSeconds));
  1542. } else {
  1543. if ($years > 0) {
  1544. // years and months and days
  1545. $relativeDate .= ($relativeDate ? ', ' : '') . $years . ' ' . __n('year', 'years', $years);
  1546. $relativeDate .= $months > 0 ? ($relativeDate ? ', ' : '') . $months . ' ' . __n('month', 'months', $months) : '';
  1547. $relativeDate .= $weeks > 0 ? ($relativeDate ? ', ' : '') . $weeks . ' ' . __n('week', 'weeks', $weeks) : '';
  1548. $relativeDate .= $days > 0 ? ($relativeDate ? ', ' : '') . $days . ' ' . __n('day', 'days', $days) : '';
  1549. } elseif (abs($months) > 0) {
  1550. // months, weeks and days
  1551. $relativeDate .= ($relativeDate ? ', ' : '') . $months . ' ' . __n('month', 'months', $months);
  1552. $relativeDate .= $weeks > 0 ? ($relativeDate ? ', ' : '') . $weeks . ' ' . __n('week', 'weeks', $weeks) : '';
  1553. $relativeDate .= $days > 0 ? ($relativeDate ? ', ' : '') . $days . ' ' . __n('day', 'days', $days) : '';
  1554. } elseif (abs($weeks) > 0) {
  1555. // weeks and days
  1556. $relativeDate .= ($relativeDate ? ', ' : '') . $weeks . ' ' . __n('week', 'weeks', $weeks);
  1557. $relativeDate .= $days > 0 ? ($relativeDate ? ', ' : '') . $days . ' ' . __n('day', 'days', $days) : '';
  1558. } elseif (abs($days) > 0) {
  1559. // days and hours
  1560. $relativeDate .= ($relativeDate ? ', ' : '') . $days . ' ' . __n('day', 'days', $days);
  1561. $relativeDate .= $hours > 0 ? ($relativeDate ? ', ' : '') . $hours . ' ' . __n('hour', 'hours', $hours) : '';
  1562. } elseif (abs($hours) > 0) {
  1563. // hours and minutes
  1564. $relativeDate .= ($relativeDate ? ', ' : '') . $hours . ' ' . __n('hour', 'hours', $hours);
  1565. $relativeDate .= $minutes > 0 ? ($relativeDate ? ', ' : '') . $minutes . ' ' . __n('minute', 'minutes', $minutes) : '';
  1566. } elseif (abs($minutes) > 0) {
  1567. // minutes only
  1568. $relativeDate .= ($relativeDate ? ', ' : '') . $minutes . ' ' . __n('minute', 'minutes', $minutes);
  1569. } else {
  1570. // seconds only
  1571. $relativeDate .= ($relativeDate ? ', ' : '') . $seconds . ' ' . __n('second', 'seconds', $seconds);
  1572. }
  1573. if (!$backwards) {
  1574. $relativeDate = __('%s ago', $relativeDate);
  1575. }
  1576. }
  1577. return $relativeDate;
  1578. }
  1579. /**
  1580. * Alias for timeAgoInWords
  1581. *
  1582. * @param mixed $dateTime Datetime string (strtotime-compatible) or Unix timestamp
  1583. * @param mixed $options Default format string, if timestamp is used in $dateTime, or an array of options to be passed
  1584. * on to timeAgoInWords().
  1585. * @return string Relative time string.
  1586. * @see TimeHelper::timeAgoInWords
  1587. * @access public
  1588. * @deprecated This method alias will be removed in future versions.
  1589. * @link http://book.cakephp.org/view/1471/Formatting
  1590. */
  1591. public function relativeTime($dateTime, $options = array()) {
  1592. return $this->timeAgoInWords($dateTime, $options);
  1593. }
  1594. /**
  1595. * Returns true if specified datetime was within the interval specified, else false.
  1596. *
  1597. * @param mixed $timeInterval the numeric value with space then time type.
  1598. * Example of valid types: 6 hours, 2 days, 1 minute.
  1599. * @param mixed $dateString the datestring or unix timestamp to compare
  1600. * @param int $userOffset User's offset from GMT (in hours)
  1601. * @return bool
  1602. * @access public
  1603. * @link http://book.cakephp.org/view/1472/Testing-Time
  1604. */
  1605. public function wasWithinLast($timeInterval, $dateString, $userOffset = null) {
  1606. $tmp = str_replace(' ', '', $timeInterval);
  1607. if (is_numeric($tmp)) {
  1608. $timeInterval = $tmp . ' ' . __('days');
  1609. }
  1610. $date = $this->fromString($dateString, $userOffset);
  1611. $interval = $this->fromString('-'.$timeInterval);
  1612. if ($date >= $interval && $date <= time()) {
  1613. return true;
  1614. }
  1615. return false;
  1616. }
  1617. /**
  1618. * Returns gmt, given either a UNIX timestamp or a valid strtotime() date string.
  1619. *
  1620. * @param string $dateString Datetime string
  1621. * @return string Formatted date string
  1622. * @access public
  1623. * @link http://book.cakephp.org/view/1471/Formatting
  1624. */
  1625. public function gmt($string = null) {
  1626. if ($string != null) {
  1627. $string = $this->fromString($string);
  1628. } else {
  1629. $string = time();
  1630. }
  1631. $string = $this->fromString($string);
  1632. $hour = intval(date("G", $string));
  1633. $minute = intval(date("i", $string));
  1634. $second = intval(date("s", $string));
  1635. $month = intval(date("n", $string));
  1636. $day = intval(date("j", $string));
  1637. $year = intval(date("Y", $string));
  1638. return gmmktime($hour, $minute, $second, $month, $day, $year);
  1639. }
  1640. /**
  1641. * Returns a formatted date string, given either a UNIX timestamp or a valid strtotime() date string.
  1642. * This function also accepts a time string and a format string as first and second parameters.
  1643. * In that case this function behaves as a wrapper for TimeHelper::i18nFormat()
  1644. *
  1645. * @param string $format date format string (or a DateTime string)
  1646. * @param string $dateString Datetime string (or a date format string)
  1647. * @param boolean $invalid flag to ignore results of fromString == false
  1648. * @param int $userOffset User's offset from GMT (in hours)
  1649. * @return string Formatted date string
  1650. * @access public
  1651. */
  1652. public function format($format, $date = null, $invalid = false, $userOffset = null) {
  1653. $time = $this->fromString($date, $userOffset);
  1654. $_time = $this->fromString($format, $userOffset);
  1655. if (is_numeric($_time) && $time === false) {
  1656. $format = $date;
  1657. return $this->i18nFormat($_time, $format, $invalid, $userOffset);
  1658. }
  1659. if ($time === false && $invalid !== false) {
  1660. return $invalid;
  1661. }
  1662. return date($format, $time);
  1663. }
  1664. /**
  1665. * Returns a formatted date string, given either a UNIX timestamp or a valid strtotime() date string.
  1666. * It take in account the default date format for the current language if a LC_TIME file is used.
  1667. *
  1668. * @param string $dateString Datetime string
  1669. * @param string $format strftime format string.
  1670. * @param boolean $invalid flag to ignore results of fromString == false
  1671. * @param int $userOffset User's offset from GMT (in hours)
  1672. * @return string Formatted and translated date string @access public
  1673. * @access public
  1674. */
  1675. public function i18nFormat($date, $format = null, $invalid = false, $userOffset = null) {
  1676. $date = $this->fromString($date, $userOffset);
  1677. if ($date === false && $invalid !== false) {
  1678. return $invalid;
  1679. }
  1680. if (empty($format)) {
  1681. $format = '%x';
  1682. }
  1683. $format = $this->convertSpecifiers($format, $date);
  1684. return strftime($format, $date);
  1685. }
  1686. }