+ /**
+ * Get the formatted time for the given timestamp and formatted for
+ * the given user.
+ *
+ * @param mixed $ts The time format which needs to be turned into a
+ * date('YmdHis') format with wfTimestamp(TS_MW,$ts)
+ * @param User $user User object used to get preferences for timezone and format
+ * @param array $options Array, can contain the following keys:
+ * - 'timecorrection': time correction, can have the following values:
+ * - true: use user's preference
+ * - false: don't use time correction
+ * - int: value of time correction in minutes
+ * - 'format': format to use, can have the following values:
+ * - true: use user's preference
+ * - false: use default preference
+ * - string: format to use
+ * @since 1.19
+ * @return string
+ */
+ public function userTime( $ts, User $user, array $options = [] ) {
+ return $this->internalUserTimeAndDate( 'time', $ts, $user, $options );
+ }
+
+ /**
+ * Get the formatted date and time for the given timestamp and formatted for
+ * the given user.
+ *
+ * @param mixed $ts The time format which needs to be turned into a
+ * date('YmdHis') format with wfTimestamp(TS_MW,$ts)
+ * @param User $user User object used to get preferences for timezone and format
+ * @param array $options Array, can contain the following keys:
+ * - 'timecorrection': time correction, can have the following values:
+ * - true: use user's preference
+ * - false: don't use time correction
+ * - int: value of time correction in minutes
+ * - 'format': format to use, can have the following values:
+ * - true: use user's preference
+ * - false: use default preference
+ * - string: format to use
+ * @since 1.19
+ * @return string
+ */
+ public function userTimeAndDate( $ts, User $user, array $options = [] ) {
+ return $this->internalUserTimeAndDate( 'both', $ts, $user, $options );
+ }
+
+ /**
+ * Get the timestamp in a human-friendly relative format, e.g., "3 days ago".
+ *
+ * Determine the difference between the timestamp and the current time, and
+ * generate a readable timestamp by returning "<N> <units> ago", where the
+ * largest possible unit is used.
+ *
+ * @since 1.26 (Prior to 1.26 method existed but was not meant to be used directly)
+ *
+ * @param MWTimestamp $time
+ * @param MWTimestamp|null $relativeTo The base timestamp to compare to (defaults to now)
+ * @param User|null $user User the timestamp is being generated for
+ * (or null to use main context's user)
+ * @return string Formatted timestamp
+ */
+ public function getHumanTimestamp(
+ MWTimestamp $time, MWTimestamp $relativeTo = null, User $user = null
+ ) {
+ if ( $relativeTo === null ) {
+ $relativeTo = new MWTimestamp();
+ }
+ if ( $user === null ) {
+ $user = RequestContext::getMain()->getUser();
+ }
+
+ // Adjust for the user's timezone.
+ $offsetThis = $time->offsetForUser( $user );
+ $offsetRel = $relativeTo->offsetForUser( $user );
+
+ $ts = '';
+ if ( Hooks::run( 'GetHumanTimestamp', [ &$ts, $time, $relativeTo, $user, $this ] ) ) {
+ $ts = $this->getHumanTimestampInternal( $time, $relativeTo, $user );
+ }
+
+ // Reset the timezone on the objects.
+ $time->timestamp->sub( $offsetThis );
+ $relativeTo->timestamp->sub( $offsetRel );
+
+ return $ts;
+ }
+
+ /**
+ * Convert an MWTimestamp into a pretty human-readable timestamp using
+ * the given user preferences and relative base time.
+ *
+ * @see Language::getHumanTimestamp
+ * @param MWTimestamp $ts Timestamp to prettify
+ * @param MWTimestamp $relativeTo Base timestamp
+ * @param User $user User preferences to use
+ * @return string Human timestamp
+ * @since 1.26
+ */
+ private function getHumanTimestampInternal(
+ MWTimestamp $ts, MWTimestamp $relativeTo, User $user
+ ) {
+ $diff = $ts->diff( $relativeTo );
+ $diffDay = (bool)( (int)$ts->timestamp->format( 'w' ) -
+ (int)$relativeTo->timestamp->format( 'w' ) );
+ $days = $diff->days ?: (int)$diffDay;
+ if ( $diff->invert || $days > 5
+ && $ts->timestamp->format( 'Y' ) !== $relativeTo->timestamp->format( 'Y' )
+ ) {
+ // Timestamps are in different years: use full timestamp
+ // Also do full timestamp for future dates
+ /**
+ * @todo FIXME: Add better handling of future timestamps.
+ */
+ $format = $this->getDateFormatString( 'both', $user->getDatePreference() ?: 'default' );
+ $ts = $this->sprintfDate( $format, $ts->getTimestamp( TS_MW ) );
+ } elseif ( $days > 5 ) {
+ // Timestamps are in same year, but more than 5 days ago: show day and month only.
+ $format = $this->getDateFormatString( 'pretty', $user->getDatePreference() ?: 'default' );
+ $ts = $this->sprintfDate( $format, $ts->getTimestamp( TS_MW ) );
+ } elseif ( $days > 1 ) {
+ // Timestamp within the past week: show the day of the week and time
+ $format = $this->getDateFormatString( 'time', $user->getDatePreference() ?: 'default' );
+ $weekday = self::$mWeekdayMsgs[$ts->timestamp->format( 'w' )];
+ // Messages:
+ // sunday-at, monday-at, tuesday-at, wednesday-at, thursday-at, friday-at, saturday-at
+ $ts = wfMessage( "$weekday-at" )
+ ->inLanguage( $this )
+ ->params( $this->sprintfDate( $format, $ts->getTimestamp( TS_MW ) ) )
+ ->text();
+ } elseif ( $days == 1 ) {
+ // Timestamp was yesterday: say 'yesterday' and the time.
+ $format = $this->getDateFormatString( 'time', $user->getDatePreference() ?: 'default' );
+ $ts = wfMessage( 'yesterday-at' )
+ ->inLanguage( $this )
+ ->params( $this->sprintfDate( $format, $ts->getTimestamp( TS_MW ) ) )
+ ->text();
+ } elseif ( $diff->h > 1 || $diff->h == 1 && $diff->i > 30 ) {
+ // Timestamp was today, but more than 90 minutes ago: say 'today' and the time.
+ $format = $this->getDateFormatString( 'time', $user->getDatePreference() ?: 'default' );
+ $ts = wfMessage( 'today-at' )
+ ->inLanguage( $this )
+ ->params( $this->sprintfDate( $format, $ts->getTimestamp( TS_MW ) ) )
+ ->text();
+
+ // From here on in, the timestamp was soon enough ago so that we can simply say
+ // XX units ago, e.g., "2 hours ago" or "5 minutes ago"
+ } elseif ( $diff->h == 1 ) {
+ // Less than 90 minutes, but more than an hour ago.
+ $ts = wfMessage( 'hours-ago' )->inLanguage( $this )->numParams( 1 )->text();
+ } elseif ( $diff->i >= 1 ) {
+ // A few minutes ago.
+ $ts = wfMessage( 'minutes-ago' )->inLanguage( $this )->numParams( $diff->i )->text();
+ } elseif ( $diff->s >= 30 ) {
+ // Less than a minute, but more than 30 sec ago.
+ $ts = wfMessage( 'seconds-ago' )->inLanguage( $this )->numParams( $diff->s )->text();
+ } else {
+ // Less than 30 seconds ago.
+ $ts = wfMessage( 'just-now' )->text();
+ }
+
+ return $ts;