java datetimeformatter 可以处理日期格式的自定义文本吗

Can java datetimeformatter handle custom text in date format

提问人:user3329518 提问时间:10/9/2023 最后编辑:Mr. Polywhirluser3329518 更新时间:11/15/2023 访问量:140

问:

我存储在数据库中:

  • 一周,即 2023年的第 10 周yyyyWww2023W10
  • 一个月的含义 2023 年 10 月2023M10

我可以使用和格式化。yyyy'W'wwyyyy'M'MMSimpleDateFormat

DateTimeFormatter在这些情况下给予。似乎只接受带有有限文本(破折号、斜杠、句点)的完整日期 ()DateTimeParseExceptionDateTimeFormatteryear+month+day

我在这里错过了什么?

我已经尝试过 和 格式化 ,两者都给出了 parseexceptions。yyyy'W'wwyyyy'M'MMdateTimeFormatter

java 时间 datetimeformatter

评论

0赞 Mr. Polywhirl 10/10/2023
Java 8+ 包的类需要日期和时间。你试过打电话吗?java.dateDateTime...LocalDate.parse
0赞 teapot418 10/10/2023
使用正则表达式替换来添加第 1 天,并将字符串的其余部分调整为可以解析的格式,例如 ISO_WEEK_DATE
0赞 Rifat Rubayatul Islam 10/10/2023
不确定我是否正确理解了您的问题。但是这两种格式都适用于我,然后在 openjdk 11 中使用。DateTimeFormatter.ofPattern()formatter.parse()
0赞 Basil Bourque 10/10/2023
@Mr.Polywhirl 不,不需要约会。只需为每种数据类型使用适当的类即可。看我的答案
0赞 Anonymous 10/11/2023
似乎 DateTimeFormatter 只接受具有有限文本(破折号、斜杠、句点)的完整日期(年+月+日)至少在两个方面通常不正确。是什么让你觉得是?

答:

2赞 Mr. Polywhirl 10/10/2023 #1

您需要使用 a 来应用默认值以及您的模式。DateTimeFormatterBuilder

一个简单的方法是提前声明所有已知格式并遍历它们。如果要消除循环,可以检查字符串是否包含“W”并调用周解析器,或者检查“M”是否包含“M”并调用月份解析器。

请注意,(大写)表示以周为基础的年份,而(小写)表示时代(正常的 365 天一年)。YYYYyyyy

import java.time.*;
import java.time.format.*;
import java.time.temporal.*;

class DateParsing {
    private static final DateTimeFormatter
        PARSE_WEEK = new DateTimeFormatterBuilder()
            .appendPattern("YYYY'W'ww")
            .parseDefaulting(WeekFields.ISO.dayOfWeek(), DayOfWeek.SUNDAY.getValue())
            .toFormatter(),
        PARSE_MONTH = new DateTimeFormatterBuilder()
            .appendPattern("yyyy'M'MM")
            .parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
            .toFormatter();

    private static final DateTimeFormatter[] PATTERNS = { PARSE_WEEK, PARSE_MONTH };
    
    public static LocalDate parseDate(String input) throws IllegalArgumentException {
        for (DateTimeFormatter pattern : PATTERNS) {
            try {
                return LocalDate.parse(input, pattern);
            } catch (DateTimeParseException e) {
                // System.err.println("Error: " + e.getMessage());
            }
        }
        throw new IllegalArgumentException(String.format("Unknown format: %s", input));
    }
    
    public static void main(String[] args) throws IllegalArgumentException {
        String[] timestamps = {
            "2023W10", // 10th week of 2023
            "2023M10"  // October 1, 2023
        };
        for (String timestamp : timestamps) {
            System.out.printf(">>> %s => %s%n", timestamp, parseDate(timestamp));
        }
    }
}

输出:

>>> 2023W10 => 2023-03-05
>>> 2023M10 => 2023-10-01

更新

如果要删除循环和异常处理,可以检查输入字符串是否包含“M”或“W”,如上所述。

import java.time.*;
import java.time.format.*;
import java.time.temporal.*;

class DateParsing {
    private static final DateTimeFormatter
        PARSE_WEEK = new DateTimeFormatterBuilder()
            .appendPattern("YYYY'W'ww")
            .parseDefaulting(WeekFields.ISO.dayOfWeek(), DayOfWeek.SUNDAY.getValue())
            .toFormatter(),
        PARSE_MONTH = new DateTimeFormatterBuilder()
            .appendPattern("yyyy'M'MM")
            .parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
            .toFormatter();

    public static LocalDate parseDate(String input) throws DateTimeParseException, IllegalArgumentException {
        if (input.contains("M")) {
            return LocalDate.parse(input, PARSE_MONTH);
        }
        if (input.contains("W")) {
            return LocalDate.parse(input, PARSE_WEEK);
        }
        throw new IllegalArgumentException(String.format("Unknown format: %s", input));
    }
    
    public static void main(String[] args) throws DateTimeParseException, IllegalArgumentException {
        String[] timestamps = {
            "2023W10", // 10th week of 2023
            "2023M10"  // October 1, 2023
        };
        for (String timestamp : timestamps) {
            System.out.printf(">>> %s => %s%n", timestamp, parseDate(timestamp));
        }
    }
}

评论

0赞 VGR 10/10/2023
我不能凭良心对代码投赞成票。为什么不直接捕获 DateTimeParseException?此外,它基本上毫无意义,因为所有方法都能够自动抛出未经检查的异常。catch (Exception e)throws IllegalArgumentException
0赞 Mr. Polywhirl 10/10/2023
@VGR我的错,我的意思是.在我引入循环之前,我最初编写了每个 try-catch out 并默认为 ,。我只是为了演示目的而取消选中它。我在底部添加了一个更安全的版本。DateTimeParseExceptionExeception
1赞 VGR 10/10/2023
谢谢。在我看来,这些条款仍然是不必要的。throws
3赞 Basil Bourque 10/10/2023 #2

TL的;博士

在您的特定情况下,您可以轻松地使用字符串操作而不是 .DateTimeFormatter

org.threeten.extra.YearWeek.parse( "2023W31".replace ( "W" , "-W" ) )

...和:

java.time.YearMonth.parse( "2023M07".replace( "M" , "-" ) )

更好的是:更改您的数据以使用标准 ISO 8601 格式。

  • 2023W31 2023-W31 ➡️
  • 2023M07 2023-07 ➡️

我在这里错过了什么?

您缺少相应的类。

YearWeek

对于您的第一个案例,即一年中的一周,请使用库 ThreeTen-Extra 中的 org.threeten.extra.YearWeek 类。该库为 JSR 310 中定义的 Java 8+ 中内置的 java.time 类添加了功能。

该类在分析/生成文本时使用标准 ISO 8601 格式。因此,您不需要指定格式模式。YearWeek

输入文本符合 ISO 8601 标准的缩写样式,但不符合首选的扩展样式。要符合扩展样式,请插入连字符 .input.replace( "W" , "-W" )

String input = "2023W31".replace ( "W" , "-W" );
YearWeek yearWeek = YearWeek.parse ( input );
System.out.println ( "yearWeek = " + yearWeek );

年周 = 2023-W31

定义“周”

仅当您对“周”的定义与 ISO 8601 标准定义一致时,才使用此类:YearWeek

  • 每年有 52 或 53 个完整的 7 天周。
  • 一周从星期一开始。
  • 第 # 1 周包含日历年的第一个星期四。

YearMonth

对于第二种情况,请使用 Java 8+ 中内置的 java.time.YearMonth 类。

一年和一个月的标准 ISO 8601 格式是 YYYY-MM。我建议您在数据库中的存储值中使用此标准格式。

YearMonth yearMonth = YearMonth.parse( "2023-07" ) ;

如果您坚持使用自定义格式,则可以定义一个 来指定您的特定格式模式。但我会用连字符替换。DateTimeFormatterM

YearMonth yearMonth = YearMonth.parse( "2023M07".replace( "M" , "-" ) ) ;

yearMonth = 2023-07

0赞 user3329518 11/15/2023 #3

非常感谢所有的意见。我不知道 ISO 日期格式,将来我会使用它们。我的要求是平均值,例如每天、每周和每月的价格。据我所知,日期/日历的缺陷不会影响我产生的值。当它们被弃用时,我会咬紧牙关重新编码。