通过 DateTimeFormatter 将时区传递给 DateTimeFormatterBuilder 时,时区是否丢失?

Are time zones lost when passing them via DateTimeFormatter to DateTimeFormatterBuilder?

我需要使用假设特定时区的几种日期格式来解析日期,然后将其输出到另一个时区(即 UTC)。我希望 DateTimeFormatterBuilder 可以提供帮助,但似乎附加到解析器和打印机的时区信息是 lost/ignored?

查看测试和输出:

import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.DateTimeFormatterBuilder;
import org.joda.time.format.DateTimeParser;
import org.junit.Test;

public class JodaBuilderTest {


@Test
public void aTestOfTimeZoneRetentionOfFormattersPassedToBuilder() throws    Exception {     

    String [] parsePatterns = new String [] { "MM/dd/yyyy HH:mm:ss", "MM/dd/yyyy" };
     String printPattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZZ";
     String inputString = "08/03/2015 01:01:01";

    DateTimeZone tz = DateTimeZone.forOffsetHours(-3);
    DateTimeParser[] parsers = new DateTimeParser[parsePatterns.length];
    for (int index = 0; index < parsePatterns.length; index++) {

        parsers[index] = DateTimeFormat.forPattern(parsePatterns[index]).withZone(tz).getParser();
    }
    DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder()
      .append(DateTimeFormat.forPattern(printPattern).withZoneUTC().getPrinter(), parsers);
    DateTimeFormatter parser = builder.toFormatter();

    DateTimeZone parserdtz = parser.getZone();
    System.out.println("parserdtz: " + parserdtz);
    DateTime dt = parser.parseDateTime(inputString);
    System.out.println("parser zone is " + dt.getZone());
    String dtout = dt.toString();
    System.out.println("expecting UTC-3: 2015-08-03T01:01:01.000-03:00");
    System.out.println("got:\t\t" + dtout);
    String dtoutFmt = dt.toString(parser);
    System.out.println("expecting UTC: 2015-08-03T04:01:01.000-00:00");
    System.out.println("got:\t\t" + dtoutFmt);
    org.junit.Assert.assertEquals("2015-08-03T04:01:01.000-00:00", dtoutFmt);

}
}

输出如下

parserdtz: null
parser zone is America/New_York
expecting UTC-3: 2015-08-03T01:01:01.000-03:00
got:        2015-08-03T01:01:01.000-04:00
expecting UTC: 2015-08-03T04:01:01.000-00:00
got:        2015-08-03T01:01:01.000-04:00

所以它似乎忽略了解析器上设置的 UTC-3 和打印机上设置的 UTC,并默认为系统区域 (EDT)。 我错过了什么或者这是预期的行为吗?我唯一的办法是不使用生成器吗?我也无法在 parser 上设置区域,即 DateTimeFormatter parser = builder.toFormatter().withZone(DateTimeZone.forOffsetHours(-3)); 因为那会覆盖打印机。我想我可以为打印机创建一个单独的格式化程序,但是构建器的打印机参数的目的是什么?我应该将其设置为空吗?感觉不对。

这里有一些说明:

DateTimeParser and DateTimePrinter instances parse/print the DateTime according to a certain formatting pattern. However, the time zone and the locale are not a part of the printer/parser instance. They are provided during runtime, on a case-by-case basis. See the signature of DateTimePrinter.printTo:

void printTo(StringBuffer buf,
             long instant,
             Chronology chrono,
             int displayOffset,
             DateTimeZone displayZone, // zone provided at runtime
             Locale locale)

至于打印机-它可以读取任何时区,所以配置一个没有关系。

记住 DateTimePrinter/Parser javadocs 中的片段:

Internal interface for creating/parsing textual representations of datetimes.

因此,printer/parser 本身并没有在内部设置时区。您可以删除 printers/parsers 上的 withZone(tz)/withZoneUTC() 配置,因为它们无论如何都不会改变任何东西。

不过,DateTimeFormatter有了。这是 top-level API 您在处理时区时应该使用的。如果您在格式化程序上设置时区 X,它将能够读取任何时区字符串,但创建的 DateTime 实例将转换为该时区 X。同样,当打印任何 DateTime 实例(在任何时区)时,它将打印为转换为时区 X。如果您确实试图将字符串专门解析为 -03:00 时区,则需要一个 DateTimeFormatter 实例,当您想以 UTC 打印它时,您需要一个不同的格式化程序实例。

因此,有了您原来的 formatter(从您的 parser 重命名),您可以

DateTime dt = formatter.withZone(DateTimeZone.forID("-03:00")).parseDateTime(inputString);

并用于打印:

String dtoutFmt = dt.toString(formatter.withZoneUTC());

我相信如果您为变量使用相关的名称会更清楚一些 - 使用 formatter 作为 DateTimeFormatter 实例以避免与实际的 DateTimeParser 混淆]s.