robWDC
robWDC

Reputation: 139

SimpleDateFormat - day, month, time are incorrect

This code converts a vendor proprietary timestamp in hex (radix16) format to a Unix time. I know from the Java docs that the SimpleDateFormat is zero-based month (Jan=0), however, when converting from a Unix timestamp to a human readable format using the format string, neither the month, day nor time is correct (see the results in the comments near the end).

Expected:

Yr 2010 Mo 1 Da 4 Time 13513407 (03:45:13:407).  

Results:

HR:20100205_032123239.  

The month is 02 instead of 01 (as expected), but the day is 05 instead of 04. The hour (03) is correct, but the mins, secs, and millis are not.

I've included the pass "text" value used for testing in the comments.

public static long parseUnsignedHex(String text) {
    // text = "07DA010400CE32BF"
    long bsUnixTs = 0;
    if (text.length() == 16) {
        String bsYr = text.substring(1, 4);
        String bsMo = text.substring(5,6);
        String bsDa = text.substring(7,8);
        String bsTime = text.substring(9,16);
        try {
            long bsYrL = Long.decode("0x" + bsYr);
            long bsMoL = Long.decode("0x" + bsMo);
            long bsDaL = Long.decode("0x" + bsDa);
            long bsTimeL = Long.decode("0x" + bsTime);
            Calendar bsCal = Calendar.getInstance();
            bsCal.set((int)bsYrL, (int)bsMoL, (int)bsDaL);
            bsCal.setTimeInMillis(bsCal.getTimeInMillis() + bsTimeL);
            bsUnixTs = bsCal.getTimeInMillis();
            System.out.printf("Yr %d Mo %d Da %d Time %d%n", bsYrL, bsMoL, bsDaL, bsTimeL);
        } catch (NumberFormatException e) {
            e.printStackTrace();
        } catch (Throwable e){
            e.printStackTrace();
        }
    }
    SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmssSSS");
    // convert unix into yyyyMMdd_HHmmssSSS Human Readable (HR) format
    String[][] arTimeString = new String[1][2];
    for(int i=0; i<arTimeString.length; i++){
        arTimeString[i][0] = Long.toString(bsUnixTs);
        arTimeString[i][1] = sdf.format(bsUnixTs);
        try {
                System.out.format("Unix:" + arTimeString[i][0] + " HR:" + arTimeString[i][1]);
                System.out.println();
                // result:
                // SimpleDateFormat - month is zero-based
                //                    the day of month may be incremented because the time is 03:45:13:407
                // Yr 2010 Mo 1 Da 4 Time 13513407
                // Unix:1265358083239 HR:20100205_032123239
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
    return bsUnixTs;
}

Upvotes: 0

Views: 149

Answers (1)

MadProgrammer
MadProgrammer

Reputation: 347194

Basically, change...

Calendar bsCal = Calendar.getInstance();
bsCal.set((int)bsYrL, (int)bsMoL, (int)bsDaL);
bsCal.setTimeInMillis(bsCal.getTimeInMillis() + bsTimeL);

To

Calendar bsCal = Calendar.getInstance();
bsCal.set((int) bsYrL, (int) bsMoL - 1, (int) bsDaL, 0, 0);
bsCal.set(Calendar.SECOND, 0);
bsCal.set(Calendar.MILLISECOND, 0);

This will result in the output

Yr 2010 Mo 1 Da 4 Time 13513407
Unix:1262537113407 HR:20100104_034513407

Calendar uses 0 based months, not SimpleDateFormat, also, when using Calendar#set it will only affect the fields you supply, leaving the hour, minute and second and millisconds unchanged from the time the Calendar was created (which is a snapshot of that time/date value)

So what your original code was doing was capturing a snapshot of the current date AND time, resetting the year, month and day values, the adding an additional time value to the CURRENT time...

Upvotes: 1

Related Questions