11

java.sql.Timestamp constructor go like this:

public Timestamp(long time) {
    super((time/1000)*1000);
    nanos = (int)((time%1000) * 1000000);
    if (nanos < 0) {
        nanos = 1000000000 + nanos;     
        super.setTime(((time/1000)-1)*1000);
    }
}

It basically accepts time in millisecond and then extracts the last 3 digits and makes it nanos. So for a millisecond value of 1304135631 421, I'm getting Timestamp.getnanos() as 421000000. This is plain calculation (adding 6 zeroes at the end)... does not seems to be optimum.

A better way could have been Timestamp constructor that accepts time in nanoseconds and then calculates the nanosecond value out of that.

If you run the below program, you'll see the difference between actual nanoseconds and the one returned by Timestamp way of calculating nanosecods.

long a = System.currentTimeMillis();
    for(;;){
        long b = System.currentTimeMillis();
        Timestamp tm = new Timestamp(System.currentTimeMillis());
        System.out.println(tm.getTime());
        System.out.println(tm.getNanos());
        System.out.println("This is actual nanos" + System.nanoTime()%1000000000);
        System.out.println("--------------------------");
        if(b-a >= 1)
            break;
    }

So all the discussion about Timestamp that says it stores time up to nanoseconds , does not seems to be so correct.. Isn't?

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Vicky
  • 121
  • 1
  • 1
  • 6

4 Answers4

12

The time in millis does not represent the time in nanos. More precise it simply can't be. You're supposed to use Timestamp#setNanos() to set the real nanos.

long timeInMillis = System.currentTimeMillis();
long timeInNanos = System.nanoTime();

Timestamp timestamp = new Timestamp(timeInMillis);
timestamp.setNanos((int) (timeInNanos % 1000000000));

// ...
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • 1
    Yes.. This will certainly work. So to make Timestamp actually store nanos, it should be two step process. Bit awkward, instead a Constructor that accepts nanos (from Jan 1, 1970) would have been a better solution. – Vicky Apr 30 '11 at 06:21
  • 3
    No this is not safe! It wraps! The problem is that `currentTimeMillis` and `nanoTime` may not agree on the larger resolution. e.g. `nanoTime` returns the values 12:06:30.9999, 12:06: **31** .1111 but `currentTimeMillis` returns 12:06:30.666 12:06: **30** .777, then using the above code to initialise the timestamp would result in timestamps 12:06:30.9999, 12:06: **30** .1111. Separately, re @Vicky 's comment, how would you initialise a timestamp before 1970? – Luciano Jul 09 '14 at 11:17
  • 2
    It is true that setting nano seconds must be set separately, but using `System#nanoTime()` like this is not correct. The API documentation for `System#nanoTime()` says: "This method can only be used to measure elapsed time and is not related to any other notion of system or wall-clock time. The value returned represents nanoseconds since some fixed but arbitrary time (perhaps in the future, so values may be negative)." (see http://download.oracle.com/javase/6/docs/api/java/lang/System.html#nanoTime%28%29) – Michael Paesold Apr 05 '16 at 11:45
5

Since the introduction of java.time.*, there is a new factory method in java.sql.Timestamp: Timestamp.from(Instant.now()) will do the job (with nanoseconds precision). There is also Timestamp.toInstant() to convert it the other way around.

  • 1
    Java 8 offers [`LocalDateTime.now()`](https://docs.oracle.com/javase/8/docs/api/java/time/LocalDateTime.html#now--) ... Or if < 8, use `System.nanoTime()` to get current timestamp .... – KarelG Mar 31 '17 at 12:06
0

Although it's an old post, I would like to add that the docs of Timestamp does state that it "holds fractional seconds by allowing the specification of fractional seconds to a precision of nanaoseconds". The confusing part is "hold". This seems confusing at first but if understood correctly, it actually does not state that it holds nanaoseconds value.It says it "holds" fractional value and allows it to be a "precision" of nanoseconds. Precision should be understood in terms of representation of total number of digits. So it essentially means that the part is actually fractional (still milliseconds) but is multiplied by 1000000 to represent it as nanoseconds.

The accepted answer (by ever helpful BaluC) sums it up nicely.

Shailendra
  • 8,874
  • 2
  • 28
  • 37
0

I like OpenJPA's implementation of TimestampHelper. It use static initializers to keep track of elapsed nanoseconds between calls to make a timestamp.

Stavr00
  • 3,219
  • 1
  • 16
  • 28