任何人都知道 Java 库可以将“30min”或“2h 15min”或“2d 15h 30min”等时间字符串解析为毫秒(或某种 Duration 对象)。 Joda-Time 可以做这样的事情吗?
(我有一个丑陋的长方法来维护它进行这样的解析,并且想摆脱它/用做得更好的东西替换它。)
您可能需要根据自己的格式稍微调整一下,但请尝试以下操作:
PeriodFormatter formatter = new PeriodFormatterBuilder()
.appendDays().appendSuffix("d ")
.appendHours().appendSuffix("h ")
.appendMinutes().appendSuffix("min")
.toFormatter();
Period p = formatter.parsePeriod("2d 5h 30min");
请注意,如果您需要使其更加灵活,则有一个
appendSuffix
需要 variants
参数。
Period.toStandardDuration()
,从那里您可以使用 getStandardSeconds()
来获取以秒为单位的经过时间 long
。
如果您使用的是没有这些方法的旧版本,您仍然可以通过假设一天中的标准 24/小时、60 分钟/小时等自行计算时间戳。(在这种情况下,请利用
DateTimeConstants
中的常量)
类以避免需要幻数。)
我想让日期、小时和分钟可选,这似乎可以做到这一点。 请注意,appendSuffix() 调用在字符后没有空格。
使用 Joda 2.3。
PeriodParser parser = new PeriodFormatterBuilder()
.appendDays().appendSuffix("d").appendSeparatorIfFieldsAfter(" ")
.appendHours().appendSuffix("h").appendSeparatorIfFieldsAfter(" ")
.appendMinutes().appendSuffix("min")
.toParser();
上面的代码通过了这些测试。
@Test
public void testConvert() {
DurationConverter c = new DurationConverter();
Duration d;
Duration expected;
d = c.convert("1d");
expected = Duration.ZERO
.withDurationAdded(Duration.standardDays(1),1);
assertEquals(d, expected);
d = c.convert("1d 1h 1min");
expected = Duration.ZERO
.withDurationAdded(Duration.standardDays(1),1)
.withDurationAdded(Duration.standardHours(1),1)
.withDurationAdded(Duration.standardMinutes(1),1);
assertEquals(d, expected);
d = c.convert("1h 1min");
expected = Duration.ZERO
.withDurationAdded(Duration.standardHours(1),1)
.withDurationAdded(Duration.standardMinutes(1),1);
assertEquals(d, expected);
d = c.convert("1h");
expected = Duration.ZERO
.withDurationAdded(Duration.standardHours(1),1);
assertEquals(d, expected);
d = c.convert("1min");
expected = Duration.ZERO
.withDurationAdded(Duration.standardMinutes(1),1);
assertEquals(d, expected);
}
仅供参考,只是写了一个多小时,仅使用
java.time.*
,非常容易理解并根据任何需要进行定制;
此版本适用于以下字符串:
3d12h
、2y
、9m10d
等
import java.time.Duration;
import java.time.Instant;
import java.time.Period;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.Locale;
private static final Pattern periodPattern = Pattern.compile("([0-9]+)([hdwmy])");
public static Long parsePeriod(String period){
if(period == null) return null;
period = period.toLowerCase(Locale.ENGLISH);
Matcher matcher = periodPattern.matcher(period);
Instant instant=Instant.EPOCH;
while(matcher.find()){
int num = Integer.parseInt(matcher.group(1));
String typ = matcher.group(2);
switch (typ) {
case "h":
instant=instant.plus(Duration.ofHours(num));
break;
case "d":
instant=instant.plus(Duration.ofDays(num));
break;
case "w":
instant=instant.plus(Period.ofWeeks(num));
break;
case "m":
instant=instant.plus(Period.ofMonths(num));
break;
case "y":
instant=instant.plus(Period.ofYears(num));
break;
}
}
return instant.toEpochMilli();
}
以下引用来自Joda-Time主页的通知:
请注意,从 Java SE 8 开始,用户被要求迁移到 java.time (JSR-310) - JDK 的核心部分,它将取代该项目。
使用现代日期时间 API java.time
的解决方案:您可以将输入字符串转换为
ISO_8601#Duration 格式,然后将其解析为 java.time.Duration
演示:
import java.time.Duration;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
// Test
Stream.of(
"30min",
"2h 15min",
"2d 15h 30min",
"30sec",
"2h 15min 10sec",
"2day 15hour 30min",
"2days 15hours 30mins"
).forEach(s -> System.out.println(s + " => " + toMillis(s) + "ms"));
}
static long toMillis(String strDuration) {
strDuration = strDuration.replaceAll("\\s+", "")
.replaceFirst("day(?:s)?", "d")
.replaceFirst("hour(?:s)?", "h")
.replaceFirst("min(?:s)?", "m")
.replaceFirst("sec(?:s)?", "s")
.replaceFirst("(\\d+d)", "P$1T");
strDuration = strDuration.charAt(0) != 'P' ? "PT" + strDuration : strDuration;
Duration duration = Duration.parse(strDuration);
return duration.toMillis();
}
}
输出:
30min => 1800000ms
2h 15min => 8100000ms
2d 15h 30min => 228600000ms
30sec => 30000ms
2h 15min 10sec => 8110000ms
2day 15hour 30min => 228600000ms
2days 15hours 30mins => 228600000ms
从 Trail:日期时间了解有关 现代日期时间 API *的更多信息。
* 如果您正在从事 Android 项目,并且您的 Android API 级别仍然不符合 Java-8,请检查 通过脱糖可用的 Java 8+ API。请注意,Android 8.0 Oreo 已经提供了对 java.time
的
支持。
java.time
:
val RELAXED_FORMATTER = DateTimeFormatter.ofPattern("yyyy[-MM[-dd[' 'HH:mm[:ss[.SSS]]]]]")
fun parseTemporalInput(input: String): LocalDateTime? {
var result = LocalDateTime.MAX.withNano(0)
if (input.lowercase() == "now")
return LocalDateTime.now()
try {
val parsed = RELAXED_FORMATTER.parse(input)
for (field in listOf(YEAR, MONTH_OF_YEAR, DAY_OF_MONTH, HOUR_OF_DAY, MINUTE_OF_HOUR, SECOND_OF_MINUTE)) {
try {
result = result.with(field, parsed.getLong(field))
} catch (ex: UnsupportedTemporalTypeException) {
result = result.with(field, if (field.isDateBased) 1 else 0)
}
}
return result
}
catch (parseEx: DateTimeParseException) {
try {
val inputToIso8601 = "P" + input.uppercase().replace("-","").replace(" ", "").replace("D", "DT").removeSuffix("T")
// Expected format: "PnDTnHnMn.nS"
val duration = Duration.parse(inputToIso8601)
val base = LocalDateTime.now().let {
if (!inputToIso8601.contains("D")) it
else it.truncatedTo(ChronoUnit.DAYS)
}
return base.minus(duration)
}
catch (ex: DateTimeParseException) {
return null
}
}
return null
}
仅支持天(因为底层 ISO 8601 没有标准化周、月等)