我最近一直在处理时区转换,并且对我得到的结果感到非常惊讶。基本上,我希望将日期从一个时区转换为另一个时区。下面是代码,转换工作正常,但我在调试时观察到的是,除非我调用Calendar#get(Calendar.FIELD)
,否则不会转换日期。
private static void convertTimeZone(String date, String time, TimeZone fromTimezone, TimeZone toTimeZone){
Calendar cal = Calendar.getInstance(fromTimezone);
String[] dateSplit = null;
String[] timeSplit = null;
if(time !=null){
timeSplit = time.split(":");
}
if(date!=null){
dateSplit = date.split("/");
}
if(dateSplit !=null){
cal.set(Calendar.DATE, Integer.parseInt(dateSplit[0]));
cal.set(Calendar.MONTH, Integer.parseInt(dateSplit[1])-1);
cal.set(Calendar.YEAR, Integer.parseInt(dateSplit[2]));
}
if(timeSplit !=null){
cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(timeSplit[0]));
cal.set(Calendar.MINUTE, Integer.parseInt(timeSplit[1]));
}
// System.out.println("Time in " + fromTimezone.getDisplayName() + " : " + cal.get(Calendar.DATE) +"/"+ (cal.get(Calendar.MONTH)+1)+"/"+ cal.get(Calendar.YEAR) +" " + ((cal.get(Calendar.HOUR_OF_DAY)<10) ? ("0"+cal.get(Calendar.HOUR_OF_DAY) ): (cal.get(Calendar.HOUR_OF_DAY)))
// +":" + (cal.get(Calendar.MINUTE)<10 ? "0"+cal.get(Calendar.MINUTE) : cal.get(Calendar.MINUTE)) );
cal.setTimeZone(toTimeZone);
System.out.println("Time in " + toTimeZone.getDisplayName() + " : " + cal.get(Calendar.DATE) +"/"+ (cal.get(Calendar.MONTH)+1)+"/"+ cal.get(Calendar.YEAR) +" " + ((cal.get(Calendar.HOUR_OF_DAY)<10) ? ("0"+cal.get(Calendar.HOUR_OF_DAY) ): (cal.get(Calendar.HOUR_OF_DAY)))
+":" + (cal.get(Calendar.MINUTE)<10 ? "0"+cal.get(Calendar.MINUTE) : cal.get(Calendar.MINUTE)) );
}
public static void main(String[] args) throws ParseException {
convertTimeZone("23/04/2013", "23:00", TimeZone.getTimeZone("EST5EDT"), TimeZone.getTimeZone("GB"));
}
预期输出: Time in Greenwich Mean Time : 24/4/2013 04:00
我在评论sysout 1时得到的输出: Time in Greenwich Mean Time : 23/4/2013 23:00
如果我取消对sysout1的评论,我会得到预期的有效输出。
感谢任何帮助
答案 0 :(得分:3)
在真正需要之前,不会评估给定日期的内部表示,直到您尝试通过这些getter访问它。但是,解析日期的最佳方法是通过SimpleDateFormat。
编辑(添加用于总结以下评论并更好地澄清我的答案)。
日历以这种方式工作以提高效率:每次调用setter时都不会重新计算,它会一直等到你调用getter。
日历应主要用于日期计算(请参阅add()
和roll()
),但您正在使用它进行解析和格式化:使用SimpleDateFormat可以更好地完成这些任务,这就是为什么我说你的日历的使用并不优雅。
见这个例子:
private static void convertTimeZone(String date, String time,
TimeZone fromTimezone, TimeZone toTimeZone) throws ParseException {
SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy HH:mm");
df.setTimeZone(fromTimezone);
Date d = df.parse(date + " " + time);
df.setTimeZone(toTimeZone);
System.out.println("Time in " + toTimeZone.getDisplayName() + " : " +
df.format(d));
}
我只使用SimpleDateFormat重新实现了您的方法。我的方法较小,没有分裂逻辑(它隐藏在parse()
中),并且输出也以更简单的方式处理。此外,日期格式以紧凑和标准的方式表示,可以使用ResourceBundle轻松实现国际化。
另请注意,时区转换只是一个格式化任务:解析日期的内部表示确实不更改。
答案 1 :(得分:2)
答案在a commented section in setTimeZone()
中部分解释:
考虑调用序列:cal.setTimeZone(EST); cal.set(HOUR,1); cal.setTimeZone(PST)。 校准设置为美国东部时间1点或太平洋标准时间1点?答:PST。更多 通常,对setTimeZone()的调用会影响对BE(和)之前的set()的调用 在下一次调用complete()之后。
换句话说,序列
被解释为“在新的时区中使用此时间”,而序列
将被解释为“在旧时区使用此时间,然后更改它”。
因此,在您的情况下,要获得您希望的行为,您需要在get()
内调用complete()
或内部调用Calendar
的任何其他方法你改变了时区。
答案 2 :(得分:0)
您是否仅限于使用TimeZone和日历?如果不是,我建议使用优秀的库JodaTime,这样可以更轻松地处理时区。
您的示例将如下所示:
public static void convertTimeZoneJoda(String date, String time, DateTimeZone fromTimezone, DateTimeZone toTimeZone) {
DateTimeFormatter dtf = DateTimeFormat.forPattern("dd/MM/yyyyHH:mm").withZone(fromTimezone);
DateTime dt = dtf.parseDateTime(date+time).withZone(toTimeZone);
System.out.println("Time in " + toTimeZone.getID() + " : " + dt.toString());
}
public static void main(String[] args) {
convertTimeZoneJoda("23/04/2013", "23:00", DateTimeZone.forID("EST5EDT"), DateTimeZone.forID("GMT"));
}
JodaTime还允许在TimeZone和DateTimeZone之间进行方便的对话。
答案 3 :(得分:0)
要添加到@Pino的答案,get()
未返回更新时间的原因是因为setTimeZone()
根本不更新字段,只需设置{{1}因为areAllFieldsSet
没有检查它,所以这是无用的。如果你问我,SUN的编码非常糟糕。以下是来自Calendar
的主管代码:
<强> get()
强>:
setTimeZone()
<强> public void setTimeZone(TimeZone value){
zone = value;
sharedZone = false;
areAllFieldsSet = areFieldsSet = false;
}
强>:
get()
答案 4 :(得分:0)
此代码适用于我转换为UTC:
使用UTC时区创建日历对象
Calendar utcTime = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
从“任意时区”日历对象
设置UTC日历对象中的时间点utcTime.setTimeInMillis(myCalendarObjectInSomeOtherTimeZone.getTimeInMillis());
utcTime
现在将包含与myCalendarObjectInSomeOtherTimeZone
转换为UTC相同的时间点。