001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.camel.util;
018
019import java.time.Duration;
020
021import org.slf4j.Logger;
022import org.slf4j.LoggerFactory;
023
024/**
025 * Time utils.
026 */
027public final class TimeUtils {
028
029    private static final Logger LOG = LoggerFactory.getLogger(TimeUtils.class);
030
031    private TimeUtils() {
032    }
033
034    public static boolean isPositive(Duration dur) {
035        return dur.getSeconds() > 0 || dur.getNano() != 0;
036    }
037
038    /**
039     * Prints since age in a human-readable format as 9s, 27m44s, 3h12m, 3d8h, as seen on Kubernetes etc.
040     *
041     * @param  time time of the event (millis since epoch)
042     * @return      age in human-readable since the given time.
043     */
044    public static String printSince(long time) {
045        long age = System.currentTimeMillis() - time;
046        return printDuration(age, false);
047    }
048
049    /**
050     * Prints since age in a human-readable format as 9s, 27m44s, 3h12m, 3d8h, as seen on Kubernetes etc.
051     *
052     * @param  time    time of the event (millis since epoch)
053     * @param  precise whether to be precise and include more details
054     * @return         age in human-readable since the given time.
055     */
056    public static String printSince(long time, boolean precise) {
057        long age = System.currentTimeMillis() - time;
058        return printDuration(age, precise);
059    }
060
061    /**
062     * Prints the age in a human-readable format as 9s, 27m44s, 3h12m, 3d8h, as seen on Kubernetes etc.
063     *
064     * @param  age age in millis
065     * @return     age in human-readable.
066     */
067    public static String printAge(long age) {
068        return printDuration(age, false);
069    }
070
071    /**
072     * Prints the age in a human-readable format as 9s, 27m44s, 3h12m, 3d8h, as seen on Kubernetes etc.
073     *
074     * @param  age     age in millis
075     * @param  precise whether to be precise and include more details
076     * @return         age in human-readable.
077     */
078    public static String printAge(long age, boolean precise) {
079        return printDuration(age, precise);
080    }
081
082    /**
083     * Prints the duration in a human-readable format as 9s, 27m44s, 3h12m, 3d8h, etc.
084     *
085     * @param  uptime the uptime in millis
086     * @return        the time used for displaying on screen or in logs
087     */
088    public static String printDuration(Duration uptime) {
089        return printDuration(uptime, false);
090    }
091
092    /**
093     * Prints the duration in a human-readable format as 9s, 27m44s, 3h12m, 3d8h, etc.
094     *
095     * @param  uptime  the uptime in millis
096     * @param  precise whether to be precise and include more details
097     * @return         the time used for displaying on screen or in logs
098     */
099    public static String printDuration(Duration uptime, boolean precise) {
100        return printDuration(uptime.toMillis(), precise);
101    }
102
103    /**
104     * Prints the duration in a human-readable format as 9s, 27m44s, 3h12m, 3d8h, etc.
105     *
106     * @param  uptime the uptime in millis
107     * @return        the time used for displaying on screen or in logs
108     */
109    public static String printDuration(long uptime) {
110        return printDuration(uptime, false);
111    }
112
113    /**
114     * Prints the duration in a human-readable format as 9s, 27m44s, 3h12m, 3d8h, etc.
115     *
116     * @param  uptime  the uptime in millis
117     * @param  precise whether to be precise and include more details
118     * @return         the time used for displaying on screen or in logs
119     */
120    public static String printDuration(long uptime, boolean precise) {
121        if (uptime <= 0) {
122            return "0ms";
123        }
124
125        StringBuilder sb = new StringBuilder();
126
127        long seconds = uptime / 1000;
128        long minutes = seconds / 60;
129        long hours = minutes / 60;
130        long days = hours / 24;
131        long millis = 0;
132        if (uptime > 1000) {
133            millis = uptime % 1000;
134        } else if (uptime < 1000) {
135            millis = uptime;
136        }
137
138        if (days > 0) {
139            sb.append(days).append("d").append(hours % 24).append("h");
140            if (precise) {
141                sb.append(minutes % 60).append("m").append(seconds % 60).append("s");
142            }
143        } else if (hours > 0) {
144            sb.append(hours % 24).append("h").append(minutes % 60).append("m");
145            if (precise) {
146                sb.append(seconds % 60).append("s");
147            }
148        } else if (minutes > 0) {
149            sb.append(minutes % 60).append("m").append(seconds % 60).append("s");
150            if (precise) {
151                sb.append(millis).append("ms");
152            }
153        } else if (seconds > 0) {
154            sb.append(seconds % 60).append("s");
155            if (precise) {
156                sb.append(millis).append("ms");
157            }
158        } else if (millis > 0) {
159            if (!precise) {
160                // less than a second so just report it as zero
161                sb.append("0s");
162            } else {
163                sb.append(millis).append("ms");
164            }
165        }
166
167        return sb.toString();
168    }
169
170    /**
171     * Converts to duration.
172     *
173     * @param source duration which can be in text format such as 15s
174     */
175    public static Duration toDuration(String source) {
176        return Duration.ofMillis(toMilliSeconds(source));
177    }
178
179    /**
180     * Converts to milliseconds.
181     *
182     * @param  source duration which can be in text format such as 15s
183     * @return        time in millis, will return 0 if the input is null or empty
184     */
185    public static long toMilliSeconds(String source) {
186        if (source == null || source.isEmpty()) {
187            return 0;
188        }
189
190        // quick conversion if its only digits
191        boolean digit = true;
192        for (int i = 0; i < source.length(); i++) {
193            char ch = source.charAt(i);
194            // special for fist as it can be negative number
195            if (i == 0 && ch == '-') {
196                continue;
197            }
198            // quick check if its 0..9
199            if (ch < '0' || ch > '9') {
200                digit = false;
201                break;
202            }
203        }
204        if (digit) {
205            return Long.parseLong(source);
206        }
207
208        long days = 0;
209        long hours = 0;
210        long minutes = 0;
211        long seconds = 0;
212        long millis = 0;
213
214        int pos = source.indexOf('d');
215        if (pos != -1) {
216            String s = source.substring(0, pos);
217            days = Long.parseLong(s);
218            source = source.substring(pos + 1);
219        }
220
221        pos = source.indexOf('h');
222        if (pos != -1) {
223            String s = source.substring(0, pos);
224            hours = Long.parseLong(s);
225            source = source.substring(pos + 1);
226        }
227
228        pos = source.indexOf('m');
229        if (pos != -1) {
230            boolean valid;
231            if (source.length() - 1 <= pos) {
232                valid = true;
233            } else {
234                // beware of minutes and not milliseconds
235                valid = source.charAt(pos + 1) != 's';
236            }
237            if (valid) {
238                String s = source.substring(0, pos);
239                minutes = Long.parseLong(s);
240                source = source.substring(pos + 1);
241            }
242        }
243
244        pos = source.indexOf('s');
245        // beware of seconds and not milliseconds
246        if (pos != -1 && source.charAt(pos - 1) != 'm') {
247            String s = source.substring(0, pos);
248            seconds = Long.parseLong(s);
249            source = source.substring(pos + 1);
250        }
251
252        pos = source.indexOf("ms");
253        if (pos != -1) {
254            String s = source.substring(0, pos);
255            millis = Long.parseLong(s);
256        }
257
258        long answer = millis;
259        if (seconds > 0) {
260            answer += 1000 * seconds;
261        }
262        if (minutes > 0) {
263            answer += 60000 * minutes;
264        }
265        if (hours > 0) {
266            answer += 3600000 * hours;
267        }
268        if (days > 0) {
269            answer += 86400000 * days;
270        }
271
272        LOG.trace("source: [{}], milliseconds: {}", source, answer);
273
274        return answer;
275    }
276
277    /**
278     * Elapsed time using milliseconds since epoch.
279     *
280     * @param      start the timestamp in milliseconds since epoch
281     * @return           the elapsed time in milliseconds
282     * @deprecated       Use the Clock API when possible
283     */
284    @Deprecated(since = "4.4.0")
285    public static long elapsedMillisSince(long start) {
286        return System.currentTimeMillis() - start;
287    }
288
289}