【问题标题】:Inconsistent `w` symbol formatting from joda time to java.time从 joda 时间到 java.time 的“w”符号格式不一致
【发布时间】:2016-02-12 16:31:52
【问题描述】:

我的团队希望从 Joda time 切换到 java.time,但我们发现使用相同模式进行格式化时会出现不同的行为。当我们使用星期几w 符号时,就会出现问题:

final String dateString = "2016-01-04 00:00:00";
final String inputPattern = "yyyy-MM-dd HH:mm:ss";

// parse the input string using Joda
final org.joda.time.format.DateTimeFormatter jodaInputFormatter = org.joda.time.format.DateTimeFormat.forPattern(inputPattern);
final org.joda.time.DateTime jodaDateTime = jodaInputFormatter.parseDateTime(dateString);

// parse the input string using two different java.time classes
final java.time.format.DateTimeFormatter javaTimeInputFormatter = java.time.format.DateTimeFormatter.ofPattern(inputPattern).withZone(java.time.ZoneOffset.UTC);
final java.time.LocalDateTime localDateTime = java.time.LocalDateTime.parse(dateString, javaTimeInputFormatter);
final java.time.ZonedDateTime zonedDateTime = java.time.ZonedDateTime.parse(dateString, javaTimeInputFormatter);

final String outputPattern = "'week' w - dd/MM/yyyy HH:mm:ss";
final org.joda.time.format.DateTimeFormatter jodaOutputFormatter = org.joda.time.format.DateTimeFormat.forPattern(outputPattern);
final java.time.format.DateTimeFormatter javaTimeOutputFormatter = java.time.format.DateTimeFormatter.ofPattern(outputPattern);

// output: week 1 - 04/01/2016 00:00:00
System.out.println("With joda: " + jodaOutputFormatter.print(jodaDateTime));
// output: week 2 - 04/01/2016 00:00:00
System.out.println("With LocalDateTime: " + javaTimeOutputFormatter.format(localDateTime));
// output: week 2 - 04/01/2016 00:00:00
System.out.println("With ZonedDateTime: " + javaTimeOutputFormatter.format(zonedDateTime));

由于某种原因,w 符号的输出在两个实现中相差一倍。

是什么导致了这种不一致? w 符号是否在 Joda 时间和 java.time 之间不一致地实现?

【问题讨论】:

  • 您的默认系统区域设置是什么?
  • 我位于美国东部标准时间 (UTC−05:00),但由于.withZone(java.time.ZoneOffset.UTC) 呼叫不应该不相关吗?
  • 我不想知道您的系统时区,而是您的系统区域设置!此类周字段取决于区域设置。

标签: java jodatime date-format date-formatting java-time


【解决方案1】:

嗯,这有点投机,但既然你告诉我你的系统时区是 EST (-05:00),我假设你坐在美国(纽约?)。美国不适用 ISO-8601 周规则。星期从星期日开始,一年中的第一周不需要至少包含 4 天(即使一天也足以算作一年的第一周)。

让我们看一下您的示例日期,即 1 月 4 日。这是一个星期一。第一个美国周是从 2016 年 1 月 1 日到 2016 年 1 月 2 日(2 天 - 对美国来说足够了)。美国的第二周从 1 月 3 日星期日开始,所以 1 月 4 日也是第二周。

现在是关键点:java.time (JSR-310) 使用基于周的本地化周作为模式符号 w,另见其backport which should have the same code。代码摘录:

} else if (cur == 'w') {
    if (count > 2) {
        throw new IllegalArgumentException("Too many pattern letters: " + cur);
    }
    appendInternal(new WeekFieldsPrinterParser('w', count));

...

static final class WeekFieldsPrinterParser implements DateTimePrinterParser {
    private final char letter;
    private final int count;

    public WeekFieldsPrinterParser(char letter, int count) {
        this.letter = letter;
        this.count = count;
    }

    @Override
    public boolean print(DateTimePrintContext context, StringBuilder buf) {
        WeekFields weekFields = WeekFields.of(context.getLocale());
        DateTimePrinterParser pp = evaluate(weekFields);
        return pp.print(context, buf);
    }

WeekFields.of(context.getLocale()) 用于模式符号“w”是显而易见的。

相比之下,Joda-Time 仅使用 ISO-8601-week-definition,它让周从星期一开始,并将该周计为一年中的第一周,其中包含当前日历年中至少四天.因此,1 月 4 日星期一是一年中第一周的开始,因为前三天不足以将 ISO-8601 算作一周。前几天被视为上一年的最后一周。

因此,Joda-Time 显示 1 月 4 日的第 1 周,而 java.time 使用美国第 2 周。

您的问题的解决方案是指定语言环境,这样格式化程序将使用 ISO 周,这样您就可以获得与 Joda-Time 相同的结果。例如,您可以选择Locale.UK,它也使用英语但其他周规则。 不要依赖你的默认语言环境。这会欺骗你。

【讨论】:

    【解决方案2】:

    编辑:正如 Richard 指出的那样,我错了 — Java SE actually does say 以一周为基础的一年的第一周是第一个以星期一为基础的一周,其中至少包含四天,就像 Joda-Time。

    来自 Java SE 的 IsoFields.WEEK_OF_WEEK_BASED_YEAR 的文档:

    基于周的年份的值介于 1 到 52 之间,如果基于周的年份有 53 周,则值为 53。

    没有提到排除任何周,因此假设所有周都被计算在内是有意义的。

    来自Joda-Time Fields overview

    在以一周为基础的年份中,周数从 1 到 52-53。一周的第一天定义为星期一,并给出值 1。一年的第一周定义为一年中至少有四天的第一周。

    1 月 1 日和 2 日是 2016 年的第一周,由于不到四天,Joda-Time 根本不将其算作一周。 1 月 4 日是包含四天或更多天的第一周。

    【讨论】:

    • 在“基于周的年”下,您链接到的 Java 8 文档页面显示“基于周的年的第一周是标准 ISO 年的第一个基于星期一的周,该周至少具有新年 4 天”,这似乎与您在 Joda 文档中的引用一致。
    猜你喜欢
    • 2018-07-10
    • 2013-04-02
    • 1970-01-01
    • 2021-07-03
    • 1970-01-01
    • 1970-01-01
    • 2016-08-17
    • 1970-01-01
    • 2014-07-07
    相关资源
    最近更新 更多