以小时,天,月和年显示相对时间

我写了一个函数

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

只有没有任何外部库的时代。 这个功能的目的是给用户一个更好的方式来可视化过去的时间。

我发现这一点:用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";         
    }
}

认识到这是两个不同的问题是有帮助的: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天,这并不是那么糟糕如果你只用它来可视化时间跨度的大小。


您可以使用.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

正如在其他答案中详细讨论的那样,由于变量的月份长度,您的代码不容易被扩展。 所以一个人不能假设这个月是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 = 2d = -13 。 现在,当您从mm = 1借款时,您需要确保d增加28,因为您在2月份进行借款。 最终的结果是15天前的1个月。

如果你从9月12日减去7月25日,结果将是18个月前的1个月。

上面的代码唯一不提供的是闰年。 这很容易扩展:如果你在2月份借钱,你只需要考虑年份并根据需要进行调整。

链接地址: http://www.djcxy.com/p/63063.html

上一篇: Display relative time in hour, day, month and year

下一篇: best practice for packing the code