Java - Calendar.set(HOUR_OF_DAY)的意外结果[重复]

问题描述 投票:0回答:5

这个问题在这里已有答案:

JVM版本是1.7。时区为GMT + 3,偏移180分钟。 1500411600000对应7/19/2017, 12:00:00 AM(我已经验证了这个online)。

我正在执行以下代码来调整Date实例的时间:

final Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
Date date = new Date(1500411600000L);
calendar.setTime(date);
calendar.set(Calendar.HOUR_OF_DAY, 23);
calendar.set(Calendar.MINUTE, 59);
calendar.set(Calendar.SECOND, 59);
calendar.set(Calendar.MILLISECOND, 999);
date = calendar.getTime();

我希望date成为7/19/2017, 11:59:59 PM但不是这个我得到7/19/2017, 2:59:59 AM。 3个小时的差异 - 与我的时区与UTC / GMT不同,所以我想这里发生了一些未被注意的转换。

debug - date evaluation

你能帮我找一些时区不可知代码来调整日期时间吗?

java date calendar timezone-offset
5个回答
2
投票

您是正确的,在偏移UTC + 3时,您的毫秒值1500411600000对应于2017年7月19日午夜(一天的开始)。在其他偏移量,它对应于7月18日或19日的其他时间。

java.time

假设你在自己的时区里有午夜并不是巧合,那个值真的应该代表一个日期,而不是一个时间,我建议你使用LocalDatejava.time来代表它:

    ZoneId yourTimeZone = ZoneId.of("Europe/Riga");
    LocalDate date = Instant.ofEpochMilli(1500411600000L)
            .atZone(yourTimeZone)
            .toLocalDate();
    System.out.println(date);

这打印出预期的

2017-07-19

如果它不是欧洲/里加,请替换正确的时区,或者使用ZoneOffset代替:.atOffset(ZoneOffset.ofHoursMinutes(3, 0))(其他行相同)。

我怀疑你真的不想在一天结束时,即使在你的问题中你试图设置它。如果这是为了确定某个时间点是否在一天结束之前,则将其与第二天的开始时间进行比较,并要求它严格地在之前。这可以省去奇怪的分钟,秒和秒的分数。

    ZonedDateTime startOfNextDay = date.plusDays(1).atStartOfDay(yourTimeZone);

java.time于2014年问世,取代了Java 1.0和1.1以及Joda-Time设计不佳的日期和时间类,从中得到了很多灵感。我热烈建议你使用它。

你在这个问题上尝试了什么

我相信当用java.time表达时,你问题中的代码也更清晰:

    OffsetDateTime endOfDay = Instant.ofEpochMilli(1500411600000L)
            .atOffset(ZoneOffset.UTC)
            .with(LocalTime.MAX);
    System.out.println(endOfDay);

这打印

2017-07-18T23:59:59.999999999Z

(7月18日在UTC结束的一天;最后的Z表示UTC)。除了小数位数,这也是你得到的结果。您可能会被Date实例打印成Wed Jul 19 02:59:59 EEST 2017(时区缩写取决于您的JVM的时区设置)这样的事实所迷惑。 Date.toString()抓取您的JVM的时区设置,并仅将生成的字符串的日期时间转换为此时区; Date实例本身未被修改,只在时间线上保留一个点,没有时区。

问题:我可以在我的Java版本中使用java.time吗?

是的你可以。你只需要至少使用Java 6。

要学习使用qazxsw poi,请参阅qazxsw poi或在网上查找其他资源。


1
投票

你正在使用java.time,但你必须使用你所在的时区。正如你所说的GMT + 3


1
投票

请参阅此处的线程,其中解释了有关日期和时区的问题。

the Oracle tutorial

Date对象将具有正确的调整时间,但在显示时,输出将使用您当地的时区。您可以使用以下代码强制设置JVM的时区,但这可能会在代码的其他部分产生意外后果。

Calendar.getInstance(TimeZone.getTimeZone("UTC"))

在理想的世界中,您将使用Java 8日期类或Joda时间库类,它们都提供了一些简单的日期操作方法。

How to set time zone of a java.util.Date?


1
投票

使用TimeZone.setDefault(TimeZone.getTimeZone("UTC")); 。这对我来说似乎是一个历史性的“错误”,一个划分日历的时间,其中setTime不会改变区域。

Java 8 date classes

当然,这可能是切换到新的Java时间API的正确参数。


1
投票

问题比我的问题中描述的要大。它源于用户时区的错误管理日期/时间。在我的应用程序中,时间戳是在用户的时区发送的,然后在服务器的时区中进行了评估,但没有考虑时区差异。我试图解决这个问题并面对问题中描述的问题。

我听了clear关于使用Joda Time的建议,我很乐意分享这个解决方案:

final Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
Date date = new Date(1500411600000L);
calendar.clear(); // To reset _all_ fields, incl. the time zone offset ZONE_OFFSET.
calendar.setTime(date);
calendar.set(Calendar.HOUR_OF_DAY, 23);
calendar.set(Calendar.MINUTE, 59);
calendar.set(Calendar.SECOND, 59);
calendar.set(Calendar.MILLISECOND, 999);
date = calendar.getTime();

我还发现@ThomasEdwin JVM参数在调试此问题时非常有用。请参阅此处获取支持的long userTimezoneOffset = 180; // it's a parameter submitted by client app Date date = new Date(1500411600000L); // it's another parameter submitted by client app final DateTimeZone zone = DateTimeZone.forOffsetMillis((int) TimeUnit.MINUTES.toMillis(userTimezoneOffset)); final DateTimeZone serverZone = DateTimeZone.getDefault(); MutableDateTime dateTime = new MutableDateTime(date, zone); dateTime.setHourOfDay(23); dateTime.setMinuteOfHour(59); dateTime.setSecondOfMinute(59); dateTime.setMillisOfSecond(999); dateTime.setZoneRetainFields(serverZone); date = dateTime.toDate(); // now date.toString() returns expected result 列表。

© www.soinside.com 2019 - 2024. All rights reserved.