Beste Möglichkeit, mit Datumsangaben in Android SQLite zu arbeiten [geschlossen]


236

Ich habe Probleme beim Arbeiten mit Datumsangaben in meiner Android-Anwendung, die SQLite verwendet. Ich habe ein paar Fragen:

  1. Welchen Typ soll ich zum Speichern von Datumsangaben in SQLite verwenden (Text, Ganzzahl, ...)?
  2. Wie speichere ich Datumsangaben mit ContentValues ​​ordnungsgemäß?
  3. Wie kann das Datum am besten aus der SQLite-Datenbank abgerufen werden?
  4. Wie kann man eine SQL-Auswahl in SQLite treffen und die Ergebnisse nach Datum sortieren?

2
Verwenden Sie einfach die Calendar-Klasse und ihre Mitgliedszeit (die die Anzahl der Millisekunden darstellt, die seit dem 1.1.1970 vergangen sind). Es gibt Elementfunktionen zum Mutieren des Zeitwerts in vom Benutzer lesbare Zeichenfolgen.
Slayton

Antworten:


42

Sie können ein Textfeld verwenden, um Daten darin zu speichern SQLite.

Wenn Sie Datumsangaben im UTC-Format speichern, können Sie standardmäßig nach der datetime('now') (yyyy-MM-dd HH:mm:ss)Datumsspalte sortieren.

Wenn Sie Datumsangaben als Zeichenfolgen abrufen SQLite, können Sie sie nach Bedarf mithilfe des Kalenders oder der android.text.format.DateUtils.formatDateTimeMethode formatieren / in lokale regionalisierte Formate konvertieren .

Hier ist eine regionalisierte Formatierungsmethode, die ich verwende.

public static String formatDateTime(Context context, String timeToFormat) {

    String finalDateTime = "";          

    SimpleDateFormat iso8601Format = new SimpleDateFormat(
            "yyyy-MM-dd HH:mm:ss");

    Date date = null;
    if (timeToFormat != null) {
        try {
            date = iso8601Format.parse(timeToFormat);
        } catch (ParseException e) {
            date = null;
        }

        if (date != null) {
            long when = date.getTime();
            int flags = 0;
            flags |= android.text.format.DateUtils.FORMAT_SHOW_TIME;
            flags |= android.text.format.DateUtils.FORMAT_SHOW_DATE;
            flags |= android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
            flags |= android.text.format.DateUtils.FORMAT_SHOW_YEAR;

            finalDateTime = android.text.format.DateUtils.formatDateTime(context,
            when + TimeZone.getDefault().getOffset(when), flags);               
        }
    }
    return finalDateTime;
}

63
Wie würden Sie mit der Abfrage von Datumsbereichen umgehen?
Joe

51
"Empfohlene Praxis"? Klingt nicht richtig.
Shim

135
In den Jahren, in denen ich SQL verwendet habe, habe ich noch nie jemanden gesehen, der empfohlen hat, Datumsangaben als Zeichenfolgen zu speichern. Wenn Sie keinen bestimmten Datumsspaltentyp haben, verwenden Sie eine Ganzzahl und speichern Sie diese in Unix-Zeit (Sekunden seit der Epoche). Es ist sortierbar und in Bereichen verwendbar und leicht zu konvertieren.
Mikebabcock

20
Das Speichern von Datumsangaben als Zeichenfolge ist in Ordnung, wenn Sie sie als "Informationen" speichern möchten, die Sie abrufen und anzeigen. Wenn Sie jedoch Daten als "Daten" speichern möchten, sollten Sie in Betracht ziehen, sie als Ganzzahl seit der Epoche zu speichern. Auf diese Weise können Sie Datumsbereiche abfragen. Dies ist Standard, sodass Sie sich keine Gedanken über Konvertierungen usw. machen müssen. Das Speichern von Datumsangaben als Zeichenfolge ist sehr einschränkend, und ich würde wirklich gerne wissen, wer diese Vorgehensweise in der Regel empfohlen hat.
Krystian

8
In der SQLite-Dokumentation wird das Speichern als Text (ISO 8601) als praktikable Lösung zum Speichern von Daten aufgeführt. Eigentlich ist es zuerst aufgeführt.
anderspitman

210

Am besten speichern Sie die Daten als Zahl, die Sie mit dem Befehl Kalender erhalten.

//Building the table includes:
StringBuilder query=new StringBuilder();
query.append("CREATE TABLE "+TABLE_NAME+ " (");
query.append(COLUMN_ID+"int primary key autoincrement,");
query.append(COLUMN_DATETIME+" int)");

//And inserting the data includes this:
values.put(COLUMN_DATETIME, System.currentTimeMillis()); 

Warum das? Zunächst einmal ist es einfach, Werte aus einem Datumsbereich abzurufen. Konvertieren Sie einfach Ihr Datum in Millisekunden und fragen Sie dann entsprechend ab. Das Sortieren nach Datum ist ähnlich einfach. Die Aufrufe zum Konvertieren zwischen verschiedenen Formaten sind ebenfalls einfach, wie ich eingeschlossen habe. Unterm Strich können Sie mit dieser Methode alles tun, was Sie tun müssen, ohne Probleme. Es wird etwas schwierig sein, einen Rohwert zu lesen, aber es macht diesen kleinen Nachteil mehr als wett, weil es leicht maschinenlesbar und verwendbar ist. Tatsächlich ist es relativ einfach, einen Reader zu erstellen (und ich weiß, dass es einige gibt), der das Zeit-Tag automatisch in ein solches konvertiert, um das Lesen zu vereinfachen.

Es ist erwähnenswert, dass die Werte, die daraus entstehen, lang sein sollten, nicht int. Ganzzahl in SQLite kann viele Dinge bedeuten, alles von 1 bis 8 Bytes, aber für fast alle Daten funktionieren 64 Bit oder eine lange.

BEARBEITEN: Wie in den Kommentaren erwähnt, müssen Sie den verwenden cursor.getLong(), um den Zeitstempel richtig zu erhalten, wenn Sie dies tun.


Ich habe Ihre Lösung auf das Speicherdatum angewendet, aber als ich den Wert von db abgerufen habe, ist es eine negative Zahl. Damit kann a das Kalenderobjekt nicht wiederherstellen. Hast du eine Idee damit? P / S: Ich habe die Spalte als BIGINTEGER definiert.
Sohn Huy TRAN

1
Das Datum sollte niemals als negative Zahl angezeigt werden ... Ich vermute, dass irgendwo eine falsche Eingabe stattfindet. Stellen Sie sicher, dass das, was in die Datenbank gelangt, das ist, was Sie daraus machen. Wenn Sie wirklich keine Lösung finden, stellen Sie hier eine neue Frage.
PearsonArtPhoto

17
Danke Mann. Lol Ich dachte an eine falsche Eingabe, konnte sie aber nicht finden. Es muss von cursor.getLong () abgerufen werden, nicht von cursor.getInt (). Lol kann nicht aufhören über mich selbst zu lachen. Danke noch einmal.
Sohn Huy TRAN

Dies sollte die akzeptierte Antwort sein, da dies die Datumsoperationen vereinfacht. Millisekunden sind jedoch nicht gut für die Lesbarkeit.
Mohammed Ali

1
Lass mich das mal klarstellen. Sie erstellen Ihre SQLite-Datumsspalte mit int. Aber wenn Sie das Datum von einem Cursor erhalten, den Sie verwenden cursor.getLong()- Lassen Sie mich fragen: Warum ist das kein Problem? Ich dachte, ein intkönnte nicht so viele Informationen speichern wie ein Long. Wie funktioniert das genau? Ist die SQLite intetwas Besonderes ...?
Micro

37
  1. Wie in diesem Kommentar angenommen , würde ich immer Ganzzahlen verwenden, um Daten zu speichern.
  2. Zum Speichern können Sie eine Dienstprogrammmethode verwenden

    public static Long persistDate(Date date) {
        if (date != null) {
            return date.getTime();
        }
        return null;
    }

    wie so:

    ContentValues values = new ContentValues();
    values.put(COLUMN_NAME, persistDate(entity.getDate()));
    long id = db.insertOrThrow(TABLE_NAME, null, values);
  3. Eine andere Gebrauchsmethode kümmert sich um das Laden

    public static Date loadDate(Cursor cursor, int index) {
        if (cursor.isNull(index)) {
            return null;
        }
        return new Date(cursor.getLong(index));
    }

    kann wie folgt verwendet werden:

    entity.setDate(loadDate(cursor, INDEX));
  4. Die Reihenfolge nach Datum ist eine einfache SQL ORDER-Klausel (da wir eine numerische Spalte haben). Folgendes wird absteigend sortiert (das neueste Datum steht an erster Stelle):

    public static final String QUERY = "SELECT table._id, table.dateCol FROM table ORDER BY table.dateCol DESC";
    
    //...
    
        Cursor cursor = rawQuery(QUERY, null);
        cursor.moveToFirst();
    
        while (!cursor.isAfterLast()) {
            // Process results
        }

Stellen Sie immer sicher, dass Sie die UTC / GMT-Zeit speichern , insbesondere wenn Sie mit der Standardzeitzone (dh der Zeitzone Ihres Geräts) arbeiten java.util.Calendarund java.text.SimpleDateFormatdiese verwenden. java.util.Date.Date()ist sicher zu verwenden, da es einen UTC-Wert erstellt.


Ich mag diese Lösung! Vielen Dank! Ich arbeite mit etwas, das nur Präzision auf den Tag braucht. Ich möchte in der Lage sein, Zeilen in der Datenbank abzufragen, deren Datum in einem bestimmten Bereich vom aktuellen Datum liegt. ZB erhalte ich die aktuelle Uhrzeit aus dem Kalender und suche innerhalb eines Monats nach allen Zeilen mit einem Datum. Aber was ist, wenn es jetzt 2:30 Uhr ist und ich vor einem Monat ein Datum um 2:29 Uhr gespeichert habe? Es würde weggelassen, weil es weniger Sekunden als vor einem Monat hat. Gibt es eine Möglichkeit, einen Monat zurück zu gehen, aber rund um den Tag?
Justinrixx

Wäre es eine schlechte Lösung, die aktuelle Uhrzeit durch Aufrufen von calendar.set (Calendar.HOUR_OF_DAY, 0) abzurufen? calendar.set (Calendar.MINUTE, 0); an allen Daten vor dem Speichern und vor dem Abfragen?
Justinrixx

@justinrixx Das ist fast eine separate Frage. Kurz gesagt: Abhängig von Ihren Anforderungen möchte ich lieber den vollständigen Zeitstempel in der Datenbank speichern, da Sie ihn möglicherweise später benötigen. Dann "kürzen" Sie die Parameterdaten der Abfrage auf Mitternacht. Siehe hier für ein Beispiel.
Schnatterer

9

SQLite kann Text-, Real- oder Integer-Datentypen zum Speichern von Datumsangaben verwenden. Darüber hinaus werden die Ergebnisse bei jeder Abfrage im Format angezeigt %Y-%m-%d %H:%M:%S.

Wenn Sie jetzt Datums- / Zeitwerte mithilfe von SQLite-Datums- / Uhrzeitfunktionen einfügen / aktualisieren, können Sie auch Millisekunden speichern. In diesem Fall werden die Ergebnisse im Format angezeigt %Y-%m-%d %H:%M:%f. Beispielsweise:

sqlite> create table test_table(col1 text, col2 real, col3 integer);
sqlite> insert into test_table values (
            strftime('%Y-%m-%d %H:%M:%f', '2014-03-01 13:01:01.123'),
            strftime('%Y-%m-%d %H:%M:%f', '2014-03-01 13:01:01.123'),
            strftime('%Y-%m-%d %H:%M:%f', '2014-03-01 13:01:01.123')
        );
sqlite> insert into test_table values (
            strftime('%Y-%m-%d %H:%M:%f', '2014-03-01 13:01:01.126'),
            strftime('%Y-%m-%d %H:%M:%f', '2014-03-01 13:01:01.126'),
            strftime('%Y-%m-%d %H:%M:%f', '2014-03-01 13:01:01.126')
        );
sqlite> select * from test_table;
2014-03-01 13:01:01.123|2014-03-01 13:01:01.123|2014-03-01 13:01:01.123
2014-03-01 13:01:01.126|2014-03-01 13:01:01.126|2014-03-01 13:01:01.126

Führen Sie nun einige Abfragen durch, um zu überprüfen, ob wir die Zeiten tatsächlich vergleichen können:

sqlite> select * from test_table /* using col1 */
           where col1 between 
               strftime('%Y-%m-%d %H:%M:%f', '2014-03-01 13:01:01.121') and
               strftime('%Y-%m-%d %H:%M:%f', '2014-03-01 13:01:01.125');
2014-03-01 13:01:01.123|2014-03-01 13:01:01.123|2014-03-01 13:01:01.123

Sie können das gleiche SELECTmit col2und überprüfen col3und erhalten die gleichen Ergebnisse. Wie Sie sehen können, wird die zweite Zeile (126 Millisekunden) nicht zurückgegeben.

Beachten Sie, dass dies BETWEENinklusive ist, daher ...

sqlite> select * from test_table 
            where col1 between 
                 /* Note that we are using 123 milliseconds down _here_ */
                strftime('%Y-%m-%d %H:%M:%f', '2014-03-01 13:01:01.123') and
                strftime('%Y-%m-%d %H:%M:%f', '2014-03-01 13:01:01.125');

... gibt den gleichen Satz zurück.

Versuchen Sie, mit verschiedenen Datums- / Zeitbereichen herumzuspielen, und alles wird sich wie erwartet verhalten.

Was ist ohne strftimeFunktion?

sqlite> select * from test_table /* using col1 */
           where col1 between 
               '2014-03-01 13:01:01.121' and
               '2014-03-01 13:01:01.125';
2014-03-01 13:01:01.123|2014-03-01 13:01:01.123|2014-03-01 13:01:01.123

Was ist ohne strftimeFunktion und ohne Millisekunden?

sqlite> select * from test_table /* using col1 */
           where col1 between 
               '2014-03-01 13:01:01' and
               '2014-03-01 13:01:02';
2014-03-01 13:01:01.123|2014-03-01 13:01:01.123|2014-03-01 13:01:01.123
2014-03-01 13:01:01.126|2014-03-01 13:01:01.126|2014-03-01 13:01:01.126

Was ist mit ORDER BY?

sqlite> select * from test_table order by 1 desc;
2014-03-01 13:01:01.126|2014-03-01 13:01:01.126|2014-03-01 13:01:01.126
2014-03-01 13:01:01.123|2014-03-01 13:01:01.123|2014-03-01 13:01:01.123
sqlite> select * from test_table order by 1 asc;
2014-03-01 13:01:01.123|2014-03-01 13:01:01.123|2014-03-01 13:01:01.123
2014-03-01 13:01:01.126|2014-03-01 13:01:01.126|2014-03-01 13:01:01.126

Funktioniert gut.

Schließlich, wenn es um tatsächliche Operationen innerhalb eines Programms geht (ohne die ausführbare SQLite-Datei zu verwenden ...)

Übrigens: Ich verwende JDBC (bei anderen Sprachen nicht sicher) ... den sqlite-jdbc-Treiber v3.7.2 von xerial - möglicherweise ändern neuere Versionen das unten erläuterte Verhalten ... Wenn Sie in Android entwickeln, tun Sie dies nicht brauche einen JDBC-Treiber. Alle SQL-Operationen können mit dem gesendet werden SQLiteOpenHelper.

JDBC hat verschiedene Methoden aktuelle Datum / Zeit - Werte aus einer Datenbank zu erhalten: java.sql.Date, java.sql.Time, und java.sql.Timestamp.

Die zugehörigen Verfahren in java.sql.ResultSetsind (natürlich) getDate(..), getTime(..)und getTimestamp()jeweils.

Beispielsweise:

Statement stmt = ... // Get statement from connection
ResultSet rs = stmt.executeQuery("SELECT * FROM TEST_TABLE");
while (rs.next()) {
    System.out.println("COL1 : "+rs.getDate("COL1"));
    System.out.println("COL1 : "+rs.getTime("COL1"));
    System.out.println("COL1 : "+rs.getTimestamp("COL1"));
    System.out.println("COL2 : "+rs.getDate("COL2"));
    System.out.println("COL2 : "+rs.getTime("COL2"));
    System.out.println("COL2 : "+rs.getTimestamp("COL2"));
    System.out.println("COL3 : "+rs.getDate("COL3"));
    System.out.println("COL3 : "+rs.getTime("COL3"));
    System.out.println("COL3 : "+rs.getTimestamp("COL3"));
}
// close rs and stmt.

Da SQLite keinen tatsächlichen Datentyp DATE / TIME / TIMESTAMP hat, geben alle diese 3 Methoden Werte zurück, als ob die Objekte mit 0 initialisiert worden wären:

new java.sql.Date(0)
new java.sql.Time(0)
new java.sql.Timestamp(0)

Die Frage ist also: Wie können wir Datums- / Zeit- / Zeitstempelobjekte tatsächlich auswählen, einfügen oder aktualisieren? Es gibt keine einfache Antwort. Sie können verschiedene Kombinationen ausprobieren, diese zwingen Sie jedoch dazu, SQLite-Funktionen in alle SQL-Anweisungen einzubetten. Es ist viel einfacher, eine Dienstprogrammklasse zu definieren, um Text in Datumsobjekte in Ihrem Java-Programm umzuwandeln. Denken Sie jedoch immer daran, dass SQLite einen beliebigen Datumswert in UTC + 0000 umwandelt.

Zusammenfassend lässt sich sagen, dass ich trotz der allgemeinen Regel, immer den richtigen Datentyp oder sogar Ganzzahlen zu verwenden, die die Unix-Zeit (Millisekunden seit der Epoche) angeben, die Verwendung des Standard-SQLite-Formats ( '%Y-%m-%d %H:%M:%f'oder in Java 'yyyy-MM-dd HH:mm:ss.SSS') viel einfacher finde , als alle Ihre SQL-Anweisungen zu komplizieren SQLite-Funktionen. Der erstere Ansatz ist viel einfacher zu pflegen.

TODO: Ich werde die Ergebnisse überprüfen, wenn ich getDate / getTime / getTimestamp in Android (API15 oder besser) verwende ... vielleicht unterscheidet sich der interne Treiber von sqlite-jdbc ...


1
Angesichts der internen Speicher-Engine von SQLite bin ich nicht davon überzeugt, dass Ihre Beispiele den von Ihnen implizierten Effekt haben: Es sieht so aus, als ob die Engine "das Speichern von speichertypisierten Werten in einer beliebigen Spalte unabhängig vom deklarierten SQL-Typ ermöglicht" ( books.google). de /… ). Es klingt für mich so, als ob in Ihrem Beispiel Real vs. Integer vs. Text Folgendes passiert: SQLite speichert den Text nur als Text in allen Baumspalten. Natürlich sind die Ergebnisse alle gut, der Speicher wird immer noch verschwendet. Wenn nur eine Ganzzahl verwendet wurde, sollten Sie Millisekunden verlieren. Ich sage nur ...
Marco

Tatsächlich können Sie bestätigen, was ich gerade gesagt habe, indem Sie eine SELECT-Datumszeit (col3, 'unixepoch') FROM test_table ausführen. Dies zeigt leere Zeilen für Ihre Beispiele an ... es sei denn, Sie fügen zu Testzwecken eine tatsächliche Ganzzahl ein. Wenn Sie beispielsweise eine Zeile mit dem col3-Wert 37 hinzufügen, zeigt die obige SELECT-Anweisung Folgendes an: 1970-01-01 00:00:37. Wenn Sie also nicht in der Lage sind, alle Ihre Daten ineffizient als Textzeichenfolge zu speichern, tun Sie nicht, was Sie vorschlagen.
Marco

Es ist lange her, dass ich diese Antwort gepostet habe ... vielleicht wurde SQLite aktualisiert. Ich kann mir nur vorstellen, die SQL-Anweisungen im Vergleich zu Ihren Vorschlägen erneut auszuführen.
Miguelt

3

Normalerweise (wie in MySQL / Postgres) speichere ich Datumsangaben in Int (MySQL / Post) oder Text (SQLite), um sie im Zeitstempelformat zu speichern.

Dann werde ich sie in Datumsobjekte konvertieren und Aktionen basierend auf dem Benutzer TimeZone ausführen


3

Der beste Weg, um datein SQlite DB zu speichern, ist das Speichern des aktuellen DateTimeMilliseconds. Unten finden Sie das Code-Snippet, um dies zu tun

  1. Bekommen das DateTimeMilliseconds
public static long getTimeMillis(String dateString, String dateFormat) throws ParseException {
    /*Use date format as according to your need! Ex. - yyyy/MM/dd HH:mm:ss */
    String myDate = dateString;//"2017/12/20 18:10:45";
    SimpleDateFormat sdf = new SimpleDateFormat(dateFormat/*"yyyy/MM/dd HH:mm:ss"*/);
    Date date = sdf.parse(myDate);
    long millis = date.getTime();

    return millis;
}
  1. Fügen Sie die Daten in Ihre Datenbank ein
public void insert(Context mContext, long dateTimeMillis, String msg) {
    //Your DB Helper
    MyDatabaseHelper dbHelper = new MyDatabaseHelper(mContext);
    database = dbHelper.getWritableDatabase();

    ContentValues contentValue = new ContentValues();
    contentValue.put(MyDatabaseHelper.DATE_MILLIS, dateTimeMillis);
    contentValue.put(MyDatabaseHelper.MESSAGE, msg);

    //insert data in DB
    database.insert("your_table_name", null, contentValue);

   //Close the DB connection.
   dbHelper.close(); 

}

Now, your data (date is in currentTimeMilliseconds) is get inserted in DB .

Der nächste Schritt ist, wenn Sie Daten aus der Datenbank abrufen möchten, müssen Sie die entsprechenden Datums- und Uhrzeit-Millisekunden in das entsprechende Datum konvertieren. Unten finden Sie das Beispielcode-Snippet, um dasselbe zu tun

  1. Konvertieren Sie Datums-Millisekunden in eine Datumszeichenfolge.
public static String getDate(long milliSeconds, String dateFormat)
{
    // Create a DateFormatter object for displaying date in specified format.
    SimpleDateFormat formatter = new SimpleDateFormat(dateFormat/*"yyyy/MM/dd HH:mm:ss"*/);

    // Create a calendar object that will convert the date and time value in milliseconds to date.
    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(milliSeconds);
    return formatter.format(calendar.getTime());
}
  1. Holen Sie sich jetzt endlich die Daten und sehen Sie, wie sie funktionieren ...
public ArrayList<String> fetchData() {

    ArrayList<String> listOfAllDates = new ArrayList<String>();
    String cDate = null;

    MyDatabaseHelper dbHelper = new MyDatabaseHelper("your_app_context");
    database = dbHelper.getWritableDatabase();

    String[] columns = new String[] {MyDatabaseHelper.DATE_MILLIS, MyDatabaseHelper.MESSAGE};
    Cursor cursor = database.query("your_table_name", columns, null, null, null, null, null);

    if (cursor != null) {

        if (cursor.moveToFirst()){
            do{
                //iterate the cursor to get data.
                cDate = getDate(cursor.getLong(cursor.getColumnIndex(MyDatabaseHelper.DATE_MILLIS)), "yyyy/MM/dd HH:mm:ss");

                listOfAllDates.add(cDate);

            }while(cursor.moveToNext());
        }
        cursor.close();

    //Close the DB connection.
    dbHelper.close(); 

    return listOfAllDates;

}

Hoffe das wird allen helfen! :) :)


SQLite unterstützt den langen Datentyp nicht. EDIT: Mein Fehler, INTEGER ist 8 Byte lang, daher sollte es diesen Datentyp unterstützen.
Antonio Vlasic


1

Ich bevorzuge das. Dies ist nicht der beste Weg, aber eine schnelle Lösung.

//Building the table includes:
StringBuilder query= new StringBuilder();
query.append("CREATE TABLE "+TABLE_NAME+ " (");
query.append(COLUMN_ID+"int primary key autoincrement,");
query.append(COLUMN_CREATION_DATE+" DATE)");

//Inserting the data includes this:
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
values.put(COLUMN_CREATION_DATE,dateFormat.format(reactionGame.getCreationDate())); 

// Fetching the data includes this:
try {
   java.util.Date creationDate = dateFormat.parse(cursor.getString(0);
   YourObject.setCreationDate(creationDate));
} catch (Exception e) {
   YourObject.setCreationDate(null);
}

0
"SELECT  "+_ID+" ,  "+_DESCRIPTION +","+_CREATED_DATE +","+_DATE_TIME+" FROM "+TBL_NOTIFICATION+" ORDER BY "+"strftime(%s,"+_DATE_TIME+") DESC";
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.