//JavaClasses/src/main/java/mattrixwv/Stopwatch.java //Matthew Ellison (Mattrixwv) // Created: 03-01-19 //Modified: 08-11-24 //This file contains a class that is used to time the execution time of other programs /* Copyright (C) 2024 Matthew Ellison This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ package com.mattrixwv; import com.mattrixwv.exceptions.InvalidResult; /** * A simple stopwatch class to measure elapsed time in various units of resolution. * *

* This class provides methods to start, stop, and reset a stopwatch, as well as retrieve the * elapsed time in nanoseconds, microseconds, milliseconds, seconds, minutes, and hours. It * also includes functionality to get the elapsed time as a formatted string in the most * appropriate time unit. *

*/ public class Stopwatch{ /** * The start time of the stopwatch in nanoseconds. */ private Long startTime; /** * The stop time of the stopwatch in nanoseconds. */ private Long stopTime; //?Constructor makes sure all values are set to defaults /** * Constructs a new Stopwatch and initializes the start and stop times to {@code null}. * This ensures that incorrect function calling order can be detected easily. */ public Stopwatch(){ //Make sure both values are null so it is easier to detect incorrect function calling order startTime = null; stopTime = null; } //?Returns a long with the elapsed time in nanoseconds. Used by other functions to get the time before converting it to the correct resolution /** * Returns the elapsed time in nanoseconds. *

* If the stopwatch has not been started, it returns {@code 0L}. If the stopwatch has * been started but not stopped, it returns the time elapsed since it was started. *

* * @return the elapsed time in nanoseconds */ private Long getTime(){ if(startTime == null){ return 0L; } else if(stopTime == null){ return System.nanoTime() - startTime; } else{ return stopTime - startTime; } } //?An enum that helps keep track of how many times the time has been reduced in the getStr function /** * Enum to represent the time resolution for formatting the time string. */ private enum TIME_RESOLUTION{ NANOSECOND, MICROSECOND, MILLISECOND, SECOND, MINUTE, HOUR, ERROR } //?Simulates starting a stopwatch by saving the time /** * Starts the stopwatch by recording the current time in nanoseconds. *

* If the stopwatch was already running, the previous start time is overwritten. *

*/ public void start(){ //Make sure the stop time is reset to 0 stopTime = null; //Get the time as close to returning from the function as possible startTime = System.nanoTime(); } //?Simulates stopping a stopwatch by saving the time /** * Stops the stopwatch by recording the current time in nanoseconds. *

* If the stopwatch has not been started, this method has no effect. *

*/ public void stop(){ //Set the stopTime as close to call time as possible stopTime = System.nanoTime(); //If the startTime has not been set then reset stopTime if(startTime == null){ stopTime = null; } } //?Resets all variables in the stopwatch /** * Resets the stopwatch, clearing the start and stop times. *

* After calling this method, the stopwatch needs to be started again before measuring * elapsed time. *

*/ public void reset(){ //Make sure all variables are reset correctly startTime = null; stopTime = null; } //?Returns the time in nanoseconds /** * Returns the elapsed time in nanoseconds. * * @return the elapsed time in nanoseconds as a {@code double} */ public double getNano(){ return getTime().doubleValue(); } //?Returns the time in microseconds /** * Returns the elapsed time in microseconds. * * @return the elapsed time in microseconds as a {@code double} */ public double getMicro(){ return getTime().doubleValue() / 1000D; } //?Returns the time in milliseconds /** * Returns the elapsed time in milliseconds. * * @return the elapsed time in milliseconds as a {@code double} */ public double getMilli(){ return getTime().doubleValue() / 1000000D; } //?Returns the time in seconds /** * Returns the elapsed time in seconds. * * @return the elapsed time in seconds as a {@code double} */ public double getSecond(){ return getTime().doubleValue() / 1000000000D; } //?Returns the time in minutes /** * Returns the elapsed time in minutes. * * @return the elapsed time in minutes as a {@code double} */ public double getMinute(){ return getTime().doubleValue() / 60000000000D; } //?Returns the time in hours /** * Returns the elapsed time in hours. * * @return the elapsed time in hours as a {@code double} */ public double getHour(){ return getTime().doubleValue() / 3600000000000D; } //?Returns the time as a string at the 'best' resolution. (Goal is xxx.xxx) /** * Returns the elapsed time as a formatted string with the most appropriate resolution. *

* The method automatically selects the best time resolution to display the time in a * human-readable format. *

* * @return a formatted string representing the elapsed time * @throws InvalidResult if the time resolution is invalid */ public String getStr() throws InvalidResult{ //Get the current duration from time return getStr(getTime().doubleValue()); } /** * Returns a formatted string representing the given time in nanoseconds with the most * appropriate resolution. * * @param nanoseconds the time in nanoseconds to format * @return a formatted string representing the given time * @throws InvalidResult if the time resolution is invalid */ public static String getStr(double nanoseconds) throws InvalidResult{ Double duration = nanoseconds; //Reduce the number to the appropriate number of digits. (xxx.x). //This loop works down to seconds TIME_RESOLUTION resolution; for(resolution = TIME_RESOLUTION.NANOSECOND;(resolution.ordinal() < TIME_RESOLUTION.SECOND.ordinal()) && (duration >= 1000);resolution = TIME_RESOLUTION.values()[resolution.ordinal() + 1]){ duration /= 1000; } //Check if the duration needs reduced to minutes if((duration >= 120) && (resolution == TIME_RESOLUTION.SECOND)){ //Reduce to minutes duration /= 60; resolution = TIME_RESOLUTION.values()[resolution.ordinal() + 1]; //Check if the duration needs reduced to hours if(duration >= 60){ //Reduce to hours duration /= 60; resolution = TIME_RESOLUTION.values()[resolution.ordinal() + 1]; } } if(duration < 0){ resolution = TIME_RESOLUTION.ERROR; } //Turn the number into a string int durationFraction = (int)Math.round(((duration % 1) * 1000)); String time = String.format("%d.%03d", duration.intValue(), durationFraction); //Tack on the appropriate suffix for resolution switch(resolution){ case NANOSECOND: time += " nanoseconds"; break; case MICROSECOND: time += " microseconds"; break; case MILLISECOND: time += " milliseconds"; break; case SECOND: time += " seconds"; break; case MINUTE: time += " minutes"; break; case HOUR: time += " hours"; break; case ERROR: default: throw new InvalidResult("timeResolution was invalid"); } //Return the string return time; } /** * Returns a string representation of the elapsed time in the most appropriate resolution. * * @return a string representation of the elapsed time */ @Override public String toString(){ return getStr(); } }