¿Cómo puedo calcular un lapso de tiempo en Java y formatear la salida?

Quiero tomar dos veces (en segundos desde época) y mostrar la diferencia entre los dos en formatos como:

  • 2 minutos
  • 1 hora, 15 minutos
  • 3 horas, 9 minutos
  • Hace 1 minuto
  • Hace 1 hora, 2 minutos

¿¿Cómo puedo lograr esto??

Como todos gritan “YOODAA !!!” pero nadie publica un ejemplo concreto, aquí está mi contribución.

También puedes hacer esto con Joda-Time . Use Period para representar un período. Para formatear el período en la representación humana deseada, use PeriodFormatter que puede construir mediante PeriodFormatterBuilder .

Aquí hay un ejemplo de inicio:

 DateTime myBirthDate = new DateTime(1978, 3, 26, 12, 35, 0, 0); DateTime now = new DateTime(); Period period = new Period(myBirthDate, now); PeriodFormatter formatter = new PeriodFormatterBuilder() .appendYears().appendSuffix(" year, ", " years, ") .appendMonths().appendSuffix(" month, ", " months, ") .appendWeeks().appendSuffix(" week, ", " weeks, ") .appendDays().appendSuffix(" day, ", " days, ") .appendHours().appendSuffix(" hour, ", " hours, ") .appendMinutes().appendSuffix(" minute, ", " minutes, ") .appendSeconds().appendSuffix(" second", " seconds") .printZeroNever() .toFormatter(); String elapsed = formatter.print(period); System.out.println(elapsed + " ago"); 

Mucho más claro y conciso, ¿no?

Esto se imprime por ahora

 32 años, 1 mes, 1 semana, 5 días, 6 horas, 56 minutos, 24 segundos

(Tos, viejo, tos)

  Date start = new Date(1167627600000L); // JANUARY_1_2007 Date end = new Date(1175400000000L); // APRIL_1_2007 long diffInSeconds = (end.getTime() - start.getTime()) / 1000; long diff[] = new long[] { 0, 0, 0, 0 }; /* sec */diff[3] = (diffInSeconds >= 60 ? diffInSeconds % 60 : diffInSeconds); /* min */diff[2] = (diffInSeconds = (diffInSeconds / 60)) >= 60 ? diffInSeconds % 60 : diffInSeconds; /* hours */diff[1] = (diffInSeconds = (diffInSeconds / 60)) >= 24 ? diffInSeconds % 24 : diffInSeconds; /* days */diff[0] = (diffInSeconds = (diffInSeconds / 24)); System.out.println(String.format( "%d day%s, %d hour%s, %d minute%s, %d second%s ago", diff[0], diff[0] > 1 ? "s" : "", diff[1], diff[1] > 1 ? "s" : "", diff[2], diff[2] > 1 ? "s" : "", diff[3], diff[3] > 1 ? "s" : "")); 

Sí, he despertado a los muertos que tengo, pero aquí está mi implementación mejorada basada en el código publicado de @mtim, ya que este hilo viene casi encima de las búsquedas, así que estoy jugando con el hueco soñoliento,

  public static String getFriendlyTime(Date dateTime) { StringBuffer sb = new StringBuffer(); Date current = Calendar.getInstance().getTime(); long diffInSeconds = (current.getTime() - dateTime.getTime()) / 1000; /*long diff[] = new long[]{0, 0, 0, 0}; /* sec * diff[3] = (diffInSeconds >= 60 ? diffInSeconds % 60 : diffInSeconds); /* min * diff[2] = (diffInSeconds = (diffInSeconds / 60)) >= 60 ? diffInSeconds % 60 : diffInSeconds; /* hours * diff[1] = (diffInSeconds = (diffInSeconds / 60)) >= 24 ? diffInSeconds % 24 : diffInSeconds; /* days * diff[0] = (diffInSeconds = (diffInSeconds / 24)); */ long sec = (diffInSeconds >= 60 ? diffInSeconds % 60 : diffInSeconds); long min = (diffInSeconds = (diffInSeconds / 60)) >= 60 ? diffInSeconds % 60 : diffInSeconds; long hrs = (diffInSeconds = (diffInSeconds / 60)) >= 24 ? diffInSeconds % 24 : diffInSeconds; long days = (diffInSeconds = (diffInSeconds / 24)) >= 30 ? diffInSeconds % 30 : diffInSeconds; long months = (diffInSeconds = (diffInSeconds / 30)) >= 12 ? diffInSeconds % 12 : diffInSeconds; long years = (diffInSeconds = (diffInSeconds / 12)); if (years > 0) { if (years == 1) { sb.append("a year"); } else { sb.append(years + " years"); } if (years < = 6 && months > 0) { if (months == 1) { sb.append(" and a month"); } else { sb.append(" and " + months + " months"); } } } else if (months > 0) { if (months == 1) { sb.append("a month"); } else { sb.append(months + " months"); } if (months < = 6 && days > 0) { if (days == 1) { sb.append(" and a day"); } else { sb.append(" and " + days + " days"); } } } else if (days > 0) { if (days == 1) { sb.append("a day"); } else { sb.append(days + " days"); } if (days < = 3 && hrs > 0) { if (hrs == 1) { sb.append(" and an hour"); } else { sb.append(" and " + hrs + " hours"); } } } else if (hrs > 0) { if (hrs == 1) { sb.append("an hour"); } else { sb.append(hrs + " hours"); } if (min > 1) { sb.append(" and " + min + " minutes"); } } else if (min > 0) { if (min == 1) { sb.append("a minute"); } else { sb.append(min + " minutes"); } if (sec > 1) { sb.append(" and " + sec + " seconds"); } } else { if (sec < = 1) { sb.append("about a second"); } else { sb.append("about " + sec + " seconds"); } } sb.append(" ago"); /*String result = new String(String.format( "%d day%s, %d hour%s, %d minute%s, %d second%s ago", diff[0], diff[0] > 1 ? "s" : "", diff[1], diff[1] > 1 ? "s" : "", diff[2], diff[2] > 1 ? "s" : "", diff[3], diff[3] > 1 ? "s" : ""));*/ return sb.toString(); } 

Obviamente se puede mejorar. Básicamente trata de hacer que el lapso de tiempo sea más amigable, aunque hay algunas limitaciones, es decir, se comportaría de manera extraña si el tiempo pasa (parámetro) en el futuro, y está limitado hasta los días, horas y segundos solamente (meses y años no manejado, para que alguien más pueda ;-).

Los resultados de muestra son:

  • hace aproximadamente un segundo
  • Hace 8 minutos y 34 segundos
  • hace una hora y 4 minutos
  • Hace un día
  • Hace 29 días
  • hace un año y 3 meses

, salud: D

EDITAR: ahora admite meses y años: P

Te recomiendo que eches un vistazo a HumanTime

Evite las clases heredadas de fecha y hora

Las otras respuestas pueden ser correctas pero están desactualizadas. Las problemáticas clases antiguas de fecha y hora en Java ahora son heredadas, suplantadas por las clases java.time.

Del mismo modo, el proyecto Joda-Time , ahora en modo de mantenimiento , aconseja la migración a las clases java.time .

Usando java.time

Instant

dos veces (en segundos desde época)

En java.time, representamos momentos en la línea de tiempo en UTC como Instant . Asumiré que tu época es la misma que la de java.time, el primer momento de 1970 en UTC ( 1970-01-01T00:00:00Z ). Tenga en cuenta que un Instant tiene una resolución de nanosegundos. Un método de fábrica de conveniencia construye un Instant partir de segundos enteros, como se solicita en la Pregunta.

 Instant start = Instant.ofEpochSecond( … ); Instant stop = Instant.ofEpochSecond( … ); 

Aquí usamos el momento actual y algunos minutos después.

 Instant start = Instant.now() ; Instant stop = start.plusSeconds( TimeUnit.MINUTES.toSeconds( 7L ) ) ; // Seven minutes as a number of seconds. 

Duration

En java.time, un lapso de tiempo no asociado a la línea de tiempo se representa de dos maneras. Para años-mes-días, tenemos Period . Durante horas, minutos y segundos, tenemos Duration .

 Duration duration = Duration.between( start , stop ); 

Medio abierto

El tiempo transcurrido se calcula con el enfoque Half-Open. En este enfoque, el comienzo es inclusivo, mientras que el final es exclusivo . Este enfoque se usa comúnmente en el trabajo de fecha y hora. Creo que el uso constante de este enfoque en su base de códigos ayudará a eliminar errores y malentendidos debido a ambigüedades, y facilitará la carga cognitiva de su progtwigción.

ISO 8601

El estándar ISO 8601 define muchos formatos prácticos para representar valores de fecha y hora como texto. Estos formatos están diseñados para evitar la ambigüedad, ser fáciles de analizar por máquina y ser intuitivos para la lectura humana en todas las culturas.

Las clases java.time usan estos formatos de forma predeterminada al analizar y generar cadenas.

Durante un lapso de tiempo no vinculado a la línea de tiempo, el estándar define un formato de PnYnMnDTnHnMnS donde P marca el comienzo y T separa cualquier años-meses-días de cualquier hora-minutos-segundos. Entonces una hora y media es PT1H30M .

En nuestro código de ejemplo usando siete minutos:

 String outputStandard = duration.toString(); 

PT7M

Vea el código de ejemplo anterior en vivo en IdeOne.com .

Sugiero seguir con estos formatos siempre que sea posible, sin duda al intercambiar o serializar datos de fecha y hora, pero también considere usarlos en una interfaz de usuario cuando corresponda y sus usuarios pueden ser entrenados para usarlos.

Recomiendo nunca usar el formato de hora del reloj (por ejemplo, 01:30 para hora y media) ya que ese formato es completamente ambiguo con una hora del día.

Recuperando partes para construir una Cadena

Si debe deletrear el lapso de tiempo como se ve en la Pregunta, necesitará construir el texto usted mismo.

  • Para Period , llame a getYears , getMonths , y getDays para recuperar cada parte.
  • Curiosamente, la clase Duration carecía de tales getters en Java 8 , pero los ganó en Java 9 y posteriores: toDaysPart , toHoursPart , toMinutesPart , toSecondsPart y toNanosPart .

Un código de ejemplo, simple y básico para comenzar.

 int days = duration.toDaysPart() ; int hours = duration.toHoursPart() ; int minutes = duration.toMinutesPart() ; int seconds = duration.toSecondsPart() ; int nanos = duration.toNanosPart() ; StringBuilder sb = new StringBuilder( 100 ); if( days != 0 ) { sb.append( days + " days" ) ; }; if( hours != 0 ) { sb.append( ( sb.length = 0 ) ? ( "" ) : ( ", " ) ) ; // Append comma if any existing text. sb.append( hours + " hours" ) ; }; if( minutes != 0 ) { sb.append( ( sb.length = 0 ) ? ( "" ) : ( ", " ) ) ; // Append comma if any existing text. sb.append( minutes + " minutes" ) ; }; if( seconds != 0 ) { sb.append( ( sb.length = 0 ) ? ( "" ) : ( ", " ) ) ; // Append comma if any existing text. sb.append( seconds + " seconds" ) ; }; if( nanos != 0 ) { sb.append( ( sb.length = 0 ) ? ( "" ) : ( ", " ) ) ; // Append comma if any existing text. sb.append( nanos + " nanos" ) ; }; String output = sb.toString(); 

O tal vez podría escribir un código más pequeño utilizando DateTimeFormatterBuilder de la manera mostrada con Joda-Time en la respuesta de Balus C.


Acerca de java.time

El marco java.time está integrado en Java 8 y posterior. Estas clases suplantan a las problemáticas antiguas clases heredadas de fecha y hora como java.util.Date , Calendar y SimpleDateFormat .

El proyecto Joda-Time , ahora en modo de mantenimiento , aconseja la migración a las clases java.time .

Para obtener más información, consulte el Tutorial de Oracle . Y busque Stack Overflow para obtener muchos ejemplos y explicaciones. La especificación es JSR 310 .

¿Dónde obtener las clases de java.time?

  • Java SE 8 y SE 9 y posteriores
    • Incorporado.
    • Parte de la API Java estándar con una implementación integrada.
    • Java 9 agrega algunas características y correcciones menores.
  • Java SE 6 y SE 7
    • Gran parte de la funcionalidad de java.time se transfiere a Java 6 y 7 en ThreeTen-Backport .
  • Androide
    • El proyecto ThreeTenABP adapta ThreeTen-Backport (mencionado anteriormente) específicamente para Android.
    • Consulte Cómo utilizar ThreeTenABP ….

El proyecto ThreeTen-Extra amplía java.time con clases adicionales. Este proyecto es un terreno de prueba para posibles adiciones futuras a java.time. Puede encontrar algunas clases útiles aquí, como Interval , YearWeek , YearQuarter yfz más .

No soy un experto en Java, pero puedes hacer t1-t2 = t3 (en segundos) y luego dividir eso por 60, te daría minutos, por otros 60 te daría segundos. Entonces solo se trata de averiguar cuántas divisiones necesita.

Espero eso ayude.

Si su tiempo abarca los límites cruzados del horario de verano (horario de verano), ¿desea informar el número de días?

Por ejemplo, de 23:00 a 23:00 el día siguiente es siempre un día, pero puede ser de 23, 24 o 25 horas, dependiendo de si cruza una transición de horario de verano.

Si te importa eso, asegúrate de tenerlo en tu elección.

algún código que juega con el formato de fecha … y hace tiempo.

 SimpleDateFormat curFormater = new SimpleDateFormat("EEE, dd MMM yyyy kk:mm:ss"); try { Date dateObj = curFormater.parse(pubDate); SimpleDateFormat postFormater = new SimpleDateFormat("MMMM dd, yyyy ss:mm:hh"); String newDateStr = postFormater.format(dateObj); Log.v("THE NEW DATE IS",newDateStr); long pubdateinmilis = dateObj.getTime(); pubDate = (String) DateUtils.getRelativeTimeSpanString(pubdateinmilis, System.currentTimeMillis(),DateUtils.HOUR_IN_MILLIS,DateUtils.FORMAT_ABBREV_RELATIVE); } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } 

Siempre comienzo con Joda Time . Trabajar con fechas y horas en Java siempre es “divertido”, pero Joda Time quita la tensión.

Tienen clases de Intervalo y Duración que hacen la mitad de lo que estás buscando. Sin embargo, no estoy seguro si tienen una función para la salida en formato legible. Seguiré buscando.

HTH

La clase de calendario puede manejar la mayoría de las matemáticas relacionadas con la fecha. Sin embargo, tendrá que obtener el resultado de compareTo y generar el formato usted mismo. No hay una biblioteca estándar que haga exactamente lo que está buscando, aunque puede haber una biblioteca de terceros que sí lo haga.

OK, después de una breve revisión de la API parece que podrías hacer lo siguiente:

  1. cree algunos archivos legibles que representen la hora de inicio y la hora de finalización.
  2. Use Hours.hours Between para obtener el número de horas
  3. use Minutes.minutes Between the para obtener la cantidad de minutos
  4. usa mod 60 en los minutos para obtener los minutos restantes
  5. et voila!

HTH

La solución de “Abduliam Rehmanius” parece muy bonita, o puede utilizar alguna biblioteca anterior o puede crear nuevas cosas simples, consulte la solución JS aquí: http://www.datejs.com/ . No es difícil transformarse en Java Lang 🙂

Espero que mi enlace sea útil para ti!

El formateo de los intervalos de tiempo en java me aparece frecuentemente en los arrastres ETL o DB. El siguiente fragmento de código es cómo obtengo el pulso de la operación.

  SimpleDateFormat ymdhms=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); DecimalFormat formatter = new DecimalFormat("#,###"); /* ("#,###.00"); gives you the decimal fractions */ int counter=0; Date beginTime0=new Date(); while () {  /*// uncomment this section durring initial exploration of the time consumer if(counter>100000){ System.out.println("debug exiting due to counter="+counter); System.exit(0); } */ if(0==counter%1000){ Date now = new Date(); long diffInSeconds = (now.getTime() - beginTime0.getTime()) / 1000; long diff[] = new long[] { 0, 0, 0, 0 }; diff[3] = (diffInSeconds >= 60 ? diffInSeconds % 60 : diffInSeconds); /* sec */ diff[2] = (diffInSeconds = (diffInSeconds / 60)) >= 60 ? diffInSeconds % 60 : diffInSeconds; /* min */ diff[1] = (diffInSeconds = (diffInSeconds / 60)) >= 24 ? diffInSeconds % 24 : diffInSeconds; /* hours */ diff[0] = (diffInSeconds = (diffInSeconds / 24)); /* days */ String delta = String.format( "%s%s%s%s" ,(diff[0]>0?String.format("%d day%s " ,diff[0],(diff[0]!=1?"s":"")):"") ,(diff[1]>0?String.format("%2d hour%s " ,diff[1],(diff[1]!=1?"s":" ")):"") ,(diff[2]>0?String.format("%2d minute%s ",diff[2],(diff[2]!=1?"s":" ")):"") , String.format("%2d second%s ",diff[3],(diff[3]!=1?"s":" ")) ); System.out.println(String.format( "%12s %s delta= %s" ,formatter.format(counter) ,ymdhms.format(new Date()) ,delta ) ); } counter++; } 

Creo que estás buscando algo como PrettyTime
PrettyTime es una biblioteca de formato de tiempo OpenSource. Totalmente personalizable, crea sellos de tiempo relativos , legibles por humanos , como los que se ven en Digg, Twitter y Facebook. ¡Está disponible en más de 30 idiomas!
enlace: https://github.com/ocpsoft/prettytime

Se puede usar en Android también

Por ejemplo

 System.out.println(p.format(new Date(System.currentTimeMillis() + 1000*60*10))); //prints: "10 minutes from now" 

Recientemente obtuve el mismo requisito cuando necesito un método básico para imprimir la duración. No tenía la opción de usar una biblioteca de terceros. Así que acabo de perfeccionar la idea de @mtim.

 public static void main(String[] args) throws IOException, ParseException { String since = "2017-02-24T04:44:05.601Z"; DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); TimeZone utc = TimeZone.getTimeZone("UTC"); dateFormat.setTimeZone(utc); Date sinceDate = dateFormat.parse(since); Date now = new Date(); long durationInSeconds = TimeUnit.MILLISECONDS.toSeconds(now.getTime() - sinceDate.getTime()); long SECONDS_IN_A_MINUTE = 60; long MINUTES_IN_AN_HOUR = 60; long HOURS_IN_A_DAY = 24; long DAYS_IN_A_MONTH = 30; long MONTHS_IN_A_YEAR = 12; long sec = (durationInSeconds >= SECONDS_IN_A_MINUTE) ? durationInSeconds % SECONDS_IN_A_MINUTE : durationInSeconds; long min = (durationInSeconds /= SECONDS_IN_A_MINUTE) >= MINUTES_IN_AN_HOUR ? durationInSeconds%MINUTES_IN_AN_HOUR : durationInSeconds; long hrs = (durationInSeconds /= MINUTES_IN_AN_HOUR) >= HOURS_IN_A_DAY ? durationInSeconds % HOURS_IN_A_DAY : durationInSeconds; long days = (durationInSeconds /= HOURS_IN_A_DAY) >= DAYS_IN_A_MONTH ? durationInSeconds % DAYS_IN_A_MONTH : durationInSeconds; long months = (durationInSeconds /=DAYS_IN_A_MONTH) >= MONTHS_IN_A_YEAR ? durationInSeconds % MONTHS_IN_A_YEAR : durationInSeconds; long years = (durationInSeconds /= MONTHS_IN_A_YEAR); String duration = getDuration(sec,min,hrs,days,months,years); System.out.println(duration); } private static String getDuration(long secs, long mins, long hrs, long days, long months, long years) { StringBuffer sb = new StringBuffer(); String EMPTY_STRING = ""; sb.append(years > 0 ? years + (years > 1 ? " years " : " year "): EMPTY_STRING); sb.append(months > 0 ? months + (months > 1 ? " months " : " month "): EMPTY_STRING); sb.append(days > 0 ? days + (days > 1 ? " days " : " day "): EMPTY_STRING); sb.append(hrs > 0 ? hrs + (hrs > 1 ? " hours " : " hour "): EMPTY_STRING); sb.append(mins > 0 ? mins + (mins > 1 ? " mins " : " min "): EMPTY_STRING); sb.append(secs > 0 ? secs + (secs > 1 ? " secs " : " secs "): EMPTY_STRING); sb.append("ago"); return sb.toString(); } 

El resultado será un intervalo de tiempo (duración) impreso en palabras, por ejemplo, 1 day 2 hours 51 mins 41 secs ago

Esta pregunta es antigua y ya tiene una respuesta, pero tengo una mejor solución. Use relativeSpan desde utilidades de fecha

  dateHere.setText(DateUtils.getRelativeTimeSpanString(timeInMillis, System.currentTimeMillis(), DateUtils.SECOND_IN_MILLIS)); 

toma argumentos: tiempo largo / int, tiempo actual y cómo lo desea, _meses, minutos, segundos, etc.

 long time1, time2; time1 = System.currentMillis(); .. drink coffee time2 = System.currentMillis(); long difference = time2 - time1 // millies between time1 and time2 java.util.Date differneceDate = new Date(difference); 

Para crear una cadena como “2 minutos”, debe usar DateFormatter / DateFormat. Puede encontrar más detalles al respecto en la especificación API de Java (java.sun.com).