Java 8:如何用毫秒,微秒或纳秒创建DateTimeFormatter? [英] Java 8: How to create DateTimeFormatter with milli, micro or nano seconds?

查看:140
本文介绍了Java 8:如何用毫秒,微秒或纳秒创建DateTimeFormatter?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要创建用于解析时间戳的格式化程序,并带有可选的毫秒,微米或纳秒的小数部分.

I need to create the formatter for parsing timestamps with optional milli, micro or nano fractions of second.

例如,出于我的需要,我看到了以下机会:

For example, for my needs I see the following opportunity:

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
                                   .append(DateTimeFormatter.BASIC_ISO_DATE)
                                   .appendLiteral('-')
                                   .append(DateTimeFormatter.ISO_LOCAL_TIME)
                                   .appendOffset("+HH:mm", "Z")
                                   .toFormatter();

或者也可以使用appendFraction(field, minWidth, maxWidth, decimalPoint).

但是,在这些情况下,可以解析任意数量的小数(最多9个或maxWidth)的时间戳.如何实现我们只能在逗号​​后解析(可选)仅3、6或9个数字?

However in these cases will it be possible to parse timestamps with any number of decimals (up to 9 or maxWidth). How to achieve that we can parse (optionally) only 3, 6 or 9 numbers after the comma?

应该可以解析以下时间部分:

It should be possible to parse the following time parts:

  • HH:mm:ss.SSS
  • HH:mm:ss.SSSSSS
  • HH:mm:ss.SSSSSSSSS
  • HH:mm:ss.SSS
  • HH:mm:ss.SSSSSS
  • HH:mm:ss.SSSSSSSSS

但无法解析:HH:mm:ss.SSSS.

推荐答案

您可以使用可选节模式(用[]分隔),并创建3个可选节:1个代表9位数字,另一个代表6位数字,另一个3位数字.

You can use the optional sections pattern (delimited by []), and create 3 optional sections: 1 for 9 digits, another for 6 digits and another one for 3 digits.

根据

According to DateTimeFormatterBuilder docs, you can use the S pattern (which is equivalent to NANO_OF_SECOND field):

Pattern  Count  Equivalent builder methods
-------  -----  --------------------------
S..S     1..n   appendFraction(ChronoField.NANO_OF_SECOND, n, n, false)

在旧版API(SimpleDateFormat)中,

In the old API (SimpleDateFormat), S is the pattern used for milliseconds, but in the new API it was changed to nanoseconds.

因此格式化程序将按以下方式创建:

So the formatter will be created like this:

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
    // here is the same as your code
    .append(DateTimeFormatter.BASIC_ISO_DATE).appendLiteral('-')
    // time (hour/minute/seconds)
    .appendPattern("HH:mm:ss")
    // optional nanos, with 9, 6 or 3 digits
    .appendPattern("[.SSSSSSSSS][.SSSSSS][.SSS]")
    // offset
    .appendOffset("+HH:mm", "Z")
    // create formatter
    .toFormatter();

一些测试:

// 3 digits
System.out.println(OffsetDateTime.parse("20161201-10:30:45.123Z", formatter)); // 2016-12-01T10:30:45.123Z

// 6 digits
System.out.println(OffsetDateTime.parse("20161201-10:30:45.123456Z", formatter)); // 2016-12-01T10:30:45.123456Z

// 9 digits
System.out.println(OffsetDateTime.parse("20161201-10:30:45.123456789Z", formatter)); // 2016-12-01T10:30:45.123456789Z

// 4 digits (throws DateTimeParseException: Text '20161201-10:30:45.1234Z' could not be parsed at index 21)
System.out.println(OffsetDateTime.parse("20161201-10:30:45.1234Z", formatter));

输出为:

2016-12-01T10:30:45.123Z
2016-12-01T10:30:45.123456Z
2016-12-01T10:30:45.123456789Z
线程主"中的异常java.time.format.DateTimeParseException:无法在索引21处解析文本"20161201-10:30:45.1234Z"

2016-12-01T10:30:45.123Z
2016-12-01T10:30:45.123456Z
2016-12-01T10:30:45.123456789Z
Exception in thread "main" java.time.format.DateTimeParseException: Text '20161201-10:30:45.1234Z' could not be parsed at index 21


注释:

  • DateTimeFormatter的此实例不适合格式,因为它会打印所有可选部分(因此,纳秒将被打印3次):

  • This instance of DateTimeFormatter is not good for formatting, because it prints all optional sections (so the nanosecond will be printed 3 times):

// don't use it to format, it prints all optional sections
// (so nanos are printed 3 times: with 9, 6 and 3 digits)
OffsetDateTime odt = OffsetDateTime.parse("20161201-10:30:45.123Z", formatter);
System.out.println(formatter.format(odt));
// output is 20161201Z-10:30:45.123000000.123000.123Z

因此,如果要以其他格式显示日期,请考虑创建另一个DateTimeFormatter.

So if you want to display your dates in another format, consider creating another DateTimeFormatter.

  • In your code you used DateTimeFormatter.ISO_LOCAL_TIME. According to javadoc, in this formatter the seconds are optional. If you want to have the same behaviour, just change the time pattern to:

// time (hour/minute) with optional seconds
.appendPattern("HH:mm[:ss]")

  • []模式是制作可选节的不错捷径,但是您也可以使用optionalStart()appendFraction()来创建它们:

  • The [] pattern is a nice shortcut to make optional sections, but you could also create them using optionalStart() with appendFraction():

    DateTimeFormatter formatter = new DateTimeFormatterBuilder()
        // here is the same as your code
        .append(DateTimeFormatter.BASIC_ISO_DATE).appendLiteral('-')
        // time (hour/minute/seconds)
        .appendPattern("HH:mm:ss")
        // optional nanos with 9 digits (including decimal point)
        .optionalStart()
        .appendFraction(ChronoField.NANO_OF_SECOND, 9, 9, true)
        .optionalEnd()
        // optional nanos with 6 digits (including decimal point)
        .optionalStart()
        .appendFraction(ChronoField.NANO_OF_SECOND, 6, 6, true)
        .optionalEnd()
        // optional nanos with 3 digits (including decimal point)
        .optionalStart()
        .appendFraction(ChronoField.NANO_OF_SECOND, 3, 3, true)
        .optionalEnd()
        // offset
        .appendOffset("+HH:mm", "Z")
        // create formatter
        .toFormatter();
    

  • 此格式化程序的工作方式与使用[]模式创建的第一个格式化程序完全相同.

    This formatter works exactly the same way as the first one created with [] pattern.

    @SeanVanGorder注意到

    As @SeanVanGorder noticed in his comment, this formatter has the side effect of accepting multiple patterns for the nanosecond field, but it works only if the values are the same:

    // side effect
    // multiple nanos values (accepts multiple values if they're all the same)
    System.out.println(OffsetDateTime.parse("20161201-10:30:45.123000.123Z", formatter)); // 2016-12-01T10:30:45.123Z
    System.out.println(OffsetDateTime.parse("20161201-10:30:45.123000000.123Z", formatter)); // 2016-12-01T10:30:45.123Z
    System.out.println(OffsetDateTime.parse("20161201-10:30:45.123000000.123000.123Z", formatter)); // 2016-12-01T10:30:45.123Z
    

    以上所有行均输出2016-12-01T10:30:45.123Z,但请注意,它们接受所有可选值(例如.123000000.123).由于这些值相同,因此可以毫无错误地进行解析.

    All the lines above output 2016-12-01T10:30:45.123Z, but notice that they accept all the optional values (like .123000000.123). As the values are the same, the parsing is done without errors.

    但是,如果值不同,则会引发异常:

    If the values are different, though, it throws an exception:

    // multiple nanos values (throws exception if values are different)
    System.out.println(OffsetDateTime.parse("20161201-10:30:45.123000.124Z", formatter)); // exception
    

    如果不需要这种行为,唯一的选择是创建许多不同的格式化程序(每种情况一个),并执行for循环,直到获得有效的解析值为止(非常类似于

    If this behaviour is not desired, the only alternative is to create lots of different formatters (one for each case) and do a for loop until you get a valid parsed value (very similar to this answer).

    首先,我创建了一个方法,该方法接收DateTimeFormatter的列表和TemporalQuery的列表,以将已解析的字符串转换为您想要的任何对象:

    First I created a method receives a list of DateTimeFormatter's and a TemporalQuery to convert the parsed string to any object you want:

    // method to parse, it receives a list of DateTimeFormatter and a TemporalQuery to convert the parsed string
    public <T> T parse(String input, TemporalQuery<T> query, DateTimeFormatter... formatters) {
        for (DateTimeFormatter fmt : formatters) {
            try {
                // try to parse
                return fmt.parse(input, query);
            } catch (Exception e) {}
        }
    
        // none worked, throw exception
        throw new DateTimeParseException("Text '" + input + "' could not be parsed", input, 0);
    }
    

    现在,您只需要创建格式化程序并在parse方法中使用它们即可:

    Now you just need to create the formatters and use them in the parse method:

    // alternative: have 3 different formatters
    DateTimeFormatter f1 = new DateTimeFormatterBuilder()
        // here is the same as your code
        .append(DateTimeFormatter.BASIC_ISO_DATE).appendLiteral('-')
        // time (hour/minute/seconds/3 digit nanos)
        .appendPattern("HH:mm:ss.SSS")
        // offset
        .appendOffset("+HH:mm", "Z")
        // create formatter
        .toFormatter();
    DateTimeFormatter f2 = new DateTimeFormatterBuilder()
        // here is the same as your code
        .append(DateTimeFormatter.BASIC_ISO_DATE).appendLiteral('-')
        // time (hour/minute/seconds/6 digit nanos)
        .appendPattern("HH:mm:ss.SSSSSS")
        // offset
        .appendOffset("+HH:mm", "Z")
        // create formatter
        .toFormatter();
    DateTimeFormatter f3 = new DateTimeFormatterBuilder()
        // here is the same as your code
        .append(DateTimeFormatter.BASIC_ISO_DATE).appendLiteral('-')
        // time (hour/minute/seconds/9 digit nanos)
        .appendPattern("HH:mm:ss.SSSSSSSSS")
        // offset
        .appendOffset("+HH:mm", "Z")
        // create formatter
        .toFormatter();
    
    // all formatters
    DateTimeFormatter[] formatters = new DateTimeFormatter[] { f1, f2, f3 };
    
    // 3 digits
    System.out.println(parse("20161201-10:30:45.123Z", OffsetDateTime::from, formatters)); // 2016-12-01T10:30:45.123Z
    // 6 digits
    System.out.println(parse("20161201-10:30:45.123456Z", OffsetDateTime::from, formatters)); // 2016-12-01T10:30:45.123456Z
    // 9 digits
    System.out.println(parse("20161201-10:30:45.123456789Z", OffsetDateTime::from, formatters)); // 2016-12-01T10:30:45.123456789Z
    
    // 4 digits (throws exception)
    System.out.println(parse("20161201-10:30:45.1234Z", OffsetDateTime::from, formatters));
    // java.time.format.DateTimeParseException: Text '20161201-10:30:45.1234Z' could not be parsed
    
    // multiple values (throws exception)
    System.out.println(parse("20161201-10:30:45.123000.123Z", OffsetDateTime::from, formatters));
    // java.time.format.DateTimeParseException: Text '20161201-10:30:45.123000.123Z' could not be parsed
    

    请注意,我将方法引用OffsetDateTime::from用作TemporalQuery,但是您可以将其更改为所需的任何查询.

    Note that I used the method reference OffsetDateTime::from as a TemporalQuery, but you can change it to any query you need.

    这篇关于Java 8:如何用毫秒,微秒或纳秒创建DateTimeFormatter?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆