'__months': [ '{lang}wcf.date.month.january{/lang}', '{lang}wcf.date.month.february{/lang}', '{lang}wcf.date.month.march{/lang}', '{lang}wcf.date.month.april{/lang}', '{lang}wcf.date.month.may{/lang}', '{lang}wcf.date.month.june{/lang}', '{lang}wcf.date.month.july{/lang}', '{lang}wcf.date.month.august{/lang}', '{lang}wcf.date.month.september{/lang}', '{lang}wcf.date.month.october{/lang}', '{lang}wcf.date.month.november{/lang}', '{lang}wcf.date.month.december{/lang}' ],
'__monthsShort': [ '{lang}wcf.date.month.short.jan{/lang}', '{lang}wcf.date.month.short.feb{/lang}', '{lang}wcf.date.month.short.mar{/lang}', '{lang}wcf.date.month.short.apr{/lang}', '{lang}wcf.date.month.short.may{/lang}', '{lang}wcf.date.month.short.jun{/lang}', '{lang}wcf.date.month.short.jul{/lang}', '{lang}wcf.date.month.short.aug{/lang}', '{lang}wcf.date.month.short.sep{/lang}', '{lang}wcf.date.month.short.oct{/lang}', '{lang}wcf.date.month.short.nov{/lang}', '{lang}wcf.date.month.short.dec{/lang}' ],
'wcf.clipboard.item.unmarkAll': '{lang}wcf.clipboard.item.unmarkAll{/lang}',
- 'wcf.date.relative.now': '{lang}wcf.date.relative.now{/lang}',
- 'wcf.date.relative.minutes': '{capture assign=relativeMinutes}{lang}wcf.date.relative.minutes{/lang}{/capture}{@$relativeMinutes|encodeJS}',
- 'wcf.date.relative.hours': '{capture assign=relativeHours}{lang}wcf.date.relative.hours{/lang}{/capture}{@$relativeHours|encodeJS}',
- 'wcf.date.relative.pastDays': '{capture assign=relativePastDays}{lang}wcf.date.relative.pastDays{/lang}{/capture}{@$relativePastDays|encodeJS}',
+ 'wcf.date.relative.now': '{lang}wcf.date.relative.nowJS{/lang}',
+ 'wcf.date.relative.minutes': '{capture assign=relativeMinutes}{lang}wcf.date.relative.minutesJS{/lang}{/capture}{@$relativeMinutes|encodeJS}',
+ 'wcf.date.relative.hours': '{capture assign=relativeHours}{lang}wcf.date.relative.hoursJS{/lang}{/capture}{@$relativeHours|encodeJS}',
+ 'wcf.date.relative.pastDays': '{capture assign=relativePastDays}{lang}wcf.date.relative.pastDaysJS{/lang}{/capture}{@$relativePastDays|encodeJS}',
'wcf.date.dateFormat': '{lang}wcf.date.dateFormat{/lang}',
'wcf.date.dateTimeFormat': '{lang}wcf.date.dateTimeFormat{/lang}',
'wcf.date.shortDateTimeFormat': '{lang}wcf.date.shortDateTimeFormat{/lang}',
'__monthsShort': [ '{lang}wcf.date.month.short.jan{/lang}', '{lang}wcf.date.month.short.feb{/lang}', '{lang}wcf.date.month.short.mar{/lang}', '{lang}wcf.date.month.short.apr{/lang}', '{lang}wcf.date.month.short.may{/lang}', '{lang}wcf.date.month.short.jun{/lang}', '{lang}wcf.date.month.short.jul{/lang}', '{lang}wcf.date.month.short.aug{/lang}', '{lang}wcf.date.month.short.sep{/lang}', '{lang}wcf.date.month.short.oct{/lang}', '{lang}wcf.date.month.short.nov{/lang}', '{lang}wcf.date.month.short.dec{/lang}' ],
'wcf.acp.search.noResults': '{lang}wcf.acp.search.noResults{/lang}',
'wcf.clipboard.item.unmarkAll': '{lang}wcf.clipboard.item.unmarkAll{/lang}',
- 'wcf.date.relative.now': '{lang}wcf.date.relative.now{/lang}',
- 'wcf.date.relative.minutes': '{capture assign=relativeMinutes}{lang}wcf.date.relative.minutes{/lang}{/capture}{@$relativeMinutes|encodeJS}',
- 'wcf.date.relative.hours': '{capture assign=relativeHours}{lang}wcf.date.relative.hours{/lang}{/capture}{@$relativeHours|encodeJS}',
- 'wcf.date.relative.pastDays': '{capture assign=relativePastDays}{lang}wcf.date.relative.pastDays{/lang}{/capture}{@$relativePastDays|encodeJS}',
+ 'wcf.date.relative.now': '{lang}wcf.date.relative.nowJS{/lang}',
+ 'wcf.date.relative.minutes': '{capture assign=relativeMinutes}{lang}wcf.date.relative.minutesJS{/lang}{/capture}{@$relativeMinutes|encodeJS}',
+ 'wcf.date.relative.hours': '{capture assign=relativeHours}{lang}wcf.date.relative.hoursJS{/lang}{/capture}{@$relativeHours|encodeJS}',
+ 'wcf.date.relative.pastDays': '{capture assign=relativePastDays}{lang}wcf.date.relative.pastDaysJS{/lang}{/capture}{@$relativePastDays|encodeJS}',
'wcf.date.dateFormat': '{lang}wcf.date.dateFormat{/lang}',
'wcf.date.dateTimeFormat': '{lang}wcf.date.dateTimeFormat{/lang}',
'wcf.date.shortDateTimeFormat': '{lang}wcf.date.shortDateTimeFormat{/lang}',
$dateTimeObject = DateUtil::getDateTimeByTimestamp($timestamp);
$date = DateUtil::format($dateTimeObject, DateUtil::DATE_FORMAT);
$time = DateUtil::format($dateTimeObject, DateUtil::TIME_FORMAT);
- $dateTime = str_replace('%time%', $time, str_replace('%date%', $date, WCF::getLanguage()->get('wcf.date.dateTimeFormat')));
+ $isFutureDate = ($timestamp > TIME_NOW);
+ $dateTime = $this->getRelativeTime($dateTimeObject, $timestamp, $date, $time, $isFutureDate);
- return '<time datetime="'.DateUtil::format($dateTimeObject, 'c').'" class="datetime" data-timestamp="'.$timestamp.'" data-date="'.$date.'" data-time="'.$time.'" data-offset="'.$dateTimeObject->getOffset().'"'.($timestamp > TIME_NOW ? ' data-is-future-date="true"' : '').'>'.$dateTime.'</time>';
+ return '<time datetime="'.DateUtil::format($dateTimeObject, 'c').'" class="datetime" data-timestamp="'.$timestamp.'" data-date="'.$date.'" data-time="'.$time.'" data-offset="'.$dateTimeObject->getOffset().'"'.($isFutureDate ? ' data-is-future-date="true"' : '').'>'.$dateTime.'</time>';
+ }
+
+ /**
+ * Returns the relative date time identical to the relative time generated
+ * through JavaScript.
+ *
+ * @param \DateTime $dateTimeObject target date object
+ * @param integer $timestamp target timestamp
+ * @param string $date localized date
+ * @param string $time localized time
+ * @param boolean $isFutureDate true if timestamp is in the future
+ * @return string relative time
+ */
+ protected function getRelativeTime(\DateTime $dateTimeObject, $timestamp, $date, $time, $isFutureDate) {
+ if ($isFutureDate) {
+ return str_replace('%time%', $time, str_replace('%date%', $date, WCF::getLanguage()->get('wcf.date.dateTimeFormat')));
+ }
+
+ // timestamp is less than 60 seconds ago
+ if ($timestamp >= TIME_NOW || TIME_NOW < ($timestamp + 60)) {
+ return WCF::getLanguage()->get('wcf.date.relative.now');
+ }
+ // timestamp is less than 60 minutes ago (display 1 hour ago rather than 60 minutes ago)
+ else if (TIME_NOW < ($timestamp + 3540)) {
+ $minutes = max(round((TIME_NOW - $timestamp) / 60), 1);
+
+ return WCF::getLanguage()->getDynamicVariable('wcf.date.relative.minutes', ['minutes' => $minutes]);
+ }
+ // timestamp is less than 24 hours ago
+ else if (TIME_NOW < ($timestamp + 86400)) {
+ $hours = round((TIME_NOW - $timestamp) / 3600);
+
+ return WCF::getLanguage()->getDynamicVariable('wcf.date.relative.hours', ['hours' => $hours]);
+ }
+ // timestamp is less than 6 days ago
+ else if (TIME_NOW < ($timestamp + 518400)) {
+ $dtoNoTime = clone $dateTimeObject;
+ $dtoNoTime->setTime(0, 0, 0);
+ $currentDateTimeObject = DateUtil::getDateTimeByTimestamp(TIME_NOW);
+ $currentDateTimeObject->setTime(0, 0, 0);
+
+ $days = $dtoNoTime->diff($currentDateTimeObject)->days;
+ $day = DateUtil::format($dateTimeObject, 'l');
+
+ return WCF::getLanguage()->getDynamicVariable('wcf.date.relative.pastDays', [
+ 'days' => $days,
+ 'day' => $day,
+ 'time' => $time
+ ]);
+ }
+
+ // timestamp is between ~700 million years BC and last week
+ $datetime = WCF::getLanguage()->get('wcf.date.shortDateTimeFormat');
+ $datetime = str_replace('%date%', $date, $datetime);
+ $datetime = str_replace('%time%', $time, $datetime);
+
+ return $datetime;
}
}
<item name="wcf.date.period.today"><![CDATA[Heute]]></item>
<item name="wcf.date.period.yesterday"><![CDATA[Gestern]]></item>
- <!-- variables for dynamic javascript datetime -->
+ <!-- variables for relative datetime for use in PHP -->
<item name="wcf.date.relative.now"><![CDATA[Vor einem Moment]]></item>
- <item name="wcf.date.relative.minutes"><![CDATA[{literal}Vor {if $minutes > 1}{#$minutes} Minuten{else}einer Minute{/if}{/literal}]]></item>
- <item name="wcf.date.relative.hours"><![CDATA[{literal}Vor {if $hours > 1}{#$hours} Stunden{else}einer Stunde{/if}{/literal}]]></item>
- <item name="wcf.date.relative.pastDays"><![CDATA[{literal}{if $days > 1}{$day}{else}Gestern{/if}, {$time}{/literal}]]></item>
+ <item name="wcf.date.relative.minutes"><![CDATA[Vor {if $minutes > 1}{#$minutes} Minuten{else}einer Minute{/if}]]></item>
+ <item name="wcf.date.relative.hours"><![CDATA[Vor {if $hours > 1}{#$hours} Stunden{else}einer Stunde{/if}]]></item>
+ <item name="wcf.date.relative.pastDays"><![CDATA[{if $days > 1}{$day}{else}Gestern{/if}, {$time}]]></item>
+
+ <!-- variables for relative datetime for use in JavaScript -->
+ <!-- should match the variables above but wrapped in {literal}…{/literal} -->
+ <item name="wcf.date.relative.nowJS"><![CDATA[Vor einem Moment]]></item>
+ <item name="wcf.date.relative.minutesJS"><![CDATA[{literal}Vor {if $minutes > 1}{#$minutes} Minuten{else}einer Minute{/if}{/literal}]]></item>
+ <item name="wcf.date.relative.hoursJS"><![CDATA[{literal}Vor {if $hours > 1}{#$hours} Stunden{else}einer Stunde{/if}{/literal}]]></item>
+ <item name="wcf.date.relative.pastDaysJS"><![CDATA[{literal}{if $days > 1}{$day}{else}Gestern{/if}, {$time}{/literal}]]></item>
<!-- variables for localized date formats -->
<item name="wcf.date.hour"><![CDATA[Stunde]]></item>
<item name="wcf.date.period.today"><![CDATA[Today]]></item>
<item name="wcf.date.period.yesterday"><![CDATA[Yesterday]]></item>
- <!-- variables for dynamic javascript datetime -->
+ <!-- variables for relative datetime for use in PHP -->
+ <item name="wcf.date.relative.now"><![CDATA[A moment ago]]></item>
+ <item name="wcf.date.relative.minutes"><![CDATA[{if $minutes > 1}{#$minutes} minutes{else}A minute{/if} ago]]></item>
+ <item name="wcf.date.relative.hours"><![CDATA[{if $hours > 1}{#$hours} hours{else}An hour{/if} ago]]></item>
+ <item name="wcf.date.relative.pastDays"><![CDATA[{if $days > 1}{$day}{else}Yesterday{/if}, {$time}]]></item>
+
+ <!-- variables for relative datetime for use in JavaScript -->
+ <!-- should match the variables above but wrapped in {literal}…{/literal} -->
<item name="wcf.date.relative.now"><![CDATA[A moment ago]]></item>
<item name="wcf.date.relative.minutes"><![CDATA[{literal}{if $minutes > 1}{#$minutes} minutes{else}A minute{/if} ago{/literal}]]></item>
<item name="wcf.date.relative.hours"><![CDATA[{literal}{if $hours > 1}{#$hours} hours{else}An hour{/if} ago{/literal}]]></item>