我正在寻找一个不错的JS代码片段,以将时间戳记(例如,来自Twitter API)转换为用户友好的相对时间(例如2秒前,一周前等)。
有人愿意分享一些他们喜欢的方法(最好不使用插件)吗?
Answers:
好吧,如果您不太担心准确性,那很容易。平凡的方法有什么问题?
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
则可能需要对其进行调整以更好地处理奇异值(例如而不是)。
这是没有插件的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/
希望能帮助到你。
[✔](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.RelativeTimeFormat在V8 v7.1.179和Chrome 71中默认可用。随着该API的广泛使用,您会发现Moment.js,Globalize和date- fns之类的库放弃了对硬编码CLDR数据库的依赖,而转而使用本机相对时间格式化功能,从而提高了加载时性能,解析-以及编译时性能,运行时性能和内存使用情况。
hours
,days
,years
,等等。然后,它的那样简单将结果传递给Intl.RelativeTimeFormat
方法。您要问的是一个完全不同的主题
Intl.RelativeTimeFormat
API看起来更像是一种具有区域性的字符串格式化程序(用于时间单位),而不是用于格式化原始日期/时间对象的某种东西。您仍然必须处理时间戳,以决定要使用的适当时间范围(分钟,小时,天等)。您不想在36,020秒前说!
unit
参数RelativeTimeFormat
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)), _=>_)
好吧,我真的必须立即开始工作...
对于感兴趣的任何人,我最终都创建了一个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';
}
});
存在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`;
请参阅插件的文档以处理相对时间(即:“多长时间?”)。
date1 - date2
无论原始时区如何,都会为您提供适当的时间增量(以毫秒为单位)。同样,new Date().getTime()
在两个时区的浏览器中,如果在正确的系统时钟上同时执行,将报告相同的数字。
如果您需要多语言并且不想像瞬间添加一个大型库。来自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"
MomentJS答案
对于Moment.js用户,它具有fromNow()函数,该函数从当前日期/时间返回“ x天”或“ x小时前”。
moment([2007, 0, 29]).fromNow(); // 4 years ago
moment([2007, 0, 29]).fromNow(true); // 4 years
您可以将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
});