In der Anwendung, die ich entwickle, muss ich das Java.time.Instant-Objekt in Java.sql.Timestamp konvertieren. Wenn ich ein Instant-Objekt erstelle, z
Instant now = Instant.now();
und prüfen Sie das Element. Ich erhalte so etwas wie 2017-03-13T14:28:59.970Z
.__ und versuche dann ein Timestamp-Objekt wie folgt zu erstellen:
Timestamp current = Timestamp.from(now);
Und wenn ich das Element inspiziere. Ich erhalte so etwas wie 2017-03-13T16:28:59.970Z
Dasselbe Ergebnis, aber mit zusätzlichen 2 Stunden Versatz .. Kann jemand erklären, was passiert, und mir eine Antwort geben, wie ich dies ohne Offset lösen kann?
Wenn ich so erstellt habe:
LocalDateTime ldt = LocalDateTime.ofInstant(Instnant.now(), ZoneOffset.UTC);
Timestamp current = Timestamp.valueOf(ldt);
Alles funktioniert gut. Ich versuche jedoch, Konvertierungen zu vermeiden. Gibt es eine Möglichkeit, dies nur mit Instant-Objekten zu tun?
Ich habe die Zeitzone meines Computers für ein Experiment in Europa/Bukarest geändert. Dies ist UTC + 2 Stunden wie in Ihrer Zeitzone.
Wenn ich nun Ihren Code kopiere, erhalte ich ein ähnliches Ergebnis wie Sie:
Instant now = Instant.now();
System.out.println(now); // prints 2017-03-14T06:16:32.621Z
Timestamp current = Timestamp.from(now);
System.out.println(current); // 2017-03-14 08:16:32.621
Die Ausgabe erfolgt in Kommentaren. Ich mache jedoch weiter:
DateFormat df = DateFormat.getDateTimeInstance();
df.setTimeZone(TimeZone.getTimeZone("UTC"));
// the following prints: Timestamp in UTC: 14-03-2017 06:16:32
System.out.println("Timestamp in UTC: " + df.format(current));
Jetzt können Sie sehen, dass die Timestamp
wirklich mit der Instant
übereinstimmt, von der wir ausgegangen sind (nur die Millisekunden werden nicht gedruckt, aber ich gehe davon aus, dass sie auch dort sind). Sie haben also alles richtig gemacht und sind nur verwirrt, weil wir beim Drucken der Timestamp
implizit die toString
-Methode aufgerufen haben. Diese Methode greift wiederum die Zeitzoneneinstellung des Computers auf und zeigt die Zeit in dieser Zone an. Nur deshalb sind die Anzeigen unterschiedlich.
Die andere Sache, die Sie mit LocalDateTime
versucht haben, scheint zu funktionieren, aber es gibt Ihnen wirklich nicht das, was Sie wollen:
LocalDateTime ldt = LocalDateTime.ofInstant(Instant.now(), ZoneOffset.UTC);
System.out.println(ldt); // 2017-03-14T06:16:32.819
current = Timestamp.valueOf(ldt);
System.out.println(current); // 2017-03-14 06:16:32.819
System.out.println("Timestamp in UTC: " + df.format(current)); // 14-03-2017 04:16:32
Wenn wir nun die Timestamp
mit unserer UTC DateFormat
drucken, können wir sehen, dass es 2 Stunden zu früh ist, 04:16:32 UTC, wenn die Instant
06:16:32 UTC ist. Diese Methode täuscht also, es sieht so aus, als würde sie funktionieren, aber sie funktioniert nicht.
Dies zeigt die Schwierigkeiten, die zum Design der Java 8-Datums- und Zeitklassen führten, um die alten zu ersetzen. Die wirkliche und gute Lösung für Ihr Problem wäre wahrscheinlich, sich einen JDBC 4.2-Treiber zu beschaffen, der ein Instant
-Objekt problemlos akzeptieren kann, sodass Sie die Konvertierung in Timestamp
vollständig vermeiden können. Ich weiß nicht, ob das noch für Sie verfügbar ist, aber ich bin überzeugt, dass dies der Fall sein wird.
LocalDateTime ldt = LocalDateTime.ofInstant(Instnant.now(), ZoneOffset.UTC);
Timestamp current = Timestamp.valueOf(ldt);
Zwei Probleme mit diesem Code.
Erstens, mischen Sie niemals die modernen Java.time Klassen (LocalDateTime
hier) mit den schrecklichen alten alten Datum-Zeit-Klassen (Java.sql.Timestamp
Hier). Das Java.time Framework ersetzt ab der Einführung von JSR 31 vollständig die schrecklichen alten Klassen. Sie müssen Timestamp
nie wieder verwenden: Ab JDBC 4.2 können wir Java.time Objekte direkt mit der Datenbank austauschen.
Das andere Problem ist, dass die Klasse LocalDateTime
per Definition keinen Moment darstellen kann. Es fehlt absichtlich eine Zeitzone oder ein Versatz von UTC. Verwenden Sie LocalDateTime
nur, wenn Sie ein Datum mit der Uhrzeit überall oder überall meinen, mit anderen Worten viele weitere Momente in einem Bereich von etwa 26 bis 27 Stunden (die aktuellen Extreme von Zeitzonen rund um den Globus).
Verwenden Sie nicht LocalDateTime
, wenn Sie einen bestimmten Moment, einen bestimmten Punkt auf der Zeitachse meinen. Verwenden Sie stattdessen:
Instant
(immer in UTC)OffsetDateTime
(Datum mit Uhrzeit und Offset von UTC)ZonedDateTime
(Datum mit Uhrzeit und Zeitzone).Dann versuche ich, ein Timestamp-Objekt zu erstellen
Nicht.
Verwenden Sie niemals Java.sql.Timestamp
. Ersetzt durch Java.time.Instant
. Lesen Sie weiter für weitere Informationen.
Verwenden Sie eine der folgenden Methoden, um den aktuellen Moment in UTC zu erfassen:
Instant instant = Instant.now() ;
…oder…
OffsetDateTime odt = OffsetDateTime.now( ZoneOffset.UTC );
Beide repräsentieren genau dasselbe, einen Moment in UTC.
Hier ist ein Beispiel SQL und der Java Code, um den aktuellen Moment in die Datenbank zu übertragen.
In diesem Beispiel wird das in Java integrierte H2-Datenbankmodul verwendet.
sql = "INSERT INTO event_ ( name_ , when_ ) " + "VALUES ( ? , ? ) ;";
try ( PreparedStatement preparedStatement = conn.prepareStatement( sql ) ; ) {
String name = "whatever";
OffsetDateTime odt = OffsetDateTime.now( ZoneOffset.UTC );
preparedStatement.setString( 1 , name );
preparedStatement.setObject( 2 , odt );
preparedStatement.executeUpdate();
}
Hier ist eine vollständige Beispiel-App, die diesen Code verwendet.
package com.basilbourque.example;
import Java.sql.*;
import Java.time.OffsetDateTime;
import Java.time.ZoneOffset;
import Java.util.UUID;
public class MomentIntoDatabase {
public static void main ( String[] args ) {
MomentIntoDatabase app = new MomentIntoDatabase();
app.doIt();
}
private void doIt ( ) {
try {
Class.forName( "org.h2.Driver" );
} catch ( ClassNotFoundException e ) {
e.printStackTrace();
}
try (
Connection conn = DriverManager.getConnection( "jdbc:h2:mem:moment_into_db_example_" ) ;
Statement stmt = conn.createStatement() ;
) {
String sql = "CREATE TABLE event_ (\n" +
" id_ UUID DEFAULT random_uuid() PRIMARY KEY ,\n" +
" name_ VARCHAR NOT NULL ,\n" +
" when_ TIMESTAMP WITH TIME ZONE NOT NULL\n" +
") ; ";
System.out.println( sql );
stmt.execute( sql );
// Insert row.
sql = "INSERT INTO event_ ( name_ , when_ ) " + "VALUES ( ? , ? ) ;";
try ( PreparedStatement preparedStatement = conn.prepareStatement( sql ) ; ) {
String name = "whatever";
OffsetDateTime odt = OffsetDateTime.now( ZoneOffset.UTC );
preparedStatement.setString( 1 , name );
preparedStatement.setObject( 2 , odt );
preparedStatement.executeUpdate();
}
// Query all.
sql = "SELECT * FROM event_ ;";
try ( ResultSet rs = stmt.executeQuery( sql ) ; ) {
while ( rs.next() ) {
//Retrieve by column name
UUID id = ( UUID ) rs.getObject( "id_" ); // Cast the `Object` object to UUID if your driver does not support JDBC 4.2 and its ability to pass the expected return type for type-safety.
String name = rs.getString( "name_" );
OffsetDateTime odt = rs.getObject( "when_" , OffsetDateTime.class );
//Display values
System.out.println( "id: " + id + " | name: " + name + " | when: " + odt );
}
}
} catch ( SQLException e ) {
e.printStackTrace();
}
}
}
In Bezug auf einen verwandten Kommentar von Melnyk ist hier ein weiteres Beispiel, das auf dem obigen Beispielcode basiert. Anstatt den aktuellen Moment zu erfassen, analysiert dieser Code eine Zeichenfolge.
In der Eingabezeichenfolge fehlt ein Indikator für Zeitzone oder Versatz-von-UTC . Also analysieren wir als LocalDateTime
, wobei wir berücksichtigen, dass dies nicht einen Moment darstellt, nicht ein Punkt auf der Timeline.
String input = "22.11.2018 00:00:00";
DateTimeFormatter f = DateTimeFormatter.ofPattern( "dd.MM.uuuu HH:mm:ss" );
LocalDateTime ldt = LocalDateTime.parse( input , f );
ldt.toString (): 2018-11-22T00: 00
Wir wurden jedoch darüber informiert, dass die Zeichenfolge einen Moment in UTC darstellen sollte, aber der Absender hat es vermasselt und diese Informationen nicht aufgenommen (z. B. Z
oder +00:00
Am Ende, um UTC zu bedeuten ). Wir können also einen Versatz von UTC von null Stunden, Minuten und Sekunden anwenden, um einen tatsächlichen Moment, einen bestimmten Punkt auf der Zeitachse, zu bestimmen. Das Ergebnis als OffsetDateTime
Objekt.
OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC );
odt.toString (): 2018-11-22T00: 00Z
Das Z
am Ende bedeutet UTC und wird als "Zulu" ausgesprochen. In der Norm ISO 8601 definiert.
Jetzt, da wir einen Moment Zeit haben, können wir ihn in einer Spalte des SQL-Standardtyps TIMESTAMP WITH TIME ZONE
An die Datenbank senden.
preparedStatement.setObject( 2 , odt );
Wenn dann diesen gespeicherten Wert abrufen.
OffsetDateTime odt = rs.getObject( "when_" , OffsetDateTime.class );
2018-11-22T00: 00Z
Hier ist die komplette App für dieses Beispiel.
package com.basilbourque.example;
import Java.sql.*;
import Java.time.LocalDateTime;
import Java.time.OffsetDateTime;
import Java.time.ZoneOffset;
import Java.time.format.DateTimeFormatter;
import Java.util.UUID;
public class MomentIntoDatabase {
public static void main ( String[] args ) {
MomentIntoDatabase app = new MomentIntoDatabase();
app.doIt();
}
private void doIt ( ) {
try {
Class.forName( "org.h2.Driver" );
} catch ( ClassNotFoundException e ) {
e.printStackTrace();
}
try (
Connection conn = DriverManager.getConnection( "jdbc:h2:mem:moment_into_db_example_" ) ;
Statement stmt = conn.createStatement() ;
) {
String sql = "CREATE TABLE event_ (\n" +
" id_ UUID DEFAULT random_uuid() PRIMARY KEY ,\n" +
" name_ VARCHAR NOT NULL ,\n" +
" when_ TIMESTAMP WITH TIME ZONE NOT NULL\n" +
") ; ";
System.out.println( sql );
stmt.execute( sql );
// Insert row.
sql = "INSERT INTO event_ ( name_ , when_ ) " + "VALUES ( ? , ? ) ;";
try ( PreparedStatement preparedStatement = conn.prepareStatement( sql ) ; ) {
String name = "whatever";
String input = "22.11.2018 00:00:00";
DateTimeFormatter f = DateTimeFormatter.ofPattern( "dd.MM.uuuu HH:mm:ss" );
LocalDateTime ldt = LocalDateTime.parse( input , f );
System.out.println( "ldt.toString(): " + ldt );
OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC );
System.out.println( "odt.toString(): " + odt );
preparedStatement.setString( 1 , name );
preparedStatement.setObject( 2 , odt );
preparedStatement.executeUpdate();
}
// Query all.
sql = "SELECT * FROM event_ ;";
try ( ResultSet rs = stmt.executeQuery( sql ) ; ) {
while ( rs.next() ) {
//Retrieve by column name
UUID id = ( UUID ) rs.getObject( "id_" ); // Cast the `Object` object to UUID if your driver does not support JDBC 4.2 and its ability to pass the expected return type for type-safety.
String name = rs.getString( "name_" );
OffsetDateTime odt = rs.getObject( "when_" , OffsetDateTime.class );
//Display values
System.out.println( "id: " + id + " | name: " + name + " | when: " + odt );
}
}
} catch ( SQLException e ) {
e.printStackTrace();
}
}
}
Das Java.time -Framework ist in Java 8 und höher. Diese Klassen ersetzen das lästige alte legacy Datum-Zeit-Klassen wie Java.util.Date
, Calendar
, & SimpleDateFormat
.
Das Joda-Time -Projekt, das sich jetzt im Wartungsmodus befindet, rät zur Migration auf Java.time = Klassen.
Weitere Informationen finden Sie im Oracle Tutorial . Durchsuchen Sie Stack Overflow nach vielen Beispielen und Erklärungen. Spezifikation ist JSR 31 .
Sie können Java.time Objekte direkt mit Ihrer Datenbank austauschen. Verwenden Sie einen JDBC-Treiber , der JDBC 4.2 oder höher entspricht. Keine Notwendigkeit für Zeichenketten, keine Notwendigkeit für Klassen Java.sql.*
.
Woher bekommen Sie die Java.time-Klassen?
Das Projekt ThreeTen-Extra erweitert Java.time um zusätzliche Klassen. Dieses Projekt ist ein Testfeld für mögliche zukünftige Ergänzungen von Java.time. Hier finden Sie möglicherweise einige nützliche Klassen wie Interval
, YearWeek
, YearQuarter
, und mehr .
Wenn Sie den aktuellen Zeitstempel verwenden möchten, verwenden Sie die folgende Funktion. Ich habe dies in verschiedenen Projekten verwendet und funktioniert einwandfrei:
public static Timestamp getTimeStamp()
{
// Calendar information
Calendar calendar = Calendar.getInstance();
Java.util.Date now = calendar.getTime();
Timestamp dbStamp = new Timestamp(now.getTime());
return dbStamp;
}
Beispiel:
System.out.println( getTimeStamp() );
Ausgabe: 2017-03-13 15: 01: 34.027
EDIT
Java 8 LocalDateTime verwenden:
public static Timestamp getTimeStamp()
{
return Timestamp.valueOf(LocalDateTime.now());
}
Beim Speichern eines Datensatzes in der SQL Server-Datenbank hatte ich das gleiche Problem. Ich habe Java.sql.Timestamp.valueOf(String s)
benutzt, um Timestamp inUTCzu bekommen:
import Java.time.Instant;
import Java.time.format.DateTimeFormatter;
....
....
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(UTC);
String dateTime = dateTimeFormatter.format(Instant date);
Timestamp timestamp = Timestamp.valueOf(dateTime);
Für mich geht das.