Search code examples
javatimestampsqldatetime

why does Timestamp print difference between run model and debug model in unit test?


    java.sql.Date date = java.sql.Date.valueOf("1900-01-01");
    //-2209017600000
    System.out.println(date.getTime());
    java.sql.Timestamp timestamp = new Timestamp(date.getTime());
    System.out.println(timestamp); 

if directly running in unit test, the result will be 1900-01-01 00:00:00.0

if running with debug in unit test, the result will be 1970-01-01 07:30:00.0

How does it output 1900-01-01 00:00:00.0? Where is it stored?

Why not output 1970-01-01 00:00:00.0 ? becase I saw the comment of Timestamp constructor says milliseconds since January 1, 1970, 00:00:00 GMT. A negative number is the number of milliseconds before January 1, 1970, 00:00:00 GMT.


Solution

  • tl;dr

    Avoid the terrible old date-time classes. Use java.time. Poof, all the bizarre behavior you are seeing is gone, and your question is moot.

    LocalDate                 // A class to represent a date-only value, without time-of-day, without time zone. Replaces `java.sql.Date` which only pretends to be date-only but actually has both a time-of-day and a time zone.
    .parse( "1900-01-01" )    // Standard ISO 8601 formatted strings are parsed directly by the *java.time* classes. 
    .atStartOfDay(            // Let java.time determine the first moment of a day.
        ZoneId.of( "Pacific/Auckland" ) 
    )                         // Returns a `ZonedDateTime` object.
    .toString()               // Generates a `String` with text in standard ISO 8601 format, wisely extended by appending the name of the time zone in square brackets.
    

    1900-01-01T00:00+11:30[Pacific/Auckland]

    You are torturing yourself with these Questions about the legacy date-time classes. Sun, Oracle, and the JCP community all gave up on those classes years ago when adopting JSR 310. I suggest you do the same.

    Never use java.sql.Date

    This class is part of the terrible old date-time classes that were supplanted years ago by java.time classes. This java.sql.Date in particular is especially badly designed. It extends java.util.Date while the documentation tells us to ignore the fact of that inheritance. As a subclass, it pretends to be a date-only value but actually has a time-of-day inherited from the other Date, which in turn is misnamed having both a date and a time-of-day. In addition, a time zone lurks deep within these classes, though inaccessible without any getter or setter method. Confusing? Yes, an awful mess. Never use java.sql.Date.

    Instead, use java.time.LocalDate.

    LocalDate ld = LocalDate.parse( "1900-01-01" ) ;
    

    ld.toString(): 1900-01-01

    Never use java.sql.Timestamp

    As with java.sql.Date, the java.sql.Timestamp class was replaced years ago. Use java.time.Instant. If handed a Timestamp, immediately convert using the new conversion methods added to the old classes.

    If you want the first moment of the day for a particular date, let LocalDate determine that. The first moment is not always 00:00:00, so never assume that. Specify the time zone of the region whose people use the particular wall-clock time you care about.

    Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).

    ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
    ZonedDateTime zdt = ld.atStartOfDay( z ) ;
    

    To see the same moment in UTC, extract a Instant. The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).

    Instant instant = zdt.toInstant() ;
    

    If you wanted the first moment of the day in UTC, use OffsetDateTime.

    OffsetDateTime odt = ld.atOffset( ZoneOffset.UTC ) ;
    

    Conversion

    If you must interoperate with old code not yet updated to java.time classes, you can convert back-and-forth. Call new methods added to the old classes.

    java.sql.Timestamp ts = Timestamp.from( instant ) ;
    

    …and…

    Instant instant = ts.toInstant() ;
    

    Ditto for date.

    java.sql.Date d = java.sql.Date.valueOf( ld ) ;
    

    …and…

    LocalDate ld = d.toLocalDate() ;
    

    About java.time

    The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

    The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

    To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

    You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

    Where to obtain the java.time classes?

    The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.