我写了一个函数
toBeautyString(epoch) : String
给出epoch
的,返回一个字符串,显示从现在起以小时和分钟为单位的相对时间
例如:
// epoch: 1346140800 -> Tue, 28 Aug 2012 05:00:00 GMT
// and now: 1346313600 -> Thu, 30 Aug 2012 08:00:00 GMT
toBeautyString(1346140800)
-> "2 days and 3 hours ago"
我现在想要将此功能扩展到月份和年份,因此可以打印:
2 years, 1 month, 3 days and 1 hour ago
只有没有任何外部库的epoch。 此功能的目的是为用户提供更好的方式来显示过去的时间。
我发现了这个:Calculate relative time in C#,但粒度不够。
function toBeautyString(epochNow, epochNow){
var secDiff = Math.abs(epochNow - epochNow);
var milliInDay = 1000 * 60 * 60 * 24;
var milliInHour = 1000 * 60 * 60;
var nbDays = Math.round(secDiff/milliInDay);
var nbHour = Math.round(secDiff/milliInHour);
var relativeHour = (nbDays === 0) ? nbHour : nbHour-(nbDays*24);
relativeHour %= 24;
if(nbHour === 0){
nbDays += 1;
}else if(nbHour === (nbDays-1)*24){
nbDays -= 1;
}
var dayS = (nbDays > 1) ? "days" : "day";
var hourS = (relativeHour > 1) ? "hours" : "hour";
var fullString = "";
if(nbDays > 0){
fullString += nbDays + " " + dayS;
if(relativeHour > 0)
fullString += " ";
}
if(relativeHour > 0){
fullString += relativeHour + " " + hourS;
}
if(epochDate > epochNow){
return "Will be in " + fullString;
}else if ((epochDate === epochNow)
|| (relativeHour === 0 && nbDays === 0)){
return "Now";
}else{
return fullString + " ago";
}
}
答案 0 :(得分:2)
将此视为两个截然不同的问题是有帮助的:1)将时间分成不同单位的各个块; 2)格式化块并将它们与您选择的逗号,连词等连接在一起。这样,您可以将文本格式逻辑与时间计算逻辑分开。
#converts a time amount into a collection of time amounts of varying size.
#`increments` is a list that expresses the ratio of successive time units
#ex. If you want to split a time into days, hours, minutes, and seconds,
#increments should be [24,60,60]
#because there are 24 hours in a day, 60 minutes in an hour, etc.
#as an example, divideTime(100000, [24,60,60]) returns [1,3,46,40],
#which is equivalent to 1 day, 3 hours, 46 minutes, 40 seconds
def divideTime(amount, increments):
#base case: there's no increments, so no conversion is necessary
if len(increments) == 0:
return [amount]
#in all other cases, we slice a bit off of `amount`,
#give it to the smallest increment,
#convert the rest of `amount` into the next largest unit,
#and solve the rest with a recursive call.
else:
conversionRate = increments[-1]
smallestIncrement = amount % conversionRate
rest = divideTime(amount / conversionRate, increments[:-1])
return rest + [smallestIncrement]
def beautifulTime(amount):
names = ["year", "month", "day", "hour", "minute", "second"]
increments = [12, 30, 24, 60, 60]
ret = []
times = divideTime(amount, increments)
for i in range(len(names)):
time = times[i]
name = names[i]
#don't display the unit if the time is zero
#e.g. we prefer "1 year 1 second" to
#"1 year 0 months 0 days 0 hours 0 minutes 1 second"
if time == 0:
continue
#pluralize name if appropriate
if time != 1:
name = name + "s"
ret.append(str(time) + " " + name)
#there's only one unit worth mentioning, so just return it
if len(ret) == 1:
return ret[0]
#when there are two units, we don't need a comma
if len(ret) == 2:
return "{0} and {1}".format(ret[0], ret[1])
#for all other cases, we want a comma and an "and" before the last unit
ret[-1] = "and " + ret[-1]
return ", ".join(ret)
print beautifulTime(100000000)
#output: 3 years, 2 months, 17 days, 9 hours, 46 minutes, and 40 seconds
这个解决方案在现实生活中有些不准确,因为它假定一年由12个月组成,每个30天。这是一个必要的抽象,否则你必须考虑到不同的月份长度和闰日和夏令时等等。用这种方法,你每年会损失大约3.75天,这并不是那么糟糕如果你只是用它来形象化时间跨度的大小。
答案 1 :(得分:2)
您可以使用Time Period Library for .NET中的 DateDiff 类来显示相对时间:
// ----------------------------------------------------------------------
public void DateDiffSample( DateTime epoch )
{
DateDiff dateDiff = new DateDiff( DateTime.Now, epoch );
Console.WriteLine( "{0} ago", dateDiff.GetDescription( 4 ) );
// > 1 Year 4 Months 12 Days 12 Hours ago
} // DateDiffSample
答案 2 :(得分:2)
正如在其他答案中详尽讨论的那样,由于月份长度可变,您的代码无法轻松扩展。所以人们根本不能假设这个月是30天。
为了具有人类可读的差异,您必须从人类可读的日期中减去。
我会这样做(JavaScript,以匹配问题):
function toBeautyString(then) {
var nowdate = new Date();
var thendate = new Date(then * 1000);
//finding the human-readable components of the date.
var y = nowdate.getFullYear() - thendate.getFullYear();
var m = nowdate.getMonth() - thendate.getMonth();
var d = nowdate.getDate() - thendate.getDate();
var h = nowdate.getHours() - thendate.getHours();
var mm = nowdate.getMinutes() - thendate.getMinutes();
var s = nowdate.getSeconds() - thendate.getSeconds();
//back to second grade math, now we must now 'borrow'.
if(s < 0) {
s += 60;
mm--;
}
if(mm < 0) {
mm += 60;
h--;
}
if(h < 0) {
h += 24;
d--;
}
if(d < 0) {
//here's where we take into account variable month lengths.
var a = thendate.getMonth();
var b;
if(a <= 6) {
if(a == 1) b = 28;
else if(a % 2 == 0) b = 31;
else b = 30;
}
else if(b % 2 == 0) b = 30;
else b = 31;
d += b;
m--;
}
if(m < 0) {
m += 12;
y--;
}
//return "y years, m months, d days, h hours, mm minutes and s seconds ago."
}
代码通过从人类可读日期中减去(使用内置的javascript命令获得)来工作。剩下的唯一工作就是确保任何借款都能顺利进行。这很容易,除非您从几个月借钱,因为月份的长度可变。
假设您从4月12日开始减去2月25日。
在借款之前,m = 2
和d = -13
。现在,当您从m
,m = 1
借款时,您需要确保d
增加28,因为您在2月份借款。最终结果是1个月,15天前。
如果你从9月12日减去7月25日,结果将是1个月,18天前。
上述代码唯一没有提供的是闰年。这很容易扩展:如果您在2月份借款,您只需要考虑年份并根据需要进行调整。
答案 3 :(得分:1)
两个功能:一个用于计算差异,另一个用于显示它(灵感来自Kevin的回答)。适用于我的所有测试,考虑月份持续时间,易于翻译,并且还可以解决日光节省时间。
/**
* Calculates difference from 'now' to a timestamp, using pretty units
* (years, months, days, hours, minutes and seconds).
* Timestamps in ms, second argument is optional (assumes "now").
*/
function abstractDifference(thenTimestamp, nowTimestamp) {
var now = nowTimestamp ? new Date(nowTimestamp) : new Date();
var then = new Date(thenTimestamp);
var nowTimestamp = Math.round(now.getTime());
console.log(nowTimestamp, thenTimestamp);
// -- part 1, in which we figure out the difference in days
var deltaSeconds = Math.round((nowTimestamp - thenTimestamp)/1000);
// adjust offset for daylight savings time: 2012/01/14 to 2012/04/14
// is '3 months', not 2 months 23 hours (for most earth-bound humans)
var offsetNow = now.getTimezoneOffset();
var offsetThen = then.getTimezoneOffset();
deltaSeconds -= (offsetNow - offsetThen) * 60;
// positive integers are easier to work with; and months are sensiteive to +/-
var inTheFuture = false;
if (deltaSeconds < 0) {
inTheFuture = true;
deltaSeconds = -deltaSeconds;
}
var seconds = deltaSeconds % 60;
var deltaMinutes = Math.floor(deltaSeconds / 60);
var minutes = deltaMinutes % 60;
var deltaHours = Math.floor(deltaMinutes / 60);
var hours = deltaHours % 24;
var deltaDays = Math.floor(deltaHours / 24);
console.log("delta days: ", deltaDays);
// -- part 2, in which months figure prominently
function daysInMonth(year, month) {
// excess days automagically wrapped around; see details at
// http://www.ecma-international.org/publications/standards/Ecma-262.htm
return 32 - new Date(year, month, 32).getDate();
}
var months = 0;
var currentMonth = now.getMonth();
var currentYear = now.getFullYear();
if ( ! inTheFuture) {
// 1 month ago means "same day-of-month, last month"
// it is the length of *last* month that is relevant
currentMonth --;
while (true) {
if (currentMonth < 0) {
currentMonth = 11;
currentYear--;
}
var toSubstract = daysInMonth(currentYear, currentMonth);
if (deltaDays >= toSubstract) {
deltaDays -= toSubstract;
months ++;
currentMonth --;
} else {
break;
}
}
} else {
// in 1 month means "same day-of-month, next month"
// it is the length of *this* month that is relevant
while (true) {
if (currentMonth > 11) {
currentMonth = 0;
currentYear++;
}
var toSubstract = daysInMonth(currentYear, currentMonth);
if (deltaDays >= toSubstract) {
deltaDays -= toSubstract;
months ++;
currentMonth ++;
} else {
break;
}
}
}
var years = Math.floor(months / 12);
var months = months % 12;
return {future: inTheFuture,
years: years, months: months, days: deltaDays,
hours: hours, minutes: minutes, seconds: seconds};
}
/**
* Returns something like "1 year, 4 days and 1 second ago", or
* "in 1 month, 3 hours, 45 minutes and 59 seconds".
* Second argument is optional.
*/
function prettyDifference(thenTimestamp, nowTimestamp) {
var o = abstractDifference(thenTimestamp, nowTimestamp);
var parts = [];
function pushPart(property, singular, plural) {
var value = o[property];
if (value) parts.push("" + value + " " + (value==1?singular:plural));
}
// to internationalize, change things here
var lastSeparator = " and ";
var futurePrefix = "in ";
var pastSuffix = " ago";
var nameOfNow = "now";
pushPart("years", "year", "years");
pushPart("months", "month", "months");
pushPart("days", "day", "days");
pushPart("hours", "hour", "hours");
pushPart("minutes", "minute", "minutes");
pushPart("seconds", "second", "seconds");
if (parts.length == 0) {
return nameOfNow;
}
var beforeLast = parts.slice(0, -1).join(", ");
var pendingRelative = parts.length > 1 ?
[beforeLast , parts.slice(-1) ].join(lastSeparator) :
parts[0];
return o.future ?
futurePrefix + pendingRelative : pendingRelative + pastSuffix;
}
答案 4 :(得分:-3)
不能有这样的算法!
一天(小时,分钟,秒),甚至几天本身的分数都没问题。问题是“一个月”的长度在28到31天之间变化。
我举个例子:
让我们说今天是28 Feb 2013
,您想要计算toBeautyString(28 Jan 2013)
:
today: 28 Feb 2013
toBeautyString(28 Jan 2013)
expected answer: 1 month ago
嗯,那真的没问题。同一天,同一年,只是月份确实发生了变化。
现在让我们在同一天计算toBeautyString(27 Jan 2013)
:
today: 28 Feb 2013
toBeautyString(27 Jan 2013)
expected answer: 1 month and 1 day ago
多数民众赞成也很简单,不是吗?我们想要前一天的值,输出结果显示持续时间长一天。
现在让我们上床睡觉,并在第二天(2013年3月1日)继续工作 试试这个:
today: 1 Mar 2013
toBeautyString(1 Feb 2013)
expected answer: 1 month ago
好吧,这么简单!与您的第一次计算相同的逻辑。只有月份确实改变了1,所以持续时间不能超过1个月 所以,让我们计算前一天的价值:
today: 1 Mar 2013
toBeautyString(31 Jan 2013)
expected answer: 1 month and 1 day ago
再次:前一天的结果必须是1天的持续时间 让我们试着让持续时间延长一天:
today: 1 Mar 2013
toBeautyString(30 Jan 2013)
expected answer: 1 month and 2 days ago
更长:
today: 1 Mar 2013
toBeautyString(29 Jan 2013)
expected answer: 1 month and 3 days ago
最后:
today: 1 Mar 2013
toBeautyString(28 Jan 2013)
expected answer: 1 month and 4 days ago
记住这个!
不,让我们重复我们昨天做的第一次计算。昨天我们确实计算了toBeautyString(28 Jan 2013)
,结果是1 month ago
。今天是一天后。如果我们今天计算toBeautyString(28 Jan 2013)
,结果应该会向我们显示一天的持续时间:
today: 1 Mar 2013
toBeautyString(28 Jan 2013)
expected answer 1 month and 1 days ago
将此与之前的计算进行比较。我们在2013年3月1日进行了两次计算。在这两种情况下,我们都计算了相同的结果:toBeautyString(28 Jan 2013)
。但我们期待两种不同的结果。令人惊讶的是,这两个期望都是正确的。
因此,为了提供真正符合我们期望的结果,算法应该能够读懂我们的想法。但这对于算法来说是不可能的,因此不可能有一种算法可以完美地完成你期望的算法。