2011-04-30 32 views
6

java.sql.Timestamp konstruktor iść tak:java.sql.Timestamp sposób przechowywania nanosekund

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); 
    } 
} 

Zasadniczo przyjmuje czas w milisekundach, a następnie wyodrębnia 3 ostatnie cyfry i sprawia, że ​​nano. Tak więc dla wartości milisekundowej 1304135631 , otrzymuję Timestamp.getnanos() jako . To proste obliczenie (dodanie 6 zer na końcu) ... nie wydaje się optymalne.

Lepszym sposobem może być konstruktor znacznika czasu, który akceptuje czas w nanosekundach, a następnie oblicza wartość nanosekundy.

Jeśli uruchomisz poniższy program, zobaczysz różnicę między rzeczywistymi nanosekundami a tą zwróconą przez sposób obliczania nanosekund.

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; 
    } 

więc cała dyskusja o Timestamp, które mówi, że przechowuje czas do nanosekund, nie wydaje się być tak poprawne .. Czy nie?

Odpowiedz

6

Czas w milisekundach nie odzwierciedla czasu w nanosach. Bardziej precyzyjny, po prostu nie może być. Powinieneś użyć Timestamp#setNanos(), aby ustawić prawdziwe nanosy.

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

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

// ... 
+1

Tak ... To na pewno zadziała. Aby znacznik czasu faktycznie przechowywał nanopy, powinien to być proces dwuetapowy. Nieco niewygodne, zamiast tego Konstruktor, który akceptuje nanos (od 1 stycznia 1970) byłby lepszym rozwiązaniem. – Vicky

+1

Nie jest to bezpieczne! Omija! Problem polega na tym, że 'currentTimeMillis' i' nanoTime' mogą nie zgodzić się na większą rozdzielczość. na przykład 'nanoTime' zwraca wartości 12: 06: 30.9999, 12:06: ** 31 ** .1111, ale' currentTimeMillis' zwraca 12: 06: 30.666 12:06: ** 30 ** .777, a następnie używając powyższego kodu aby zainicjować znacznik czasu, pojawią się znaczniki czasu 12: 06: 30.9999, 12:06: ** 30 ** .1111. Oddzielnie, komentarz re @Vicky, jak zainicjowałbyś znacznik czasu przed 1970? – Luciano

+0

To prawda, że ​​ustawienie sekund nano musi być ustawione osobno, ale użycie 'System # nanoTime()' w ten sposób nie jest poprawne. Dokumentacja API dla 'System # nanoTime()' mówi: "Ta metoda może być używana tylko do pomiaru czasu, który upłynął i nie jest związana z żadnym innym pojęciem czasu systemu lub zegara ściennego.Zwrócona wartość reprezentuje nanosekundy od pewnego ustalonego, ale arbitralnego czasu (być może w przyszłości, więc wartości mogą być ujemne). "(Zobacz http://download.oracle.com/javase/6/docs/api/java/lang/System .html # nanoTime% 28% 29) –

0

Mimo to stary post, chciałbym dodać, że docs znacznika czasu robi państwo, że „trzyma ułamków sekund, pozwalając specyfikację ułamków sekund z dokładnością do nanaoseconds”. Częścią mylącą jest "przytrzymaj". Na początku wydaje się to mylące, ale jeśli jest zrozumiane poprawnie, to w rzeczywistości nie stwierdza, że ​​ma wartość nanaosekundy. Oznacza to, że "utrzymuje" wartość ułamkową i pozwala na "dokładność" nanosekund. Dokładność należy rozumieć pod względem reprezentacji całkowitej liczby cyfr. Oznacza to więc, że część jest faktycznie ułamkowa (nadal milisekundy), ale jest pomnożona przez 1000000, aby przedstawić ją jako nanosekundy.

Przyjęta odpowiedź (przez zawsze pomocny BaluC) podsumowuje to ładnie.

0

Podoba mi się wdrożenie OpenJPA TimestampHelper. Używa statycznych inicjalizatorów do śledzenia upływających nanosekund między połączeniami w celu utworzenia znacznika czasu.

1

Od czasu wprowadzenia , jest nowa metoda fabryczna w java.sql.Timestamp: Timestamp.from(Instant.now()) wykona zadanie (z precyzją nanosekund). Istnieje również Timestamp.toInstant(), aby przekonwertować go na odwrót.

+0

Java 8 oferuje ['LocalDateTime.now()'] (https://docs.oracle.com/javase/8/docs/api/java/time/LocalDateTime.html#now -) ... Lub jeśli <8, użyj 'System.nanoTime()', aby uzyskać aktualny timestamp .... – KarelG

Powiązane problemy