从CST到GMT的日期格式转换无效

时间:2017-04-20 15:08:56

标签: java date timezone simpledateformat

我在CST(24小时)有一个日期字符串,我想将其转换为GMT(12小时)。 我有一个像下面这样的java方法,当我的系统时间是加尔各答时,它工作正常。 (我运行java方法的系统) 但是当我的系统在上海时,GMT时间不正确。

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

3 个答案:

答案 0 :(得分:4)

避免伪时区

3-4个字母的时区缩写,例如CST不是实际时区。它们不是标准化的。它们甚至都不是唯一的!

您的CST可能是“中国标准时间”,也可能是美洲的“中央标准时间”,或者可能是其他内容。 无法知道哪一个。

另一个例子:IST可能意味着“印度标准时间”或“爱尔兰标准时间”或其他。

无法可靠地解密这些伪区域。 Joda-Time图书馆有一个拒绝尝试的明智政策。不幸的是,java.time类在解析时会猜测,但结果可能不是您期望的区域。

所以永远不要使用这些无用的伪区域。使用continent/region格式的proper time zone name,例如America/ChicagoAsia/KolkataPacific/Auckland

避免遗留日期时间类

你正在使用现在遗留下来的麻烦的旧日期时间类,取而代之的是java.time类。

解决方法

如果您知道所有输入都是针对同一时区,那么请关闭伪区域并以LocalDateTime处理,将预期区域应用为ZoneId以生成{{ 3}}。从那里你可以为UTC时间提取ZonedDateDate

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中执行的任何应用程序的任何线程中的任何代码进行更改。因为这可以随时改变,你不能依赖某个值。

如果省略时区的可选参数,则日期时间类会隐式静默应用当前的默认时区。所以解决方案很简单:始终指定预期/期望的时区

请注意上面的代码示例如何指定每个机会的区域。

(顺便提一下Instant。明确指定而不是依赖当前默认值。)

答案 1 :(得分:2)

你的问题是CST的含糊不清。在大多数情况下,SimpleDateFormat将CST理解为您预期的中央标准时间。但是当你的计算机运行上海时间时,这就成了格式化程序日历的时区,然后它突然将CST理解为中国标准时间,与上海时间一样。

因此Darshan Mehta’s solutioninputFormatter的时区设置为中国标准时间以外的其他时区,可以在我的计算机上运行。我不知道为什么它对你不起作用(我将它设置为TimeZone.getTimeZone("America/Chicago")以匹配CST的预期解释)。

正确而好的解决方案是完全避免使用三个和四个字母的时区缩写。在许多他们造成麻烦的地方,你的只是其中一个例子。如果可以,请为您的输入字符串提供与UTC明确的区域偏移量,这绝不是不明确的:

    String inputDate = "01-19-2017 06:01 -0600";

现在,无论计算机的时区设置如何,您的代码都能正常运行。您已经拥有的模式字符串中的Z很好地匹配-0600

所有这些都说,如果你可以使用Java 8日期和时间类,你可以帮自己切换到它们。我毫不犹豫地将它们称为“Java 8日期和时间类”,因为它们也被反向移植到Java 6和7,所以如果你能够使用库依赖项直到Java 8,那么你应该有机会。较新的类对程序员更友好,使用起来更方便。帮助您入门的一个简单示例:

    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

答案 2 :(得分:0)

尝试将TimeZone设置为inputFormatter,例如:

SimpleDateFormat inputFormatter = new SimpleDateFormat("MM-dd-yyyy hh:mm Z");
inputFormatter.setTimeZone(TimeZone.getTimeZone("GMT"));

如果您没有指定时区,则会考虑默认值,在您的情况下为Asia/Shanghai,因此结果不正确。

您可以在当前系统中将GMT替换为上述代码段中的Asia/Shanghai来重现