001/* 002 * (C) Copyright 2019 Nuxeo (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Kevin Leturc <kleturc@nuxeo.com> 018 */ 019package org.nuxeo.common.utils; 020 021import static org.apache.commons.lang3.StringUtils.isBlank; 022 023import java.time.Duration; 024import java.time.format.DateTimeParseException; 025import java.util.regex.Matcher; 026import java.util.regex.Pattern; 027 028/** 029 * @since 11.1 030 */ 031public final class DurationUtils { 032 033 public static final Pattern DURATION_SIMPLE_FORMAT = Pattern.compile( 034 "(?:(\\d+)d)?(?:(\\d+)h)?(?:(\\d+)m)?(?:(\\d+)s)?(?:(\\d+)ms)?"); 035 036 private DurationUtils() { 037 // utility class 038 } 039 040 /** 041 * Obtains a {@code Duration} from a text string such as {@code PnDTnHnMn.nS} or {@code _d_h_m_s_ms}. 042 * <p> 043 * See {@link Duration#parse(CharSequence)} for {@code PnDTnHnMn.nS} format. 044 * <p> 045 * For {@code _d_h_m_s_ms}, there are five sections, each consisting of a number and a suffix. The suffixes are "d", 046 * "h", "m", "s" and "ms" for days, hours, minutes, seconds and milliseconds. The suffixes must occur in order and 047 * at least one of them must be present. 048 * 049 * @throws DateTimeParseException if the text cannot be parsed to a duration 050 * @see Duration#parse(CharSequence) 051 */ 052 public static Duration parse(String value) { 053 if (value.startsWith("P") || value.startsWith("-P")) { 054 // Duration JDK format 055 return Duration.parse(value); 056 } 057 Matcher matcher = DURATION_SIMPLE_FORMAT.matcher(value); 058 if (matcher.matches()) { 059 060 long days = 0; 061 long hours = 0; 062 long minutes = 0; 063 long seconds = 0; 064 long millis = 0; 065 if (matcher.group(1) != null) { 066 days = Long.parseLong(matcher.group(1)); 067 } 068 if (matcher.group(2) != null) { 069 hours = Long.parseLong(matcher.group(2)); 070 } 071 if (matcher.group(3) != null) { 072 minutes = Long.parseLong(matcher.group(3)); 073 } 074 if (matcher.group(4) != null) { 075 seconds = Long.parseLong(matcher.group(4)); 076 } 077 if (matcher.group(5) != null) { 078 millis = Long.parseLong(matcher.group(5)); 079 } 080 return Duration.ofDays(days).plusHours(hours).plusMinutes(minutes).plusSeconds(seconds).plusMillis(millis); 081 } 082 throw new DateTimeParseException("Text cannot be parsed to a Duration", value, 0); 083 } 084 085 /** 086 * Obtains a {@code Duration} from a text string according to {@link #parse(String)}, but in case of invalid, zero 087 * or negative duration returns a default. 088 * 089 * @param value the value to parse 090 * @param defaultDuration the default duration to return for invalid, zero or negative duration 091 * @return the parsed duration (positive), or the default 092 * @since 11.1 093 */ 094 public static Duration parsePositive(String value, Duration defaultDuration) { 095 if (isBlank(value)) { 096 return defaultDuration; 097 } 098 try { 099 Duration duration = parse(value); 100 if (duration.isZero() || duration.isNegative()) { 101 return defaultDuration; 102 } else { 103 return duration; 104 } 105 } catch (DateTimeParseException e) { 106 return defaultDuration; 107 } 108 } 109 110}