2

I have monitored my java program using jconsole recently. I saved CPU usage data as a csv file. This is what I've got:

Time,CPU Usage
43690.008014,1,8
43690.008060,0,1
43690.008106,0,1
43690.008153,0,1
43690.008199,0,1
43690.008245,0,1

The CPU Usage column is clear, but I cannot say the same thing about the Time column. What is 43690.008014? How can I parse it into Date? I haven't seen anything like this in my life.

Michiel Leegwater
  • 1,172
  • 4
  • 11
  • 27
Tony
  • 459
  • 1
  • 5
  • 18
  • Is your answer in the [Using JConsole](https://docs.oracle.com/javase/7/docs/technotes/guides/management/jconsole.html) doc? lots of info there. – Kaan Aug 13 '19 at 21:17
  • The time format is based on the number of days since `1/1/1900` (based on this [`link`](https://coderanch.com/t/203072/java/JConsole-csv-time-format)). – second Aug 13 '19 at 21:23
  • But if they really based it on excel, then it seem to suffer from the [`time leap bug`](https://support.microsoft.com/en-us/help/214326/excel-incorrectly-assumes-that-the-year-1900-is-a-leap-year) as well, as its one day off. – second Aug 13 '19 at 22:05

2 Answers2

2

The duration recorded in the CSV file is the number of days since 1899-12-311. In order to get the current date, you can add this duration to LocalDate.of(1899, 12, 30).atStartOfDay().atOffset(ZoneOffset.UTC). I recommend you use java.time.Duration which is modelled on ISO-8601 standards and was introduced with Java-8 as part of JSR-310 implementation. The function, Duration#ofNanos gives you a Duration representing the specified number of nanoseconds. The reason why I am recommending you to use this function despite the fact that there is already a function, Duration#ofDays is that these functions take a long value as the argument and if you cast the duration in your log file (e.g. 43690.008014) to long, its fractional part will be lost giving you an incorrect result.

Therefore, convert these days to nanoseconds, get Duration from the resulting nanoseconds and add the same to LocalDate.of(1899, 12, 30).atStartOfDay().atOffset(ZoneOffset.UTC) to get the current date and time in UTC.

Demo:

import java.time.Duration;
import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;

public class Main {
    public static void main(String[] args) {
        OffsetDateTime startDateTime = LocalDate.of(1899, 12, 30).atStartOfDay().atOffset(ZoneOffset.UTC);
        OffsetDateTime current = startDateTime
                .plus(Duration.ofNanos((long) (43690.008014 * 24 * 60 * 60 * 1000_000_000)));
        System.out.println(current);
    }
}

Output:

2019-08-13T00:11:32.409600512Z

Learn about the modern date-time API from Trail: Date Time.


1 Why is 1899-12-30 the zero date in Access / SQL Server instead of 12/31?

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
0

The time format is daysSinceEpoch.fractionOfCurrentDay

The daysSinceEpoch starting at 1899-12-30 instead of 1900-01-01

The fractionOfCurrentDay is a millionth of the current day, ranging from zero to 999999 (23:59:59)

You can use the following function to convert to LocalDateTime:

   public static LocalDateTime convert( final String jconsoleDateFormat )
   {
      String[] split = jconsoleDateFormat.split( "\\." );
      long daysSinceEpoch = Long.parseLong( split[0] );
      long dayFraction = Long.parseLong( split[1] );

      LocalDateTime epochDate = LocalDateTime.of( 1899,12,30, 0,0 );
      LocalDateTime currentDate = epochDate.plusDays( daysSinceEpoch );

      long secondsADay = 24 * 60 * 60L; // 86_400
      long secondsSinceDayStarted = secondsADay * dayFraction / 1_000_000;
      return currentDate.plusSeconds( secondsSinceDayStarted );
   }

   public static void main( String[] args )
   {
      System.out.println( convert( "43690.008014" ) ); // 2019-08-13T00:11:32
   }