7

関数を書きました

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";         
    }
}
4

5 に答える 5

2

.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
于 2012-09-07T08:54:44.597 に答える
2

これを 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

この解決策は、1 年が 12 か月で構成され、各月が 30 日であると想定しているため、実際の年に関しては多少不正確です。これは必要な抽象化です。さもなければ、さまざまな月の長さ、うるう日、夏時間などを考慮に入れる必要があります。この方法では、年間約 3.75 日を失うことになりますが、それほど悪くはありません。時間範囲の大きさを視覚化するためだけに使用している場合。

于 2012-08-31T16:42:19.470 に答える
2

他の回答で徹底的に説明されているように、月の長さが可変であるため、コードを簡単に拡張することはできません。したがって、1 か月が 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. ここで、 から借りる場合mm = 1が 28 増加することを確認する必要がありますd。これは、2 月全体で借りるためです。最終結果は 1 か月と 15 日前です。

9 月 12 日から 7 月 25 日を引くと、結果は 1 か月と 18 日前になります。

上記のコードで唯一提供されていないのは、うるう年です。これは簡単に拡張できます。2 月を超えて借りる場合は、その年を考慮して、必要な年に合わせて調整するだけです。

于 2012-09-10T17:43:56.883 に答える
1

2 つの関数: 1 つは差を計算し、もう 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;
}
于 2012-09-10T22:45:45.167 に答える
-3

そんなアルゴリズムあり得ない!

1 日の端数 (時、分、秒)、さらには日自体も問題ありません。問題は、「1 か月」の長さが 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

それも簡単ですね。前日の値が必要で、出力は期間が 1 日長いことを示しています。

さて、寝て次の日 (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 日長い期間でなければなりません。
期間を 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)と、結果は 1 日長い期間を示すはずです。

today: 1 Mar 2013
toBeautyString(28 Jan 2013)
expected answer 1 month and 1 days ago

これを前の計算と比較します。2013 年 3 月 1 日に両方の計算を行いました。どちらの場合も同じ計算を行いましたtoBeautyString(28 Jan 2013)。しかし、私たちは 2 つの異なる結果を期待しています。驚くべきことに、両方の予想が正しいです。

したがって、私たちの期待に本当に合う結果をもたらすために、アルゴリズムは私たちの心を読むことができなければなりません. しかし、これはアルゴリズムでは不可能であるため、期待どおりに完全に機能するアルゴリズムはありません。

于 2012-09-04T12:12:55.963 に答える