Javascript时间戳相对时间


Answers:


142

好吧,如果您不太担心准确性,那很容易。平凡的方法有什么问题?

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';   
    }
}

这里的工作示例。

如果这会让您感到困扰1 day1 days则可能需要对其进行调整以更好地处理奇异值(例如而不是)。


2
我发现这有点小怪异,原因是四舍五入后会说,例如,如果少于24小时,但比24多于23,则改为“ 24小时前”而不是“ 1天前”。使用Math.floor而不是Math.round应该可以解决问题。
dynamitereed

要求OP将时间戳记为XX年前。您的脚本不是使用时间戳,而是使用日期。请解决。
user198989 '02

看起来,如果要使用时间戳(秒)并更新变量,例如,您只需要取出* 1000。msPerMinute至sPerMinute。另外,如果使用Date.now()只需要使用前10位数字(匹配时间戳的长度)
雅各布·戴维·C·坎宁安

26

这是没有插件的Twitter时间的精确模拟:

function timeSince(timeStamp) {
  var now = new Date(),
    secondsPast = (now.getTime() - timeStamp) / 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;
  }
}

const currentTimeStamp = new Date().getTime();

console.log(timeSince(currentTimeStamp));

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

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

希望能帮助到你。


3
这不应该是答案。您的脚本输出“ 2001年9月9日”,他的要求是“ XX分钟/秒/小时/天”之前
user198989

21

Intl.RelativeTimeFormat-本机API

[✔](12月18日)是第3阶段的提案,已经在Chrome 71
[✔](10月20日)的第4阶段(已完成)中实施,并准备纳入正式的ECMAScript标准

// in miliseconds
var units = {
  year  : 24 * 60 * 60 * 1000 * 365,
  month : 24 * 60 * 60 * 1000 * 365/12,
  day   : 24 * 60 * 60 * 1000,
  hour  : 60 * 60 * 1000,
  minute: 60 * 1000,
  second: 1000
}

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

var getRelativeTime = (d1, d2 = new Date()) => {
  var elapsed = d1 - d2

  // "Math.abs" accounts for both "past" & "future" scenarios
  for (var u in units) 
    if (Math.abs(elapsed) > units[u] || u == 'second') 
      return rtf.format(Math.round(elapsed/units[u]), u)
}

// test-list of dates to compare with current date
[
  '10/20/1984',
  '10/20/2015',
  +new Date() - units.year,
  +new Date() - units.month,
  +new Date() - units.day,
  +new Date() - units.hour,
  +new Date() - units.minute,
  +new Date() + units.minute*2,
  +new Date() + units.day*7,
]
.forEach(d => console.log(   
  new Date(d).toLocaleDateString(),
  new Date(d).toLocaleTimeString(), 
  '(Relative to now) →',
  getRelativeTime(+new Date(d))
))

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


很棒,但是从约会开始怎么做?
Dayd

2
@Dayd -更新答案与为例,但如果你想在一个可靠的方法来比较日期则必须首先两个日期之间创建比较方法和理解,如果保证金是hoursdaysyears,等等。然后,它的那样简单将结果传递给Intl.RelativeTimeFormat方法。您要问的是一个完全不同的主题
vsync

谢谢您的回答:)
Dayd

作为答案,这是不完整的,没有从时间戳记到相对时间。该Intl.RelativeTimeFormatAPI看起来更像是一种具有区域性的字符串格式化程序(用于时间单位),而不是用于格式化原始日期/时间对象的某种东西。您仍然必须处理时间戳,以决定要使用的适当时间范围(分钟,小时,天等)。您不想在36,020秒前说!
伊曼纽尔

@Emmanuel-@kigiri发布了一个更完整的答案,但我将更新我的版本,使其能够自动检测实例的“最佳”unit参数RelativeTimeFormat
vsync

7

Inspirated对迭戈卡斯蒂略awnser的,并在timeago.js插件,我写我自己的香草插件此。

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());
<time datetime="2016-06-13"></time>


5
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)), _=>_)

好吧,我真的必须立即开始工作...


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';
        }
    });

1

存在Datetime插件是因为很难正确设置日期。这段解释日期时间不一致的视频将使您对该问题有所了解。

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

对于使用日期和时间使用插件是更可取的。在处理它的数百个插件中,我们使用Moment.js来完成工作。

twitter API文档中,我们可以看到其时间戳格式:

"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`;

请参阅插件的文档以处理相对时间(即:“多长时间?”)。


1
您不需要插件库来处理Javascript Date对象之间的相对差异。它们已经处理到UTC的转换,并且当转换为整数时,给出自UTC时代以来的绝对毫秒数。因此,date1 - date2无论原始时区如何,都会为您提供适当的时间增量(以毫秒为单位)。同样,new Date().getTime()在两个时区的浏览器中,如果在正确的系统时钟上同时执行,将报告相同的数字。
Yetanotherjosh

1

如果您需要多语言并且不想像瞬间添加一个大型库。来自Yahoo的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"

1

MomentJS答案


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

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

Moment.js不再受支持的库。您应该考虑使用其他库。
sunpietro

-1

您可以将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
});
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.