it-swarm.com.de

Java - Konvertiert Java.time.Instant in Java.sql.Timestamp ohne Zonenoffset

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?

22

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.

32
Ole V.V.

Falsche Klassen zu verwenden

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:

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.

Table of date-time types in Java (both modern and legacy) and in standard SQL.

Aktueller Moment

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.

Datenbank

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

Parsing-Zeichenfolge

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

About Java.time

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 .

14
Basil Bourque

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());
}   
1
DazstaV3

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.

0
Taras Melnyk