我试图理解GregorianCalendar作为一名经验丰富的Delphi(pascal)开发人员学习java。根据文件,1970年1月1日0:00:00是计算从这一点起经过的时间(以秒为单位)的参考。因此,在实验中,我设置了一个新的变量
GregorianCalendar cal2 = new GregorianCalendar(1970, 0, 1, 0, 0, 0); //January=0, Day=1, Hour=0, Min=0, Sec=0
然后我读了时间
cal2.getTimeInMillis()
根据定义,这应该等于零。然而我得到了1800000毫秒。这是5个小时。我想这和时区有关?有什么建议吗?我在东区。
如果是,我该如何解释?我真的在努力理解,这样我就可以用秒来计算两次之间的差异。如果不了解这一点,我就无法继续!谢谢Doug
package so;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;
public class BeginningOfTime {
public static void main (final String[] args) {
final Calendar localTZ = new GregorianCalendar (1970, 0, 1, 0, 0, 0);
dump (localTZ);
final Calendar utcTZ = new GregorianCalendar ();
utcTZ.clear ();
utcTZ.setTimeZone (TimeZone.getTimeZone ("UTC"));
dump (utcTZ);
}
private static final void dump (final Calendar c) {
System.out.printf ("%s: %d (offset %d)%n",
c.getTimeZone ().getDisplayName (),
c.getTimeInMillis (),
c.get (Calendar.ZONE_OFFSET));
}
}
收益率:
Eastern Standard Time: 18000000 (offset -18000000)
Coordinated Universal Time: 0 (offset 0)
我知道这不能直接回答您的问题,但GregorianCalendar
可能不是最好的方法。我建议你使用Joda Time这样的库,因为你专门寻找Duration和Period等概念。还可以看看ThreeTen JSR310的RI,它是为Java8计划的。
这是一个JodaTime解决方案,用于计算以秒为单位的运行时间。
DateTime start = new DateTime(2012, 3, 25, 0, 0, 0, 0);
DateTime end = new DateTime(2012, 3, 26, 0, 0, 0, 0);
int seconds = Seconds.secondsBetween(start, end).getSeconds();
我打赌它是东部时区。最大的问题是:你必须对此负责吗?你打算在同一时区计算/记录时间吗?这个类有很多功能,以至于它变得非常臃肿,但非常方便。
GregorianCalendar类确实包含时区功能,还可以执行诸如提供DST偏移之类的操作。
北美东海岸与UTC
您的GregorianCalendar
带有时区。但你的时代的定义是UTC。1970年北美东海岸的第一个时刻比1970年UTC的第一个瞬间晚了五个小时。所以你把苹果比作桔子。
让我们再看一看,但使用现代的java.time类。
Instant instantEpochConstant = Instant.EPOCH ; // Expect 1970-01-01T00:00:00Z.
instantEpochConstant.toString():1970-01-01T00:00:00Z
Instant instantZeroMillis = Instant.ofEpochMilli( 0L ) ; // Expect 1970-01-01T00:00:00Z.
instantZeroMillis.toString():1970-01-01T00:00:00Z
long millisSinceEpoch = Instant.parse( "1970-01-01T00:00:00Z" ).toEpochMilli() ; // Expect zero.
millisInchEpoch.toString():0
相反,猜测您的时区是America/New_York
,该区域1970年的第一个时刻比UTC 1970年的第一时刻晚5小时(1800000毫秒)。
ZonedDateTime zdt = ZonedDateTime.of(
LocalDate.of( 1970 , Month.JANUARY , 1 ) ,
LocalTime.MIN ,
ZoneId.of( "America/New_York" )
);
Instant instantFromNewYork = zdt.toInstant() ;
long epochMillisNewYork = instantFromNewYork.toEpochMilli() ;
zdt.toString():1970-01-01T00:00-05:00[美国/纽约]
instantFromNewYork.toString():1970-01-01T05:00:00Z
划时代百万纽约:1800000
5*60*60*1000=1800000=5小时*60分钟/小时*60秒/分钟*1000毫秒/秒
java.time
GregorianCalendar
类是一个有缺陷、设计糟糕、麻烦的类。避开它。它现在被java.time.ZonedDateTime
取代。
Instant
Instant
类表示以UTC为单位的时间线上的一个时刻,分辨率为纳秒(最多九(9)位小数)。
Instant instant = Instant.now() ;
instant.toString():2018-02-06T02:59:56.156787Z
ZonedDateTime
您可以从UTC调整到特定时区。你最终会得到同一时刻,时间线上的同一点,但不同的挂钟时间。
以continent/region
的格式指定适当的时区名称,例如America/Montreal
、Africa/Casablanca
或Pacific/Auckland
。切勿使用3-4个字母的伪时区,如EST
或IST
,因为它们是非真实时区,不是标准化的,甚至不是唯一的(!)。没有"东部时间"这样的东西,使用America/New_York
这样的时区。
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
应用于Instant
以获得ZonedDateTime
。
ZonedDateTime zdt = instant.atZone( z ) ;
zdt.toString():2018-02-06T03:59:56.156787+01:00【非洲/突尼斯】
从epoch开始计数
将日期时间值作为历元参考日期的计数处理是不明智的。人眼不可能将这样的整数读取为日期时间,因此调试和发现无效值是困难的或不可能的。
但如果你坚持的话,你可以从1970年第一个时刻的历元(UTC,1970-01-01T00:00Z)中提取毫秒数。
小心数据丢失。在生成以毫秒为单位的计数时,Instant
中的任何微秒或纳秒都将被忽略。
long millisSinceEpoch = instant.toEpochMilli() ;
1517885996156
关于java.time
java.time框架构建在Java8及更高版本中。这些类取代了诸如java.util.Date
、Calendar
、&SimpleDateFormat
。
现在处于维护模式的JodaTime项目建议迁移到java.Time类。
要了解更多信息,请参阅Oracle教程。并在Stack Overflow中搜索许多示例和解释。规范是JSR310。
从哪里获得java.time类?
- Java SE 8、Java SE 9及更高版本
- 内置
- 标准Java API的一部分,带有捆绑实现
- Java 9添加了一些小功能和修复程序
- Java SE 6和Java SE 7
- 大部分java.time功能都是向后移植到Java6&7英寸ThreeTen背包
- 安卓
- java.time类的Android捆绑包实现的后续版本
- 对于早期的Android,ThreeTenABP项目适用于ThreeTen Backport(如上所述)。请参阅如何使用ThreeTenABP…
ThreeTen Extra项目通过附加类扩展java.time。这个项目是将来可能添加到java.time的试验场。您可以在这里找到一些有用的类,如Interval
、YearWeek
、YearQuarter
等等。