Quiero almacenar una cadena en una base de datos (SQLite) para una aplicación de Android con la hora y fecha actuales. Para ese propósito, estoy usando SimpleDateFormat. Desafortunadamente, no muestra la hora correcta cuando. Probé dos opciones.

Primera opción (de SimpleDateFormat con TimeZone)

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z", Locale.getDefault());
        sdf.format(new Date());

Segunda opción (de Java SimpleDateFormat ("yyyy-MM-dd'T ' HH: mm: ss'Z '") da la zona horaria como IST)

    SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss'Z'");
    sdf2.setTimeZone(TimeZone.getTimeZone("CEST"));

En ambos casos, el momento es simplemente incorrecto. No es la hora local que muestra mi computadora portátil o teléfono, pero la hora de salida es 2 horas antes. ¿Cómo puedo cambiar eso? Me gustaría tener la hora actual de Berlín (CEST) que también se muestra en mi computadora. Agradezco cada comentario.

4
VanessaF 27 sep. 2020 a las 16:08

3 respuestas

La mejor respuesta

Utilice Europe/Berlin en lugar de CEST y obtendrá el resultado esperado.

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;

public class Main {
    public static void main(String[] args) {
        SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
        sdf2.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));
        System.out.println(sdf2.format(new Date()));
    }
}

Salida:

2020-09-27 18:38:04 +0200

Un pequeño consejo:

Le recomiendo que cambie de la API de fecha y hora java.util obsoleta y propensa a errores y SimpleDateFormat a API de fecha y hora moderna java.time y la API de formato correspondiente (paquete, java.time.format). Obtenga más información sobre la API de fecha y hora moderna en Ruta: fecha y hora . Si su nivel de API de Android aún no es compatible con Java-8, consulte Java 8+ API disponibles a través de desugaring y Cómo usar ThreeTenABP en Android Project.

Uso de la API de fecha y hora moderna:

import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;

public class Main {
    public static void main(String[] args) {
        ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Europe/Berlin"));

        // Default format
        System.out.println(zdt);

        // Some custom format
        System.out.println(zdt.format(DateTimeFormatter.ofPattern("EEEE dd uuuu hh:mm:ss a z")));
    }
}

Salida:

2020-09-27T18:42:53.620168+02:00[Europe/Berlin]
Sunday 27 2020 06:42:53 pm CEST

La API moderna le alertará mientras que la API heredada puede realizar una conmutación por error:

import java.time.ZoneId;
import java.time.ZonedDateTime;

public class Main {
    public static void main(String[] args) {
        ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("CEST"));
        // ...
    }
}

Salida:

Exception in thread "main" java.time.zone.ZoneRulesException: Unknown time-zone ID: CEST
    at java.base/java.time.zone.ZoneRulesProvider.getProvider(ZoneRulesProvider.java:279)
    at java.base/java.time.zone.ZoneRulesProvider.getRules(ZoneRulesProvider.java:234)
    at java.base/java.time.ZoneRegion.ofId(ZoneRegion.java:120)
    at java.base/java.time.ZoneId.of(ZoneId.java:408)
    at java.base/java.time.ZoneId.of(ZoneId.java:356)
    at Main.main(Main.java:6)

Como puede ver, obtiene una excepción en este caso, mientras que SimpleDateFormat le dará un resultado no deseado como se muestra a continuación:

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;

public class Main {
    public static void main(String[] args) {
        SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
        sdf2.setTimeZone(TimeZone.getTimeZone("CEST"));
        System.out.println(sdf2.format(new Date()));
    }
}

Salida:

2020-09-27 16:47:45 +0000

Quizás se pregunte a qué se refiere este resultado indeseable. La respuesta es : cuando SimpleDateFormat no comprende una zona horaria, realiza una conmutación por error (predeterminado) a GMT (igual que UTC), es decir, ha ignorado CEST y aplicado GMT en este caso (no es una buena característica en mi humilde opinión 😊).

2
Arvind Kumar Avinash 27 sep. 2020 a las 17:43

ISO 8601

Suponiendo que su SQLite no tiene un tipo de datos datetime, le recomiendo que use el formato ISO 8601, el estándar internacional, para almacenar sus fechas y horas como cadenas en SQLite. A continuación, considere usar java.time, la API moderna de fecha y hora de Java, para su trabajo de fecha y hora. Las dos sugerencias van muy bien de la mano. Las recomendaciones comunes dicen almacenar la fecha y la hora en UTC, pero tengo entendido que prefiere la hora de Europa / Berlín.

    ZoneId databaseTimeZone = ZoneId.of("Europe/Berlin");
    
    ZonedDateTime now = ZonedDateTime.now(databaseTimeZone);
    String databaseTime = now.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
    
    System.out.println(databaseTime);

Salida de lo anterior cuando se ejecuta ahora:

2020-09-27T15: 54: 21.53 + 02: 00

He incluido a propósito el desplazamiento de UTC en la cadena. Esto permitirá a cualquiera que recupere la cadena convertir la hora a UTC o la zona horaria de su preferencia. Si el usuario viaja a la India para ver el Taj Mahal y recupera los datos allí, la conversión a la hora estándar de la India no es un problema. El desplazamiento también elimina la ambigüedad de las horas de la noche de octubre cuando Berlín cambia del horario de verano (DST) a la hora estándar y se repiten los mismos horarios. Los tiempos antes del cambio tendrán un desplazamiento +02: 00, los tiempos después del cambio tendrán +01: 00.

¿Cómo puedo cambiar el formato (?)

Editar: si insiste en su propio formato para la información y la legibilidad humana, cree un formateador para eso. El ZonedDateTime ya tiene la hora en su zona horaria elegida, por lo que esa hora también es la que tendrá cuando la formatee:

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss Z");
    String databaseTime = now.format(formatter);

Ahora el resultado es:

27 de septiembre de 2020 16:22:23 +0200

Edición adicional: dado que la legibilidad humana es el único requisito para esa columna, hágalo todo y use el formato localizado predefinido de Java, por ejemplo:

    DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG)
            .withLocale(Locale.GERMAN);
  1. Septiembre de 2020 19:06:03 MESZ

Si es demasiado largo para ti, usa FormatStyle.MEDIUM en su lugar.

Más adelante editar: ¿Y por qué? La pregunta es si 27. September 2020 19:06:03 MESZ es más fácil de leer y comprender correctamente que 2020-09-27 16:22:23 +0200. Debería hacérselo tan fácil como sea razonablemente posible. Sin embargo, tiene sentido incluir el desplazamiento, +0200, ya que no es ambiguo, mientras que una abreviatura de zona horaria como MESZ no está garantizada (muchas abreviaturas de zona horaria son ambiguo).

¿Qué salió mal en tu código?

Probablemente esté ejecutando su código en una computadora con su zona horaria establecida en UTC (o en alguna otra zona horaria que esté actualmente dos horas por detrás de la hora de Berlín). En su segundo fragmento, intenta compensar este hecho estableciendo la zona horaria de su formateador en CEST (horario de verano de Europa Central). La forma en que lo está haciendo no es lo que desea y tampoco funciona. Ambos tienen que ver con el hecho de que CEST no es una zona horaria. CEST está dos horas por delante de UTC, y si hubiera funcionado, habría tenido dos horas de adelanto de UTC también durante la época estándar del año en la que Berlín está solo 1 hora por delante de UTC, es decir, la hora incorrecta. Dado que CEST no es una zona horaria, TimeZone no la reconoce como una zona horaria. Y esto es tan confuso como lo es la clase TimeZone: en lugar de objetar, te da GMT tácitamente, por lo que no has llegado a ninguna parte. Realmente recomiendo evitar usar esa clase. El identificador de zona horaria correcto para Berlín es Europe/Berlin, el que también estoy usando en mi código. Los identificadores de zona horaria vienen en formato región / ciudad .Can't load full resultsTry againRetrying...Retrying...

Pregunta: ¿java.time no requiere el nivel 26 de la API de Android?

Java.time funciona bien en dispositivos Android más antiguos y más nuevos. Solo requiere al menos Java 6 .

  • En Java 8 y versiones posteriores y en dispositivos Android más nuevos (desde el nivel 26 de API), la API moderna viene incorporada.
  • En Java 6 y 7 que no sean Android, obtenga ThreeTen Backport, el backport de las clases modernas (ThreeTen para JSR 310; vea los enlaces en la parte inferior).
  • En Android más antiguo, use desugaring o la edición de Android de ThreeTen Backport. Se llama ThreeTenABP. En el último caso, asegúrese de importar las clases de fecha y hora de org.threeten.bp con subpaquetes.

Enlaces

4
Ole V.V. 29 sep. 2020 a las 05:59

Bueno, enfrenté el mismo problema hace una semana y descubrí que el problema está en la configuración de TimeZone

Si obtiene la fecha como una cadena y necesita formatearla a otro formato, use el código a continuación

public String getCalendarDate(String inputDate){
        Date date = getDateFromSource(inputDate);
        SimpleDateFormat formatter = new SimpleDateFormat("EEEE, d MMMM yyyy", Locale.getDefault());
        formatter.setTimeZone(TimeZone.getDefault());
        return formatter.format(date);
    }

    Date getDateFromSource(String apiDate){
        Date newFormattedDate = null;
        SimpleDateFormat parser = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ENGLISH);
        parser.setTimeZone(TimeZone.getTimeZone("UTC"));
        try {
             newFormattedDate = parser.parse(apiDate);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return newFormattedDate;
    }

En la función getDateFromSource, cambie el formato de fecha al formato de origen, mientras que en la función getCalendarDate, cambie el formato al formato requerido.

Si ya tiene el objeto Date, puede ignorar la función getDateFromSource y ponerla directamente en la segunda

Para aquellos que usan Kotlin, este es el código equivalente

    fun getCalendarDate(apiDate: String): String{
        val date = getDateFromApi(apiDate)
        val formatter = SimpleDateFormat("EEEE, d MMMM yyyy", Locale.getDefault())
        formatter.timeZone = TimeZone.getDefault()
        return formatter.format(date)
    }

    private fun getDateFromApi(apiDate: String) :Date{
        val parser = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ENGLISH)
        parser.timeZone = TimeZone.getTimeZone("UTC")
        return parser.parse(apiDate)!!
    }
1
Hamza Sharuf 27 sep. 2020 a las 13:58