【问题标题】:Java Date Conversion from Millisecond time毫秒时间的 Java 日期转换
【发布时间】:2015-08-19 12:05:26
【问题描述】:

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

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

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

我的目标是将每个偏移量(年/月/日/小时/分钟/秒)存储在一个数组中,以便以后使用。 但是,当我使用 Calendar 类运行此计算时,由于某种原因,使用 offsetConverter(2678400000) 调用时,我会获得额外的时间

Output: 0 years 0 months 31 days 19 hours 0 minutes 0 seconds 

这是我从这个链接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;
}

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

【问题讨论】:

标签: java date calendar


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

    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

    条款

    这里的“偏移”一词是一个糟糕的术语选择。该词在日期时间处理中具有特定含义:时区来自 UTC 的小时、分钟和秒数。请参阅UTC offset 的维基百科条目。

    您似乎更关心的是与时间轴无关的时间跨度。在 java.time 类中,如果以小时-分钟-秒为单位,此类跨度称为 Duration,如果以年-月-日为单位,则称为 Period

    java.time

    现代方法使用 java.time 类取代了麻烦的旧日期时间类,例如 Date/Calendar

    Duration

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

    Duration d = Duration.ofMillis( 2_678_400_000L );
    

    这恰好是 744 小时。

    d.toString(): PT744H

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

    d.toDaysPart(): 31

    看来您确实打算一个月或 31 天。无论哪种方式,Java 都有相应的类。

    如果你真的想要:(31 * 24 小时),那么请使用Duration。但是用更多的自记录代码构造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

    您的输入字符串几乎符合日期时间格式的 ISO 8601 标准。 java.time 类在解析/生成字符串时默认使用标准格式。

    将您的字符串转换为符合要求。将中间的空格替换为T

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

    2015-08-18T00:00:00

    LocalDateTime

    将您的输入解析为LocalDateTime,因为您的输入缺少offset-from-UTCtime zone 的指示符。

    LocalDateTime ldt = LocalDateTime.parse( input ) ;
    

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

    添加您的Duration 对象以获得新的LocalDateTimejava.time 类使用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 确实代表一个实际的时刻,即时间线上的一个特定点。如果没有时区的上下文或与 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[太平洋/奥克兰]

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

    ZonedDateTime thirtyOneTimes24HoursLaterZdt = zdt.plus( d ) ;
    

    thirtyOneTimes24HoursLaterZdt.toString(): 2015-09-18T00:00+12:00[太平洋/奥克兰]

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

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

    thirtyOneDaysLater.toString(): 2015-09-18T00:00+12:00[太平洋/奥克兰]

    如果您的业务逻辑确实打算使用日历月而不是特定天数,请使用不同的Period。该课程根据需要进行调整以处理不同月份的长度(read the doc 并关注that doc’s link)。

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

    monthLater.toString(): 2015-09-18T00:00+12:00[太平洋/奥克兰]


    关于java.time

    java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

    Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

    要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

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

    从哪里获得 java.time 类?

    ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-11-26
      • 1970-01-01
      • 1970-01-01
      • 2013-10-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多