Search code examples
javabukkit

Java Calendar Duplicating


I'm making an Elapsed Timer, which works pretty good besides the fact that when the timer reaches 0:59 it jumps to 2:00 instead of 1:00. Not sure if it's doing the same for 10:00 or hours, but I don't know why it's doing this.

public long time() {
    long ends = System.nanoTime();
    return ends - starts;
}

public int time(TimeUnit unit) {
    return (int) unit.convert(time(), TimeUnit.NANOSECONDS);
}

public String toHoursMinutesSeconds() {
    SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");

    Calendar now = Calendar.getInstance();
    now.set(Calendar.HOUR_OF_DAY, time(TimeUnit.HOURS));
    now.set(Calendar.MINUTE, time(TimeUnit.MINUTES));
    now.set(Calendar.SECOND, time(TimeUnit.SECONDS));

    return sdf.format(now.getTime());
}

Solution

  • If you are using Java 8 or greater, you can take advantage of the new Date and Time library.

    public class EnhancedTimer {
        static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("HH:mm:ss");
        private final long initialTime;
    
        public EnhancedTimer() {
            initialTime = System.nanoTime();
        }
    
        public long elapsedTime() {
            return System.nanoTime() - initialTime;
        }
    
        public String toString() {
            return formatTime(elapsedTime());
        }
    
        public static String formatTime(long timestamp) {
            return LocalTime.ofNanoOfDay(timestamp).format(DATE_FORMAT);
        }
    }
    

    Usage

    import java.util.concurrent.TimeUnit;
    
    public class TimerTest {
        public static void main(String[] args) {
            EnhancedTimer timer = new EnhancedTimer();
            long sleepDelay = 5000; // Sleep every 5 seconds.
    
            try {
                while (true) {
                    System.out.printf("%s (%d)%n", timer, timer.elapsedTime());
                    Thread.sleep(sleepDelay);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                e.printStackTrace();
            }
        }
    }
    

    Original Response

    The following is my original response, in case there are people who still do not have access to Java 8.

    You need to set the values from the most significant to the least siginificant.

    e.g.: days → hours → minutes → seconds → etc.

    Or else the values will compound. I wrote a nice Timer that is based on your original code. I just made some adjustments. The main method will print the elapsed time and then sleep for 5 seconds. This repeat until the program is killed.

    import java.text.DateFormat;
    import java.text.SimpleDateFormat;
    import java.util.Calendar;
    import java.util.concurrent.TimeUnit;
    
    public class Timer {
        protected static final DateFormat DATE_FORMAT = new SimpleDateFormat("HH:mm:ss");
        protected static final TimeUnit UNIT = TimeUnit.NANOSECONDS;
        private final long initialTime;
    
        private static final long NANOSECONDS_IN_DAY = TimeUnit.DAYS.toNanos(1);
        private static final long NANOSECONDS_IN_HOUR = TimeUnit.HOURS.toNanos(1);
        private static final long NANOSECONDS_IN_MINUTE = TimeUnit.MINUTES.toNanos(1);
    
        public Timer() {
            initialTime = System.nanoTime();
        }
    
        public long elapsedTime() {
            return System.nanoTime() - initialTime;
        }
    
        public String toString() {
            return formatTime(elapsedTime(), UNIT, DATE_FORMAT);
        }
    
        private static final int nanosecondsToHours(long nanoseconds) {
            return (int) UNIT.toHours(nanoseconds % NANOSECONDS_IN_DAY);
        }
    
        private static final int nanosecondsToMinutes(long nanoseconds) {
            return (int) UNIT.toMinutes(nanoseconds % NANOSECONDS_IN_HOUR);
        }
    
        private static final int nanosecondsToSeconds(long nanoseconds) {
            return (int) UNIT.toSeconds(nanoseconds % NANOSECONDS_IN_MINUTE);
        }
    
        public static String formatTime(long timestamp, TimeUnit unit, DateFormat dateFormat) {
            Calendar cal = Calendar.getInstance();
    
            cal.set(Calendar.HOUR_OF_DAY, nanosecondsToHours(timestamp));
            cal.set(Calendar.MINUTE, nanosecondsToMinutes(timestamp));
            cal.set(Calendar.SECOND, nanosecondsToSeconds(timestamp));
    
            return dateFormat.format(cal.getTime());
        }
    }
    

    Usage

    import java.util.concurrent.TimeUnit;
    
    public class TimerTest {
        public static void main(String[] args) {
            testTime();
            timeLoop();
        }
    
        private static void testTime() {
            long hours_23      = TimeUnit.HOURS.toNanos(23);
            long mins_55       = TimeUnit.MINUTES.toNanos(55);
            long secs_55       = TimeUnit.SECONDS.toNanos(55);
            long time_23_55_55 = hours_23 + mins_55 + secs_55;
    
            System.out.println(Timer.formatTime(time_23_55_55, Timer.UNIT, Timer.DATE_FORMAT)); // 23:55:55
        }
    
        private static void timeLoop() {
            Timer timer = new Timer();
            long sleepDelay = 5000; // Sleep every 5 seconds.
    
            try {
                while (true) {
                    System.out.printf("%s (%d)%n", timer, timer.elapsedTime());
                    Thread.sleep(sleepDelay);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                e.printStackTrace();
            }
        }
    }