我有以下代码,它采用一个毫秒的字符串(将来自RSS提要,因此将是一个字符串,下面的示例是一个快速测试程序)并将这些毫秒转换为Date对象。
public static void main(String[] args) {
String ms = "1302805253";
SimpleDateFormat dateFormatter = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz");
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(Long.parseLong(ms));
try {
String dateFormat = dateFormatter.format(calendar.getTime());
System.out.println("Date Format = " + dateFormat);
Date dateParse = dateFormatter.parse(dateFormatter.format(calendar.getTime()));
System.out.println("Date Parse = " + dateParse);
} catch (ParseException e) {
// TODO: handle exception
}
}
Output:
Date Format = Fri, 16 Jan 1970 02:53:25 GMT
Date Parse = Fri Jan 16 03:53:25 GMT 1970
正如您所看到的,在日历对象的格式化和解析生成的String之间,一小时正在丢失。此外,输出的格式已更改。任何人都可以帮我解释为什么会发生这种情况,以及如何解决这个问题?我希望Date对象的格式与“Date Format”输出格式相同。
答案 0 :(得分:8)
我相信这种情况正在发生,因为英国在1970年实际上没有使用GMT,而且Java有一个错误...它将在1970年格式化一个日期好像英国正在使用GMT,但没有实际改变偏移量。简单的例子:
Date date = new Date(0);
SimpleDateFormat sdf = new SimpleDateFormat("dd MMM yyyy HH:mm:ss zzz");
sdf.setTimeZone(TimeZone.getTimeZone("Europe/London"));
System.out.println(sdf.format(date));
结果:
01 Jan 1970 01:00:00 GMT
请注意,它声称格林尼治标准时间凌晨1点......这是不正确的。在欧洲/伦敦时间凌晨1点 ,但是欧洲/伦敦没有观察到GMT。
Joda Time得到了正确的结果,它打印出BST - 但Joda Time不喜欢用时区缩写解析值。但是,您可以使用时区 offets 来代替:
import org.joda.time.*;
import org.joda.time.format.*;
public class Test {
public static void main(String[] args) throws Exception {
DateTime date = new DateTime(0, DateTimeZone.forID("Europe/London"));
DateTimeFormatter formatter = DateTimeFormat.forPattern(
"dd MMM yyyy HH:mm:ss Z");
String text = formatter.print(date); // 01 Jan 1970 01:00:00 +0100
System.out.println(text);
DateTime parsed = formatter.parseDateTime(text);
System.out.println(parsed.equals(date)); // true
}
}
答案 1 :(得分:0)
Answer by Jon Skeet是正确的。
让我们通过java.time运行相同的输入来查看结果。
指定proper time zone name。切勿使用诸如BST
,EST
或IST
之类的3-4字母缩写,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。所以我们使用Europe/London
。
Instant
类代表UTC中时间轴上的一个时刻,分辨率为nanoseconds。
String input = "1302805253";
long millis = Long.parseLong ( input );
Instant instant = Instant.ofEpochMilli ( millis );
应用时区以生成ZonedDateTime
对象。
ZoneId zoneId = ZoneId.of ( "Europe/London" );
ZonedDateTime zdt = instant.atZone ( zoneId );
转储到控制台。我们确实看到Europe/London
时间比那时提前一小时。因此,时间为02
小时而不是01
小时。两者都代表了时间轴上的同一时刻,只是通过两个不同wall-clock times的镜头来观察。
System.out.println ( "input: " + input + " | instant: " + instant + " | zdt: " + zdt );
输入:1302805253 |瞬间:1970-01-16T01:53:25.253Z | zdt:1970-01-16T02:53:25.253 + 01:00 [欧洲/伦敦]
顺便说一下,我怀疑你的输入字符串代表自1970年UTC世纪以来的整个秒,而不是毫秒。解释说,在这个问题发布的月份,我们在2011年获得了一个日期。
String output = Instant.ofEpochSecond ( Long.parseLong ( "1302805253" ) ).atZone ( ZoneId.of ( "Europe/London" ) ).toString ();
2011-04-14T19:20:53 + 01:00 [欧洲/伦敦]
java.time框架内置于Java 8及更高版本中。这些类取代了旧的麻烦日期时间类,例如java.util.Date
,.Calendar
和& java.text.SimpleDateFormat
。
现在位于Joda-Time的maintenance mode项目建议迁移到java.time。
要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。
大部分java.time功能都被反向移植到Java 6& ThreeTen-Backport中的7,并进一步适应Android中的ThreeTenABP。
ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。