2012-06-25 3 views
4

Есть ли быстрый способ с низким уровнем мусора? Я не могу просто выполнить простую модульную арифметику, так как это не учитывает прыжковые секунды и другое время/время смешного бизнеса.Как я могу конвертировать секунды с эпохи в часы/минуты/секунды в Java?

ответ

1

Это быстрый, нулевой мусор решение. Крайне важно не создавать новый экземпляр для каждого вызова, потому что это довольно тяжелый объект, беря 448 байтов кучи и почти микросекунд для инициализации (Java 6, 64-бит HotSpot, OS X).

HmsCalculator предназначен для использования из одного потока (каждый поток должен использовать другой экземпляр).

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

P.S. Я не вставлял все эти статические импорта (скучно).

+0

Это решение не является потокобезопасным. –

+0

@ ChristofferHammarström Теперь мы все знаем, что вы не читали последнее предложение ответа :) –

+0

О, право. Это было очевидно неочевидно, поэтому я исправил его, надеюсь, вы не против. –

4

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

Java не счет високосных секунд в целом - или, вернее, официально это до платформы, но я не верю, что он реализован в любой из общих производственных платформ. Вы уверены, что нуждаетесь в для учета прыжков секунд? Если вы это сделаете, вы сможете сделать простой поиск на основе таблиц количества секунд для добавления или удаления, в зависимости от источника данных и того, что вы хотите, чтобы он отражал.

Что касается «другой даты/времени смешного дела» - я не думаю, что is любое смешное дело для данного конкретного расчета. Например, временные зоны неактуальны с точки зрения прошедшего времени с эпохи.

+0

Временные зоны неактуальны в течение секунд с момента эпохи, но не имеют значения при преобразовании в местное время ЧЧ: ММ: СС. Тем не менее, это просто постоянное смещение, так что это не большое дело. –

+0

@ user939259: Я предположил, что вы имели в виду «часы, минуты, секунды» с эпохи ... ваш вопрос очень неясен. Если вы имели в виду, что хотите преобразовать временную метку в локальное время, вы можете использовать 'java.util.Date' и' java.util.Calendar' или, предпочтительно, Joda Time. –

2

Предполагая, что с "эпохи" вы имеете в виду 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)); 
+1

Низкий мусор? Думаю, нет. –

+0

@MarkoTopolnik Объясните? Единственное, что вы создаете, это объект 'Date' (без учета форматирования с помощью SimpleDateFormat). Это не очень дорого. – Jesper

+0

Дата, SimpleDateFormat, внутренние элементы обоих. Это не так уж и мало, и результат по-прежнему является строкой вместо кортежа чисел. Плюс OP сказал, что он хочет только HMS, может, он имел в виду часы с эпохи? Не ясно. –

1
Calendar = Calendar.getInstance(); 
calendar.setTimeInMillis(secondsSinceTheEpoch*1000); 
10

Я выяснил, как справляться с високосными годами в целочисленной арифметике и реализовал конвертер с секунд с эпохи до даты/времени (это никогда не дает вам более 59 секунд). Следующий код C должен быть очень простым для переноса на Java.

#include <string.h> 
#include <time.h> 

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: 
    n n n l \ 
    n n n l } 24 times 
    ... /
    n n n l/
    n n n n 

    2nd hundred: 
    n n n l \ 
    n n n l } 24 times 
    ... /
    n n n l/
    n n n n 

    3rd hundred: 
    n n n l \ 
    n n n l } 24 times 
    ... /
    n n n l/
    n n n n 

    4th hundred: 
    n n n l \ 
    n n n l } 24 times 
    ... /
    n n n l/
    n n n 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; 
} 

See a test run at ideone.

+0

Хорошая работа! Это именно то, что мне нужно. Чтобы подтвердить, чеки для зажима столетий/четырехлетних/однолетних были только для вашего здравомыслия во время дева права? Мне кажется, что математически это никогда не произойдет. Также после каждого из них вы выполняете 'sec - = duration * secs', что эквивалентно использованию'% '- которое вы используете для' quadricentennials', только незначительная несогласованность. Я бы выбрал либо '- =', либо '% =' для всех. – David

+0

@Dave Они предназначены для здравомыслия, да, но самое главное для правильности. :) Попробуйте удалить их и повторить небольшой тестовый код на ideone. Обратите внимание, как внезапно 978307199 = «Вс 31 декабря 23:59:59 2000» становится 978307199 = «Солнце 1 января 23:59:59 2001», но 978307199 + 1 = «Пн Янв 1 00:00:00 2001» остается неизменным , Последняя секунда 2000 года составляет менее 400 лет от 1/1/1601 (правда, просто сравните эти годы), но более чем на 4 * 100 лет от 1/1/1601 (365,24 дней в году, то есть) , Удалив первый зажим, вы не будете должным образом учитывать 97-й високосный год каждые 400 лет, наш случай здесь. –

+0

На моей 32-битной сборке мне пришлось изменить одну строку. Эта строка: 'sec = SecondsSinceEpoch + 11644473600;' должно быть 'sec = SecondsSinceEpoch + 11644473600LL;'. Обратите внимание на 'LL', поскольку он не будет работать на 32-битных сборках. Уверен, что он будет работать только в том случае, если типы 'long long' имеют ширину 64 бит. –

0

java.time

На Java 8, а затем, встроенный в классе, чтобы использовать это Instant из java.time рамок.

Instant instant = Instant.ofEpochSecond (1_469_168_058L); 

Дамп для консоли.

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

мгновенным: 2016-07-22T06: 14: 18Z

Performance

Я не знаю точно как java.time.Instant выполняемое с точки зрения скорости из-исполнения или мусороперерабатывающего производства.Но вы должны протестировать против этого класса. В моем прочтении the source code in Java 9 он выглядит довольно простым и быстрым.

С помощью нескольких прыжков с помощью метода он просто назначает пару целых чисел, (a) количество секунд от эпохи (64-бит long) и (b) количество наносекунд как долю секунды (a 32-бит int), выполнив пару быстрых проверок:

Сначала ищет значения нуля, и в этом случае он возвращает статический экземпляр для самой эпохи.

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

Во-вторых, проверяет состояние секунд на пару постоянных мин/макс.

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

Затем вызывает конструктор, который назначает пару целых значений к паре переменных-членов (long и int примитивов соответственно).

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

Конечно, это просто строительство. Опрос на такие детали, как время дня, означает большую работу. Как и создание строки с помощью метода toString, который включает в себя другой класс, DateTimeFormatter. Исходный код toString - одна строка.

return DateTimeFormatter.ISO_INSTANT.format(this); 

И помните, что если вы хотите детали, такие как год, месяц, день-месяц, час, и так далее в часовом поясе, кроме UTC, что означает больше работы с участием ZoneId и ZonedDateTime классов. Например:

ZoneId zoneId = ZoneId.of("America/Montreal"); 
ZonedDateTime zdt = instant.atZone(zoneId); 
Смежные вопросы