Javascript fuzzy time (например, "10 минут назад" ), что в точные секунды

Я делаю счетчик javascript, который считается секундой назад. У меня есть время в объекте времени JS, и я нашел фрагмент функции разницы во времени здесь при переполнении стека, но он отображает "2 часа назад". Как я могу заставить его отображать "5 часов, 10 минут и 37 секунд назад".

Вот с чем я работаю:

Эта функция преобразует текущее время и временную метку чего-то в "20 секунд назад" вместо загадочной даты:

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';   
    }
}

И вот что я использую, чтобы "подсчитывать" время каждую секунду. Я бы хотел сказать "5 часов, 3 минуты, 10 секунд назад", а затем через 1 секунду "5 часов, 3 минуты, 11 секунд назад"

var newTime = new Date(data.popular[i].timestamp*1000)
var relTime = timeDifference(new Date(),newTime)

setInterval(function(){
    var theTimeEl = $('.timestamp-large').filter(function(){
        return $(this).html() == relTime
    });
    newTime.setSeconds(newTime.getSeconds() + 1);
    var relTime = timeDifference(new Date(), newTime);
    $(theTimeEl).html(relTime);
    console.log(relTime)
}, 1000)

Переменная newTime - это время в формате даты javascript UTC. relTime - это то, что в формате "секунды назад". Интервал проходит через кучу элементов временной метки и выбирает правильный для каждой отметки времени. Затем он добавляет секунду к времени, преобразует его обратно в "нечеткое время" (несколько секунд назад), заменяет html новым временем и записывает его в консоль.

Как мне изменить "5 часов назад" на "5 часов, 37 минут, 10 секунд назад"? Функция временной разницы должна быть изменена.

Ответ 1

Здесь функция, близкая к тому, что вы просите.

var timeparts = [
   {name: 'year', div: 31536000000, mod: 10000},
   {name: 'day', div: 86400000, mod: 365},
   {name: 'hour', div: 3600000, mod: 24},
   {name: 'minute', div: 60000, mod: 60},
   {name: 'second', div: 1000, mod: 60}
];

function timeAgoNaive(comparisonDate) {
   var
      i = 0,
      l = timeparts.length,
      calc,
      values = [],
      interval = new Date().getTime() - comparisonDate.getTime();
   while (i < l) {
      calc = Math.floor(interval / timeparts[i].div) % timeparts[i].mod;
      if (calc) {
         values.push(calc + ' ' + timeparts[i].name + (calc != 1 ? 's' : ''));
      }
      i += 1;
   }
   if (values.length === 0) { values.push('0 seconds'); }
   return values.join(', ') + ' ago';
}

console.log(timeAgoNaive(new Date(Date.parse('Jun 12 2006 11:52:33'))));
console.log(timeAgoNaive(new Date(new Date().getTime() - 3600000)));
console.log(timeAgoNaive(new Date()));

Результаты:

6 years, 33 days, 4 hours, 52 minutes, 22 seconds ago
1 hour ago
0 seconds ago

Я назвал его "наивным", потому что на самом деле он не обращает внимания на человеческий путь, который мы вычисляем. Если это "1/1/2013 12:01:00 утра" точно, по сравнению с "1/1/2012 12:01:00 утра" должно уступить "1 год, 0 месяцев, 0 дней, 0 часов, 0 минут, 0 секунд назад". Но это не будет сделано, расширяя логику в функции, которую вы представили, и она не будет делать это в моей функции (плюс моя функция не будет использовать месяцы). Лучшее приближение лет 365 дней - 365,24, но это также игнорируется.

Я исключил пустые временные части по вашему запросу, оставив "0 ​​секунд" как минимум, когда нет временных частей.

Теперь, если вы хотите, чтобы человекоподобный способ расчета, вам нужно решить некоторые вещи. Вы не можете просто использовать границы, пересеченные, потому что с 28 февраля по 1 марта не целый месяц. Во-вторых, здесь возникает вопрос о реальной проблеме:

  • Сколько месяцев и дней с 2 ​​по 31 марта?

Если вы вычисляете 2 февраля по 2 марта как один месяц, то это 1 месяц 29 дней. Но что, если бы было 2 января до 1 марта? То же самое количество дней прошло между ними. Это сейчас 1 месяц (за весь апрель) + 1 день в марте + 31 день в январе за 1 месяц 32 дня? Вы хотите, чтобы ваши месяцы совпадали с физическим календарем, чтобы человек мог отследить пальцем и получить правильную дату? Это намного сложнее, чем вы думаете.

Если вы можете ответить разумными и полными правилами о том, как вы будете делать "человекоподобное истекшее время", то, возможно, я могу написать вам другую функцию, чтобы сделать это.

Обновление

Здесь новая функция, которая делает месяцы и имеет 365,24 дня в году (30.43666666 дней в месяц):

var timeparts = [
   {name: 'millenni', div: 31556736000, p: 'a', s: 'um'},
   {name: 'centur', div: 3155673600, p: 'ies', s: 'y'},
   {name: 'decade', div: 315567360},
   {name: 'year', div: 31556736},
   {name: 'month', div: 2629728},
   {name: 'day', div: 86400},
   {name: 'hour', div: 3600},
   {name: 'minute', div: 60},
   {name: 'second', div: 1}
];

function timeAgoNaive2(comparisonDate) {
   var i = 0,
      parts = [],
      interval = Math.floor((new Date().getTime() - comparisonDate.getTime()) / 1000);
   for ( ; interval > 0; i += 1) {
      value = Math.floor(interval / timeparts[i].div);
      interval = interval - (value * timeparts[i].div);
      if (value) {
         parts.push(value + ' ' + timeparts[i].name + (value !== 1 ? timeparts[i].p || 's' : timeparts[i].s || ''));
      }
   }
   if (parts.length === 0) { return 'now'; }
   return parts.join(', ') + ' ago';
}

console.log(timeAgoNaive2(new Date(Date.parse('Jun 12 2006 11:52:33'))));
console.log(timeAgoNaive2(new Date(new Date().getTime() - 3600000)));
console.log(timeAgoNaive2(new Date()));
console.log(timeAgoNaive2(new Date(-92709631247000)));

Вывод:

6 years, 1 month, 1 day, 10 hours, 53 minutes, 44 seconds ago
1 hour ago
now
2 millennia, 9 centuries, 8 decades, 4 months, 26 days, 22 hours, 41 minutes, 47 seconds ago

Это все еще наивно, но он немного лучше работает. Плюс это будет работать для ДЕЙСТВИТЕЛЬНО старых дат, таких как B.C. из них.:)

Ответ 2

Измените логику, чтобы вместо того, чтобы просто найти самую большую единицу измерения, она делает что-то с остатком.

В основном то, что вам нужно сделать, это начать с наибольшего приращения, найти значение, а затем вычесть его из общей суммы, чтобы получить остаток. Затем повторите.

Что-то вроде этого, может быть, я его не тестировал.

var elapsed = current - previous;
var remainder = elapsed;

int years;
int months;

years = Math.floor(remainder/msPerYear);
remainder = remainder % msPerYear;

months = Math.floor(remainder/msPerMonth);
remainder = remainder % msPerMonth;

// repeat

Затем просто создайте строку с переменными.

Ответ 3

Это должно сделать трюк:

var msPerMinute = 60 * 1000;
var msPerHour = msPerMinute * 60;
var msPerDay = msPerHour * 24;
var msPerMonth = msPerDay * 30;
var msPerYear = msPerDay * 365;

function timeDifference(current, previous) {

var remainder = current - previous;   
var message = "";
var sep = "";

var years = Math.floor(remainder/msPerYear);
remainder = remainder - years * msPerYear;
if (years > 0) {
   message += years + " years";
   sep = ", ";
   console.log(message);
}

var months = Math.floor(remainder/msPerMonth);
remainder = remainder - months * msPerMonth;
if (months > 0) {
   message += sep + months + " months";
   sep = ", ";
   console.log(message);
}

var days = Math.floor(remainder/msPerDay);
remainder = remainder - days * msPerDay;
if (days > 0) {
   message += sep + days + " days";
   sep = ", ";
   console.log(message);
}

var hours = Math.floor(remainder/msPerHour);
remainder = remainder - hours * msPerHour;
if (hours > 0) {
   message += sep + hours + " hours";
   sep = ", ";
   console.log(message);
}

var minutes = Math.floor(remainder/msPerMinute);
remainder = remainder - minutes * msPerMinute;
if (months > 0) {
   message += sep + minutes + " minutes";
   sep = ", ";
   console.log(message);
}

var seconds = Math.floor(remainder/1000);
remainder = remainder - seconds * 1000;
if (months > 0) {
   message += sep + seconds + " seconds";
   sep = ", ";
   console.log(message);
}

message += " ago";

var pos = message.lastIndexOf(',');
message = message.substring(0,pos) + ' and' + message.substring(pos+1)

return message;
};

var output = timeDifference(new Date(2012, 10, 20, 12, 0, 59), new Date(2012, 2, 13, 10, 15, 12));
console.log(output);

Результат: 8 месяцев, 12 дней, 1 час, 45 минут и 47 секунд назад

Это можно было бы, конечно, реорганизовать, чтобы быть немного менее повторяющимся. Вы можете попробовать эту скрипку с ней: http://jsfiddle.net/vawEf/