java 带有 ISO 参数的 DateTimeFormat 不能正确解析时区

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/37110016/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 02:13:03  来源:igfitidea点击:

DateTimeFormat with ISO parameter not parsing timezone correctly

javaspringdatetimedatetime-format

提问by Mathijs Segers

I have this controller which I'm trying to test using mockMVC

我有这个控制器,我正在尝试使用 mockMVC 进行测试

@RequestMapping(value = "/something/{language}", method = RequestMethod.GET, produces = { "application/json", "application/xml" })
    public ResponseEntity<someEntity> getInfo( 
            @PathVariable String language, 
            @DateTimeFormat(iso= DateTimeFormat.ISO.DATE_TIME) @RequestParam(required = false) Date fromDate
    )

So I'm expecting to allow date formats like in docs to be parsable: DATE_TIME The most common ISO DateTime Format yyyy-MM-dd'T'HH:mm:ss.SSSZ, e.g.

所以我希望允许像文档中的日期格式可以解析:DATE_TIME 最常见的 ISO 日期时间格式 yyyy-MM-dd'T'HH:mm:ss.SSSZ,例如

However I keep on getting things like this:

但是我一直在得到这样的东西:

Handler execution resulted in exception: Failed to convert value of type 'java.lang.String' to required type 'java.util.Date'; nested exception is

处理程序执行导致异常:无法将类型“java.lang.String”的值转换为所需类型“java.util.Date”;嵌套异常是

org.springframework.core.convert.ConversionFailedException: 
Failed to conv ert from type java.lang.String to type 
@org.springframework.format.annotation.DateTimeFormat 
@org.springframework.web.bind.annotation.RequestParam java.util.Date for value '2015-09-26T01:30:00.000Z'; nested exception is
java.lang.IllegalArgumentException: Unable to parse '2015-09-26T01:30:00.000Z'

As far as I can see I'm not doing anything wrong, which must be of course. Can anyone shine a light what my bad is? I don't think I need to post more code since the exception does show the correct value which I'm passing to the API right?

据我所知,我没有做错任何事,这当然是肯定的。任何人都可以照亮我的坏处吗?我认为我不需要发布更多代码,因为异常确实显示了我传递给 API 的正确值,对吗?

回答by linuxunil

How it works

怎么运行的

You receive the Date as an String(HTTP Request is text based) and instruct Spring on how to convert it to an Date Object by a pattern.

您将日期作为String(HTTP 请求是基于文本的)接收并指示 Spring 如何通过pattern.

//Spring controller 
@GetMapping
public List<Foobar> find(
   @RequestParam(name = "startDate", required = false)
   @DateTimeFormat(pattern = "YOUR_DATE_PATTERN" or iso="ISO Enum member") //how to convert the date string
   Date startDate {
  return service.find(startDate); //work with the java.util.Date object
}

Spring will delegate this task to the java.text.DateTimeFormat, so the pattern should be a valid one for the formatter class to work with.

Spring 会将这个任务委托给java.text.DateTimeFormat,因此该模式应该是格式化程序类可以使用的有效模式

@DateTimeFormat - Pattern VS Iso

@DateTimeFormat - 模式 VS Iso

  • Pattern : Specify your pattern. Will be passed directly to the formatter.
  • Iso : An member of org.springframework.format.annotation.DateTimeFormat.ISOEnum, that has a pre-built Date String Pattern. From the Enum docs:
  • 图案:指定您的图案。将直接传递给格式化程序。
  • Iso :org.springframework.format.annotation.DateTimeFormat.ISOEnum的成员,具有预构建的日期字符串模式。从枚举文档:

DATE The most common ISO Date Format yyyy-MM-dd, e.g.

DATE_TIME The most common ISO DateTime Format yyyy-MM-dd'T'HH:mm:ss.SSSZ, e.g.

NONE Indicates that no ISO-based format pattern should be applied.

TIME The most common ISO Time Format HH:mm:ss.SSSZ, e.g.

DATE 最常见的 ISO 日期格式 yyyy-MM-dd,例如

DATE_TIME 最常见的 ISO 日期时间格式 yyyy-MM-dd'T'HH:mm:ss.SSSZ,例如

NONE 表示不应应用基于 ISO 的格式模式。

TIME 最常见的 ISO 时间格式 HH:mm:ss.SSSZ,例如

  • Notice that ALL Enum members (except for NONE), uses 'Z' for the timezone.
  • 请注意,所有枚举成员(NONE 除外)都使用“Z”作为时区。

What 'Z' means in the date pattern?

日期模式中的“Z”是什么意思?

  • Looking at the Javadocs of the Date and Time Patterns, we have two options to handle timezones:

    1. 'Z' Char : RFC 822 Timezone Syntax (Spring will use this syntax)
  • 查看日期和时间模式的 Javadocs,我们有两个选项来处理时区:

    1. 'Z' 字符:RFC 822 时区语法(Spring 将使用此语法)
zone      =  "UT"  / "GMT"                ; Universal Time
                                        ; North American : UT

        /  "EST" / "EDT"                ;  Eastern:  - 5/ - 4

        /  "CST" / "CDT"                ;  Central:  - 6/ - 5

        /  "MST" / "MDT"                ;  Mountain: - 7/ - 6

        /  "PST" / "PDT"                ;  Pacific:  - 8/ - 7

        /  1ALPHA                       ; Military: Z = UT;
                                        ;  A:-1; (J not used)
                                        ;  M:-12; N:+1; Y:+12

        / ( ("+" / "-") 4DIGIT )        ; Local differential
                                        ;  hours+min. (HHMM)
zone      =  "UT"  / "GMT"                ; Universal Time
                                        ; North American : UT

        /  "EST" / "EDT"                ;  Eastern:  - 5/ - 4

        /  "CST" / "CDT"                ;  Central:  - 6/ - 5

        /  "MST" / "MDT"                ;  Mountain: - 7/ - 6

        /  "PST" / "PDT"                ;  Pacific:  - 8/ - 7

        /  1ALPHA                       ; Military: Z = UT;
                                        ;  A:-1; (J not used)
                                        ;  M:-12; N:+1; Y:+12

        / ( ("+" / "-") 4DIGIT )        ; Local differential
                                        ;  hours+min. (HHMM)
  1. 'X' Char : ISO 8601 Timezone designators
  1. 'X' 字符:ISO 8601 时区指示符

Time offsets (15:00?03:30) OR 'Z' for UTC/GMT timezone

UTC/GMT 时区的时间偏移 (15:00?03:30) 或“Z”

The issue

问题

  • If you select org.springframework.format.annotation.DateTimeFormat.ISO.DATE_TIMEEnum member, it will use the yyyy-MM-dd'T'HH:mm:ss.SSSZpattern with the RFC 822 syntax for the timezones.
  • 如果您选择org.springframework.format.annotation.DateTimeFormat.ISO.DATE_TIMEEnum 成员,它将使用yyyy-MM-dd'T'HH:mm:ss.SSSZ具有 RFC 822 语法的时区模式。

The fix

修复

  • Use the same pattern but with the 'X' for the timezones (use the ISO 8601 syntax) :

    yyyy-MM-dd'T'HH:mm:ss.SSSX

  • 使用相同的模式,但时区使用“X”(使用 ISO 8601 语法):

    yyyy-MM-dd'T'HH:mm:ss.SSSX

About the Z Timezone

关于 Z 时区

  • ISO 8601 states:
  • ISO 8601 规定:

Coordinated Universal Time (UTC)

If the time is in UTC, add a Z directly after the time without a space. Z is the zone designator for the zero UTC offset. "09:30 UTC" is therefore represented as "09:30Z" or "0930Z". "14:45:15 UTC" would be "14:45:15Z" or "144515Z".

The Z suffix in the ISO 8601 time representation is sometimes referred to as "Zulu time" because the same letter is used to designate the Zulu time zone. However the ACP 121 standard that defines the list of military time zones makes no mention of UTC and derives the "Zulu time" from the Greenwich Mean Time[27] which was formerly used as the international civil time standard.

协调世界时 (UTC)

如果时间是 UTC,则直接在时间后添加一个 Z,不带空格。Z 是零 UTC 偏移的区域指示符。因此,“09:30 UTC”表示为“09:30Z”或“0930Z”。“14:45:15 UTC”将是“14:45:15Z”或“144515Z”。

ISO 8601 时间表示中的 Z 后缀有时称为“祖鲁时间”,因为相同的字母用于指定祖鲁时区。然而,定义军用时区列表的 ACP 121 标准并未提及 UTC,而是从格林威治标准时间 [27] 派生出“祖鲁时间”,格林威治标准时间 [27] 以前用作国际民用时间标准。

Related Links

相关链接

回答by Sanjeev

As per DateTimeFormat.ISO.DATE_TIME

按照 DateTimeFormat.ISO.DATE_TIME

The most common ISO DateTime Format yyyy-MM-dd'T'HH:mm:ss.SSSZ, e.g. 2000-10-31 01:30:00.000-05:00.

最常见的 ISO 日期时间格式 yyyy-MM-dd'T'HH:mm:ss.SSSZ,例如 2000-10-31 01:30:00.000-05:00。

Where a Zrepresents a timezone value for example -05:00.

其中 aZ表示时区值,例如 -05:00。

Your string value which is unparseable is 2015-09-26T01:30:00.000Zwhere Z must be replaced with actual timezone value.

2015-09-26T01:30:00.000Z无法解析的字符串值是Z 必须替换为实际时区值的地方。

For example 2015-09-26T01:30:00.000-04:00will be parsed by ISO.DATE_TIMEcorrectly

例如2015-09-26T01:30:00.000-04:00将被ISO.DATE_TIME正确解析

回答by Francesco

Try notto pass the parameter fromDateas a String.

尽量不要将参数fromDate作为字符串传递。

I had the same issue and when I unwrapped the parameter's value from double quotes (") in the API call (I used Postman), it worked. You might have to encode some of the characters (i.e. +) though.

我遇到了同样的问题,当我"在 API 调用(我使用 Postman)中从双引号 ( ) 中解开参数的值时,它起作用了。不过,您可能需要对某些字符(即 +)进行编码。