将周期/持续时间字符串文字解析为Java中的周期/持续时间实例

问题描述 投票:0回答:2

我想将任何持续时间/周期字符串文字(采用下面示例中所示的格式)解析为 Java 中的周期/持续时间实例。

我所说的持续时间/期间是指包含基于日期和基于时间的数量的时间量。在 Java 中,单独的

Duration
仅适用于基于时间的数量,如秒、分钟和小时,而单独的
Period
仅适用于基于日期的数量,如年、月和日。

要解析的字符串并不总是包含所有数量。

示例:

  • 29 days 23 hours 59 minutes 20 seconds
  • 4 years 10 months 10 seconds
  • 10 days 9 minutes
  • 1 month

如果可能的话我会怎么做?

java date parsing time
2个回答
5
投票

将字符串转换为 ISO 8601 格式并解析为 periodDuration

我的想法是使用 ThreeTen Extra 项目的

PeriodDuration
类。它结合了您已经提到的
Period
Duration
类。一个限制是它的
parse
方法仅接受 ISO 8601 格式,因此我们需要首先将您的字符串转换为该格式。 ISO 8601 格式就像 10 天 9 分钟的
P10DT9M
P
是固定的。如果有一个(小时、分钟、秒)将其与基于日期的部分(年、月、周、日)分开,则
T
标记基于时间的部分的开始。一个很好的结果是,我们总是可以判断
M
是否表示月份或分钟:如果
M
T
之前或者没有
T
,则表示月份。如果
M
T
之后,则表示分钟。我的第一个代码片段将您的字符串转换为 ISO 8601。

    String[] examples = {
            "29 days 23 hours 59 minutes 20 seconds", 
            "4 years 10 months 10 seconds",
            "10 days 9 minutes", 
            "1 month" 
    };
    
    for (String pds : examples) {
        // If hours, minutes or seconds are present, put a T before them
        String temp = pds.replaceFirst("(\\d+ *(?:hour|minute|second))", " T $1");
        // Abbreviate all units to 1 letter; remove spaces
        temp = temp.replaceAll("([ymwdhms])[a-z]*", "$1").replace(" ", "");
        // Prepend P
        String iso = "P" + temp;
        System.out.println(iso);
    }

到目前为止的输出是:

P29dT23h59m20s
P4y10mT10s
P10dT9m
P1m

在此之后我们应该能够解析:

        PeriodDuration pd = PeriodDuration.parse(iso);

我还没有安装 ThreeTen Extra,所以在发布之前我没有尝试过最后一行。 OP 在评论中报道称,它在 ThreeTen Extra 1.6.0 上就像一个魅力。唯一需要注意的是,解析字符串时单位必须按降序排列,否则会抛出

DateTimeParseException

不用担心 ISO 8601 格式通常以大写形式给出,而您的单位则以小写形式给出。

parse
方法的文档指出:

这些部分的 ASCII 后缀“Y”代表年份,“M”代表月份, “W”代表周,“D”代表天,“H”代表小时,“M”代表分钟,“S”代表 秒,接受大写或小写。

编辑:感谢 Arvind Kumar Avinash 的评论,如果您也需要接受大写字母,请在正则表达式中添加

(?i)
标志表达式以启用不区分大小写的匹配:

        temp = temp.replaceAll("(?i)([ymwdhms])[a-z]*", "$1").replace(" ", "");

链接


0
投票

ThreeTen Extra 库似乎是一个很好的解决方案,但就我而言,我宁愿不包含一个大型库,只是为了使用一个类的特权,该类基本上是

java.time.Duration
java.time.Period
的简单包装器(尽管在
threeten
的情况下,它可能是他们的
Duration
Period
版本的包装 - 我遇到过并且大多觉得很烦人)。

所以我写了这个替代品(在这里发布在 CC0 和/或任何 SO 假装他们可以强迫人们许可其受版权保护的内容下):


import java.time.Duration;
import java.time.Instant;
import java.time.Period;

/**
 * Copyright Oded Arbel <[email protected]>
 * Licensed under [CC0](https://creativecommons.org/public-domain/cc0/)
 */
public class PeriodDuration {
    public static final ZoneId ZONE_UTC = ZoneId.ofOffset("", ZoneOffset.UTC);
    private Period period;
    private Duration duration;
    
    private PeriodDuration(Period period, Duration duration) {
        this.period = period;
        this.duration = duration;
    }

    public static PeriodDuration parse(String iso8601period) {
        var val = iso8601period.toLowerCase().startsWith("p") ? iso8601period.substring(1) : iso8601period;
        var parts = val.split("T",2);
        return new PeriodDuration(
                parts[0].isEmpty() ? Period.ZERO : Period.parse("P" + parts[0]),
                parts.length < 2 || parts[1].isEmpty() ? Duration.ZERO : Duration.parse("PT" + parts[1]));
    }
    
    @Override
    public String toString() {
        return period.toString() + "T" + duration.toString().split("T")[1];
    }
    
    public Period getPeriod() {
        return period;
    }
    
    public Duration getDuration() {
        return duration;
    }
    
    public Instant addTo(Instant time) {
        return time.atZone(ZONE_UTC).plus(period).plus(duration).toInstant();
    }
    
    public Instant substractFrom(Instant time) {
        return time.atZone(ZONE_UTC).minus(period).minus(duration).toInstant();
    }
}

我没有审查任何 ThreeTen 代码 - 所以这个实现可能缺少很多功能和/或做错事情 - YMMV。

© www.soinside.com 2019 - 2024. All rights reserved.