Work with a time span in Javascript

后端 未结 7 658
感动是毒
感动是毒 2020-11-30 05:53

Using Date.js already, but can also use another library if necessary.

Not sure what is the best way to work with time deltas. Specifically, I want to display the tim

相关标签:
7条回答
  • 2020-11-30 06:12

    a simple timestamp formatter in pure JS with custom patterns support and locale-aware, using Intl.RelativeTimeFormat

    some formatting examples

    /** delta: 1234567890, @locale: 'en-US', @style: 'long' */
    
    /* D~ h~ m~ s~ */
    14 days 6 hours 56 minutes 7 seconds
    
    /* D~ h~ m~ s~ f~ */
    14 days 6 hours 56 minutes 7 seconds 890
    
    /* D#"d" h#"h" m#"m" s#"s" f#"ms" */
    14d 6h 56m 7s 890ms
    
    /* D,h:m:s.f */
    14,06:56:07.890
    
    /* D~, h:m:s.f */
    14 days, 06:56:07.890
    
    /* h~ m~ s~ */
    342 hours 56 minutes 7 seconds
    
    /* s~ m~ h~ D~ */
    7 seconds 56 minutes 6 hours 14 days
    
    /* up D~, h:m */
    up 14 days, 06:56
    

    the code & test

    /**
        Init locale formatter:
        
            timespan.locale(@locale, @style)
        
        Example:
    
            timespan.locale('en-US', 'long');
            timespan.locale('es', 'narrow');
    
        Format time delta:
        
            timespan.format(@pattern, @milliseconds)
    
            @pattern tokens:
                D: days, h: hours, m: minutes, s: seconds, f: millis
    
            @pattern token extension:
                h  => '0'-padded value, 
                h# => raw value,
                h~ => locale formatted value
    
        Example:
    
            timespan.format('D~ h~ m~ s~ f "millis"', 1234567890);
            
            output: 14 days 6 hours 56 minutes 7 seconds 890 millis
    
        NOTES:
    
        * milliseconds unit have no locale translation
        * may encounter declension issues for some locales
        * use quoted text for raw inserts
                
    */
    
    const timespan = (() => {
        let rtf, tokensRtf;
        const
        tokens = /[Dhmsf][#~]?|"[^"]*"|'[^']*'/g,
        map = [
            {t: [['D', 1], ['D#'], ['D~', 'day']], u: 86400000},
            {t: [['h', 2], ['h#'], ['h~', 'hour']], u: 3600000},
            {t: [['m', 2], ['m#'], ['m~', 'minute']], u: 60000},
            {t: [['s', 2], ['s#'], ['s~', 'second']], u: 1000},
            {t: [['f', 3], ['f#'], ['f~']], u: 1}
        ],
        locale = (value, style = 'long') => {
            try {
                rtf = new Intl.RelativeTimeFormat(value, {style});
            } catch (e) {
                if (rtf) throw e;
                return;
            }
            const h = rtf.format(1, 'hour').split(' ');
            tokensRtf = new Set(rtf.format(1, 'day').split(' ')
                .filter(t => t != 1 && h.indexOf(t) > -1));
            return true;
        },
        fallback = (t, u) => u + ' ' + t.fmt + (u == 1 ? '' : 's'),
        mapper = {
            number: (t, u) => (u + '').padStart(t.fmt, '0'),
            string: (t, u) => rtf ? rtf.format(u, t.fmt).split(' ')
                .filter(t => !tokensRtf.has(t)).join(' ')
                .trim().replace(/[+-]/g, '') : fallback(t, u),
        },
        replace = (out, t) => out[t] || t.slice(1, t.length - 1),
        format = (pattern, value) => {
            if (typeof pattern !== 'string')
                throw Error('invalid pattern');
            if (!Number.isFinite(value))
                throw Error('invalid value');
            if (!pattern)
                return '';
            const out = {};
            value = Math.abs(value);
            pattern.match(tokens)?.forEach(t => out[t] = null);
            map.forEach(m => {
                let u = null;
                m.t.forEach(t => {
                    if (out[t.token] !== null)
                        return;
                    if (u === null) {
                        u = Math.floor(value / m.u);
                        value %= m.u;
                    }
                    out[t.token] = '' + (t.fn ? t.fn(t, u) : u);
                })
            });
            return pattern.replace(tokens, replace.bind(null, out));
        };
        map.forEach(m => m.t = m.t.map(t => ({
            token: t[0], fmt: t[1], fn: mapper[typeof t[1]]
        })));
        locale('en');
        return {format, locale};
    })();
    
    
    /************************** test below *************************/
    
    const
    cfg = {
      locale: 'en,de,nl,fr,it,es,pt,ro,ru,ja,kor,zh,th,hi',
      style: 'long,narrow'
    },
    el = id => document.getElementById(id),
    locale = el('locale'), loc = el('loc'), style = el('style'),
    fd = new Date(), td = el('td'), fmt = el('fmt'),
    run = el('run'), out = el('out'),
    test = () => {
      try {
          const tv = new Date(td.value);
          if (isNaN(tv)) throw Error('invalid "datetime2" value');
          timespan.locale(loc.value || locale.value, style.value);
          const delta = fd.getTime() - tv.getTime();
          out.innerHTML = timespan.format(fmt.value, delta);
      } catch (e) { out.innerHTML = e.message; }
    };
    el('fd').innerText = el('td').value = fd.toISOString();
    el('fmt').value = 'D~ h~ m~ s~ f~ "ms"';
    for (const [id, value] of Object.entries(cfg)) {
      const elm = el(id);
      value.split(',').forEach(i => elm.innerHTML += `<option>${i}</option>`);
    }
    i {color:green}
    locale: <select id="locale"></select>
    custom: <input id="loc" style="width:8em"><br>
    style: <select id="style"></select><br>
    datetime1: <i id="fd"></i><br>
    datetime2: <input id="td"><br>
    pattern: <input id="fmt">
    <button id="run" onclick="test()">test</button><br><br>
    <i id="out"></i>

    0 讨论(0)
  • 2020-11-30 06:14

    You can use momentjs duration object

    Example:

    const diff = moment.duration(Date.now() - new Date(2010, 1, 1))
    console.log(`${diff.years()} years ${diff.months()} months ${diff.days()} days ${diff.hours()} hours ${diff.minutes()} minutes and ${diff.seconds()} seconds`)
    
    0 讨论(0)
  • 2020-11-30 06:15
    /**
     * 计算时间对象与当前时间的差距,并显示友好的文本 
     * English: Calculating the difference between the given time and the current time and then showing the results.
     */
    function date2Text(date) {
        var milliseconds = new Date() - date;
        var timespan = new TimeSpan(milliseconds);
        if (milliseconds < 0) {
            return timespan.toString() + "之后";
        }else{
            return timespan.toString() + "前";
        }
    }
    
    /**
     * 用于计算时间间隔的对象
     * English: Using a function to calculate the time interval
     * @param milliseconds 毫秒数
     */
    var TimeSpan = function (milliseconds) {
        milliseconds = Math.abs(milliseconds);
        var days = Math.floor(milliseconds / (1000 * 60 * 60 * 24));
        milliseconds -= days * (1000 * 60 * 60 * 24);
    
        var hours = Math.floor(milliseconds / (1000 * 60 * 60));
        milliseconds -= hours * (1000 * 60 * 60);
    
        var mins = Math.floor(milliseconds / (1000 * 60));
        milliseconds -= mins * (1000 * 60);
    
        var seconds = Math.floor(milliseconds / (1000));
        milliseconds -= seconds * (1000);
        return {
            getDays: function () {
                return days;
            },
            getHours: function () {
                return hours;
            },
            getMinuts: function () {
                return mins;
            },
            getSeconds: function () {
                return seconds;
            },
            toString: function () {
                var str = "";
                if (days > 0 || str.length > 0) {
                    str += days + "天";
                }
                if (hours > 0 || str.length > 0) {
                    str += hours + "小时";
                }
                if (mins > 0 || str.length > 0) {
                    str += mins + "分钟";
                }
                if (days == 0 && (seconds > 0 || str.length > 0)) {
                    str += seconds + "秒";
                }
                return str;
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-30 06:20

    If you're not too worried in accuracy after days, you can simply do the maths

    function timeSince(when) { // this ignores months
        var obj = {};
        obj._milliseconds = (new Date()).valueOf() - when.valueOf();
        obj.milliseconds = obj._milliseconds % 1000;
        obj._seconds = (obj._milliseconds - obj.milliseconds) / 1000;
        obj.seconds = obj._seconds % 60;
        obj._minutes = (obj._seconds - obj.seconds) / 60;
        obj.minutes = obj._minutes % 60;
        obj._hours = (obj._minutes - obj.minutes) / 60;
        obj.hours = obj._hours % 24;
        obj._days = (obj._hours - obj.hours) / 24;
        obj.days = obj._days % 365;
        // finally
        obj.years = (obj._days - obj.days) / 365;
        return obj;
    }
    

    then timeSince(pastDate); and use the properties as you like.

    Otherwise you can use .getUTC* to calculate it, but note it may be slightly slower to calculate

    function timeSince(then) {
        var now = new Date(), obj = {};
        obj.milliseconds = now.getUTCMilliseconds() - then.getUTCMilliseconds();
        obj.seconds = now.getUTCSeconds() - then.getUTCSeconds();
        obj.minutes = now.getUTCMinutes() - then.getUTCMinutes();
        obj.hours = now.getUTCHours() - then.getUTCHours();
        obj.days = now.getUTCDate() - then.getUTCDate();
        obj.months = now.getUTCMonth() - then.getUTCMonth();
        obj.years = now.getUTCFullYear() - then.getUTCFullYear();
        // fix negatives
        if (obj.milliseconds < 0) --obj.seconds, obj.milliseconds = (obj.milliseconds + 1000) % 1000;
        if (obj.seconds < 0) --obj.minutes, obj.seconds = (obj.seconds + 60) % 60;
        if (obj.minutes < 0) --obj.hours, obj.minutes = (obj.minutes + 60) % 60;
        if (obj.hours < 0) --obj.days, obj.hours = (obj.hours + 24) % 24;
        if (obj.days < 0) { // months have different lengths
            --obj.months;
            now.setUTCMonth(now.getUTCMonth() + 1);
            now.setUTCDate(0);
            obj.days = (obj.days + now.getUTCDate()) % now.getUTCDate();
        }
        if (obj.months < 0)  --obj.years, obj.months = (obj.months + 12) % 12;
        return obj;
    }
    
    0 讨论(0)
  • 2020-11-30 06:23

    Here a .NET C# similar implementation of a timespan class that supports days, hours, minutes and seconds. This implementation also supports negative timespans.

    const MILLIS_PER_SECOND = 1000;
    const MILLIS_PER_MINUTE = MILLIS_PER_SECOND * 60;   //     60,000
    const MILLIS_PER_HOUR = MILLIS_PER_MINUTE * 60;     //  3,600,000
    const MILLIS_PER_DAY = MILLIS_PER_HOUR * 24;        // 86,400,000
    
    export class TimeSpan {
        private _millis: number;
    
        private static interval(value: number, scale: number): TimeSpan {
            if (Number.isNaN(value)) {
                throw new Error("value can't be NaN");
            }
    
            const tmp = value * scale;
            const millis = TimeSpan.round(tmp + (value >= 0 ? 0.5 : -0.5));
            if ((millis > TimeSpan.maxValue.totalMilliseconds) || (millis < TimeSpan.minValue.totalMilliseconds)) {
                throw new TimeSpanOverflowError("TimeSpanTooLong");
            }
    
            return new TimeSpan(millis);
        }
    
        private static round(n: number): number {
            if (n < 0) {
                return Math.ceil(n);
            } else if (n > 0) {
                return Math.floor(n);
            }
    
            return 0;
        }
    
        private static timeToMilliseconds(hour: number, minute: number, second: number): number {
            const totalSeconds = (hour * 3600) + (minute * 60) + second;
            if (totalSeconds > TimeSpan.maxValue.totalSeconds || totalSeconds < TimeSpan.minValue.totalSeconds) {
                throw new TimeSpanOverflowError("TimeSpanTooLong");
            }
    
            return totalSeconds * MILLIS_PER_SECOND;
        }
    
        public static get zero(): TimeSpan {
            return new TimeSpan(0);
        }
    
        public static get maxValue(): TimeSpan {
            return new TimeSpan(Number.MAX_SAFE_INTEGER);
        }
    
        public static get minValue(): TimeSpan {
            return new TimeSpan(Number.MIN_SAFE_INTEGER);
        }
    
        public static fromDays(value: number): TimeSpan {
            return TimeSpan.interval(value, MILLIS_PER_DAY);
        }
    
        public static fromHours(value: number): TimeSpan {
            return TimeSpan.interval(value, MILLIS_PER_HOUR);
        }
    
        public static fromMilliseconds(value: number): TimeSpan {
            return TimeSpan.interval(value, 1);
        }
    
        public static fromMinutes(value: number): TimeSpan {
            return TimeSpan.interval(value, MILLIS_PER_MINUTE);
        }
    
        public static fromSeconds(value: number): TimeSpan {
            return TimeSpan.interval(value, MILLIS_PER_SECOND);
        }
    
        public static fromTime(hours: number, minutes: number, seconds: number): TimeSpan;
        public static fromTime(days: number, hours: number, minutes: number, seconds: number, milliseconds: number): TimeSpan;
        public static fromTime(daysOrHours: number, hoursOrMinutes: number, minutesOrSeconds: number, seconds?: number, milliseconds?: number): TimeSpan {
            if (milliseconds != undefined) {
                return this.fromTimeStartingFromDays(daysOrHours, hoursOrMinutes, minutesOrSeconds, seconds, milliseconds);
            } else {
                return this.fromTimeStartingFromHours(daysOrHours, hoursOrMinutes, minutesOrSeconds);
            }
        }
    
        private static fromTimeStartingFromHours(hours: number, minutes: number, seconds: number): TimeSpan {
            const millis = TimeSpan.timeToMilliseconds(hours, minutes, seconds);
            return new TimeSpan(millis);
        }
    
        private static fromTimeStartingFromDays(days: number, hours: number, minutes: number, seconds: number, milliseconds: number): TimeSpan {
            const totalMilliSeconds = (days * MILLIS_PER_DAY) +
                (hours * MILLIS_PER_HOUR) +
                (minutes * MILLIS_PER_MINUTE) +
                (seconds * MILLIS_PER_SECOND) +
                milliseconds;
    
            if (totalMilliSeconds > TimeSpan.maxValue.totalMilliseconds || totalMilliSeconds < TimeSpan.minValue.totalMilliseconds) {
                throw new TimeSpanOverflowError("TimeSpanTooLong");
            }
            return new TimeSpan(totalMilliSeconds);
        }
    
        constructor(millis: number) {
            this._millis = millis;
        }
    
        public get days(): number {
            return TimeSpan.round(this._millis / MILLIS_PER_DAY);
        }
    
        public get hours(): number {
            return TimeSpan.round((this._millis / MILLIS_PER_HOUR) % 24);
        }
    
        public get minutes(): number {
            return TimeSpan.round((this._millis / MILLIS_PER_MINUTE) % 60);
        }
    
        public get seconds(): number {
            return TimeSpan.round((this._millis / MILLIS_PER_SECOND) % 60);
        }
    
        public get milliseconds(): number {
            return TimeSpan.round(this._millis % 1000);
        }
    
        public get totalDays(): number {
            return this._millis / MILLIS_PER_DAY;
        }
    
        public get totalHours(): number {
            return this._millis / MILLIS_PER_HOUR;
        }
    
        public get totalMinutes(): number {
            return this._millis / MILLIS_PER_MINUTE;
        }
    
        public get totalSeconds(): number {
            return this._millis / MILLIS_PER_SECOND;
        }
    
        public get totalMilliseconds(): number {
            return this._millis;
        }
    
        public add(ts: TimeSpan): TimeSpan {
            const result = this._millis + ts.totalMilliseconds;
            return new TimeSpan(result);
        }
    
        public subtract(ts: TimeSpan): TimeSpan {
            const result = this._millis - ts.totalMilliseconds;
            return new TimeSpan(result);
        }
    }
    

    How to use

    Create a new TimeSpan object

    From zero
    
        const ts = TimeSpan.zero;
    
    
    From milliseconds
    
        const milliseconds = 10000; // 1 second
    
        // by using the constructor
        const ts1 = new TimeSpan(milliseconds);
    
        // or as an alternative you can use the static factory method
        const ts2 = TimeSpan.fromMilliseconds(milliseconds);
    
    
    From seconds
    
        const seconds = 86400; // 1 day
        const ts = TimeSpan.fromSeconds(seconds);
    
    
    From minutes
    
        const minutes = 1440; // 1 day
        const ts = TimeSpan.fromMinutes(minutes);
    
    
    From hours
    
        const hours = 24; // 1 day
        const ts = TimeSpan.fromHours(hours);
    
    
    From days
    
        const days = 1; // 1 day
        const ts = TimeSpan.fromDays(days);
    
    
    From time with given hours, minutes and seconds
    
        const hours = 1;
        const minutes = 1;
        const seconds = 1;
        const ts = TimeSpan.fromTime(hours, minutes, seconds);
    
    
    From time2 with given days, hours, minutes, seconds and milliseconds
    
        const days = 1;
        const hours = 1;
        const minutes = 1;
        const seconds = 1;
        const milliseconds = 1;
        const ts = TimeSpan.fromTime(days, hours, minutes, seconds, milliseconds);
    
    
    From maximal safe integer
    
        const ts = TimeSpan.maxValue;
    
    
    From minimal safe integer
    
        const ts = TimeSpan.minValue;
    
    
    From minimal safe integer
    
        const ts = TimeSpan.minValue;
    
    
    Add
    
        const ts1 = TimeSpan.fromDays(1);
        const ts2 = TimeSpan.fromHours(1);
        const ts = ts1.add(ts2);
    
        console.log(ts.days);               // 1
        console.log(ts.hours);              // 1
        console.log(ts.minutes);            // 0
        console.log(ts.seconds);            // 0
        console.log(ts.milliseconds);           // 0
    
    
    Subtract
    
        const ts1 = TimeSpan.fromDays(1);
        const ts2 = TimeSpan.fromHours(1);
        const ts = ts1.subtract(ts2);
    
        console.log(ts.days);               // 0
        console.log(ts.hours);              // 23
        console.log(ts.minutes);            // 0
        console.log(ts.seconds);            // 0
        console.log(ts.milliseconds);           // 0
    
    
    Getting the intervals
    
        const days = 1;
        const hours = 1;
        const minutes = 1;
        const seconds = 1;
        const milliseconds = 1;
        const ts = TimeSpan.fromTime2(days, hours, minutes, seconds, milliseconds);
    
        console.log(ts.days);               // 1
        console.log(ts.hours);              // 1
        console.log(ts.minutes);            // 1
        console.log(ts.seconds);            // 1
        console.log(ts.milliseconds);           // 1
    
        console.log(ts.totalDays)           // 1.0423726967592593;
        console.log(ts.totalHours)          // 25.016944722222224;
        console.log(ts.totalMinutes)            // 1501.0166833333333;
        console.log(ts.totalSeconds)            // 90061.001;
        console.log(ts.totalMilliseconds);      // 90061001;
    
    

    See also here: https://github.com/erdas/timespan

    0 讨论(0)
  • 2020-11-30 06:31

    Sounds like you need moment.js

    e.g.

    moment().subtract('days', 6).calendar();
    

    => last Sunday at 8:23 PM

    moment().startOf('hour').fromNow();
    

    => 26 minutes ago

    Edit:

    Pure JS date diff calculation:

    var date1 = new Date("7/Nov/2012 20:30:00");
    var date2 = new Date("20/Nov/2012 19:15:00");
    
    var diff = date2.getTime() - date1.getTime();
    
    var days = Math.floor(diff / (1000 * 60 * 60 * 24));
    diff -=  days * (1000 * 60 * 60 * 24);
    
    var hours = Math.floor(diff / (1000 * 60 * 60));
    diff -= hours * (1000 * 60 * 60);
    
    var mins = Math.floor(diff / (1000 * 60));
    diff -= mins * (1000 * 60);
    
    var seconds = Math.floor(diff / (1000));
    diff -= seconds * (1000);
    
    document.write(days + " days, " + hours + " hours, " + mins + " minutes, " + seconds + " seconds");

    0 讨论(0)
提交回复
热议问题