Java 8个时区转换
Java 8 timezone conversion
我知道有类似的问题,但我不太能找到与我的相似的例子。我了解了 LocalDateTime
和 ZonedDateTime
但我不知道如何告诉我的 ZonedDateTime
什么是解析日期的假定时区。
我正在从 Java 7 迁移到 Java 8。在我的代码中,我有一个这样的方法:
public String changeTZ(String tzFrom, String tzTo, String dateToChange) {
ZoneId zoneFrom = ZoneId.of(tzFrom);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormat.toPattern());
LocalDateTime localtDateAndTime = LocalDateTime.parse(dateToChange, formatter);
ZonedDateTime dateAndTimeINeed = ZonedDateTime.of(localtDateAndTime, zoneFrom );
ZonedDateTime rezDate = dateAndTimeINeed.withZoneSameInstant(ZoneId.of(tzTo));
return formatter.format(rezDate);
}
示例用法是:
String rez = changeTZ("CEST", "UTC", "2017-08-10 14:23:58");
如您所见,它以字符串形式接收日期时间,时区日期在 tzFrom
变量中,我需要 TZ(toTo
变量)。
在 Java 7 中执行此操作的代码既荒谬又复杂,因此我不会在此处列出。你能告诉我如何在不改变方法接口(参数和 return 类型)的情况下在 Java 8 中实现相同的目标吗?夏令时呢?是在Java8自动处理吗?
请注意,该方法还支持 "CEST"、"CET"、"UTC" 形式的时区以及 "Europe/London".
等标准时区
此解决方案使用 Hugo 在评论中提到的 IANA 时区名称来获取 ZoneId
(更多详细信息 here)。如果将它与 CEST.
一起使用,将抛出异常
public static String changeTZ(String tzFrom, String tzTo, String dateToChange){
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.of(tzFrom));
ZonedDateTime zdt = ZonedDateTime.parse(dateToChange, dtf);
DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.of(tzTo));
return zdt.format(dtf2);
}
像这样使用:
String rez = changeTZ("US/Alaska", "Europe/Berlin", "2017-08-10 14:23:58");
Java 8 使用 IANA timezones names(格式总是 Region/City
,如 America/Sao_Paulo
或 Europe/Berlin
)。
避免使用短缩写(如 CEST
或 PST
),因为它们是 ambiguous and not standard.
实际上,这些名称不适用于 ZoneId
,主要是因为这种歧义(例如,CST 可以是 "Central Standard Time"、"Cuba Standard Time" 或 "China Standard Time") . 实际上,由于追溯兼容性的原因,其中一些可能会起作用,但不能保证对所有它们都起作用。
我假设 CEST 是 Central European Summer Time。目前有许多不同的国家(和时区)处于 CEST,因此 API 无法决定选择哪个时区,如果您只是将 "CEST" 传递给它。
那是因为一个时区包含一个地区在其历史上所有不同的偏移量。今天可能有很多国家/地区使用 CEST,但它们过去的历史不同(有些国家可能在不同年份使用 DST,或者使用不同的偏移量然后更改等),这就是为什么它们每个都有一个时区。
要使用这样的短名称(如 CEST
),您可以为每个名称定义一些默认值(这将是一个任意选择)并将这些选择放在一个映射中:
// map of custom zone names
Map<String, String> map = new HashMap<>();
// setting my arbitrary choices for each name
map.put("CEST", "Europe/Berlin"); // Berlin during DST period
map.put("CET", "Europe/Berlin"); // Berlin during non-DST period
// ... and so on
然后您可以使用此地图创建 ZoneId
:
// use the custom map to create the ZoneId
ZoneId zoneFrom = ZoneId.of(tzFrom, map);
...
// use the custom map to create the ZoneId
ZonedDateTime rezDate = dateAndTimeINeed.withZoneSameInstant(ZoneId.of(tzTo, map));
我选择了Europe/Berlin
,当然您可以将其更改为您需要的任何时区。您可以通过调用 ZoneId.getAvailableZoneIds()
.
获取可用时区列表(并选择最适合您的系统的时区)
使用上面的地图:
System.out.println(changeTZ("CEST", "UTC", "2017-08-10 14:23:58"));
此代码输出:
2017-08-10 12:23:58
请注意 CEST
中的 14:23(我选择 Europe/Berlin
)在 UTC 中是 12:23,这是正确的,因为在 8 月 柏林采用夏令时(偏移量为 +02:00
)。
ZoneId
和 ZonedDateTime
类 自动处理 DST 效果。您可以通过选择一月份的日期(当 DST 在柏林不生效时)来检查:
// January is not DST, so use CET
System.out.println(changeTZ("CET", "UTC", "2017-01-10 14:23:58"));
输出为:
2017-01-10 13:23:58
在一月柏林不在夏令时,所以偏移量是+01:00
,然后柏林的14:23变成UTC的13:23。
当然理想的是始终使用全名(如 Europe/Berlin
),但如果您无法控制输入,自定义地图是一种替代方法。
Java 8 也有一个 built-in predefined map,但与任何其他预定义的东西一样,选择是任意的,不一定是你需要的。
这里有一个 class 可以完成这项工作。
public class TimeZoneConverter {
private static String datePattern = "yyyy-MM-dd HH:mm:ss";
public static void main(String[] args) throws ParseException {
String sourceDate = "2017-02-27 16:00:00";
String resultDate = convertTimeZone("EET", "UTC", sourceDate);
System.out.println("EET: "+ sourceDate);
System.out.println("UTC: "+ resultDate);
}
public static String convertTimeZone(String timeZoneFrom, String timeZoneTo, String date) throws ParseException {
long timestamp = stringToTimestamp(date, timeZoneFrom);
String result = timestampToString(timestamp, timeZoneTo);
return result;
}
public static long stringToTimestamp(String time, String timeZone) throws ParseException {
DateFormat format = getDateFormat(timeZone);
return format.parse(time).getTime();
}
public static String timestampToString(long timestamp, String timeZone) {
DateFormat format = getDateFormat(timeZone);
return format.format(new Date(timestamp));
}
private static DateFormat getDateFormat(String timeZone) {
DateFormat format = new SimpleDateFormat(datePattern);
format.setTimeZone(TimeZone.getTimeZone(timeZone));
return format;
}
}
我知道有类似的问题,但我不太能找到与我的相似的例子。我了解了 LocalDateTime
和 ZonedDateTime
但我不知道如何告诉我的 ZonedDateTime
什么是解析日期的假定时区。
我正在从 Java 7 迁移到 Java 8。在我的代码中,我有一个这样的方法:
public String changeTZ(String tzFrom, String tzTo, String dateToChange) {
ZoneId zoneFrom = ZoneId.of(tzFrom);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormat.toPattern());
LocalDateTime localtDateAndTime = LocalDateTime.parse(dateToChange, formatter);
ZonedDateTime dateAndTimeINeed = ZonedDateTime.of(localtDateAndTime, zoneFrom );
ZonedDateTime rezDate = dateAndTimeINeed.withZoneSameInstant(ZoneId.of(tzTo));
return formatter.format(rezDate);
}
示例用法是:
String rez = changeTZ("CEST", "UTC", "2017-08-10 14:23:58");
如您所见,它以字符串形式接收日期时间,时区日期在 tzFrom
变量中,我需要 TZ(toTo
变量)。
在 Java 7 中执行此操作的代码既荒谬又复杂,因此我不会在此处列出。你能告诉我如何在不改变方法接口(参数和 return 类型)的情况下在 Java 8 中实现相同的目标吗?夏令时呢?是在Java8自动处理吗?
请注意,该方法还支持 "CEST"、"CET"、"UTC" 形式的时区以及 "Europe/London".
等标准时区此解决方案使用 Hugo 在评论中提到的 IANA 时区名称来获取 ZoneId
(更多详细信息 here)。如果将它与 CEST.
public static String changeTZ(String tzFrom, String tzTo, String dateToChange){
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.of(tzFrom));
ZonedDateTime zdt = ZonedDateTime.parse(dateToChange, dtf);
DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.of(tzTo));
return zdt.format(dtf2);
}
像这样使用:
String rez = changeTZ("US/Alaska", "Europe/Berlin", "2017-08-10 14:23:58");
Java 8 使用 IANA timezones names(格式总是 Region/City
,如 America/Sao_Paulo
或 Europe/Berlin
)。
避免使用短缩写(如 CEST
或 PST
),因为它们是 ambiguous and not standard.
实际上,这些名称不适用于 ZoneId
,主要是因为这种歧义(例如,CST 可以是 "Central Standard Time"、"Cuba Standard Time" 或 "China Standard Time") . 实际上,由于追溯兼容性的原因,其中一些可能会起作用,但不能保证对所有它们都起作用。
我假设 CEST 是 Central European Summer Time。目前有许多不同的国家(和时区)处于 CEST,因此 API 无法决定选择哪个时区,如果您只是将 "CEST" 传递给它。
那是因为一个时区包含一个地区在其历史上所有不同的偏移量。今天可能有很多国家/地区使用 CEST,但它们过去的历史不同(有些国家可能在不同年份使用 DST,或者使用不同的偏移量然后更改等),这就是为什么它们每个都有一个时区。
要使用这样的短名称(如 CEST
),您可以为每个名称定义一些默认值(这将是一个任意选择)并将这些选择放在一个映射中:
// map of custom zone names
Map<String, String> map = new HashMap<>();
// setting my arbitrary choices for each name
map.put("CEST", "Europe/Berlin"); // Berlin during DST period
map.put("CET", "Europe/Berlin"); // Berlin during non-DST period
// ... and so on
然后您可以使用此地图创建 ZoneId
:
// use the custom map to create the ZoneId
ZoneId zoneFrom = ZoneId.of(tzFrom, map);
...
// use the custom map to create the ZoneId
ZonedDateTime rezDate = dateAndTimeINeed.withZoneSameInstant(ZoneId.of(tzTo, map));
我选择了Europe/Berlin
,当然您可以将其更改为您需要的任何时区。您可以通过调用 ZoneId.getAvailableZoneIds()
.
使用上面的地图:
System.out.println(changeTZ("CEST", "UTC", "2017-08-10 14:23:58"));
此代码输出:
2017-08-10 12:23:58
请注意 CEST
中的 14:23(我选择 Europe/Berlin
)在 UTC 中是 12:23,这是正确的,因为在 8 月 柏林采用夏令时(偏移量为 +02:00
)。
ZoneId
和 ZonedDateTime
类 自动处理 DST 效果。您可以通过选择一月份的日期(当 DST 在柏林不生效时)来检查:
// January is not DST, so use CET
System.out.println(changeTZ("CET", "UTC", "2017-01-10 14:23:58"));
输出为:
2017-01-10 13:23:58
在一月柏林不在夏令时,所以偏移量是+01:00
,然后柏林的14:23变成UTC的13:23。
当然理想的是始终使用全名(如 Europe/Berlin
),但如果您无法控制输入,自定义地图是一种替代方法。
Java 8 也有一个 built-in predefined map,但与任何其他预定义的东西一样,选择是任意的,不一定是你需要的。
这里有一个 class 可以完成这项工作。
public class TimeZoneConverter {
private static String datePattern = "yyyy-MM-dd HH:mm:ss";
public static void main(String[] args) throws ParseException {
String sourceDate = "2017-02-27 16:00:00";
String resultDate = convertTimeZone("EET", "UTC", sourceDate);
System.out.println("EET: "+ sourceDate);
System.out.println("UTC: "+ resultDate);
}
public static String convertTimeZone(String timeZoneFrom, String timeZoneTo, String date) throws ParseException {
long timestamp = stringToTimestamp(date, timeZoneFrom);
String result = timestampToString(timestamp, timeZoneTo);
return result;
}
public static long stringToTimestamp(String time, String timeZone) throws ParseException {
DateFormat format = getDateFormat(timeZone);
return format.parse(time).getTime();
}
public static String timestampToString(long timestamp, String timeZone) {
DateFormat format = getDateFormat(timeZone);
return format.format(new Date(timestamp));
}
private static DateFormat getDateFormat(String timeZone) {
DateFormat format = new SimpleDateFormat(datePattern);
format.setTimeZone(TimeZone.getTimeZone(timeZone));
return format;
}
}