Java 毫秒时间的日期转换

Java Date Conversion from Millisecond time

我正在尝试计算给定日期的偏移时间。我将日期作为字符串获取并解析它,并且我有以毫秒为单位的偏移量。例如

日期:2015-08-18 00:00:00

偏移时间:2678400000,应用于日期时,等于 2015-09-18 00:00:00,31 天后。

我的目标是将每个偏移量 (years/months/days/hours/minutes/seconds) 存储在一个数组中,以便以后使用。 但是,当我 运行 使用日历 class 进行此计算时,由于某些原因,当使用 offsetConverter(2678400000)

调用时,我得到了额外的时间
Output: 0 years 0 months 31 days 19 hours 0 minutes 0 seconds 

这是我的代码,我从这个 link Best way to convert Milliseconds to number of years, months and days

中找到并稍作修改
    public static int[] offsetConverter(long offset) {
    int[] delay =  new int[6];
    //delay 0-3 = years/months/days/seconds

    Calendar c = Calendar.getInstance(); 
    c.setTimeInMillis(offset);
    delay[0] = c.get(Calendar.YEAR) - 1970;
    delay[1] = c.get(Calendar.MONTH);
    delay[2] = c.get(Calendar.DAY_OF_MONTH);
    delay[3] = c.get(Calendar.HOUR_OF_DAY);
    delay[4] = c.get(Calendar.MINUTE);
    delay[5] = c.get(Calendar.SECOND);

    for (int i = 0; i< delay.length; i++)
        System.out.print(delay[i] + " ");
    System.out.println();
    return delay;
}

如果有人看到我做错了什么或有更简单的方法来做到这一点,我将不胜感激。谢谢!

Date#getTime() 开始的毫秒到达 UTC 时区,但您使用默认时区(即您的本地时区)实例化日历。这会为您的结果增加额外的时间。

要解决此问题,请使用 UTC 时区创建一个 Calendar 实例:

Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC"));

tl;博士

LocalDateTime.parse( 
    "2015-08-18 00:00:00".replace( " " , "T" )   // Convert input string to comply with ISO 8601 standard format, replacing SPACE in the middle with a `T`.
).plus( 
    Duration.ofDays( 31 )      // If you meant ( 31 * 24-hours ). 
    // Period.ofDays( 31 )     // If you meant 31 calendar-days. 
    // Period.ofMonths( 1 )    // If you meant a calendar-month.
)                              // Returns a fresh `LocalDateTime` object, distinct from original, per Immutable Objects pattern.
.toString()                    // Generate a string in standard ISO 8601 format to textually represent this object’s value.

2015-09-18T00:00

条款

这里的“偏移”一词是一个糟糕的术语选择。该词在 date-time 处理中具有特定含义:时区来自 UTC 的小时、分钟和秒数。请参阅 UTC offset.

的维基百科条目

您似乎更关心与时间轴无关的时间跨度。在 java.time classes 中,如果在 hours-minutes-seconds 的范围内,这样的跨度称为 Duration,并称为 Period 如果在 years-months-days.

中缩放

java.time

现代方法使用 java.time classes 取代了麻烦的旧 date-time classes,例如 Date/Calendar.

Duration

将时间跨度从毫秒数转换为 Duration 对象。

Duration d = Duration.ofMillis( 2_678_400_000L );

这恰好是 744 小时。

d.toString(): PT744H

出于好奇,如果我们将“天”定义为 24 小时的块,我检查了 744 小时内的天数。

d.toDaysPart(): 31

看来你真的打算一个月或 31 天的时间。无论哪种方式,Java 都有 classes。

如果您真的想要:(31 * 24 小时),请使用 Duration。但是用更多 self-documenting 代码构造 Duration 对象。

Duration d = Duration.ofDays( 31 ) ;  // Exact same effect as `Duration.ofMillis( 2_678_400_000L )` but more clear as to your intention.

ISO 8601

您输入的字符串几乎符合 date-time 格式的 ISO 8601 标准。 java.time classes 在 parsing/generating 字符串时默认使用标准格式。

转换您的字符串以符合要求。将中间的 SPACE 替换为 T.

String input = "2015-08-18 00:00:00".replace( " " , "T" ) ;

2015-08-18T00:00:00

LocalDateTime

将您的输入解析为 LocalDateTime as your input lacks an indicator of offset-from-UTC or time zone

LocalDateTime ldt = LocalDateTime.parse( input ) ;

ldt.toString(): 2015-08-18T00:00

添加您的 Duration 对象以获得新的 LocalDateTime. The java.time classes use immutable objects,这样原始对象就完好无损。

LocalDateTime thirtyOneTimes24HoursLaterLdt = ldt.plus( d ) ;  // Adding a span-of-time to our `LocalDateTime` object to get another `LocalDateTime` object.

thirtyOneTimes24HoursLaterLdt.toString(): 2015-09-18T00:00

请注意 LocalDateTime 而不是 代表实际时刻,时间轴上的特定点。在没有时区或 offset-from-UTC 的情况下,这只是大约 26-27 小时范围内 潜在 时刻的粗略概念。

ZonedDateTime

如果您确定您的输入字符串是为了表示特定区域中的时刻,请应用 ZoneId 以获得 ZonedDateTime 对象(实际时刻)。

ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime zdt = ldt.atZone( z ) ;  // Now we have determined an actual moment, a point on the timeline.

zdt.toString(): 2015-08-18T00:00+12:00[Pacific/Auckland]

如果您不关心日期(日历上的整天),您可以添加 Duration

ZonedDateTime thirtyOneTimes24HoursLaterZdt = zdt.plus( d ) ;

thirtyOneTimes24HoursLaterZdt.toString(): 2015-09-18T00:00+12:00[Pacific/Auckland]

如果您的业务逻辑确实是日历上的 thirty-one 天,请使用 Period

ZonedDateTime thirtyOneDaysLater = zdt.plus( Period.ofDays( 31 ) ) ;

thirtyOneDaysLater.toString(): 2015-09-18T00:00+12:00[Pacific/Auckland]

如果您的业务逻辑确实需要一个日历月而不是特定的天数,请使用不同的 Period。 class 根据需要进行调整以处理不同月份的长度 (read the doc and follow that doc’s link)。

ZonedDateTime monthLater = zdt.plus( Period.ofMonths( 1 ) ) ;

monthLater.toString(): 2015-09-18T00:00+12:00[Pacific/Auckland]


关于java.time

java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

要了解更多信息,请参阅 Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310

使用 JDBC driver compliant with JDBC 4.2 或更高版本,您可以直接与数据库交换 java.time 对象。不需要字符串,也不需要 java.sql.* classes.

从哪里获得java.time classes?

  • Java SE 8, Java SE 9,及以后
    • Built-in。
    • 标准 Java API 的一部分,带有捆绑实施。
    • Java 9 添加了一些小功能和修复。
  • Java SE 6 and Java SE 7
    • java.time 的大部分功能是 back-ported 到 Java ThreeTen-Backport 中的 6 和 7。
  • Android
    • Android java.time classes.
    • 捆绑实施的更高版本
    • 对于较早的Android,ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See

ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.