gpt4 book ai didi

java - 弹性搜寻和Y10k(超过4位数字的年份)

转载 作者:行者123 更新时间:2023-12-01 09:05:10 24 4
gpt4 key购买 nike

我在与Elastic Search查询有关的问题中发现了此问题,但是由于ES date format documentation链接到API documentation for the java.time.format.DateTimeFormatter类,因此该问题并不是真正针对ES的。
简短摘要:我们遇到的问题是9999年以后的日期,更确切地说是4位以上的年份。
ES中存储的文档具有日期字段,该日期字段在索引描述符中使用“日期”格式定义,该格式使用DateTimeFormatter中的模式语言对应于“yyyy-MM-dd”。我们正在获取用户输入,使用org.apache.commons.validator.DateValidator.isValid并使用模式“yyyy-MM-dd”验证输入,如果有效,我们将使用用户输入创建ES查询。如果用户输入类似20202-12-03的内容,则执行失败。搜索词可能不是故意的,但是预期的行为是不会找到任何东西,也不是软件咳嗽了一个异常(exception)。
问题是org.apache.commons.validator.DateValidator在内部使用较旧的SimpleDateFormat类来验证输入是否符合模式,并且SimpleDateFormat解释的“yyyy”含义类似于:使用至少4位数字,但如果需要,则允许更多位数。因此,使用模式“yyyy-MM-dd”创建SimpleDateFormat既可以解析“20202-07-14”之类的输入,又可以类似地格式化年份超过9999的Date对象。
新的DateTimeFormatter类更加严格,意味着“yyyy”正好是四个数字。它将无法解析“20202-07-14”之类的输入字符串,也无法格式化年份超过9999的Temporal对象。值得注意的是,DateTimeFormatter本身具有处理可变长度字段的能力。常量DateTimeFormatter.ISO_LOCAL_DATE例如不等同于“yyyy-MM-dd”,但是符合ISO8601的年份允许多于四位数字,但将至少使用四位数字。使用DateTimeFormatterBuilder而不是使用模式字符串以编程方式创建此常量。
ES无法配置为使用DateTimeFormatter中定义的常量(例如ISO_LOCAL_DATE),而只能使用模式字符串。 ES还知道预定义模式的列表,文档中有时还会引用ISO标准,但是它们似乎是错误的,并且忽略了有效的ISO日期字符串可以包含五位数字的年份。
我可以使用多个允许的日期模式列表来配置ES,例如“yyyy-MM-dd || yyyyy-MM-dd”。这将允许一年中的四位数和五位数,但在六位数的年份中会失败。我可以通过添加另一个允许的模式来支持六位数字的年份:“yyyy-MM-dd || yyyyy-MM-dd || yyyyyy-MM-dd”,但是它将失败七位数的年份,依此类推。
我是在监督什么,还是真的无法将ES(或使用模式字符串的DateTimeFormatter实例)配置为具有ISO标准所使用的至少四位数(但可能更多)的Year字段?

最佳答案

编辑
ISO 8601
由于您的要求是要符合ISO 8601,所以我们首先来看一下ISO 8601的内容(引自底部的链接):

To represent years before 0000 or after 9999, the standard alsopermits the expansion of the year representation but only by prioragreement between the sender and the receiver. An expanded yearrepresentation [±YYYYY] must have an agreed-upon number of extra yeardigits beyond the four-digit minimum, and it must be prefixed with a +or − sign instead of the more common AD/BC (or CE/BCE) notation; …


因此, 20202-12-03在ISO 8601中不是有效的日期。如果您明确告知用户您接受(例如,最长6位数字的年份),则 +20202-12-03-20202-12-03是有效的,并且仅带有 +-符号。
接受超过4位数字
格式模式 uuuu-MM-dd格式并根据ISO 8601解析日期,该年份也是具有四位数以上的年份。例如:
    DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("uuuu-MM-dd");
LocalDate date = LocalDate.parse("+20202-12-03", dateFormatter);
System.out.println("Parsed: " + date);
System.out.println("Formatted back: " + date.format(dateFormatter));
输出:
Parsed: +20202-12-03
Formatted back: +20202-12-03

对于带前缀的减号(而不是加号),它的工作原理非常相似。
接受超过4位数字且无符号
    yyyy-MM-dd||yyyyy-MM-dd||yyyyyy-MM-dd||yyyyyyy-MM-dd||yyyyyyyy-MM-dd||yyyyyyyyy-MM-dd
就像我说的那样,这与ISO 8601不同。我也同意您的看法,这并不好。很显然它将失败10位或更多位数字,但是无论如何都会失败:java.time处理-999 999 999到+999 999 999区间内的年份。因此尝试 yyyyyyyyyy-MM-dd(10位数年)将使您满意除了在极端情况下(用户输入的年份前导零),这会带来严重的麻烦。
对不起,这是最好的。 DateTimeFormatter格式模式不支持您所要求的所有内容。没有(单个)模式可以为您提供0000到9999范围内的四位数年份,在此之后的年份中可以提供更多位数。 DateTimeFormatter的文档中介绍了有关格式和解析年份的信息:

Year: The count of letters determines the minimum field width below which padding is used. If the count of letters is two, then areduced two digit form is used. For printing, this outputs therightmost two digits. For parsing, this will parse using the basevalue of 2000, resulting in a year within the range 2000 to 2099inclusive. If the count of letters is less than four (but not two),then the sign is only output for negative years as perSignStyle.NORMAL. Otherwise, the sign is output if the pad width isexceeded, as per SignStyle.EXCEEDS_PAD.


因此,无论您要使用哪种模式字母,您都将无法解析没有符号的数字较多的年份,而位数较少的年份将以这么多的数字加上前导零来格式化。
原始答案
您可能可以摆脱 u-MM-dd模式。示范:
    String formatPattern = "u-MM-dd";

DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(formatPattern);

LocalDate normalDate = LocalDate.parse("2020-07-14", dateFormatter);
String formattedAgain = normalDate.format(dateFormatter);
System.out.format("LocalDate: %s. String: %s.%n", normalDate, formattedAgain);

LocalDate largeDate = LocalDate.parse("20202-07-14", dateFormatter);
String largeFormattedAgain = largeDate.format(dateFormatter);
System.out.format("LocalDate: %s. String: %s.%n", largeDate, largeFormattedAgain);
输出:
LocalDate: 2020-07-14. String: 2020-07-14.
LocalDate: +20202-07-14. String: 20202-07-14.

违反直觉但非常实用的一种格式字母并不表示1位数字,而是指所需要的位数。因此,上述情况的另一面是,将在1000年之前的年份中使用少于4位数字进行格式化。正如您所说,它与ISO 8601不同。
有关年份的模式字母 yu之间的区别,请参阅底部的链接。
您可能还会考虑一个 M和/或一个 d接受 2020-007-014,但是同样,这将导致小于10的数字仅格式化为1位数字,例如 2020-7-14,这可能不是您想要的,并且再次与ISO不一致。
链接

Wikipedia文章的
  • Years section:ISO 8601
  • Documentation of DateTimeFormatter
  • uuuu versus yyyy in DateTimeFormatter formatting pattern codes in Java?
  • 关于java - 弹性搜寻和Y10k(超过4位数字的年份),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62541394/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com