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 the since ago 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      ago 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 the ago in a human-readable format as 9s, 27m44s, 3h12m, 3d8h, as seen on Kubernetes etc.
051     *
052     * @param  age age in millis
053     * @return     ago in human-readable.
054     */
055    public static String printAge(long age) {
056        return printDuration(age, false);
057    }
058
059    /**
060     * Prints the duration in a human-readable format as 9s, 27m44s, 3h12m, 3d8h, etc.
061     *
062     * @param  uptime the uptime in millis
063     * @return        the time used for displaying on screen or in logs
064     */
065    public static String printDuration(Duration uptime) {
066        return printDuration(uptime, false);
067    }
068
069    /**
070     * Prints the duration in a human-readable format as 9s, 27m44s, 3h12m, 3d8h, etc.
071     *
072     * @param  uptime  the uptime in millis
073     * @param  precise whether to be precise and include more details
074     * @return         the time used for displaying on screen or in logs
075     */
076    public static String printDuration(Duration uptime, boolean precise) {
077        return printDuration(uptime.toMillis(), precise);
078    }
079
080    /**
081     * Prints the duration in a human-readable format as 9s, 27m44s, 3h12m, 3d8h, etc.
082     *
083     * @param  uptime the uptime in millis
084     * @return        the time used for displaying on screen or in logs
085     */
086    public static String printDuration(long uptime) {
087        return printDuration(uptime, false);
088    }
089
090    /**
091     * Prints the duration in a human-readable format as 9s, 27m44s, 3h12m, 3d8h, etc.
092     *
093     * @param  uptime  the uptime in millis
094     * @param  precise whether to be precise and include more details
095     * @return         the time used for displaying on screen or in logs
096     */
097    public static String printDuration(long uptime, boolean precise) {
098        if (uptime <= 0) {
099            return "0ms";
100        }
101
102        StringBuilder sb = new StringBuilder();
103
104        long seconds = uptime / 1000;
105        long minutes = seconds / 60;
106        long hours = minutes / 60;
107        long days = hours / 24;
108        long millis = 0;
109        if (uptime > 1000) {
110            millis = uptime % 1000;
111        } else if (uptime < 1000) {
112            millis = uptime;
113        }
114
115        if (days > 0) {
116            sb.append(days).append("d").append(hours % 24).append("h");
117            if (precise) {
118                sb.append(minutes % 60).append("m").append(seconds % 60).append("s");
119            }
120        } else if (hours > 0) {
121            sb.append(hours % 24).append("h").append(minutes % 60).append("m");
122            if (precise) {
123                sb.append(seconds % 60).append("s");
124            }
125        } else if (minutes > 0) {
126            sb.append(minutes % 60).append("m").append(seconds % 60).append("s");
127            if (precise) {
128                sb.append(millis).append("ms");
129            }
130        } else if (seconds > 0) {
131            sb.append(seconds % 60).append("s");
132            if (precise) {
133                sb.append(millis).append("ms");
134            }
135        } else if (millis > 0) {
136            if (!precise) {
137                // less than a second so just report it as zero
138                sb.append("0s");
139            } else {
140                sb.append(millis).append("ms");
141            }
142        }
143
144        return sb.toString();
145    }
146
147    /**
148     * Converts to duration.
149     *
150     * @param source duration which can be in text format such as 15s
151     */
152    public static Duration toDuration(String source) {
153        return Duration.ofMillis(toMilliSeconds(source));
154    }
155
156    /**
157     * Converts to milliseconds.
158     *
159     * @param  source duration which can be in text format such as 15s
160     * @return        time in millis, will return 0 if the input is null or empty
161     */
162    public static long toMilliSeconds(String source) {
163        if (source == null || source.isEmpty()) {
164            return 0;
165        }
166
167        // quick conversion if its only digits
168        boolean digit = true;
169        for (int i = 0; i < source.length(); i++) {
170            char ch = source.charAt(i);
171            // special for fist as it can be negative number
172            if (i == 0 && ch == '-') {
173                continue;
174            }
175            // quick check if its 0..9
176            if (ch < '0' || ch > '9') {
177                digit = false;
178                break;
179            }
180        }
181        if (digit) {
182            return Long.parseLong(source);
183        }
184
185        long days = 0;
186        long hours = 0;
187        long minutes = 0;
188        long seconds = 0;
189        long millis = 0;
190
191        int pos = source.indexOf('d');
192        if (pos != -1) {
193            String s = source.substring(0, pos);
194            days = Long.parseLong(s);
195            source = source.substring(pos + 1);
196        }
197
198        pos = source.indexOf('h');
199        if (pos != -1) {
200            String s = source.substring(0, pos);
201            hours = Long.parseLong(s);
202            source = source.substring(pos + 1);
203        }
204
205        pos = source.indexOf('m');
206        if (pos != -1) {
207            boolean valid;
208            if (source.length() - 1 <= pos) {
209                valid = true;
210            } else {
211                // beware of minutes and not milliseconds
212                valid = source.charAt(pos + 1) != 's';
213            }
214            if (valid) {
215                String s = source.substring(0, pos);
216                minutes = Long.parseLong(s);
217                source = source.substring(pos + 1);
218            }
219        }
220
221        pos = source.indexOf('s');
222        // beware of seconds and not milliseconds
223        if (pos != -1 && source.charAt(pos - 1) != 'm') {
224            String s = source.substring(0, pos);
225            seconds = Long.parseLong(s);
226            source = source.substring(pos + 1);
227        }
228
229        pos = source.indexOf("ms");
230        if (pos != -1) {
231            String s = source.substring(0, pos);
232            millis = Long.parseLong(s);
233        }
234
235        long answer = millis;
236        if (seconds > 0) {
237            answer += 1000 * seconds;
238        }
239        if (minutes > 0) {
240            answer += 60000 * minutes;
241        }
242        if (hours > 0) {
243            answer += 3600000 * hours;
244        }
245        if (days > 0) {
246            answer += 86400000 * days;
247        }
248
249        LOG.trace("source: [{}], milliseconds: {}", source, answer);
250
251        return answer;
252    }
253
254    /**
255     * Elapsed time using milliseconds since epoch.
256     *
257     * @param  start the timestamp in milliseconds since epoch
258     * @return       the elapsed time in milliseconds
259     */
260    public static long elapsedMillisSince(long start) {
261        return System.currentTimeMillis() - start;
262    }
263
264}