SimpleDateFormat - 如何防止重新格式化时区

SimpleDateFormat - how to prevent timezone being reformatted

当我解析 String 然后格式化生成的 Date.

时,我遇到 SimpleDateFormat 更改 datetimestamp 外观的问题

下面的代码片段演示了这个问题。注意:我知道用相同的 SimpleDateFormat 解析然后格式化相同的日期似乎是徒劳的,但这是一个人为的例子,只是为了说明原理:

DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
String dateStringToConvert = "2016-03-12T22:00:00.000-00:00";
try {
    Date date = dateFormat.parse(dateStringToConvert);
    String convertedDateString = dateFormat.format(date);
    System.out.println("Wanted : " + dateStringToConvert);
    System.out.println("Actual : " + convertedDateString);
} catch (ParseException e) {
    e.printStackTrace();
}

输出结果如下:

Wanted : 2016-03-12T22:00:00.000-00:00

Actual : 2016-03-12T22:00:00.000Z

由于此日期将用于自动测试以填写表格,因此 datetimestamp 的格式在被 [=11= 解析和格式化后保持完全相同很重要], 所以我不希望它删除 -00:00 并添加 Z.

这似乎是一个非常简单的问题,但我找不到任何明显的答案。

试试这个:

String pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
SimpleDateFormat df = new SimpleDateFormat(pattern) {
    public StringBuffer format(Date date, StringBuffer prefix, FieldPosition fieldPosition) {
        StringBuffer finalStr = super.format(date, prefix, fieldPosition);
        finalStr = finalStr.insert(finalStr.length()-2, ':');
        finalStr = finalStr.deleteCharAt(finalStr.length() - 6);
        finalStr = finalStr.insert(finalStr.length() - 5, '-');
        return finalStr;
    };
};
System.out.println(df.format(yourDate));

正如 Jon Skeet 在 中所说,没有办法绕过硬编码这一特殊要求。

    DateTimeFormatter formatter;
    String dateStringToConvert = "2016-03-12T22:00:00.000-00:00";
    if (dateStringToConvert.endsWith("-00:00")) {
        formatter = new DateTimeFormatterBuilder()
                .appendPattern("uuuu-MM-dd'T'HH:mm:ss.SSS'-00:00'")
                .parseDefaulting(ChronoField.OFFSET_SECONDS, 0)
                .toFormatter();
    } else {
        formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSxxx");
    }
    OffsetDateTime dateTime = OffsetDateTime.parse(dateStringToConvert, formatter);
    String convertedDateString = dateTime.format(formatter);
    System.out.println("Wanted : " + dateStringToConvert);
    System.out.println("Actual : " + convertedDateString);

这会打印

Wanted : 2016-03-12T22:00:00.000-00:00
Actual : 2016-03-12T22:00:00.000-00:00

正如已经说过的,-00:00 的偏移量(负零)不等同于零偏移量,因此将字符串解析为 [=13 确实是不正确的=] 和我一样。在这种情况下,代码的正确版本将解析为 LocalDateTime 并使用相同的格式化程序格式化 LocalDateTime 。它总体上会更长一些,另一方面,您将不再需要 parseDefaulting 调用。

我正在使用并热烈推荐 java.time,现代 Java 日期和时间 API。因为 DateDateFormatSimpleDateFormat 早已过时,并且已被证明在不同程度上使用起来很麻烦。现代的 API 好多了。

Link: Oracle tutorial: Date Time 解释如何使用 java.time

恐怕SimpleDateFormat帮不了你。这个class设计的很差,而且非常有限,更不用说它存在的所有问题了:https://eyalsch.wordpress.com/2009/05/29/sdf/

如果你有Java8,就用java.timeAPI。不确定为什么要将 String 转换为 Date 只是为了将其转换回另一个 String,但是如果您希望最终结果为 String-00:00作为偏移量,那么你可以这样做:

DateTimeFormatter fmt = new DateTimeFormatterBuilder()
  .appendPattern("yyyy-MM-dd'T'HH:mm:ss.SSS")
  // offset, use "-00:00" when it's zero
  .appendOffset("+HH:MM", "-00:00")
  // create formatter, always work in UTC
  .toFormatter().withZone(ZoneOffset.UTC);
String dateStringToConvert = "2016-03-12T22:00:00.000-00:00";
Instant instant = fmt.parse(dateStringToConvert, Instant::from);
String result = fmt.format(instant);
System.out.println(result);

这将打印:

2016-03-12T22:00:00.000-00:00