2828/**
2929 * Support {@code Duration} parsing and printing in several styles, as listed in
3030 * {@link DurationFormat.Style}.
31+ *
3132 * <p>Some styles may not enforce any unit to be present, defaulting to {@code DurationFormat.Unit#MILLIS}
3233 * in that case. Methods in this class offer overloads that take a {@link DurationFormat.Unit} to
3334 * be used as a fall-back instead of the ultimate MILLIS default.
3940 */
4041public abstract class DurationFormatterUtils {
4142
42- private DurationFormatterUtils () {
43- // singleton
44- }
43+ private static final Pattern ISO_8601_PATTERN = Pattern .compile ("^[+-]?[pP].*$" );
4544
46- /**
47- * Parse the given value to a duration.
48- * @param value the value to parse
49- * @param style the style in which to parse
50- * @return a duration
51- */
52- public static Duration parse (String value , DurationFormat .Style style ) {
53- return parse (value , style , null );
54- }
45+ private static final Pattern SIMPLE_PATTERN = Pattern .compile ("^([+-]?\\ d+)([a-zA-Z]{0,2})$" );
46+
47+ private static final Pattern COMPOSITE_PATTERN = Pattern .compile ("^([+-]?)\\ (?\\ s?(\\ d+d)?\\ s?(\\ d+h)?\\ s?(\\ d+m)?" +
48+ "\\ s?(\\ d+s)?\\ s?(\\ d+ms)?\\ s?(\\ d+us)?\\ s?(\\ d+ns)?\\ )?$" );
5549
56- /**
57- * Parse the given value to a duration.
58- * @param value the value to parse
59- * @param style the style in which to parse
60- * @param unit the duration unit to use if the value doesn't specify one ({@code null}
61- * will default to ms)
62- * @return a duration
63- */
64- public static Duration parse (String value , DurationFormat .Style style , @ Nullable DurationFormat .Unit unit ) {
65- Assert .hasText (value , () -> "Value must not be empty" );
66- return switch (style ) {
67- case ISO8601 -> parseIso8601 (value );
68- case SIMPLE -> parseSimple (value , unit );
69- case COMPOSITE -> parseComposite (value );
70- };
71- }
7250
7351 /**
7452 * Print the specified duration in the specified style.
@@ -97,27 +75,30 @@ public static String print(Duration value, DurationFormat.Style style, @Nullable
9775 }
9876
9977 /**
100- * Detect the style then parse the value to return a duration.
78+ * Parse the given value to a duration.
10179 * @param value the value to parse
102- * @return the parsed duration
103- * @throws IllegalArgumentException if the value is not a known style or cannot be
104- * parsed
80+ * @param style the style in which to parse
81+ * @return a duration
10582 */
106- public static Duration detectAndParse (String value ) {
107- return detectAndParse (value , null );
83+ public static Duration parse (String value , DurationFormat . Style style ) {
84+ return parse (value , style , null );
10885 }
10986
11087 /**
111- * Detect the style then parse the value to return a duration.
88+ * Parse the given value to a duration.
11289 * @param value the value to parse
90+ * @param style the style in which to parse
11391 * @param unit the duration unit to use if the value doesn't specify one ({@code null}
11492 * will default to ms)
115- * @return the parsed duration
116- * @throws IllegalArgumentException if the value is not a known style or cannot be
117- * parsed
93+ * @return a duration
11894 */
119- public static Duration detectAndParse (String value , @ Nullable DurationFormat .Unit unit ) {
120- return parse (value , detect (value ), unit );
95+ public static Duration parse (String value , DurationFormat .Style style , @ Nullable DurationFormat .Unit unit ) {
96+ Assert .hasText (value , () -> "Value must not be empty" );
97+ return switch (style ) {
98+ case ISO8601 -> parseIso8601 (value );
99+ case SIMPLE -> parseSimple (value , unit );
100+ case COMPOSITE -> parseComposite (value );
101+ };
121102 }
122103
123104 /**
@@ -141,10 +122,30 @@ public static DurationFormat.Style detect(String value) {
141122 throw new IllegalArgumentException ("'" + value + "' is not a valid duration, cannot detect any known style" );
142123 }
143124
144- private static final Pattern ISO_8601_PATTERN = Pattern .compile ("^[+-]?[pP].*$" );
145- private static final Pattern SIMPLE_PATTERN = Pattern .compile ("^([+-]?\\ d+)([a-zA-Z]{0,2})$" );
146- private static final Pattern COMPOSITE_PATTERN = Pattern .compile ("^([+-]?)\\ (?\\ s?(\\ d+d)?\\ s?(\\ d+h)?\\ s?(\\ d+m)?" +
147- "\\ s?(\\ d+s)?\\ s?(\\ d+ms)?\\ s?(\\ d+us)?\\ s?(\\ d+ns)?\\ )?$" );
125+ /**
126+ * Detect the style then parse the value to return a duration.
127+ * @param value the value to parse
128+ * @return the parsed duration
129+ * @throws IllegalArgumentException if the value is not a known style or cannot be
130+ * parsed
131+ */
132+ public static Duration detectAndParse (String value ) {
133+ return detectAndParse (value , null );
134+ }
135+
136+ /**
137+ * Detect the style then parse the value to return a duration.
138+ * @param value the value to parse
139+ * @param unit the duration unit to use if the value doesn't specify one ({@code null}
140+ * will default to ms)
141+ * @return the parsed duration
142+ * @throws IllegalArgumentException if the value is not a known style or cannot be
143+ * parsed
144+ */
145+ public static Duration detectAndParse (String value , @ Nullable DurationFormat .Unit unit ) {
146+ return parse (value , detect (value ), unit );
147+ }
148+
148149
149150 private static Duration parseIso8601 (String value ) {
150151 try {
@@ -155,6 +156,11 @@ private static Duration parseIso8601(String value) {
155156 }
156157 }
157158
159+ private static String printSimple (Duration duration , @ Nullable DurationFormat .Unit unit ) {
160+ unit = (unit == null ? DurationFormat .Unit .MILLIS : unit );
161+ return unit .print (duration );
162+ }
163+
158164 private static Duration parseSimple (String text , @ Nullable DurationFormat .Unit fallbackUnit ) {
159165 try {
160166 Matcher matcher = SIMPLE_PATTERN .matcher (text );
@@ -171,34 +177,6 @@ private static Duration parseSimple(String text, @Nullable DurationFormat.Unit f
171177 }
172178 }
173179
174- private static String printSimple (Duration duration , @ Nullable DurationFormat .Unit unit ) {
175- unit = (unit == null ? DurationFormat .Unit .MILLIS : unit );
176- return unit .print (duration );
177- }
178-
179- private static Duration parseComposite (String text ) {
180- try {
181- Matcher matcher = COMPOSITE_PATTERN .matcher (text );
182- Assert .state (matcher .matches () && matcher .groupCount () > 1 , "Does not match composite duration pattern" );
183- String sign = matcher .group (1 );
184- boolean negative = sign != null && sign .equals ("-" );
185-
186- Duration result = Duration .ZERO ;
187- DurationFormat .Unit [] units = DurationFormat .Unit .values ();
188- for (int i = 2 ; i < matcher .groupCount () + 1 ; i ++) {
189- String segment = matcher .group (i );
190- if (StringUtils .hasText (segment )) {
191- DurationFormat .Unit unit = units [units .length - i + 1 ];
192- result = result .plus (unit .parse (segment .replace (unit .asSuffix (), "" )));
193- }
194- }
195- return negative ? result .negated () : result ;
196- }
197- catch (Exception ex ) {
198- throw new IllegalArgumentException ("'" + text + "' is not a valid composite duration" , ex );
199- }
200- }
201-
202180 private static String printComposite (Duration duration ) {
203181 if (duration .isZero ()) {
204182 return DurationFormat .Unit .SECONDS .print (duration );
@@ -243,4 +221,27 @@ private static String printComposite(Duration duration) {
243221 return result .toString ();
244222 }
245223
224+ private static Duration parseComposite (String text ) {
225+ try {
226+ Matcher matcher = COMPOSITE_PATTERN .matcher (text );
227+ Assert .state (matcher .matches () && matcher .groupCount () > 1 , "Does not match composite duration pattern" );
228+ String sign = matcher .group (1 );
229+ boolean negative = sign != null && sign .equals ("-" );
230+
231+ Duration result = Duration .ZERO ;
232+ DurationFormat .Unit [] units = DurationFormat .Unit .values ();
233+ for (int i = 2 ; i < matcher .groupCount () + 1 ; i ++) {
234+ String segment = matcher .group (i );
235+ if (StringUtils .hasText (segment )) {
236+ DurationFormat .Unit unit = units [units .length - i + 1 ];
237+ result = result .plus (unit .parse (segment .replace (unit .asSuffix (), "" )));
238+ }
239+ }
240+ return negative ? result .negated () : result ;
241+ }
242+ catch (Exception ex ) {
243+ throw new IllegalArgumentException ("'" + text + "' is not a valid composite duration" , ex );
244+ }
245+ }
246+
246247}
0 commit comments