| // Copyright 2009 The Closure Library Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS-IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| /** |
| * @fileoverview Functions for formatting relative dates. Such as "3 days ago" |
| * "3 hours ago", "14 minutes ago", "12 days ago", "Today", "Yesterday". |
| * |
| * For better quality localization of plurals ("hours"/"minutes"/"days") and |
| * to use local digits, goog.date.relativeWithPlurals can be loaded in addition |
| * to this namespace. |
| * |
| */ |
| |
| goog.provide('goog.date.relative'); |
| goog.provide('goog.date.relative.TimeDeltaFormatter'); |
| goog.provide('goog.date.relative.Unit'); |
| |
| goog.require('goog.i18n.DateTimeFormat'); |
| |
| |
| /** |
| * Number of milliseconds in a minute. |
| * @type {number} |
| * @private |
| */ |
| goog.date.relative.MINUTE_MS_ = 60000; |
| |
| |
| /** |
| * Number of milliseconds in a day. |
| * @type {number} |
| * @private |
| */ |
| goog.date.relative.DAY_MS_ = 86400000; |
| |
| |
| /** |
| * Enumeration used to identify time units internally. |
| * @enum {number} |
| */ |
| goog.date.relative.Unit = { |
| MINUTES: 0, |
| HOURS: 1, |
| DAYS: 2 |
| }; |
| |
| |
| /** |
| * Full date formatter. |
| * @type {goog.i18n.DateTimeFormat} |
| * @private |
| */ |
| goog.date.relative.fullDateFormatter_; |
| |
| |
| /** |
| * Short time formatter. |
| * @type {goog.i18n.DateTimeFormat} |
| * @private |
| */ |
| goog.date.relative.shortTimeFormatter_; |
| |
| |
| /** |
| * Month-date formatter. |
| * @type {goog.i18n.DateTimeFormat} |
| * @private |
| */ |
| goog.date.relative.monthDateFormatter_; |
| |
| |
| /** |
| * @typedef {function(number, boolean, goog.date.relative.Unit): string} |
| */ |
| goog.date.relative.TimeDeltaFormatter; |
| |
| |
| /** |
| * Handles formatting of time deltas. |
| * @private {goog.date.relative.TimeDeltaFormatter} |
| */ |
| goog.date.relative.formatTimeDelta_; |
| |
| |
| /** |
| * Sets a different formatting function for time deltas ("3 days ago"). |
| * While its visibility is public, this function is Closure-internal and should |
| * not be used in application code. |
| * @param {goog.date.relative.TimeDeltaFormatter} formatter The function to use |
| * for formatting time deltas (i.e. relative times). |
| */ |
| goog.date.relative.setTimeDeltaFormatter = function(formatter) { |
| goog.date.relative.formatTimeDelta_ = formatter; |
| }; |
| |
| |
| /** |
| * Returns a date in month format, e.g. Mar 15. |
| * @param {Date} date The date object. |
| * @return {string} The formatted string. |
| * @private |
| */ |
| goog.date.relative.formatMonth_ = function(date) { |
| if (!goog.date.relative.monthDateFormatter_) { |
| goog.date.relative.monthDateFormatter_ = |
| new goog.i18n.DateTimeFormat('MMM dd'); |
| } |
| return goog.date.relative.monthDateFormatter_.format(date); |
| }; |
| |
| |
| /** |
| * Returns a date in short-time format, e.g. 2:50 PM. |
| * @param {Date|goog.date.DateTime} date The date object. |
| * @return {string} The formatted string. |
| * @private |
| */ |
| goog.date.relative.formatShortTime_ = function(date) { |
| if (!goog.date.relative.shortTimeFormatter_) { |
| goog.date.relative.shortTimeFormatter_ = new goog.i18n.DateTimeFormat( |
| goog.i18n.DateTimeFormat.Format.SHORT_TIME); |
| } |
| return goog.date.relative.shortTimeFormatter_.format(date); |
| }; |
| |
| |
| /** |
| * Returns a date in full date format, e.g. Tuesday, March 24, 2009. |
| * @param {Date|goog.date.DateTime} date The date object. |
| * @return {string} The formatted string. |
| * @private |
| */ |
| goog.date.relative.formatFullDate_ = function(date) { |
| if (!goog.date.relative.fullDateFormatter_) { |
| goog.date.relative.fullDateFormatter_ = new goog.i18n.DateTimeFormat( |
| goog.i18n.DateTimeFormat.Format.FULL_DATE); |
| } |
| return goog.date.relative.fullDateFormatter_.format(date); |
| }; |
| |
| |
| /** |
| * Accepts a timestamp in milliseconds and outputs a relative time in the form |
| * of "1 hour ago", "1 day ago", "in 1 hour", "in 2 days" etc. If the date |
| * delta is over 2 weeks, then the output string will be empty. |
| * @param {number} dateMs Date in milliseconds. |
| * @return {string} The formatted date. |
| */ |
| goog.date.relative.format = function(dateMs) { |
| var now = goog.now(); |
| var delta = Math.floor((now - dateMs) / goog.date.relative.MINUTE_MS_); |
| |
| var future = false; |
| |
| if (delta < 0) { |
| future = true; |
| delta *= -1; |
| } |
| |
| if (delta < 60) { // Minutes. |
| return goog.date.relative.formatTimeDelta_( |
| delta, future, goog.date.relative.Unit.MINUTES); |
| |
| } else { |
| delta = Math.floor(delta / 60); |
| if (delta < 24) { // Hours. |
| return goog.date.relative.formatTimeDelta_( |
| delta, future, goog.date.relative.Unit.HOURS); |
| |
| } else { |
| // We can be more than 24 hours apart but still only 1 day apart, so we |
| // compare the closest time from today against the target time to find |
| // the number of days in the delta. |
| var midnight = new Date(goog.now()); |
| midnight.setHours(0); |
| midnight.setMinutes(0); |
| midnight.setSeconds(0); |
| midnight.setMilliseconds(0); |
| |
| // Convert to days ago. |
| delta = Math.ceil( |
| (midnight.getTime() - dateMs) / goog.date.relative.DAY_MS_); |
| |
| if (future) { |
| delta *= -1; |
| } |
| |
| // Uses days for less than 2-weeks. |
| if (delta < 14) { |
| return goog.date.relative.formatTimeDelta_( |
| delta, future, goog.date.relative.Unit.DAYS); |
| |
| } else { |
| // For messages older than 2 weeks do not show anything. The client |
| // should decide the date format to show. |
| return ''; |
| } |
| } |
| } |
| }; |
| |
| |
| /** |
| * Accepts a timestamp in milliseconds and outputs a relative time in the form |
| * of "1 hour ago", "1 day ago". All future times will be returned as 0 minutes |
| * ago. |
| * |
| * This is provided for compatibility with users of the previous incarnation of |
| * the above {@see #format} method who relied on it protecting against |
| * future dates. |
| * |
| * @param {number} dateMs Date in milliseconds. |
| * @return {string} The formatted date. |
| */ |
| goog.date.relative.formatPast = function(dateMs) { |
| var now = goog.now(); |
| if (now < dateMs) { |
| dateMs = now; |
| } |
| return goog.date.relative.format(dateMs); |
| }; |
| |
| |
| /** |
| * Accepts a timestamp in milliseconds and outputs a relative day. i.e. "Today", |
| * "Yesterday", "Tomorrow", or "Sept 15". |
| * |
| * @param {number} dateMs Date in milliseconds. |
| * @param {function(!Date):string=} opt_formatter Formatter for the date. |
| * Defaults to form 'MMM dd'. |
| * @return {string} The formatted date. |
| */ |
| goog.date.relative.formatDay = function(dateMs, opt_formatter) { |
| var today = new Date(goog.now()); |
| |
| today.setHours(0); |
| today.setMinutes(0); |
| today.setSeconds(0); |
| today.setMilliseconds(0); |
| |
| var yesterday = new Date(today.getTime() - goog.date.relative.DAY_MS_); |
| var tomorrow = new Date(today.getTime() + goog.date.relative.DAY_MS_); |
| var dayAfterTomorrow = new Date(today.getTime() + |
| 2 * goog.date.relative.DAY_MS_); |
| |
| var message; |
| if (dateMs >= tomorrow.getTime() && dateMs < dayAfterTomorrow.getTime()) { |
| /** @desc Tomorrow. */ |
| var MSG_TOMORROW = goog.getMsg('Tomorrow'); |
| message = MSG_TOMORROW; |
| } else if (dateMs >= today.getTime() && dateMs < tomorrow.getTime()) { |
| /** @desc Today. */ |
| var MSG_TODAY = goog.getMsg('Today'); |
| message = MSG_TODAY; |
| } else if (dateMs >= yesterday.getTime() && dateMs < today.getTime()) { |
| /** @desc Yesterday. */ |
| var MSG_YESTERDAY = goog.getMsg('Yesterday'); |
| message = MSG_YESTERDAY; |
| } else { |
| // If we don't have a special relative term for this date, then return the |
| // short date format (or a custom-formatted date). |
| var formatFunction = opt_formatter || goog.date.relative.formatMonth_; |
| message = formatFunction(new Date(dateMs)); |
| } |
| return message; |
| }; |
| |
| |
| /** |
| * Formats a date, adding the relative date in parenthesis. If the date is less |
| * than 24 hours then the time will be printed, otherwise the full-date will be |
| * used. Examples: |
| * 2:20 PM (1 minute ago) |
| * Monday, February 27, 2009 (4 days ago) |
| * Tuesday, March 20, 2005 // Too long ago for a relative date. |
| * |
| * @param {Date|goog.date.DateTime} date A date object. |
| * @param {string=} opt_shortTimeMsg An optional short time message can be |
| * provided if available, so that it's not recalculated in this function. |
| * @param {string=} opt_fullDateMsg An optional date message can be |
| * provided if available, so that it's not recalculated in this function. |
| * @return {string} The date string in the above form. |
| */ |
| goog.date.relative.getDateString = function( |
| date, opt_shortTimeMsg, opt_fullDateMsg) { |
| return goog.date.relative.getDateString_( |
| date, goog.date.relative.format, opt_shortTimeMsg, opt_fullDateMsg); |
| }; |
| |
| |
| /** |
| * Formats a date, adding the relative date in parenthesis. Functions the same |
| * as #getDateString but ensures that the date is always seen to be in the past. |
| * If the date is in the future, it will be shown as 0 minutes ago. |
| * |
| * This is provided for compatibility with users of the previous incarnation of |
| * the above {@see #getDateString} method who relied on it protecting against |
| * future dates. |
| * |
| * @param {Date|goog.date.DateTime} date A date object. |
| * @param {string=} opt_shortTimeMsg An optional short time message can be |
| * provided if available, so that it's not recalculated in this function. |
| * @param {string=} opt_fullDateMsg An optional date message can be |
| * provided if available, so that it's not recalculated in this function. |
| * @return {string} The date string in the above form. |
| */ |
| goog.date.relative.getPastDateString = function( |
| date, opt_shortTimeMsg, opt_fullDateMsg) { |
| return goog.date.relative.getDateString_( |
| date, goog.date.relative.formatPast, opt_shortTimeMsg, opt_fullDateMsg); |
| }; |
| |
| |
| /** |
| * Formats a date, adding the relative date in parenthesis. If the date is less |
| * than 24 hours then the time will be printed, otherwise the full-date will be |
| * used. Examples: |
| * 2:20 PM (1 minute ago) |
| * Monday, February 27, 2009 (4 days ago) |
| * Tuesday, March 20, 2005 // Too long ago for a relative date. |
| * |
| * @param {Date|goog.date.DateTime} date A date object. |
| * @param {function(number) : string} relativeFormatter Function to use when |
| * formatting the relative date. |
| * @param {string=} opt_shortTimeMsg An optional short time message can be |
| * provided if available, so that it's not recalculated in this function. |
| * @param {string=} opt_fullDateMsg An optional date message can be |
| * provided if available, so that it's not recalculated in this function. |
| * @return {string} The date string in the above form. |
| * @private |
| */ |
| goog.date.relative.getDateString_ = function( |
| date, relativeFormatter, opt_shortTimeMsg, opt_fullDateMsg) { |
| var dateMs = date.getTime(); |
| |
| var relativeDate = relativeFormatter(dateMs); |
| |
| if (relativeDate) { |
| relativeDate = ' (' + relativeDate + ')'; |
| } |
| |
| var delta = Math.floor((goog.now() - dateMs) / goog.date.relative.MINUTE_MS_); |
| if (delta < 60 * 24) { |
| // TODO(user): this call raises an exception if date is a goog.date.Date. |
| return (opt_shortTimeMsg || goog.date.relative.formatShortTime_(date)) + |
| relativeDate; |
| } else { |
| return (opt_fullDateMsg || goog.date.relative.formatFullDate_(date)) + |
| relativeDate; |
| } |
| }; |
| |
| |
| /* |
| * TODO(user): |
| * |
| * I think that this whole relative formatting should move to DateTimeFormat. |
| * But we would have to wait for the next version of CLDR, which is cleaning |
| * the data for relative dates (even ICU has incomplete support for this). |
| */ |
| /** |
| * Gets a localized relative date string for a given delta and unit. |
| * @param {number} delta Number of minutes/hours/days. |
| * @param {boolean} future Whether the delta is in the future. |
| * @param {goog.date.relative.Unit} unit The units the delta is in. |
| * @return {string} The message. |
| * @private |
| */ |
| goog.date.relative.getMessage_ = function(delta, future, unit) { |
| var deltaFormatted = goog.i18n.DateTimeFormat.localizeNumbers(delta); |
| if (!future && unit == goog.date.relative.Unit.MINUTES) { |
| /** |
| * @desc Relative date indicating how many minutes ago something happened |
| * (singular). |
| */ |
| var MSG_MINUTES_AGO_SINGULAR = |
| goog.getMsg('{$num} minute ago', {'num' : deltaFormatted}); |
| |
| /** |
| * @desc Relative date indicating how many minutes ago something happened |
| * (plural). |
| */ |
| var MSG_MINUTES_AGO_PLURAL = |
| goog.getMsg('{$num} minutes ago', {'num' : deltaFormatted}); |
| |
| return delta == 1 ? MSG_MINUTES_AGO_SINGULAR : MSG_MINUTES_AGO_PLURAL; |
| |
| } else if (future && unit == goog.date.relative.Unit.MINUTES) { |
| /** |
| * @desc Relative date indicating in how many minutes something happens |
| * (singular). |
| */ |
| var MSG_IN_MINUTES_SINGULAR = |
| goog.getMsg('in {$num} minute', {'num' : deltaFormatted}); |
| |
| /** |
| * @desc Relative date indicating in how many minutes something happens |
| * (plural). |
| */ |
| var MSG_IN_MINUTES_PLURAL = |
| goog.getMsg('in {$num} minutes', {'num' : deltaFormatted}); |
| |
| return delta == 1 ? MSG_IN_MINUTES_SINGULAR : MSG_IN_MINUTES_PLURAL; |
| |
| } else if (!future && unit == goog.date.relative.Unit.HOURS) { |
| /** |
| * @desc Relative date indicating how many hours ago something happened |
| * (singular). |
| */ |
| var MSG_HOURS_AGO_SINGULAR = |
| goog.getMsg('{$num} hour ago', {'num' : deltaFormatted}); |
| |
| /** |
| * @desc Relative date indicating how many hours ago something happened |
| * (plural). |
| */ |
| var MSG_HOURS_AGO_PLURAL = |
| goog.getMsg('{$num} hours ago', {'num' : deltaFormatted}); |
| |
| return delta == 1 ? MSG_HOURS_AGO_SINGULAR : MSG_HOURS_AGO_PLURAL; |
| |
| } else if (future && unit == goog.date.relative.Unit.HOURS) { |
| /** |
| * @desc Relative date indicating in how many hours something happens |
| * (singular). |
| */ |
| var MSG_IN_HOURS_SINGULAR = |
| goog.getMsg('in {$num} hour', {'num' : deltaFormatted}); |
| |
| /** |
| * @desc Relative date indicating in how many hours something happens |
| * (plural). |
| */ |
| var MSG_IN_HOURS_PLURAL = |
| goog.getMsg('in {$num} hours', {'num' : deltaFormatted}); |
| |
| return delta == 1 ? MSG_IN_HOURS_SINGULAR : MSG_IN_HOURS_PLURAL; |
| |
| } else if (!future && unit == goog.date.relative.Unit.DAYS) { |
| /** |
| * @desc Relative date indicating how many days ago something happened |
| * (singular). |
| */ |
| var MSG_DAYS_AGO_SINGULAR = |
| goog.getMsg('{$num} day ago', {'num' : deltaFormatted}); |
| |
| /** |
| * @desc Relative date indicating how many days ago something happened |
| * (plural). |
| */ |
| var MSG_DAYS_AGO_PLURAL = |
| goog.getMsg('{$num} days ago', {'num' : deltaFormatted}); |
| |
| return delta == 1 ? MSG_DAYS_AGO_SINGULAR : MSG_DAYS_AGO_PLURAL; |
| |
| } else if (future && unit == goog.date.relative.Unit.DAYS) { |
| /** |
| * @desc Relative date indicating in how many days something happens |
| * (singular). |
| */ |
| var MSG_IN_DAYS_SINGULAR = |
| goog.getMsg('in {$num} day', {'num' : deltaFormatted}); |
| |
| /** |
| * @desc Relative date indicating in how many days something happens |
| * (plural). |
| */ |
| var MSG_IN_DAYS_PLURAL = |
| goog.getMsg('in {$num} days', {'num' : deltaFormatted}); |
| |
| return delta == 1 ? MSG_IN_DAYS_SINGULAR : MSG_IN_DAYS_PLURAL; |
| |
| } else { |
| return ''; |
| } |
| }; |
| |
| goog.date.relative.setTimeDeltaFormatter(goog.date.relative.getMessage_); |