2929/**
3030 * Support {@code Duration} parsing and printing in several styles, as listed in
3131 * {@link DurationFormat.Style}.
32+ *
3233 * <p>Some styles may not enforce any unit to be present, defaulting to {@code DurationFormat.Unit#MILLIS}
3334 * in that case. Methods in this class offer overloads that take a {@link DurationFormat.Unit} to
3435 * be used as a fall-back instead of the ultimate MILLIS default.
4041 */
4142public abstract class DurationFormatterUtils {
4243
43- private DurationFormatterUtils () {
44- // singleton
45- }
44+ private static final Pattern ISO_8601_PATTERN = Pattern .compile ("^[+-]?[pP].*$" );
4645
47- /**
48- * Parse the given value to a duration.
49- * @param value the value to parse
50- * @param style the style in which to parse
51- * @return a duration
52- */
53- public static Duration parse (String value , DurationFormat .Style style ) {
54- return parse (value , style , null );
55- }
46+ private static final Pattern SIMPLE_PATTERN = Pattern .compile ("^([+-]?\\ d+)([a-zA-Z]{0,2})$" );
47+
48+ private static final Pattern COMPOSITE_PATTERN = Pattern .compile ("^([+-]?)\\ (?\\ s?(\\ d+d)?\\ s?(\\ d+h)?\\ s?(\\ d+m)?" +
49+ "\\ s?(\\ d+s)?\\ s?(\\ d+ms)?\\ s?(\\ d+us)?\\ s?(\\ d+ns)?\\ )?$" );
5650
57- /**
58- * Parse the given value to a duration.
59- * @param value the value to parse
60- * @param style the style in which to parse
61- * @param unit the duration unit to use if the value doesn't specify one ({@code null}
62- * will default to ms)
63- * @return a duration
64- */
65- public static Duration parse (String value , DurationFormat .Style style , DurationFormat .@ Nullable Unit unit ) {
66- Assert .hasText (value , () -> "Value must not be empty" );
67- return switch (style ) {
68- case ISO8601 -> parseIso8601 (value );
69- case SIMPLE -> parseSimple (value , unit );
70- case COMPOSITE -> parseComposite (value );
71- };
72- }
7351
7452 /**
7553 * Print the specified duration in the specified style.
@@ -98,27 +76,30 @@ public static String print(Duration value, DurationFormat.Style style, DurationF
9876 }
9977
10078 /**
101- * Detect the style then parse the value to return a duration.
79+ * Parse the given value to a duration.
10280 * @param value the value to parse
103- * @return the parsed duration
104- * @throws IllegalArgumentException if the value is not a known style or cannot be
105- * parsed
81+ * @param style the style in which to parse
82+ * @return a duration
10683 */
107- public static Duration detectAndParse (String value ) {
108- return detectAndParse (value , null );
84+ public static Duration parse (String value , DurationFormat . Style style ) {
85+ return parse (value , style , null );
10986 }
11087
11188 /**
112- * Detect the style then parse the value to return a duration.
89+ * Parse the given value to a duration.
11390 * @param value the value to parse
91+ * @param style the style in which to parse
11492 * @param unit the duration unit to use if the value doesn't specify one ({@code null}
11593 * will default to ms)
116- * @return the parsed duration
117- * @throws IllegalArgumentException if the value is not a known style or cannot be
118- * parsed
94+ * @return a duration
11995 */
120- public static Duration detectAndParse (String value , DurationFormat .@ Nullable Unit unit ) {
121- return parse (value , detect (value ), unit );
96+ public static Duration parse (String value , DurationFormat .Style style , DurationFormat .@ Nullable Unit unit ) {
97+ Assert .hasText (value , () -> "Value must not be empty" );
98+ return switch (style ) {
99+ case ISO8601 -> parseIso8601 (value );
100+ case SIMPLE -> parseSimple (value , unit );
101+ case COMPOSITE -> parseComposite (value );
102+ };
122103 }
123104
124105 /**
@@ -142,10 +123,30 @@ public static DurationFormat.Style detect(String value) {
142123 throw new IllegalArgumentException ("'" + value + "' is not a valid duration, cannot detect any known style" );
143124 }
144125
145- private static final Pattern ISO_8601_PATTERN = Pattern .compile ("^[+-]?[pP].*$" );
146- private static final Pattern SIMPLE_PATTERN = Pattern .compile ("^([+-]?\\ d+)([a-zA-Z]{0,2})$" );
147- private static final Pattern COMPOSITE_PATTERN = Pattern .compile ("^([+-]?)\\ (?\\ s?(\\ d+d)?\\ s?(\\ d+h)?\\ s?(\\ d+m)?" +
148- "\\ s?(\\ d+s)?\\ s?(\\ d+ms)?\\ s?(\\ d+us)?\\ s?(\\ d+ns)?\\ )?$" );
126+ /**
127+ * Detect the style then parse the value to return a duration.
128+ * @param value the value to parse
129+ * @return the parsed duration
130+ * @throws IllegalArgumentException if the value is not a known style or cannot be
131+ * parsed
132+ */
133+ public static Duration detectAndParse (String value ) {
134+ return detectAndParse (value , null );
135+ }
136+
137+ /**
138+ * Detect the style then parse the value to return a duration.
139+ * @param value the value to parse
140+ * @param unit the duration unit to use if the value doesn't specify one ({@code null}
141+ * will default to ms)
142+ * @return the parsed duration
143+ * @throws IllegalArgumentException if the value is not a known style or cannot be
144+ * parsed
145+ */
146+ public static Duration detectAndParse (String value , DurationFormat .@ Nullable Unit unit ) {
147+ return parse (value , detect (value ), unit );
148+ }
149+
149150
150151 private static Duration parseIso8601 (String value ) {
151152 try {
@@ -156,6 +157,11 @@ private static Duration parseIso8601(String value) {
156157 }
157158 }
158159
160+ private static String printSimple (Duration duration , DurationFormat .@ Nullable Unit unit ) {
161+ unit = (unit == null ? DurationFormat .Unit .MILLIS : unit );
162+ return unit .print (duration );
163+ }
164+
159165 private static Duration parseSimple (String text , DurationFormat .@ Nullable Unit fallbackUnit ) {
160166 try {
161167 Matcher matcher = SIMPLE_PATTERN .matcher (text );
@@ -172,34 +178,6 @@ private static Duration parseSimple(String text, DurationFormat.@Nullable Unit f
172178 }
173179 }
174180
175- private static String printSimple (Duration duration , DurationFormat .@ Nullable Unit unit ) {
176- unit = (unit == null ? DurationFormat .Unit .MILLIS : unit );
177- return unit .print (duration );
178- }
179-
180- private static Duration parseComposite (String text ) {
181- try {
182- Matcher matcher = COMPOSITE_PATTERN .matcher (text );
183- Assert .state (matcher .matches () && matcher .groupCount () > 1 , "Does not match composite duration pattern" );
184- String sign = matcher .group (1 );
185- boolean negative = sign != null && sign .equals ("-" );
186-
187- Duration result = Duration .ZERO ;
188- DurationFormat .Unit [] units = DurationFormat .Unit .values ();
189- for (int i = 2 ; i < matcher .groupCount () + 1 ; i ++) {
190- String segment = matcher .group (i );
191- if (StringUtils .hasText (segment )) {
192- DurationFormat .Unit unit = units [units .length - i + 1 ];
193- result = result .plus (unit .parse (segment .replace (unit .asSuffix (), "" )));
194- }
195- }
196- return negative ? result .negated () : result ;
197- }
198- catch (Exception ex ) {
199- throw new IllegalArgumentException ("'" + text + "' is not a valid composite duration" , ex );
200- }
201- }
202-
203181 private static String printComposite (Duration duration ) {
204182 if (duration .isZero ()) {
205183 return DurationFormat .Unit .SECONDS .print (duration );
@@ -244,4 +222,27 @@ private static String printComposite(Duration duration) {
244222 return result .toString ();
245223 }
246224
225+ private static Duration parseComposite (String text ) {
226+ try {
227+ Matcher matcher = COMPOSITE_PATTERN .matcher (text );
228+ Assert .state (matcher .matches () && matcher .groupCount () > 1 , "Does not match composite duration pattern" );
229+ String sign = matcher .group (1 );
230+ boolean negative = sign != null && sign .equals ("-" );
231+
232+ Duration result = Duration .ZERO ;
233+ DurationFormat .Unit [] units = DurationFormat .Unit .values ();
234+ for (int i = 2 ; i < matcher .groupCount () + 1 ; i ++) {
235+ String segment = matcher .group (i );
236+ if (StringUtils .hasText (segment )) {
237+ DurationFormat .Unit unit = units [units .length - i + 1 ];
238+ result = result .plus (unit .parse (segment .replace (unit .asSuffix (), "" )));
239+ }
240+ }
241+ return negative ? result .negated () : result ;
242+ }
243+ catch (Exception ex ) {
244+ throw new IllegalArgumentException ("'" + text + "' is not a valid composite duration" , ex );
245+ }
246+ }
247+
247248}
0 commit comments