2012-06-25 76 views

回答

1

這是一個快速,零垃圾的解決方案。在每個調用中不要創建Calendar的新實例是非常重要的,因爲它是一個相當重的對象,需要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 佔閏秒一般 - 或者更確切地說,正式這是走上講臺,但我不相信這是在任何的共同生產平臺上實現。你確定你需要來計算閏秒嗎?如果你這樣做,你應該能夠做一個簡單的基於表的查詢秒數來添加或刪除,這取決於你的數據源是什麼以及你希望它反映什麼。

至於「其他日期/時間有趣的業務」 - 我不認爲有這個特定的計算任何有趣的業務。例如,時區自從時代開始經歷的時間是不相關的。

+0

時區與時代以來秒無關,但轉換爲本地HH:MM:SS時並非無關緊要。不過,這只是一個不變的抵消,所以沒有什麼大不了的。 –

+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

Date,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 =「Sun Dec 31 23:59:59 2000」變爲978307199 =「Sun Jan 1 23:59:59 2001」,但是978307199 + 1 =「Mon Jan 1 00:00:00 2001」保持不變。 2000年的最後一秒距離160年1月1日不到400年(對嗎?只是比較幾年),但距離1/1/1601(即365.24天長的年份)超過4 * 100年, 。通過移除第一個夾具,您將無法正確地解釋每隔400年的第97個閏年,這裏是我們的案例。 –

+0

在我的32位版本,我不得不改變一行。這一行:'sec = SecondsSinceEpoch + 11644473600;'必須是'sec = SecondsSinceEpoch + 11644473600LL;'。注意''LL',因爲它會在32位版本上失敗。如果「long long」類型是64位寬,肯定會起作用。 –

0

java.time

從Java 8及更高版本,內置類使用的是Instantjava.time框架。

Instant instant = Instant.ofEpochSecond (1_469_168_058L); 

轉儲到控制檯。

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

瞬間:2016-07-22T06:14:18Z

性能

我不知道究竟速度的執行中或方面如何java.time.Instant執行垃圾生產。但是你應該對這個班級進行考試。在我細讀the source code in Java 9時,它看起來非常簡單和快速。 (a)來自曆元(64位long)的秒數和(b)作爲秒的分數(a)的秒數(b) 32位int),在做了幾次快速檢查之後:

首先查找零值,在這種情況下,它會爲紀元本身返回一個靜態實例。

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

其次對與最小/最大常量對的秒數進行完整性檢查。

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

然後調用構造,其對整數值指派給一對的成員變量(longint原語分別地)的。

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

當然,這只是施工。詢問諸如時間之類的部分意味着更多的工作。正如通過toString方法生成一個字符串涉及另一個類,DateTimeFormattertoString源代碼是一行。

return DateTimeFormatter.ISO_INSTANT.format(this); 

請記住,如果你想要的部分,如年,月,日的日,小時,等的不是UTC以外的時區,這意味着涉及ZoneIdZonedDateTime類更多的工作。例如:

ZoneId zoneId = ZoneId.of("America/Montreal"); 
ZonedDateTime zdt = instant.atZone(zoneId); 
相關問題