从 CST 到 GMT 的日期格式转换不起作用
Date format conversion from CST to GMT is not working
我有一个 CST(24 小时)日期字符串,我想将其转换为 GMT(12 小时)。
我有一个如下所示的 java 方法,当我的系统时间是加尔各答时间时,它可以正常工作。 (我所在的系统运行 java 方法)
但是当我的系统是上海时间时,格林威治标准时间不正确。
String inputDate = "01-19-2017 06:01 CST";
SimpleDateFormat inputFormatter = new SimpleDateFormat("MM-dd-yyyy hh:mm Z");
parsedInput = inputFormatter.parse(inputDate);
// parsedInput -> Tue Jan 19 06:01:00 CST 2017 -> When system time is Shanghai time
// parsedInput -> Thu Jan 19 17:31:00 IST 2017 -> When system time is Kolkata time
SimpleDateFormat formatter = new SimpleDateFormat("MM-dd-yyyy hh:mm a Z");
TimeZone gmt = TimeZone.getTimeZone("GMT");
formatter.setTimeZone(gmt);
String formattedDate = formatter.format(parsedInput);
// formattedDate -> 01-18-2017 10:01 PM +0000 -> When system time is Shanghai time (Incorrect)
// formattedDate -> 01-19-2017 12:01 PM +0000 -> When system time is Kolkata time
也尝试将 TimeZone
设置为 inputFormatter
,例如:
SimpleDateFormat inputFormatter = new SimpleDateFormat("MM-dd-yyyy hh:mm Z");
inputFormatter.setTimeZone(TimeZone.getTimeZone("GMT"));
如果您不指定时区,它会考虑默认值,在您的情况下为 Asia/Shanghai
,因此结果不正确。
您可以通过将上述代码段中的 GMT
替换为 Asia/Shanghai
来在当前系统中重现它
避开伪时区
CST
等 3-4 个字母的时区缩写 不是 实际时区。它们不是标准化的。它们甚至都不是独一无二的!
你的CST
可能是“中国标准时间”,也可能是美洲的“中部标准时间”,也可能是其他时间。 无法知道是哪一个。
另一个例子:IST
可能表示“印度标准时间”或“爱尔兰标准时间”或其他。
可靠地破译这些伪区是不可能的。 Joda-Time 库有一个明智的策略,就是拒绝尝试。不幸的是 java.time 类 在解析时进行了猜测,但结果可能不是您期望的区域。
所以永远不要使用这些无用的伪区域。使用 continent/region
格式的 proper time zone name,例如 America/Chicago
、Asia/Kolkata
、Pacific/Auckland
.
避免遗留日期时间类
您正在使用麻烦的旧日期时间 类,现在已成为遗留问题,已被 java.time 类.
取代
解决方法
如果您知道您的所有输入都针对同一时区,则去掉伪时区并作为 UTC 时间的 LocalDateTime
, apply the intended zone as a ZoneId
to produce a ZonedDateDate
. From that you can extract an Instant
进行处理。
String input = "01-19-2017 06:01".replace( " " , "T" ) ; // Insert a “T” to comply with standard ISO 8601 format used by default in java.time.
LocalDateTime ldt = LocalDateTime.parse( input ); // Lacks any concept of time zone or offset-from-UTC. So *not* an actual moment on the timeline.
ZoneId z = ZoneId.of( "America/Chicago" ); // Assuming “CST” meant this zone in North America.
ZonedDateTime zdt = ldt.atZone( z ); // Assign a time zone to determine an actual moment on the timeline.
Instant instant = zdt.toInstant(); // Extract a value in UTC.
您可以选择通过印度挂钟时间的镜头看到同一时刻。
ZonedDateTime zdtKolkata = zdt.withZoneSameInstant( ZoneId.of( "Asia/Kolkata" ) ) ;
从不依赖默认区域
您的 JVM 当前默认时区可以随时通过在该 JVM 中执行的任何应用程序的任何线程中的任何代码更改。由于这随时可能发生变化,因此您不能依赖某个值。
当您省略时区的可选参数时,日期时间 类 隐式地应用当前默认时区。所以解决方案很简单:始终指定 expected/desired 时区。
请注意上面的代码示例是如何在每次机会时指定区域的。
(同上 Locale
,顺便说一句。明确指定而不是依赖当前默认值。)
你的问题是CST的歧义。在大多数情况下,SimpleDateFormat
将 CST 理解为您所期望的中央标准时间。但是当你的电脑是运行上海时间的时候,这就变成了格式器日历的时区,然后突然把CST理解为中国标准时间,和上海时间一样。
因此 ,将 inputFormatter
的时区设置为中国标准时间以外的其他时间,在我的电脑上有效。我不知道为什么它对你的不起作用(我将其设置为 TimeZone.getTimeZone("America/Chicago")
以匹配 CST 的预期解释)。
不过,正确且好的解决方案是完全避免使用三字母和四字母时区缩写。你的只是他们造成麻烦的众多例子中的一个。如果可以,请为您的输入字符串提供一个与 UTC 的显式时区偏移量,这样绝不会含糊不清:
String inputDate = "01-19-2017 06:01 -0600";
现在,无论计算机的时区设置如何,您的代码都能按预期工作。您已有的模式字符串中的 Z
与 -0600
匹配得很好。
综上所述,如果您可以使用 Java 8 日期和时间 类,您可以帮自己一个忙,切换到它们。我犹豫着称它们为“Java 8 日期和时间 类”,因为它们也被移植到 Java 6 和 7,所以如果你可以忍受库依赖,直到你到达Java8、你应该有机会。较新的 类 对程序员更友好,使用起来也更方便。一个让您入门的简单示例:
String inputDate = "01-19-2017 06:01 -0600";
DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("MM-dd-yyyy HH:mm Z");
ZonedDateTime parsedInput = ZonedDateTime.parse(inputDate, inputFormatter);
OffsetDateTime gmtTime = parsedInput.toOffsetDateTime().withOffsetSameInstant(ZoneOffset.UTC);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM-dd-yyyy hh:mm a Z");
String formattedDate = gmtTime.format(formatter);
System.out.println(formattedDate); // prints 01-19-2017 12:01 PM +0000
我有一个 CST(24 小时)日期字符串,我想将其转换为 GMT(12 小时)。 我有一个如下所示的 java 方法,当我的系统时间是加尔各答时间时,它可以正常工作。 (我所在的系统运行 java 方法) 但是当我的系统是上海时间时,格林威治标准时间不正确。
String inputDate = "01-19-2017 06:01 CST";
SimpleDateFormat inputFormatter = new SimpleDateFormat("MM-dd-yyyy hh:mm Z");
parsedInput = inputFormatter.parse(inputDate);
// parsedInput -> Tue Jan 19 06:01:00 CST 2017 -> When system time is Shanghai time
// parsedInput -> Thu Jan 19 17:31:00 IST 2017 -> When system time is Kolkata time
SimpleDateFormat formatter = new SimpleDateFormat("MM-dd-yyyy hh:mm a Z");
TimeZone gmt = TimeZone.getTimeZone("GMT");
formatter.setTimeZone(gmt);
String formattedDate = formatter.format(parsedInput);
// formattedDate -> 01-18-2017 10:01 PM +0000 -> When system time is Shanghai time (Incorrect)
// formattedDate -> 01-19-2017 12:01 PM +0000 -> When system time is Kolkata time
也尝试将 TimeZone
设置为 inputFormatter
,例如:
SimpleDateFormat inputFormatter = new SimpleDateFormat("MM-dd-yyyy hh:mm Z");
inputFormatter.setTimeZone(TimeZone.getTimeZone("GMT"));
如果您不指定时区,它会考虑默认值,在您的情况下为 Asia/Shanghai
,因此结果不正确。
您可以通过将上述代码段中的 GMT
替换为 Asia/Shanghai
来在当前系统中重现它
避开伪时区
CST
等 3-4 个字母的时区缩写 不是 实际时区。它们不是标准化的。它们甚至都不是独一无二的!
你的CST
可能是“中国标准时间”,也可能是美洲的“中部标准时间”,也可能是其他时间。 无法知道是哪一个。
另一个例子:IST
可能表示“印度标准时间”或“爱尔兰标准时间”或其他。
可靠地破译这些伪区是不可能的。 Joda-Time 库有一个明智的策略,就是拒绝尝试。不幸的是 java.time 类 在解析时进行了猜测,但结果可能不是您期望的区域。
所以永远不要使用这些无用的伪区域。使用 continent/region
格式的 proper time zone name,例如 America/Chicago
、Asia/Kolkata
、Pacific/Auckland
.
避免遗留日期时间类
您正在使用麻烦的旧日期时间 类,现在已成为遗留问题,已被 java.time 类.
取代解决方法
如果您知道您的所有输入都针对同一时区,则去掉伪时区并作为 UTC 时间的 LocalDateTime
, apply the intended zone as a ZoneId
to produce a ZonedDateDate
. From that you can extract an Instant
进行处理。
String input = "01-19-2017 06:01".replace( " " , "T" ) ; // Insert a “T” to comply with standard ISO 8601 format used by default in java.time.
LocalDateTime ldt = LocalDateTime.parse( input ); // Lacks any concept of time zone or offset-from-UTC. So *not* an actual moment on the timeline.
ZoneId z = ZoneId.of( "America/Chicago" ); // Assuming “CST” meant this zone in North America.
ZonedDateTime zdt = ldt.atZone( z ); // Assign a time zone to determine an actual moment on the timeline.
Instant instant = zdt.toInstant(); // Extract a value in UTC.
您可以选择通过印度挂钟时间的镜头看到同一时刻。
ZonedDateTime zdtKolkata = zdt.withZoneSameInstant( ZoneId.of( "Asia/Kolkata" ) ) ;
从不依赖默认区域
您的 JVM 当前默认时区可以随时通过在该 JVM 中执行的任何应用程序的任何线程中的任何代码更改。由于这随时可能发生变化,因此您不能依赖某个值。
当您省略时区的可选参数时,日期时间 类 隐式地应用当前默认时区。所以解决方案很简单:始终指定 expected/desired 时区。
请注意上面的代码示例是如何在每次机会时指定区域的。
(同上 Locale
,顺便说一句。明确指定而不是依赖当前默认值。)
你的问题是CST的歧义。在大多数情况下,SimpleDateFormat
将 CST 理解为您所期望的中央标准时间。但是当你的电脑是运行上海时间的时候,这就变成了格式器日历的时区,然后突然把CST理解为中国标准时间,和上海时间一样。
因此 inputFormatter
的时区设置为中国标准时间以外的其他时间,在我的电脑上有效。我不知道为什么它对你的不起作用(我将其设置为 TimeZone.getTimeZone("America/Chicago")
以匹配 CST 的预期解释)。
不过,正确且好的解决方案是完全避免使用三字母和四字母时区缩写。你的只是他们造成麻烦的众多例子中的一个。如果可以,请为您的输入字符串提供一个与 UTC 的显式时区偏移量,这样绝不会含糊不清:
String inputDate = "01-19-2017 06:01 -0600";
现在,无论计算机的时区设置如何,您的代码都能按预期工作。您已有的模式字符串中的 Z
与 -0600
匹配得很好。
综上所述,如果您可以使用 Java 8 日期和时间 类,您可以帮自己一个忙,切换到它们。我犹豫着称它们为“Java 8 日期和时间 类”,因为它们也被移植到 Java 6 和 7,所以如果你可以忍受库依赖,直到你到达Java8、你应该有机会。较新的 类 对程序员更友好,使用起来也更方便。一个让您入门的简单示例:
String inputDate = "01-19-2017 06:01 -0600";
DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("MM-dd-yyyy HH:mm Z");
ZonedDateTime parsedInput = ZonedDateTime.parse(inputDate, inputFormatter);
OffsetDateTime gmtTime = parsedInput.toOffsetDateTime().withOffsetSameInstant(ZoneOffset.UTC);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM-dd-yyyy hh:mm a Z");
String formattedDate = gmtTime.format(formatter);
System.out.println(formattedDate); // prints 01-19-2017 12:01 PM +0000