提问人:Joe 提问时间:9/20/2014 更新时间:9/20/2014 访问量:1384
Java 日期转换 - UTC 到本地 - 工作方式因时区而异
Java Date Conversion - UTC to Local - works differently depending on the timezone
问:
我在将字符串转换为 UTC 数据,然后转换为各种时区时遇到问题。我的程序似乎根据我是转换为 EST 还是 PST 而有所不同。这是我的代码:
SimpleDateFormat utcFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
utcFormat.setTimeZone(java.util.TimeZone.getTimeZone("UTC"));
Date date = utcFormat.parse("2014-08-18 17:00:17");
SimpleDateFormat localFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
localFormat.setTimeZone(java.util.TimeZone.getTimeZone("PST"));
System.out.println(localFormat.format(date));
如果我运行上面的代码,这是我的输出:
2014-08-18 10:00:17
这反映了与提供的 UTC 时间 2014-08-18 17:00:17 的 7 小时偏移量。这是我所期望的。现在,如果我将该日期更改为 2014-11-18 17:00:17(将月份从 8 月更改为 11 月),则生成的输出如下:
2014-11-18 09:00:17
据我所知,这也很好。输出反映了 UTC 的 8 小时偏移量,我相信这是因为 11 月不是夏令时,而 8 月是夏令时。
我遇到的问题是,如果我将时区从“PST”更改为“EST”,上面的相同代码的工作方式会有所不同。当我更改为 EST 时,无论我的日期是在 8 月还是 11 月,我都会获得相同的时间输出。
这是使用 EST 和 2014-08-18 17:00:17 的输出
2014-08-18 12:00:17
这是使用 EST 和 2014-11-18 17:00:17 的输出
2014-11-18 12:00:17
在这两种情况下,输出都表示与 UTC 的 5 小时偏移量,这仅在 11 月有意义,而在 8 月没有意义。
谁能向我解释我做错了什么?
答:
那是因为在保存之外,它的偏移是恒定的,并且它是夏令时的补充区域。EST
ET
EDT
因此,您应该使用来获取预期的行为。
更多维基百科ET
您应该使用 or(这些是别名),而不是使用 。三个字母的时区缩写是模棱两可的,你无法确定你得到了什么。EST
America/New_York
US/Eastern
评论
America/New_York
US/Eastern
Continent/CityOrRegion
从 TimeZone 的文档
为了与 JDK 1.1.x 兼容,还支持其他一些三个字母的时区 ID(例如“PST”、“CTT”、“AST”)。但是,它们已被弃用,因为相同的缩写通常用于多个时区(例如,“CST”可能是美国“中部标准时间”和“中国标准时间”),然后 Java 平台只能识别其中一个时区。
而不是 ,会更清楚地了解您的意图。"EST"
"US/Eastern"
这些是受支持的 US 别名。
- 美国/阿拉斯加
- 美国/阿留申群岛
- 美国/亚利桑那州
- 美国/中部
- 美国/东印第安纳州
- 美国/东部
- 美国/夏威夷
- 美国/印第安纳-斯塔克
- 美国/密歇根州
- 美国/山地
- 美国/太平洋地区
- 美国/太平洋-新
- 美国/萨摩亚
@Compass是对的。 以下是您将使用的代码:
public static void main(String[] args) {
SimpleDateFormat utcFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
utcFormat.setTimeZone(java.util.TimeZone.getTimeZone("UTC"));
Date date = null;
try {
date = utcFormat.parse("2014-08-18 17:00:17");
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
SimpleDateFormat localFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
localFormat.setTimeZone(java.util.TimeZone.getTimeZone("US/Eastern"));
System.out.println(localFormat.format(date));
}
戴夫·莫里西(Dave Morrissey)的答案是正确的。
谁能向我解释我做错了什么?
是的。您正在使用一个可怕且令人困惑的日期时间库。
避免java.util.Date
java.util.Date 和 .日历类是出了名的麻烦,在设计和实现上都存在缺陷。使用一个像样的库。在 Java 中,这意味着 Joda-Time 或 Java 8 中的新 java.time 包(受 JSR 310 定义的 Joda-Time 的启发)。
时区
虽然 j.u.Date 没有时区,但在 Joda-Time 和 java.time 中,date-time 对象确实知道自己分配的时区。使这项工作变得更加容易和明智。
时区名称
使用正确的时区名称。避免使用 2、3 或 4 个字母的代码,因为它们既不是标准化的,也不是唯一的。这些专有名称中的大多数是 .Continent/CityOrRegion
夏令时
您不必担心夏令时。让日期时间库在那里完成繁重的工作。您需要做的就是确保您的图书馆使用的是新版本的时区数据库。政客们喜欢重新定义 DST。
国际标准化组织 8601
Joda-Time 和 java.time 都支持 ISO 8601 格式作为其解析和生成日期时间值的字符串表示形式的默认值。
Joda-Time 示例
以下是 Joda-Time 2.4 中的一些示例代码。此示例中的所有 DateTime 对象都表示宇宙历史上的同一同时时刻,但经过调整以显示每个位置的人所看到的挂钟时间。
String inputRaw = "2014-08-18 17:00:17"; // Nearly in [ISO 8601][7] format.
String input = inputRaw.replace( " ", "T" );
DateTime dateTimeUtc = DateTime.parse( input, DateTimeZone.UTC );
DateTime dateTimeLosAngeles = dateTimeUtc.withZone( DateTimeZone.forID( "America/Los_Angeles" ) );
DateTime dateTimeNewYork = dateTimeUtc.withZone( DateTimeZone.forID( "America/New_York" ) );
DateTime dateTimeMontréal = dateTimeUtc.withZone( DateTimeZone.forID( "America/Montreal" ) );
DateTime dateTimeKolkata = dateTimeUtc.withZone( DateTimeZone.forID( "Asia/Kolkata" ) );
评论