/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache license, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the license for the specific language governing permissions and
 * limitations under the license.
 */

package org.apache.logging.log4j.core.appender.rolling.action;

import java.io.Serializable;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Simplified implementation of the ISO-8601 Durations standard. The supported format is PnDTnHnMnS, with 'P' and 'T' optional. Days are considered to be exactly 24 hours.

Similarly to the java.time.Duration class, this class does not support year or month sections in the format. This implementation does not support fractions or negative values.

See Also:
/** * Simplified implementation of the <a href="https://en.wikipedia.org/wiki/ISO_8601#Durations">ISO-8601 Durations</a> * standard. The supported format is {@code PnDTnHnMnS}, with 'P' and 'T' optional. Days are considered to be exactly 24 * hours. * <p> * Similarly to the {@code java.time.Duration} class, this class does not support year or month sections in the format. * This implementation does not support fractions or negative values. * * @see #parse(CharSequence) */
public class Duration implements Serializable, Comparable<Duration> { private static final long serialVersionUID = -3756810052716342061L;
Constant for a duration of zero.
/** * Constant for a duration of zero. */
public static final Duration ZERO = new Duration(0);
Hours per day.
/** * Hours per day. */
private static final int HOURS_PER_DAY = 24;
Minutes per hour.
/** * Minutes per hour. */
private static final int MINUTES_PER_HOUR = 60;
Seconds per minute.
/** * Seconds per minute. */
private static final int SECONDS_PER_MINUTE = 60;
Seconds per hour.
/** * Seconds per hour. */
private static final int SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR;
Seconds per day.
/** * Seconds per day. */
private static final int SECONDS_PER_DAY = SECONDS_PER_HOUR * HOURS_PER_DAY;
The pattern for parsing.
/** * The pattern for parsing. */
private static final Pattern PATTERN = Pattern.compile("P?(?:([0-9]+)D)?" + "(T?(?:([0-9]+)H)?(?:([0-9]+)M)?(?:([0-9]+)?S)?)?", Pattern.CASE_INSENSITIVE);
The number of seconds in the duration.
/** * The number of seconds in the duration. */
private final long seconds;
Constructs an instance of Duration using seconds.
Params:
  • seconds – the length of the duration in seconds, positive or negative
/** * Constructs an instance of {@code Duration} using seconds. * * @param seconds the length of the duration in seconds, positive or negative */
private Duration(final long seconds) { super(); this.seconds = seconds; }
Obtains a Duration from a text string such as PnDTnHnMnS.

This will parse a textual representation of a duration, including the string produced by toString(). The formats accepted are based on the ISO-8601 duration format PnDTnHnMnS with days considered to be exactly 24 hours.

This implementation does not support negative numbers or fractions (so the smallest non-zero value a Duration can have is one second).

The string optionally starts with the ASCII letter "P" in upper or lower case. There are then four sections, each consisting of a number and a suffix. The sections have suffixes in ASCII of "D", "H", "M" and "S" for days, hours, minutes and seconds, accepted in upper or lower case. The suffixes must occur in order. The ASCII letter "T" may occur before the first occurrence, if any, of an hour, minute or second section. At least one of the four sections must be present, and if "T" is present there must be at least one section after the "T". The number part of each section must consist of one or more ASCII digits. The number may not be prefixed by the ASCII negative or positive symbol. The number of days, hours, minutes and seconds must parse to a long.

Examples:

   "PT20S" -- parses as "20 seconds"
   "PT15M"     -- parses as "15 minutes" (where a minute is 60 seconds)
   "PT10H"     -- parses as "10 hours" (where an hour is 3600 seconds)
   "P2D"       -- parses as "2 days" (where a day is 24 hours or 86400 seconds)
   "P2DT3H4M"  -- parses as "2 days, 3 hours and 4 minutes"
Params:
  • text – the text to parse, not null
Throws:
Returns:the parsed duration, not null
/** * Obtains a {@code Duration} from a text string such as {@code PnDTnHnMnS}. * <p> * This will parse a textual representation of a duration, including the string produced by {@code toString()}. The * formats accepted are based on the ISO-8601 duration format {@code PnDTnHnMnS} with days considered to be exactly * 24 hours. * <p> * This implementation does not support negative numbers or fractions (so the smallest non-zero value a Duration can * have is one second). * <p> * The string optionally starts with the ASCII letter "P" in upper or lower case. There are then four sections, each * consisting of a number and a suffix. The sections have suffixes in ASCII of "D", "H", "M" and "S" for days, * hours, minutes and seconds, accepted in upper or lower case. The suffixes must occur in order. The ASCII letter * "T" may occur before the first occurrence, if any, of an hour, minute or second section. At least one of the four * sections must be present, and if "T" is present there must be at least one section after the "T". The number part * of each section must consist of one or more ASCII digits. The number may not be prefixed by the ASCII negative or * positive symbol. The number of days, hours, minutes and seconds must parse to a {@code long}. * <p> * Examples: * * <pre> * "PT20S" -- parses as "20 seconds" * "PT15M" -- parses as "15 minutes" (where a minute is 60 seconds) * "PT10H" -- parses as "10 hours" (where an hour is 3600 seconds) * "P2D" -- parses as "2 days" (where a day is 24 hours or 86400 seconds) * "P2DT3H4M" -- parses as "2 days, 3 hours and 4 minutes" * </pre> * * @param text the text to parse, not null * @return the parsed duration, not null * @throws IllegalArgumentException if the text cannot be parsed to a duration */
public static Duration parse(final CharSequence text) { Objects.requireNonNull(text, "text"); final Matcher matcher = PATTERN.matcher(text); if (matcher.matches()) { // check for letter T but no time sections if ("T".equals(matcher.group(2)) == false) { final String dayMatch = matcher.group(1); final String hourMatch = matcher.group(3); final String minuteMatch = matcher.group(4); final String secondMatch = matcher.group(5); if (dayMatch != null || hourMatch != null || minuteMatch != null || secondMatch != null) { final long daysAsSecs = parseNumber(text, dayMatch, SECONDS_PER_DAY, "days"); final long hoursAsSecs = parseNumber(text, hourMatch, SECONDS_PER_HOUR, "hours"); final long minsAsSecs = parseNumber(text, minuteMatch, SECONDS_PER_MINUTE, "minutes"); final long seconds = parseNumber(text, secondMatch, 1, "seconds"); try { return create(daysAsSecs, hoursAsSecs, minsAsSecs, seconds); } catch (final ArithmeticException ex) { throw new IllegalArgumentException("Text cannot be parsed to a Duration (overflow) " + text, ex); } } } } throw new IllegalArgumentException("Text cannot be parsed to a Duration: " + text); } private static long parseNumber(final CharSequence text, final String parsed, final int multiplier, final String errorText) { // regex limits to [0-9]+ if (parsed == null) { return 0; } try { final long val = Long.parseLong(parsed); return val * multiplier; } catch (final Exception ex) { throw new IllegalArgumentException("Text cannot be parsed to a Duration: " + errorText + " (in " + text + ")", ex); } } private static Duration create(final long daysAsSecs, final long hoursAsSecs, final long minsAsSecs, final long secs) { return create(daysAsSecs + hoursAsSecs + minsAsSecs + secs); }
Obtains an instance of Duration using seconds.
Params:
  • seconds – the length of the duration in seconds, positive only
/** * Obtains an instance of {@code Duration} using seconds. * * @param seconds the length of the duration in seconds, positive only */
private static Duration create(final long seconds) { if ((seconds) == 0) { return ZERO; } return new Duration(seconds); }
Converts this duration to the total length in milliseconds.
Returns:the total length of the duration in milliseconds
/** * Converts this duration to the total length in milliseconds. * * @return the total length of the duration in milliseconds */
public long toMillis() { return seconds * 1000L; } @Override public boolean equals(final Object obj) { if (obj == this) { return true; } if (!(obj instanceof Duration)) { return false; } final Duration other = (Duration) obj; return other.seconds == this.seconds; } @Override public int hashCode() { return (int) (seconds ^ (seconds >>> 32)); }
A string representation of this duration using ISO-8601 seconds based representation, such as PT8H6M12S.

The format of the returned string will be PnDTnHnMnS, where n is the relevant days, hours, minutes or seconds part of the duration. If a section has a zero value, it is omitted. The hours, minutes and seconds are all positive.

Examples:

   "20 seconds"                     -- "PT20S
   "15 minutes" (15 * 60 seconds)   -- "PT15M"
   "10 hours" (10 * 3600 seconds)   -- "PT10H"
   "2 days" (2 * 86400 seconds)     -- "P2D"
Returns:an ISO-8601 representation of this duration, not null
/** * A string representation of this duration using ISO-8601 seconds based representation, such as {@code PT8H6M12S}. * <p> * The format of the returned string will be {@code PnDTnHnMnS}, where n is the relevant days, hours, minutes or * seconds part of the duration. If a section has a zero value, it is omitted. The hours, minutes and seconds are * all positive. * <p> * Examples: * * <pre> * "20 seconds" -- "PT20S * "15 minutes" (15 * 60 seconds) -- "PT15M" * "10 hours" (10 * 3600 seconds) -- "PT10H" * "2 days" (2 * 86400 seconds) -- "P2D" * </pre> * * @return an ISO-8601 representation of this duration, not null */
@Override public String toString() { if (this == ZERO) { return "PT0S"; } final long days = seconds / SECONDS_PER_DAY; final long hours = (seconds % SECONDS_PER_DAY) / SECONDS_PER_HOUR; final int minutes = (int) ((seconds % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE); final int secs = (int) (seconds % SECONDS_PER_MINUTE); final StringBuilder buf = new StringBuilder(24); buf.append("P"); if (days != 0) { buf.append(days).append('D'); } if ((hours | minutes | secs) != 0) { buf.append('T'); } if (hours != 0) { buf.append(hours).append('H'); } if (minutes != 0) { buf.append(minutes).append('M'); } if (secs == 0 && buf.length() > 0) { return buf.toString(); } buf.append(secs).append('S'); return buf.toString(); } /* * (non-Javadoc) * * @see java.lang.Comparable#compareTo(java.lang.Object) */ @Override public int compareTo(final Duration other) { return Long.signum(toMillis() - other.toMillis()); } }