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
而不是@GrabResolver(name = 'nexus', root = 'https://local-nexus-server:8443/repository/maven-public', m2Compatible = true)
@Grab('com.google.errorprone:error_prone_annotations:2.1.3')
@Grab('com.google.guava:guava:23.0')
@GrabExclude('com.google.errorprone:error_prone_annotations')
import com.google.common.base.Strings
class Util {
void msg(int a, String b, Map c) {
println 'Message printed by msg method inside Util.groovy'
println "Print 5 asterisks using the Guava dependency ${Strings.repeat("*", 5)}"
println "Arguments are a=$a, b=$b, c=$c"
}
}
。 3小时的差异 - 与我的时区与UTC / GMT不同,所以我想这里会发生一些未被注意的转换。
你能帮我找一些时区不可知代码来调整日期时间吗?
答案 0 :(得分:2)
您是正确的,在偏移UTC + 3时,您的毫秒值1500411600000对应于2017年7月19日午夜(一天的开始)。在其他偏移量,它对应于7月18日或19日的其他时间。
假设您在自己的时区内有午夜并不是巧合,那么该值实际上应该代表日期,而不是时间,我建议您使用LocalDate
中的java.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.time
吗?是的,你可以。您只需要使用至少Java 6 。
要学习使用java.time
,请参阅this question: How to use ThreeTenABP in Android Project或在网上查找其他资源。
答案 1 :(得分:1)
您正在使用Calendar.getInstance(TimeZone.getTimeZone("UTC"))
,但必须使用您所在的时区。正如您所说 GMT + 3
答案 2 :(得分:1)
请参阅此主题,其中解释了有关日期和时区的问题。
How to set time zone of a java.util.Date?
Date对象将具有正确的调整时间,但在显示时,输出将使用您当地的时区。您可以使用以下代码强制设置JVM的时区,但这可能会对代码的其他部分产生意想不到的后果。
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
在理想的世界中,您将使用Java 8日期类或Joda时间库类,它们都提供了一些简单的日期操作方法。
答案 3 :(得分:1)
使用clear
。这对我来说似乎是一个历史性的“错误”,时间划分为日历,其中setTime不会改变区域。
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();
当然,这可能是切换到新的Java时间API的正确参数。
答案 4 :(得分:1)
问题比我的问题中描述的要大。它源于用户时区的错误管理日期/时间。在我的应用程序中,时间戳是在用户的时区发送的,然后在服务器的时区中进行了评估,但没有考虑时区差异。我试图解决这个问题并面对问题中描述的问题。
我听了@ThomasEdwin使用Joda Time的建议,我很乐意分享这个解决方案:
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
此外,我发现-Duser.timezone
JVM参数在调试此问题时非常有用。请参阅此处以获取支持的timezone IDs列表。