DateTimeFormatter.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  5. * SPDX-License-Identifier: AGPL-3.0-only
  6. */
  7. namespace OC;
  8. class DateTimeFormatter implements \OCP\IDateTimeFormatter {
  9. /** @var \DateTimeZone */
  10. protected $defaultTimeZone;
  11. /** @var \OCP\IL10N */
  12. protected $defaultL10N;
  13. /**
  14. * Constructor
  15. *
  16. * @param \DateTimeZone $defaultTimeZone Set the timezone for the format
  17. * @param \OCP\IL10N $defaultL10N Set the language for the format
  18. */
  19. public function __construct(\DateTimeZone $defaultTimeZone, \OCP\IL10N $defaultL10N) {
  20. $this->defaultTimeZone = $defaultTimeZone;
  21. $this->defaultL10N = $defaultL10N;
  22. }
  23. /**
  24. * Get TimeZone to use
  25. *
  26. * @param \DateTimeZone $timeZone The timezone to use
  27. * @return \DateTimeZone The timezone to use, falling back to the current user's timezone
  28. */
  29. protected function getTimeZone($timeZone = null) {
  30. if ($timeZone === null) {
  31. $timeZone = $this->defaultTimeZone;
  32. }
  33. return $timeZone;
  34. }
  35. /**
  36. * Get \OCP\IL10N to use
  37. *
  38. * @param \OCP\IL10N $l The locale to use
  39. * @return \OCP\IL10N The locale to use, falling back to the current user's locale
  40. */
  41. protected function getLocale($l = null) {
  42. if ($l === null) {
  43. $l = $this->defaultL10N;
  44. }
  45. return $l;
  46. }
  47. /**
  48. * Generates a DateTime object with the given timestamp and TimeZone
  49. *
  50. * @param mixed $timestamp
  51. * @param \DateTimeZone $timeZone The timezone to use
  52. * @return \DateTime
  53. */
  54. protected function getDateTime($timestamp, ?\DateTimeZone $timeZone = null) {
  55. if ($timestamp === null) {
  56. return new \DateTime('now', $timeZone);
  57. } elseif (!$timestamp instanceof \DateTime) {
  58. $dateTime = new \DateTime('now', $timeZone);
  59. $dateTime->setTimestamp($timestamp);
  60. return $dateTime;
  61. }
  62. if ($timeZone) {
  63. $timestamp->setTimezone($timeZone);
  64. }
  65. return $timestamp;
  66. }
  67. /**
  68. * Formats the date of the given timestamp
  69. *
  70. * @param int|\DateTime $timestamp Either a Unix timestamp or DateTime object
  71. * @param string $format Either 'full', 'long', 'medium' or 'short'
  72. * full: e.g. 'EEEE, MMMM d, y' => 'Wednesday, August 20, 2014'
  73. * long: e.g. 'MMMM d, y' => 'August 20, 2014'
  74. * medium: e.g. 'MMM d, y' => 'Aug 20, 2014'
  75. * short: e.g. 'M/d/yy' => '8/20/14'
  76. * The exact format is dependent on the language
  77. * @param \DateTimeZone $timeZone The timezone to use
  78. * @param \OCP\IL10N $l The locale to use
  79. * @return string Formatted date string
  80. */
  81. public function formatDate($timestamp, $format = 'long', ?\DateTimeZone $timeZone = null, ?\OCP\IL10N $l = null) {
  82. return $this->format($timestamp, 'date', $format, $timeZone, $l);
  83. }
  84. /**
  85. * Formats the date of the given timestamp
  86. *
  87. * @param int|\DateTime $timestamp Either a Unix timestamp or DateTime object
  88. * @param string $format Either 'full', 'long', 'medium' or 'short'
  89. * full: e.g. 'EEEE, MMMM d, y' => 'Wednesday, August 20, 2014'
  90. * long: e.g. 'MMMM d, y' => 'August 20, 2014'
  91. * medium: e.g. 'MMM d, y' => 'Aug 20, 2014'
  92. * short: e.g. 'M/d/yy' => '8/20/14'
  93. * The exact format is dependent on the language
  94. * Uses 'Today', 'Yesterday' and 'Tomorrow' when applicable
  95. * @param \DateTimeZone $timeZone The timezone to use
  96. * @param \OCP\IL10N $l The locale to use
  97. * @return string Formatted relative date string
  98. */
  99. public function formatDateRelativeDay($timestamp, $format = 'long', ?\DateTimeZone $timeZone = null, ?\OCP\IL10N $l = null) {
  100. if (!str_ends_with($format, '^') && !str_ends_with($format, '*')) {
  101. $format .= '^';
  102. }
  103. return $this->format($timestamp, 'date', $format, $timeZone, $l);
  104. }
  105. /**
  106. * Gives the relative date of the timestamp
  107. * Only works for past dates
  108. *
  109. * @param int|\DateTime $timestamp Either a Unix timestamp or DateTime object
  110. * @param int|\DateTime $baseTimestamp Timestamp to compare $timestamp against, defaults to current time
  111. * @param \OCP\IL10N $l The locale to use
  112. * @return string Formatted date span. Dates returned are:
  113. * < 1 month => Today, Yesterday, n days ago
  114. * < 13 month => last month, n months ago
  115. * >= 13 month => last year, n years ago
  116. */
  117. public function formatDateSpan($timestamp, $baseTimestamp = null, ?\OCP\IL10N $l = null) {
  118. $l = $this->getLocale($l);
  119. $timestamp = $this->getDateTime($timestamp);
  120. $timestamp->setTime(0, 0, 0);
  121. if ($baseTimestamp === null) {
  122. $baseTimestamp = time();
  123. }
  124. $baseTimestamp = $this->getDateTime($baseTimestamp);
  125. $baseTimestamp->setTime(0, 0, 0);
  126. $dateInterval = $timestamp->diff($baseTimestamp);
  127. if ($dateInterval->y == 0 && $dateInterval->m == 0 && $dateInterval->d == 0) {
  128. return $l->t('today');
  129. } elseif ($dateInterval->y == 0 && $dateInterval->m == 0 && $dateInterval->d == 1) {
  130. if ($timestamp > $baseTimestamp) {
  131. return $l->t('tomorrow');
  132. } else {
  133. return $l->t('yesterday');
  134. }
  135. } elseif ($dateInterval->y == 0 && $dateInterval->m == 0) {
  136. if ($timestamp > $baseTimestamp) {
  137. return $l->n('in %n day', 'in %n days', $dateInterval->d);
  138. } else {
  139. return $l->n('%n day ago', '%n days ago', $dateInterval->d);
  140. }
  141. } elseif ($dateInterval->y == 0 && $dateInterval->m == 1) {
  142. if ($timestamp > $baseTimestamp) {
  143. return $l->t('next month');
  144. } else {
  145. return $l->t('last month');
  146. }
  147. } elseif ($dateInterval->y == 0) {
  148. if ($timestamp > $baseTimestamp) {
  149. return $l->n('in %n month', 'in %n months', $dateInterval->m);
  150. } else {
  151. return $l->n('%n month ago', '%n months ago', $dateInterval->m);
  152. }
  153. } elseif ($dateInterval->y == 1) {
  154. if ($timestamp > $baseTimestamp) {
  155. return $l->t('next year');
  156. } else {
  157. return $l->t('last year');
  158. }
  159. }
  160. if ($timestamp > $baseTimestamp) {
  161. return $l->n('in %n year', 'in %n years', $dateInterval->y);
  162. } else {
  163. return $l->n('%n year ago', '%n years ago', $dateInterval->y);
  164. }
  165. }
  166. /**
  167. * Formats the time of the given timestamp
  168. *
  169. * @param int|\DateTime $timestamp Either a Unix timestamp or DateTime object
  170. * @param string $format Either 'full', 'long', 'medium' or 'short'
  171. * full: e.g. 'h:mm:ss a zzzz' => '11:42:13 AM GMT+0:00'
  172. * long: e.g. 'h:mm:ss a z' => '11:42:13 AM GMT'
  173. * medium: e.g. 'h:mm:ss a' => '11:42:13 AM'
  174. * short: e.g. 'h:mm a' => '11:42 AM'
  175. * The exact format is dependent on the language
  176. * @param \DateTimeZone $timeZone The timezone to use
  177. * @param \OCP\IL10N $l The locale to use
  178. * @return string Formatted time string
  179. */
  180. public function formatTime($timestamp, $format = 'medium', ?\DateTimeZone $timeZone = null, ?\OCP\IL10N $l = null) {
  181. return $this->format($timestamp, 'time', $format, $timeZone, $l);
  182. }
  183. /**
  184. * Gives the relative past time of the timestamp
  185. *
  186. * @param int|\DateTime $timestamp Either a Unix timestamp or DateTime object
  187. * @param int|\DateTime $baseTimestamp Timestamp to compare $timestamp against, defaults to current time
  188. * @param \OCP\IL10N $l The locale to use
  189. * @return string Formatted time span. Dates returned are:
  190. * < 60 sec => seconds ago
  191. * < 1 hour => n minutes ago
  192. * < 1 day => n hours ago
  193. * < 1 month => Yesterday, n days ago
  194. * < 13 month => last month, n months ago
  195. * >= 13 month => last year, n years ago
  196. */
  197. public function formatTimeSpan($timestamp, $baseTimestamp = null, ?\OCP\IL10N $l = null) {
  198. $l = $this->getLocale($l);
  199. $timestamp = $this->getDateTime($timestamp);
  200. if ($baseTimestamp === null) {
  201. $baseTimestamp = time();
  202. }
  203. $baseTimestamp = $this->getDateTime($baseTimestamp);
  204. $diff = $timestamp->diff($baseTimestamp);
  205. if ($diff->y > 0 || $diff->m > 0 || $diff->d > 0) {
  206. return $this->formatDateSpan($timestamp, $baseTimestamp, $l);
  207. }
  208. if ($diff->h > 0) {
  209. if ($timestamp > $baseTimestamp) {
  210. return $l->n('in %n hour', 'in %n hours', $diff->h);
  211. } else {
  212. return $l->n('%n hour ago', '%n hours ago', $diff->h);
  213. }
  214. } elseif ($diff->i > 0) {
  215. if ($timestamp > $baseTimestamp) {
  216. return $l->n('in %n minute', 'in %n minutes', $diff->i);
  217. } else {
  218. return $l->n('%n minute ago', '%n minutes ago', $diff->i);
  219. }
  220. }
  221. if ($timestamp > $baseTimestamp) {
  222. return $l->t('in a few seconds');
  223. } else {
  224. return $l->t('seconds ago');
  225. }
  226. }
  227. /**
  228. * Formats the date and time of the given timestamp
  229. *
  230. * @param int|\DateTime $timestamp Either a Unix timestamp or DateTime object
  231. * @param string $formatDate See formatDate() for description
  232. * @param string $formatTime See formatTime() for description
  233. * @param \DateTimeZone $timeZone The timezone to use
  234. * @param \OCP\IL10N $l The locale to use
  235. * @return string Formatted date and time string
  236. */
  237. public function formatDateTime($timestamp, $formatDate = 'long', $formatTime = 'medium', ?\DateTimeZone $timeZone = null, ?\OCP\IL10N $l = null) {
  238. return $this->format($timestamp, 'datetime', $formatDate . '|' . $formatTime, $timeZone, $l);
  239. }
  240. /**
  241. * Formats the date and time of the given timestamp
  242. *
  243. * @param int|\DateTime $timestamp Either a Unix timestamp or DateTime object
  244. * @param string $formatDate See formatDate() for description
  245. * Uses 'Today', 'Yesterday' and 'Tomorrow' when applicable
  246. * @param string $formatTime See formatTime() for description
  247. * @param \DateTimeZone $timeZone The timezone to use
  248. * @param \OCP\IL10N $l The locale to use
  249. * @return string Formatted relative date and time string
  250. */
  251. public function formatDateTimeRelativeDay($timestamp, $formatDate = 'long', $formatTime = 'medium', ?\DateTimeZone $timeZone = null, ?\OCP\IL10N $l = null) {
  252. if (!str_ends_with($formatDate, '^') && !str_ends_with($formatDate, '*')) {
  253. $formatDate .= '^';
  254. }
  255. return $this->format($timestamp, 'datetime', $formatDate . '|' . $formatTime, $timeZone, $l);
  256. }
  257. /**
  258. * Formats the date and time of the given timestamp
  259. *
  260. * @param int|\DateTime $timestamp Either a Unix timestamp or DateTime object
  261. * @param string $type One of 'date', 'datetime' or 'time'
  262. * @param string $format Format string
  263. * @param \DateTimeZone $timeZone The timezone to use
  264. * @param \OCP\IL10N $l The locale to use
  265. * @return string Formatted date and time string
  266. */
  267. protected function format($timestamp, $type, $format, ?\DateTimeZone $timeZone = null, ?\OCP\IL10N $l = null) {
  268. $l = $this->getLocale($l);
  269. $timeZone = $this->getTimeZone($timeZone);
  270. $timestamp = $this->getDateTime($timestamp, $timeZone);
  271. return $l->l($type, $timestamp, [
  272. 'width' => $format,
  273. ]);
  274. }
  275. }