2012-07-13 6 views
8

Estoy haciendo un contador de JavaScript que cuenta' segundos atrás '. Tengo mi tiempo en un objeto de tiempo JS, y encontré un fragmento de función de "diferencia de tiempo" aquí en el desbordamiento de la pila, pero aparece "hace 2 horas". ¿Cómo puedo hacer que muestre "5 horas, 10 minutos y 37 segundos atrás"?Javascript fuzzy time (por ejemplo, '10 minutos atrás ') en segundos exactos

Aquí es lo que estoy trabajando con:

Esta función convierte la hora actual y la fecha y hora de algo en "Hace 20 segundos" en lugar de una fecha críptica:

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

Y aquí es lo que Estoy usando para "contar" el tiempo cada segundo. Me gustaría que decir "5 horas, 3 minutos, 10 segundos" y luego 1 segundo más tarde, "5 horas, 3 minutos, 11 segundos atrás"

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) 

El Newtime variable es el tiempo en el UTC formato de fecha de javascript. relTime es eso en formato "segundos atrás". El intervalo recorre un grupo de elementos de marca de tiempo y selecciona el correcto para cada marca de tiempo. Luego agrega un segundo al tiempo, lo convierte nuevamente en "tiempo difuso" (segundos atrás), reemplaza el html con el nuevo horario y lo registra en la consola.

¿Cómo cambio "5 horas atrás" en "5 horas, 37 minutos, hace 10 segundos"? La función de diferencia de tiempo necesita ser modificada.

+0

¿Por qué estás volviendo a declarar 'var relTime' dentro de setInterval? – ErikE

+0

P.S. Utilizaría un método muy diferente para encontrar el elemento html correcto. Tiene que haber una etiqueta de datos [algo] que pueda agregar a cada uno que pueda correlacionar la marca de tiempo apropiada con el html correcto. – ErikE

+0

@ErikE Tengo que declarar relTime nuevamente o de lo contrario siempre tendrá el valor inicial. ¿Y cómo es mejor utilizar .attr ('datasomething') que .html()? – alt

Respuesta

2

Cambie la lógica para que en lugar de encontrar la unidad de medida más grande que pueda, haga algo con el resto.

Básicamente lo que tendría que hacer es comenzar con el mayor incremento, encontrar el valor y luego restarlo del total para obtener el resto. Entonces repite.

Algo así como tal vez, no lo he probado.

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 

Luego construya su cadena de variables.

+0

¿Por qué necesita la variable restante? – alt

+0

@Jackson, usando su ejemplo digamos que tenía 5 horas y 37 minutos, no puede seguir haciendo los cálculos con el valor original 'transcurrido'. De lo contrario, obtendría 5 horas, 337 minutos (5 horas * 60 minutos + 37 minutos). Entonces toma el valor 'transcurrido', resta la cantidad de horas completas que pudo obtener y lo almacena en 'resto 'para que pueda calcular los minutos restantes. – Brandon

0

Esto debería hacer el truco:

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); 

de salida: 8 meses, 12 días, 1 hora, 45 minutos y hace

47 segundos Esto supuesto, se podrían refactorizan a ser un poco menos repetitivas . Puedes probar este violín con él funcionando: http://jsfiddle.net/vawEf/

+0

Si pongo algo con PST, regresa correctamente. Tengo PDT y vuelve "y hace 5 horas". ¿Alguna idea para que funcione con mis marcas de tiempo? Se ven así: 'Thu 12 de julio 2012 16:57:41 GMT-0700 (PDT)' – alt

6

Esta es una función que está cerca de lo que estás pidiendo.

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())); 

Resultados:

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

me llamaron "ingenua", porque en realidad no se presta atención a la forma humana que calculamos el tiempo. Si es "1/1/2013 12:01:00 am" exactamente, en comparación con "1/1/2012 12:01:00 am" debe obtener "1 año, 0 meses, 0 días, 0 horas, 0 minutos , Hace 0 segundos ". Pero no hará eso extendiendo la lógica en la función que presentó, y tampoco lo hará en mi función (además, mi función no usará meses). Una mejor aproximación de años que 365 días es 365.24, pero eso también se ignora.

Excluí las partes de tiempo vacías según lo solicitado, dejando "0 segundos" como mínimo cuando no se encuentran piezas de tiempo.

Ahora, si quieres una forma humana de calcular, tienes que decidir algunas cosas. No puedes usar los límites cruzados porque del 28 de febrero al 1 de marzo no es un mes completo. En segundo lugar, esta es una pregunta que expondrá el problema real:

  • ¿Cuántos meses y días es del 2 al 31 de marzo?

Si califica el 2 de febrero al 2 de marzo como un mes, entonces es 1 mes 29 días. Pero ¿y si fuera del 2 de enero al 1 de marzo? Esa es la misma cantidad de días transcurridos entre ellos. ¿Es eso ahora 1 mes (para todo abril) + 1 día en marzo + 31 días en enero para 1 mes 32 días? ¿Desea que sus meses coincidan con un calendario físico para que un humano pueda realizar un seguimiento con el dedo y obtener la fecha correcta? Eso es mucho más difícil de lo que piensas.

Si puede responder con reglas sensatas y completas acerca de cómo haría "calcular el tiempo transcurrido como el humano", entonces quizás pueda escribirle otra función para hacerlo.

actualización

He aquí una nueva función que hace meses, y tiene 365,24 días en un año (30.43666666 días en un mes):

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))); 

Salida:

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 

Todavía es ingenuo, pero hace un trabajo un poco mejor. Además, funcionará para fechas MUY antiguas como B.C. unos. :)

+0

Debe ocultar todo el "0 ____ hace". Creo que de lo contrario es perfecto. – alt

+0

@JacksonGariety Actualizado. ¿Está bien que este no incluya meses? Estoy trabajando en una versión que sí lo hace. – ErikE

+0

¡Muy bueno! ¿Oculta los 0? ¿Cómo voy a hacer eso? – alt

Cuestiones relacionadas