Javascript时间戳到相对时间(例如2秒前,一周前等),最佳方法?

时间:2011-05-24 10:08:24

标签: javascript datetime time relative-date

我正在寻找一个很好的JS片段来将时间戳(例如来自Twitter API)转换为友好的用户友好相对时间(例如2秒前,一周前等)。

任何人都想分享一些他们喜欢的方法(最好不要使用插件)?

13 个答案:

答案 0 :(得分:91)

如果你不过分关注准确性,那很容易。这个琐碎的方法有什么问题?

function timeDifference(current, previous) {

    var msPerMinute = 60 * 1000;
    var msPerHour = msPerMinute * 60;
    var msPerDay = msPerHour * 24;
    var msPerMonth = msPerDay * 30;
    var msPerYear = msPerDay * 365;

    var elapsed = current - previous;

    if (elapsed < msPerMinute) {
         return Math.round(elapsed/1000) + ' seconds ago';   
    }

    else if (elapsed < msPerHour) {
         return Math.round(elapsed/msPerMinute) + ' minutes ago';   
    }

    else if (elapsed < msPerDay ) {
         return Math.round(elapsed/msPerHour ) + ' hours ago';   
    }

    else if (elapsed < msPerMonth) {
        return 'approximately ' + Math.round(elapsed/msPerDay) + ' days ago';   
    }

    else if (elapsed < msPerYear) {
        return 'approximately ' + Math.round(elapsed/msPerMonth) + ' months ago';   
    }

    else {
        return 'approximately ' + Math.round(elapsed/msPerYear ) + ' years ago';   
    }
}

工作示例here

您可能想要调整它以更好地处理奇异值(例如1 day而不是1 days),如果这让您感到困扰。

答案 1 :(得分:19)

以下是没有插件的Twitter时间的精确模仿:

  function timeSince(timeStamp) {
    var now = new Date(),
      secondsPast = (now.getTime() - timeStamp.getTime()) / 1000;
    if(secondsPast < 60){
      return parseInt(secondsPast) + 's';
    }
    if(secondsPast < 3600){
      return parseInt(secondsPast/60) + 'm';
    }
    if(secondsPast <= 86400){
      return parseInt(secondsPast/3600) + 'h';
    }
    if(secondsPast > 86400){
        day = timeStamp.getDate();
        month = timeStamp.toDateString().match(/ [a-zA-Z]*/)[0].replace(" ","");
        year = timeStamp.getFullYear() == now.getFullYear() ? "" :  " "+timeStamp.getFullYear();
        return day + " " + month + year;
    }
  }

要点https://gist.github.com/timuric/11386129

小提琴http://jsfiddle.net/qE8Lu/1/

希望它有所帮助。

答案 2 :(得分:5)

多田! Timeago:http://timeago.yarp.com/

哦,坚持下去 - 没有插件?那为什么呢?我想你可以打开插件文件并破解其中的内容。

答案 3 :(得分:4)

Diego Castillo awnser'stimeago.js插件中启示,我为此编写了自己的vanilla插件。

    Handlebars.RegisterHelper("formatDate", New HandlebarsHelper(Sub(w, c, p)
                                                                     w.WriteSafeString("moment(" + p(0) + ").format(" + p(1) + ");")
                                                                 End Sub))

    Handlebars.RegisterHelper("formatPercent", New HandlebarsHelper(Sub(w, c, p)
                                                                        If p(1) = 0 Then
                                                                            w.WriteSafeString("0")
                                                                        Else
                                                                            w.WriteSafeString("Math.ceil(" + 100 * p(0) / p(1) + ");")
                                                                        End If
                                                                    End Sub))

var timeElement = document.querySelector('time'),
    time = new Date(timeElement.getAttribute('datetime'));

timeElement.innerText = TimeAgo.inWords(time.getTime());
var TimeAgo = (function() {
  var self = {};
  
  // Public Methods
  self.locales = {
    prefix: '',
    sufix:  'ago',
    
    seconds: 'less than a minute',
    minute:  'about a minute',
    minutes: '%d minutes',
    hour:    'about an hour',
    hours:   'about %d hours',
    day:     'a day',
    days:    '%d days',
    month:   'about a month',
    months:  '%d months',
    year:    'about a year',
    years:   '%d years'
  };
  
  self.inWords = function(timeAgo) {
    var seconds = Math.floor((new Date() - parseInt(timeAgo)) / 1000),
        separator = this.locales.separator || ' ',
        words = this.locales.prefix + separator,
        interval = 0,
        intervals = {
          year:   seconds / 31536000,
          month:  seconds / 2592000,
          day:    seconds / 86400,
          hour:   seconds / 3600,
          minute: seconds / 60
        };
    
    var distance = this.locales.seconds;
    
    for (var key in intervals) {
      interval = Math.floor(intervals[key]);
      
      if (interval > 1) {
        distance = this.locales[key + 's'];
        break;
      } else if (interval === 1) {
        distance = this.locales[key];
        break;
      }
    }
    
    distance = distance.replace(/%d/i, interval);
    words += distance + separator + this.locales.sufix;

    return words.trim();
  };
  
  return self;
}());


// USAGE
var timeElement = document.querySelector('time'),
    time = new Date(timeElement.getAttribute('datetime'));

timeElement.innerText = TimeAgo.inWords(time.getTime());

答案 4 :(得分:4)

Intl.RelativeTimeFormat-本机API

当前(十二月18日)一个Stage 3 proposal,并且已经在Chrome 71中实现

const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });

const millisecondsPerDay = 24 * 60 * 60 * 1000;

[
  [3.14 , 'second' ],
  [-15  , 'minute' ],
  [8    , 'hour'   ],
  [-1   , 'day'    ],
  [3    , 'week'   ],
  [-5   , 'month'  ],
  [2    , 'quarter'],
  [-42  , 'year'   ],
  [(new Date('9/22/2018') - new Date())/millisecondsPerDay,'day']
].forEach(d => console.log(   rtf.format(d[0], d[1])  ));

  

Intl.RelativeTimeFormat V8 v7.1.179 中默认可用   Chrome 71 。随着该API的广泛使用,您会发现   Moment.jsGlobalizedate-fns之类的库   对硬编码CLDR数据库的依赖性,以本机相对   时间格式化功能,从而缩短了加载时间   性能,分析和编译时性能,运行时   性能和内存使用情况。

答案 5 :(得分:4)

const units = [
  ['year', 31536000000],
  ['month', 2628000000],
  ['day', 86400000],
  ['hour', 3600000],
  ['minute', 60000],
  ['second', 1000],
]

const rtf = new Intl.RelativeTimeFormat('en', { style:'narrow'})
const relatime = elapsed => {
  for (const [unit, amount] of units) {
    if (Math.abs(elapsed) > amount || unit === 'second') {
      return rtf.format(Math.round(elapsed/amount), unit)
    }
  }
}

打高尔夫球192b很有趣

const relatime = e=>{for(let[u,a]of Object.entries({year:31536e6,month:2628e6,day:864e5,hour:36e5,minute:6e4,second:1e3})){if(Math.abs(e)>a||a===1e3){return new Intl.RelativeTimeFormat('en',{style:'narrow'}).format(~~(e/a),u)}}}

我还在打高尔夫球时测试了功能版本:

const rtf = new Intl.RelativeTimeFormat('en', { style:'narrow'})
const relatime = Object.entries({year:31536e6,month:2628e6,day:864e5,hour:36e5,minute:6e4,second:1e3})
  .reduce((f, [unit, amount]) => amount === 1e3
    ? f(elapsed => rtf.format(Math.round(elapsed/amount), unit))
    : next => f(e => Math.abs(e) < amount
      ? next(elapsed)
      : rtf.format(Math.round(elapsed/amount), unit)), _=>_)

好吧,我真的必须立即恢复工作...

答案 6 :(得分:2)

Typescript 和 Intl.RelativeTimeFormat (2020)

使用 Web API RelativeTimeFormat 结合 Typescript 实现 @vsync 和 @kigiri 方法。

const units: {unit: Intl.RelativeTimeFormatUnit; ms: number}[] = [
    {unit: "year", ms: 31536000000},
    {unit: "month", ms: 2628000000},
    {unit: "day", ms: 86400000},
    {unit: "hour", ms: 3600000},
    {unit: "minute", ms: 60000},
    {unit: "second", ms: 1000},
];
const rtf = new Intl.RelativeTimeFormat("en", {numeric: "auto"});

/**
 * Get language-sensitive relative time message from Dates.
 * @param relative  - the relative dateTime, generally is in the past or future
 * @param pivot     - the dateTime of reference, generally is the current time
 */
export function relativeTimeFromDates(relative: Date | null, pivot: Date = new Date()): string {
    if (!relative) return "";
    const elapsed = relative.getTime() - pivot.getTime();
    return relativeTimeFromElapsed(elapsed);
}

/**
 * Get language-sensitive relative time message from elapsed time.
 * @param elapsed   - the elapsed time in milliseconds
 */
export function relativeTimeFromElapsed(elapsed: number): string {
    for (const {unit, ms} of units) {
        if (Math.abs(elapsed) > ms || unit === "second") {
            return rtf.format(Math.round(elapsed / ms), unit);
        }
    }
    return "";
}

答案 7 :(得分:1)

对于任何有兴趣的人,我最终创建了一个Handlebars助手来完成此任务。 用法:

    {{#beautify_date}}
        {{timestamp_ms}}
    {{/beautify_date}}

助手:

    Handlebars.registerHelper('beautify_date', function(options) {
        var timeAgo = new Date(parseInt(options.fn(this)));

        if (Object.prototype.toString.call(timeAgo) === "[object Date]") {
            if (isNaN(timeAgo.getTime())) {
                return 'Not Valid';
            } else {
                var seconds = Math.floor((new Date() - timeAgo) / 1000),
                intervals = [
                    Math.floor(seconds / 31536000),
                    Math.floor(seconds / 2592000),
                    Math.floor(seconds / 86400),
                    Math.floor(seconds / 3600),
                    Math.floor(seconds / 60)
                ],
                times = [
                    'year',
                    'month',
                    'day',
                    'hour',
                    'minute'
                ];

                var key;
                for(key in intervals) {
                    if (intervals[key] > 1)  
                        return intervals[key] + ' ' + times[key] + 's ago';
                    else if (intervals[key] === 1) 
                        return intervals[key] + ' ' + times[key] + ' ago';
                }

                return Math.floor(seconds) + ' seconds ago';
            }
        } else {
            return 'Not Valid';
        }
    });

答案 8 :(得分:1)

存在日期时间插件,因为很难做到正确。这个video explaining date-time inconsistencies将为这个问题提供一些启示。

以上没有插件的解决方案都不正确。

使用日期和时间最好使用插件。在处理它的数百个插件中,我们使用Moment.js并且它正在完成这项工作。

twitter API dcumentation我们可以看到他们的时间戳格式:

"created_at":"Wed Aug 27 13:08:45 +0000 2008"

我们可以使用Moment.js

解析它
const postDatetime = moment(
  "Wed Aug 27 13:08:45 +0000 2008",
  "dddd, MMMM Do, h:mm:ss a, YYYY"
);
const now = moment();
const timeAgo = now.diff(postDatetime, 'seconds');

要指定diff的首选时间单位,我们可以使用isSame方法。例如:

if (now.isSame(postDatetime, 'day')) {
  const timeUnit = 'days';
}
总的来说,构建类似的东西:

`Posted ${timeAgo} ${timeUnit} ago`;

请参阅插件的文档以了解处理相对时间(即:“多久以前?”)计算。

答案 9 :(得分:1)

此目的还有sugar.jsrelative功能。

relative - 以相对于当前日期的单位输出字符串(&#34;前&#34;或&#34;从现在开始&#34;)。

答案 10 :(得分:1)

如果您需要多语言,并且不想像片刻那样添加大型图书馆。来自雅虎的intl-relativeformat这是一个很好的解决方案。

var rf = new IntlRelativeFormat('en-US');

var posts = [
    {
        id   : 1,
        title: 'Some Blog Post',
        date : new Date(1426271670524)
    },
    {
        id   : 2,
        title: 'Another Blog Post',
        date : new Date(1426278870524)
    }
];

posts.forEach(function (post) {
    console.log(rf.format(post.date));
});
// => "3 hours ago"
// => "1 hour ago"

答案 11 :(得分:0)

您可以将machinepack-datetime用于此目的。它定义的API很简单明了。

tutorialSchema.virtual('createdOn').get(function () {
    const DateTime = require('machinepack-datetime');
    let timeAgoString = "";
    try {
        timeAgoString = DateTime.timeFrom({
            toWhen: DateTime.parse({
                datetime: this.createdAt
            }).execSync(),
            fromWhen: new Date().getTime()
        }).execSync();
    } catch(err) {
        console.log('error getting createdon', err);
    }
    return timeAgoString; // a second ago
});

答案 12 :(得分:0)

对于Moment.js用户,它具有fromNow()函数,该函数从当前日期/时间返回“ x天”或“ x小时前”。

moment([2007, 0, 29]).fromNow();     // 4 years ago
moment([2007, 0, 29]).fromNow(true); // 4 years