Javascript 时间戳到相对时间

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6108819/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 20:15:39  来源:igfitidea点击:

Javascript timestamp to relative time

javascriptdatetimetimerelative-date

提问by wilsonpage

I'm looking for a nice JS snippet to convert a timestamp (e.g. from Twitter API) to a nice user friendly relative time (e.g. 2 seconds ago, one week ago etc).

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

Anyone care to share some of their favourite methods (preferably not using plugins)?

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

回答by fearofawhackplanet

Well it's pretty easy if you aren't overly concerned with accuracy. What wrong with the trivial method?

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

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

Working example here.

工作示例在这里

You might want to tweak it to handle the singular values better (e.g. 1 dayinstead of 1 days) if that bothers you.

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

回答by Timur Carpeev

Here is the exact mimic of twitter time ago without plugins:

这是以前没有插件的推特的精确模仿:

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

Gist https://gist.github.com/timuric/11386129

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

Fiddle http://jsfiddle.net/qE8Lu/1/

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

Hope it helps.

希望能帮助到你。

回答by vsync

Intl.RelativeTimeFormat- Native API

Intl.RelativeTimeFormat- 原生 API

Currently (Dec' 18) a Stage 3proposal, and already implemented in Chrome 71

目前(12 月 18 日)第 3 阶段提案,并已在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.RelativeTimeFormatis available by default in V8 v7.1.179and Chrome 71. As this API becomes more widely available, you'll find libraries such as Moment.js, Globalize, and date-fnsdropping their dependency on hardcoded CLDR databases in favor of the native relative time formatting functionality, thereby improving load-time performance, parse- and compile-time performance, run-time performance, and memory usage.

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

回答by Caio Tarifa

Inspirated on Diego Castillo awnser'sand in the timeago.jsplugin, I wrote my own vanilla plugin for this.

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>

回答by Nitin Jadhav

MomentJS Answer

MomentJS 答案



For Moment.js users, it has fromNow() function that returns "x days" or "x hours ago" from current date/time.

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

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

回答by kigiri

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

had some fun golfing it 192bhehe

打高尔夫球玩得很开心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)}}}

I also tested a functionnal version while golfing:

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

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

All right i really have to get back to work now...

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

回答by abumalick

If you need multilingual and don't want to add a big library like moment. intl-relativeformatfrom yahoo it a nice solution.

如果您需要多语言并且不想添加像 moment.js 这样的大库。来自雅虎的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"

回答by Art Knipe

Datetime plugins exist because it's very hard to get it right. This video explaining date-time inconsistencieswill shed some light on the issue.

日期时间插件的存在是因为很难做到正确。这段解释日期时间不一致的视频将阐明这个问题。

All above solutions without plugins are incorrect.

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

For working with Dates and times using a plugin is preferable. Out of the hundreds of plugins that deal with it, we use Moment.jsand it's doing the job.

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

From the twitter API dcumentationwe can see their timestamp format:

twitter API 文件中我们可以看到它们的时间戳格式:

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

We can parse with it with Moment.js

我们可以用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');

To specify the preferred time unit for the diff, we can use the isSamemethod. eg:

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

if (now.isSame(postDatetime, 'day')) {
  const timeUnit = 'days';
}

Overall, constructing something like:

总的来说,构建类似的东西:

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

Refer to your plugin's documentation for handling relative time (ie: "How long ago?") calculations.

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

回答by Diego Castillo

For anyone interested, I ended up creating a Handlebars helper to do this. Usage:

对于任何感兴趣的人,我最终创建了一个 Handlebars 助手来执行此操作。用法:

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

Helper:

帮手:

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

回答by Piyush Patel

You can use machinepack-datetime for this purpose. It is easy and clear with its defined API.

为此,您可以使用 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
});