Como posso converter segundos desde a época para horas / minutos / segundos em Java?

Existe uma maneira rápida e de baixo consumo de lixo para fazer isso? Eu não posso simplesmente fazer aritmética de módulo simples, já que isso não leva em conta os segundos bissextos e outros negócios engraçados de data / hora.

Esta é uma solução rápida e sem lixo. É de fundamental importância não criar uma nova instância do Calendar em cada chamada porque é um object bastante pesado, levando 448 bytes de heap e quase um microssegundo para inicializar (Java 6, HotSpot de 64 bits, OS X).

HmsCalculator é destinado ao uso de um único thread (cada thread deve usar uma instância diferente).

 public class HmsCalculator { private final Calendar c = Calendar.getInstance(); public Hms toHms(long t) { return toHms(t, new Hms()); } public Hms toHms(long t, Hms hms) { c.setTimeInMillis(t*1000); return hms.init(c); } public static class Hms { public int h, m, s; private Hms init(Calendar c) { h = c.get(HOUR_OF_DAY); m = c.get(MINUTE); s = c.get(SECOND); return this; } public String toString() { return String.format("%02d:%02d:%02d",h,m,s); } } public static void main(String[] args) { System.out.println(new HmsCalculator().toHms( System.currentTimeMillis()/1000)); } } 

PS Eu não colei todas as importações estáticas (chato).

Eu descobri como lidar com anos bissextos em aritmética de números inteiros e implementei um conversor de segundos desde o Epoch até a data / hora (isso nunca dá mais de 59 segundos, no entanto). O código C abaixo deve ser muito fácil de portar para o Java.

 #include  #include  typedef unsigned uint; typedef unsigned long long uint64; struct tm* SecondsSinceEpochToDateTime(struct tm* pTm, uint64 SecondsSinceEpoch) { uint64 sec; uint quadricentennials, centennials, quadrennials, annuals/*1-ennial?*/; uint year, leap; uint yday, hour, min; uint month, mday, wday; static const uint daysSinceJan1st[2][13]= { {0,31,59,90,120,151,181,212,243,273,304,334,365}, // 365 days, non-leap {0,31,60,91,121,152,182,213,244,274,305,335,366} // 366 days, leap }; /* 400 years: 1st hundred, starting immediately after a leap year that's a multiple of 400: nnnl \ nnnl } 24 times ... / nnnl / nnnn 2nd hundred: nnnl \ nnnl } 24 times ... / nnnl / nnnn 3rd hundred: nnnl \ nnnl } 24 times ... / nnnl / nnnn 4th hundred: nnnl \ nnnl } 24 times ... / nnnl / nnn L <- 97'th leap year every 400 years */ // Re-bias from 1970 to 1601: // 1970 - 1601 = 369 = 3*100 + 17*4 + 1 years (incl. 89 leap days) = // (3*100*(365+24/100) + 17*4*(365+1/4) + 1*365)*24*3600 seconds sec = SecondsSinceEpoch + 11644473600LL; wday = (uint)((sec / 86400 + 1) % 7); // day of week // Remove multiples of 400 years (incl. 97 leap days) quadricentennials = (uint)(sec / 12622780800ULL); // 400*365.2425*24*3600 sec %= 12622780800ULL; // Remove multiples of 100 years (incl. 24 leap days), can't be more than 3 // (because multiples of 4*100=400 years (incl. leap days) have been removed) centennials = (uint)(sec / 3155673600ULL); // 100*(365+24/100)*24*3600 if (centennials > 3) { centennials = 3; } sec -= centennials * 3155673600ULL; // Remove multiples of 4 years (incl. 1 leap day), can't be more than 24 // (because multiples of 25*4=100 years (incl. leap days) have been removed) quadrennials = (uint)(sec / 126230400); // 4*(365+1/4)*24*3600 if (quadrennials > 24) { quadrennials = 24; } sec -= quadrennials * 126230400ULL; // Remove multiples of years (incl. 0 leap days), can't be more than 3 // (because multiples of 4 years (incl. leap days) have been removed) annuals = (uint)(sec / 31536000); // 365*24*3600 if (annuals > 3) { annuals = 3; } sec -= annuals * 31536000ULL; // Calculate the year and find out if it's leap year = 1601 + quadricentennials * 400 + centennials * 100 + quadrennials * 4 + annuals; leap = !(year % 4) && (year % 100 || !(year % 400)); // Calculate the day of the year and the time yday = sec / 86400; sec %= 86400; hour = sec / 3600; sec %= 3600; min = sec / 60; sec %= 60; // Calculate the month for (mday = month = 1; month < 13; month++) { if (yday < daysSinceJan1st[leap][month]) { mday += yday - daysSinceJan1st[leap][month - 1]; break; } } // Fill in C's "struct tm" memset(pTm, 0, sizeof(*pTm)); pTm->tm_sec = sec; // [0,59] pTm->tm_min = min; // [0,59] pTm->tm_hour = hour; // [0,23] pTm->tm_mday = mday; // [1,31] (day of month) pTm->tm_mon = month - 1; // [0,11] (month) pTm->tm_year = year - 1900; // 70+ (year since 1900) pTm->tm_wday = wday; // [0,6] (day since Sunday AKA day of week) pTm->tm_yday = yday; // [0,365] (day since January 1st AKA day of year) pTm->tm_isdst = -1; // daylight saving time flag return pTm; } 

Veja um teste executado no ideone .

Eu não posso simplesmente fazer aritmética de módulo simples, já que isso não leva em conta os segundos bissextos e outros negócios engraçados de data / hora.

O Java não é responsável por segundos bissextos em geral – ou melhor, oficialmente, depende da plataforma, mas não acredito que tenha sido implementado em nenhuma das plataformas de produção comuns. Tem certeza de que precisa responder por segundos bissextos? Se fizer isso, você poderá fazer uma consulta simples baseada em tabela com o número de segundos para adicionar ou remover, dependendo de qual é a sua fonte de dados e o que você deseja que ela reflita.

Quanto a “outro negócio engraçado de data / hora” – não acho que haja algum negócio engraçado para este cálculo específico. Os fusos horários são irrelevantes em termos de tempo decorrido desde a época, por exemplo.

Assumindo com “epoch” você quer dizer 01-01-1970, 00:00:00 GMT:

 long secondsSinceEpoch = ...; // The constructor of Date expects milliseconds // since 01-01-1970, 00:00:00 GMT Date date = new Date(secondsSinceEpoch * 1000L); DateFormat df = new SimpleDateFormat("dd/MM/yyyy"); System.out.println(df.format(date)); 
 Calendar = Calendar.getInstance(); calendar.setTimeInMillis(secondsSinceTheEpoch*1000); 

java.time

A partir do Java 8 e posterior, a class interna a ser usada é Instant no framework java.time .

 Instant instant = Instant.ofEpochSecond ( 1_469_168_058L ); 

Despeje para consolar.

 System.out.println ( "instant: " + instant ); 

instante: 2016-07-22T06: 14: 18Z

atuação

Eu não sei exatamente como o java.time.Instant executado em termos de velocidade de execução ou produção de lixo. Mas você deve testar contra essa class. Na minha leitura do código-fonte no Java 9 , parece bem simples e rápido.

Com alguns saltos de método, basicamente, apenas atribui um par de inteiros, (a) número de segundos de epoch (um long 64 bits) e (b) uma contagem de nanossegundos como a fração de segundo (um int 32 bits). ), depois de fazer algumas verificações rápidas:

Primeiro, procura valores de zero, caso em que retorna uma instância estática para a própria época.

 if ((seconds | nanoOfSecond) == 0) { return EPOCH; } 

Em segundo lugar, faz um teste de sanidade do número de segundos em relação ao par de constantes min / max.

 if (seconds < MIN_SECOND || seconds > MAX_SECOND) { throw new DateTimeException("Instant exceeds minimum or maximum instant"); } 

Em seguida, chama o construtor, que atribui o par de valores inteiros a um par de variables ​​de membro (primitivos long e int respectivamente).

 this.seconds = epochSecond; this.nanos = nanos; 

Claro que isso é apenas construção. Interrogar por partes como a hora do dia significa mais trabalho. Assim como gerar uma String através do método toString , que envolve outra class, um DateTimeFormatter . O código fonte toString é uma linha.

 return DateTimeFormatter.ISO_INSTANT.format(this); 

E lembre-se de que, se você quiser partes como ano, mês, dia do mês, hora e assim por diante em um fuso horário diferente de UTC, isso significa mais trabalho envolvendo as classs ZonedDateTime e ZonedDateTime . Por exemplo:

 ZoneId zoneId = ZoneId.of( "America/Montreal" ); ZonedDateTime zdt = instant.atZone( zoneId ); 
Intereting Posts