2012-06-25 75 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); 
相关问题