Javascript 时间戳到相对时间

IT技术 javascript datetime time relative-date
2021-01-26 06:10:08

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

有没有人愿意分享一些他们最喜欢的方法(最好不要使用插件)?

6个回答

好吧,如果您不太关心准确性,这很容易。微不足道的方法有什么问题?

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 day代替1 days)。

OP 被要求提供到 XX 之前的时间戳。您的脚本使用的不是时间戳而是日期。请修复。
2021-03-21 06:10:08
我注意到这有一个小怪癖,因为它会四舍五入,例如,如果不到 24 小时,它会说“24 小时前”而不是“1 天前”,但比 23 小时更接近 24。使用 Math.floor而不是 Math.round 应该可以解决问题。
2021-03-29 06:10:08
如果您想使用时间戳(秒)和更新变量,例如,您只需要取出 * 1000 即可。msPerMinute 到 sPerMinute。此外,如果使用 Date.now() 只需要使用前 10 位数字(匹配时间戳的长度)
2021-04-13 06:10:08

2021 年 4 月 4 日更新

我已将以下代码转换为节点包这是存储库


Intl.RelativeTimeFormat - 原生 API

[✔](12 月 18 日)第 3 阶段提案,并已第 4 阶段完成)的Chrome 71
[✔](10 月 20 日)中实现,并准备好包含在正式的 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 数据库,转而支持本机相对时间格式化功能,从而提高加载时性能,解析- 以及编译时性能、运行时性能和内存使用情况。

感谢您的回答 :)
2021-03-23 06:10:08
@vsync 我昨天正准备签到修改我的评论,但 Stack Overflow 已停机维护。'10/20/1984' 是您的示例日期之一。今天是“10-02-2021”,不等于或超过“​​10-20-2021”,所以我减去了 2020 年到 1984 年。我意识到Intl.RelativeTimeFormat可能会四舍五入日期/生成近似日期(例如 36 和 11 个月被四舍五入-到 37 岁)。现在我说对不起,你的代码没有错误。
2021-03-31 06:10:08
@Dayd -更新答案与为例,但如果你想在一个可靠的方法来比较日期则必须首先两个日期之间创建比较方法和理解,如果保证金是hoursdaysyears,等等。然后,它的那样简单将该结果传递给该Intl.RelativeTimeFormat方法。你问的是一个完全不同的话题
2021-04-09 06:10:08
作为答案,如果没有从时间戳到相对时间,这是不完整的Intl.RelativeTimeFormatAPI 似乎更像是一个本地化的字符串格式化程序(用于时间单位),而不是格式化原始日期/时间对象的东西。仍然需要处理时间戳来决定要使用的适当时间范围(分钟、小时、天等)。您不想在 36,020 秒前说!
2021-04-10 06:10:08
很棒,但如何从约会开始呢?
2021-04-13 06:10:08

这是之前没有插件的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/

希望能帮助到你。

这是一个更新版本,它将始终返回“timeAgo”格式,包括月份和年份:jsfiddle.net/u3p9s8kn/65
2021-03-16 06:10:08
这不应该是一个答案。您的脚本输出“2001 年 9 月 9 日”,他要求的是“XX 分钟/秒/小时/天”前
2021-03-31 06:10:08

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>

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

好吧,我现在真的得回去工作了……

谢谢@MSOACC 投反对票和有见地的评论,如果你想要可维护,你可以使用长版本。
2021-03-15 06:10:08
在编写尽可能短的代码时,这可能是一个有趣的练习,但任何阅读本文的人都应避免复制粘贴此答案;这不是可维护的代码。
2021-03-24 06:10:08