2015-05-02 2 views
0

Я делаю некоторые улучшения библиотеки дат, которые уже возвращают различия в единицах даты, как указано в название этого вопроса. Я хочу, чтобы он возвращал относительные единицы. Я написал код, но мне кажется, что я работаю с неправильным концом математики.Относительная дата: 1/1/1981 01:01:01 - 1/1/1980 = 1y 366d ect, want 1y 1d и т. Д.

Примечание: Забудьте о високосных годах и других тонкостях DateTime. Это для не требовательного приложения.

Вот код, находящийся на рассмотрении.

// get ms between UTC dates and make into "difference" date 
var iDiffMS = dt2.valueOf() - dt1.valueOf(); 
var dtDiff = new Date(iDiffMS); 

// calc various diffs 
var nYears = dt2.getUTCFullYear() - dt1.getUTCFullYear(); 
var nMonths = dt2.getUTCMonth() - dt1.getUTCMonth() + (nYears!=0 ? nYears*12 : 0); 
var nQuarters = parseInt(nMonths/3); //<<-- different than VBScript, which watches rollover not completion 

// these are in absolute terms, ie totals of unit differences, 1/2/1981 - 1/1/1980 = 1y 366d 
var nMilliseconds = iDiffMS; 
var nSeconds = parseInt(iDiffMS/1000); 
var nMinutes = parseInt(nSeconds/60); 
var nHours = parseInt(nMinutes/60); 
var nDays = parseInt(nHours/24); // <-- now fixed for DST switch days 
var nWeeks = parseInt(nDays/7); 
// save absolutes 
var nYears0=nYears; 
var nMonths0=nMonths; 
var nQuarters0=nQuarters; 
var nWeeks0=nWeeks; 
var nDays0=nDays; 
var nHours0=nHours; 
var nMinutes0=nMinutes; 
var nSeconds0=nSeconds; 

// HERE! go from absolute to relative, 1/2/1981 - 1/1/1980 = 1y 1d etc, not 1y 366d etc 
nQuarters  -=nYears0*4; 
nMonths  -=nYears0*12; 
nWeeks  -=parseInt((nYears0*52)+(nMonths0*(365.25/52))); 
nCalWeeks  -=parseInt((nYears0*52)+(nMonths0*(365.25/52))); 
nQuarters  -=nYears0*4; 
nDays   -=parseInt((nYears0*365.25)    +(nMonths*(365.25/12))); 
nHours  -=parseInt((nYears0*365.25*24)   +(nMonths*(365.25/12*24))   +(nDays*24)); 
nMinutes  -=parseInt((nYears0*365.25*24*60)   +(nMonths*(365.25/12*24*60))  +(nDays*24*60)   +(nHours*60)); 
nSeconds  -=parseInt((nYears0*365.25*24*60*60)  +(nMonths*(365.25/12*24*60*60))  +(nDays*24*60*60)  +(nHours*60*60)  +(nMinutes*60)); 
nMilliseconds -=parseInt((nYears0*365.25*24*60*60*1000) +(nMonths*(365.25/12*24*60*60*1000))+(nDays*24*60*60*1000) +(nHours*60*60*1000) +(nMinutes*60*1000) + (nSeconds*1000)); 
// END 
+0

В вашем желаемом формате, какой правильный ответ за 3/3/1981 - 1/1/1980? Вы хотите «1 год, 2 месяца, 2 дня»? –

+1

Да, просто используйте разницу в миллисекундах (после 1970 года) и преобразуйте его обратно – maraca

+0

Да, 1 год, 2 месяца, 2 дня, x часов и т. Д. –

ответ

0

можно осуществить это, работая с более крупных единиц на более мелкие единицы, но это, вероятно, легче работать от мала до велика, потому что вы не должны умножить факторы заранее. Например, вам не нужно заранее проверять, что день составляет 24 * 60 * 60 * 1000 миллисекунд. Вместо этого вы можете наращивать факторы по мере продвижения от наименьшей единицы к самой большой единице.

На первом этапе вы знаете, что вам нужно разделить количество миллисекунд на 1000, чтобы получить количество секунд. На втором этапе вы разделите секунды на 60, чтобы получить количество минут. Затем вы делите на 60, чтобы получить часы. Затем вы делите на 24, чтобы получить дни.

Для обеспечения того, чтобы вы не являлись единицами двойного счета, вы можете использовать оператор modulo, %. Например, если количество минут равно 135, вычислите 135 % 60, чтобы получить 15, что составляет минуты, оставшиеся после вычитания всех часов. В общем, перед отображением числа текущих единиц вы хотите взять модуль по отношению к следующему большему блоку.

Следующий сценарий реализует этот подход.

function getDifferenceInWords(dateStart, dateEnd) { 
 
    var delta = dateEnd.valueOf() - dateStart.valueOf(); 
 
    if (delta < 0) { 
 
    var describe = "earlier"; 
 
    delta = -delta; 
 
    } else { 
 
    var describe = "later"; 
 
    } 
 
    var units = [{ name: 'second', multiple: 1000 }, 
 
       { name: 'minute', multiple: 60 },  
 
       { name: 'hour', multiple: 60 },  
 
       { name: 'day', multiple: 24 },  
 
       { name: 'week', multiple: 7 },  
 
       { name: 'quarter', multiple: 13 }, 
 
       { name: 'year', multiple: 4 }], 
 
     divisor = 1, 
 
     parts = []; 
 
    for (var i = 0; i < units.length; ++i) { 
 
    var unit = units[i]; 
 
    divisor *= unit.multiple; 
 
    if (divisor > delta) { 
 
     break; 
 
    } 
 
    var count = Math.floor(delta/divisor); 
 
    if (i+1 < units.length) { 
 
     count %= units[i+1].multiple; 
 
    } 
 
    if (count != 0) { 
 
     parts.push(count + ' ' + unit.name + (count == 1 ? '' : 's')); 
 
    } 
 
    } 
 
    parts.reverse(); 
 
    return parts.join(', ') + ' ' + describe; 
 
} 
 

 
var dateStart = new Date('January 1, 1995 09:01:30'), 
 
    dateEnd = new Date('May 1, 2015 23:05:15'); 
 

 
document.write(getDifferenceInWords(dateStart, dateEnd));

Вы можете настроить код по своему вкусу с помощью сокращенных названий единицы или изменить последовательность единиц.

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

Другим способом изменения единиц может быть добавление десятилетий, веков и тысячелетий. Пока вы следуете шаблону структуры данных units, где каждый блок является фиксированным кратным предыдущего устройства, вы не ошибетесь.

+0

Мне нравится этот подход, хотя он смешивает вывод строки с вычислениями. Я уверен, что месяц может быть разработан с каким-то обратным расчетом, но мой мозг не справляется с этим прямо сейчас. –

+0

Строки вообще не влияют на вычисления. Я просто ограничиваю вывод, создавая множественные имена единиц и оставляя нулевые единицы. –

0

Хорошо, это то, что я искал, обратите внимание на то, как он меняется, когда вы переходите к часам. Продолжение на пути t1-= приведет к ошибкам времени из-за високосных лет. Кроме того, месяцы должны быть рассчитаны таким образом, чтобы исправить прыжки. Я подозреваю, что проблема с расчетом weeks.

var d1=new Date('1/1/1980 00:00:00:000'); 
var d2=new Date('2/8/1981 01:01:01:001'); 
//BUG "1y 1m 1w 1d 23h 59mins 59secs 999ms" should be 0d 
//var d2=new Date('2/7/1981 23:59:59:999'); 

var t1=d2.valueOf() - d1.valueOf(); 
console.log(t1); 
var nYears= parseInt(t1/(1000*60*60*24*365.25)); 
t1-=nYears*(1000*60*60*24*365.25); 
var s=''; 
s+=nYears+'y'; 
var nMonths=d2.getUTCMonth() - d1.getUTCMonth(); 
nMonths=nMonths>0?nMonths:nMonths+12; 
s+=' '+nMonths+'m' 
t1-=nMonths*1000*60*60*24*(365.25/12); 
var nWeeks=parseInt(t1/(1000*60*60*24*7)); 
s+=' '+nWeeks+'w' 
t1-=nWeeks*(1000*60*60*24*7); 
var nDays=parseInt(t1/(1000*60*60*24)); 
s+=' '+nDays+'d' 
t1-=nDays*(1000*60*60*24); 
var nHours=d2.getHours()-d1.getHours(); 
s+=' '+nHours+'h'; 
var nMinutes=d2.getMinutes()-d1.getMinutes(); 
s+=' '+nMinutes+'mins'; 
var nSeconds=d2.getSeconds()-d1.getSeconds(); 
s+=' '+nSeconds+'secs'; 
var nMilliseconds=d2.getMilliseconds()-d1.getMilliseconds(); 
s+=' '+nMilliseconds+'ms'; 
//console.log(d1.getHours()); 
console.log(s); //"1y 1m 1w 1d 1h 1mins 1secs 1ms" 
+0

Этот код не работает. Попробуйте его с помощью: 'var d1 = new Date ('12/25/1980 10: 10: 00: 000 '); var d2 = new Дата ('2/8/1981 01: 01: 01: 001'); ' –

+0

Исправлено, спасибо –

+0

Все еще не работает. Если вы хотите использовать встроенные функции 'get ...()', вы должны убедиться, что вы исходите из прошлого в будущее или из будущего в прошлое. Или вы можете избежать всего этого и просто использовать арифметические операции, как я показал в своем ответе. –

Смежные вопросы