Как анализировать временную метку UTC в формате гггг-ММ-ддTHH:мм:сс.SSS+ZZZZ

У меня есть метка времени как 2020-12-03T05:35:59.398+0000 в формате String, полученная в пакете потоковой передачи.

Я хочу только 2020-12-03 05:35:59 как экземпляр java.sql.Timestamp, чтобы иметь возможность сравнить его с другим экземпляром Timestamp.

Получение следующей ошибки с функцией Timestamp.valueOf():

Exception in thread "main" java.time.format.DateTimeParseException : Text '2020-12-03T05:35:59.398+0000' could not be parsed at index 23

Я попробовал ответ, данный здесь , и преобразование произошло, но время было изменено на 2020-12-03 11:05:59

Я попытался изменить форматы, указанные здесь, но решения пока нет.

Есть ли вообще формат временной метки со странными + между 398+0000?


person DrthSprk_    schedule 03.12.2020    source источник
comment
комментарии приветствуются для отрицательного голосования по вопросу   -  person DrthSprk_    schedule 03.12.2020
comment
Пожалуйста, проверьте сейчас после редактирования.   -  person DrthSprk_    schedule 04.12.2020
comment
Можно ли избежать использования Timestamp? Этот класс плохо и запутанно разработан и давно устарел. Если вы сообщите нам, откуда вы взяли другие Timestamp, мы сможем предложить лучшие альтернативы, заменяя (или преобразовывая) классы из java.time, современный API даты и времени Java, например, OffsetDateTIme.   -  person Ole V.V.    schedule 04.12.2020
comment
Другая временная метка, с которой я сравниваю, также имеет тип String. В основном я использую Timestamp.valueOf только для преобразования строки в TImestamp.   -  person DrthSprk_    schedule 04.12.2020
comment
Похоже, у вас есть 2 даты типа String, и вы хотите преобразовать их в тип даты/времени, чтобы вызвать для них методы сравнения. Это правильно? Если это так, я бы рекомендовал использовать библиотеку java.time и держаться подальше от java.sql.Timestamp, которая представляет собой тонкую обертку вокруг старой и устаревшей библиотеки java.util.Date.   -  person jwvh    schedule 04.12.2020
comment
но время было изменено на 2020-12-03 11:05:59. Нет, не меняли. Он был только что напечатан в вашем часовом поясе (вероятно, Азия/Калькутта).   -  person Ole V.V.    schedule 04.12.2020
comment
@ОлеВ.В. Если это так, то как я могу проверить часовой пояс из Timestap и соответствующим образом обновить его?   -  person DrthSprk_    schedule 04.12.2020
comment
@jwvh Да. Я сравниваю временные метки   -  person DrthSprk_    schedule 04.12.2020
comment
Не могу ничего поделать с проблемой X, поскольку это ввод необработанных неструктурированных данных. Так что проблему Y нужно только усилить.   -  person DrthSprk_    schedule 04.12.2020


Ответы (3)


Java.время

Я рекомендую вам использовать java.time, современный API даты и времени Java, для вашей работы с датой и временем.

    DateTimeFormatter isoFormatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSxx");
    DateTimeFormatter sqlTimestampFormatter = new DateTimeFormatterBuilder()
            .append(DateTimeFormatter.ISO_LOCAL_DATE)
            .appendLiteral(' ')
            .append(DateTimeFormatter.ISO_LOCAL_TIME)
            .toFormatter();
    
    String aTimestampString = "2020-12-03T05:35:59.398+0000";
    String anotherTimestampString = "2020-12-04 06:43:58.556385";
    
    Instant anInstant = isoFormatter.parse(aTimestampString, Instant::from);
    Instant anotherInstant = LocalDateTime.parse(anotherTimestampString, sqlTimestampFormatter)
            .atOffset(ZoneOffset.UTC)
            .toInstant();
    
    if (anInstant.isBefore(anotherInstant)) {
        System.out.println(aTimestampString + " is earlier");
    } else {
        System.out.println(anotherTimestampString + " is earlier");
    }

Выход из этого примера:

2020-12-03T05:35:59.398+0000 раньше

+0000 в предыдущей строке выше — это смещение от UTC — смещение на 00 часов 00 минут. Поскольку он равен нулю, мы знаем, что время указано в формате UTC. Я не знаю часовой пояс или смещение UTC другой строки. Вам нужно знать, иначе вы получите неправильные результаты. В приведенном выше коде я предположил, что другая строка также находится в формате UTC.

Не используйте метку времени

Я попробовал ответ, данный здесь, и преобразование действительно произошло, но время было изменено на 2020-12-03 11:05:59

Вот как сбивает с толку класс Timestamp. Вы получили правильное значение метки времени. Что происходит, когда вы распечатываете объект Timestamp, так это то, что вы (неявно или явно) вызываете его метод toString. Timestamp.toString() сбивает с толку часовой пояс вашей JVM по умолчанию для рендеринга строки. Таким образом, если ваша временная метка равна 2020-12-03T05:35:59.398 UTC, а ваш часовой пояс, скажем, Азия/Калькутта, тогда время преобразуется в часовой пояс Азии/Калькутты, а строка 2020-12-03 11:05:59 возвращается и печатается.

У вас нет ничего хорошего, для чего можно было бы использовать старомодный класс java.sql.Timestamp. Первоначально он предназначался для передачи значений меток времени с часовым поясом и без него в базы данных SQL и из них. Начиная с JDBC 4.2 мы предпочитаем для этой цели OffsetDateTime, Instant и LocalDateTime. Так что просто забудьте о классе Timestamp.

Ссылка на сайт

учебное пособие по Oracle: дата и время, объясняющее, как использовать java.time.

person Ole V.V.    schedule 04.12.2020

Есть ли вообще формат временной метки со странным + между 398+0000?

Часть 398 представляет собой доли секунды (миллисекунды), а часть +0000 представляет собой часть смещения зоны.

Вы можете преобразовать 2020-12-03T05:35:59.398+0000 в OffsetDateTime, используя шаблон формата uuuu-MM-dd'T'HH:mm:ss.SSSX.

Демонстрация:

import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;

public class Main {
    public static void main(String[] args) {
        String text = "2020-12-03T05:35:59.398+0000";
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSX");
        OffsetDateTime odt = OffsetDateTime.parse(text, formatter);
        System.out.println(odt);
    }
}

Вывод:

2020-12-03T05:35:59.398Z

Посетите страницу документации DateTimeFormatter, чтобы узнать больше о буквах, используемых для форматирования.

Вы можете использовать функции isBefore и isAfter OffsetDateTime для сравнения двух его экземпляров.

Демонстрация:

import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;

public class Main {
    public static void main(String[] args) {
        String text = "2020-12-03T05:35:59.398+0000";
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSX");
        OffsetDateTime odt = OffsetDateTime.parse(text, formatter);
        OffsetDateTime odtNow = OffsetDateTime.now();
        System.out.println(odtNow.isBefore(odt));
        System.out.println(odtNow.isAfter(odt));
    }
}

Вывод:

false
true

Узнайте больше о современном API даты и времени на странице Trail: Date Time< /а>. Если вы работаете над проектом Android и ваш уровень Android API по-прежнему не соответствует Java-8, проверьте Java 8+ API, доступные через дешугаринг и Как для использования ThreeTenABP в Android Project.

API даты и времени java.util и их API форматирования SimpleDateFormat устарели и подвержены ошибкам. Рекомендуется полностью прекратить их использование и перейти на современный API даты и времени. Поскольку java.sql.Timestamp расширяет java.util.Date, рекомендуется чтобы прекратить использовать это, а также. Однако по какой-либо причине, если вы все еще хотите использовать преобразование между современным и устаревшим API даты и времени, используйте Instant в качестве моста.

import java.sql.Timestamp;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;

public class Main {
    public static void main(String[] args) {
        String text = "2020-12-03T05:35:59.398+0000";
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSX");
        OffsetDateTime odt = OffsetDateTime.parse(text, formatter);
        Instant instant = odt.toInstant();
        Timestamp timestamp = new Timestamp(instant.toEpochMilli());
        System.out.println(timestamp);
    }
}

Вывод:

2020-12-03 05:35:59.398
person Arvind Kumar Avinash    schedule 05.12.2020

Вы можете использовать пользовательский DateFormatter для нестандартных форматов. Вот рабочий пример для вашего варианта использования.

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;

import static java.time.temporal.ChronoField.*;

public class Main {

    private static final DateTimeFormatter INPUT_NON_STANDARD_FORMAT;

    static {
        INPUT_NON_STANDARD_FORMAT =
                new DateTimeFormatterBuilder()
                        .parseCaseInsensitive()
                        .append(DateTimeFormatter.ISO_LOCAL_DATE)
                        .appendLiteral('T')
                        .appendValue(HOUR_OF_DAY, 2)
                        .appendLiteral(':')
                        .appendValue(MINUTE_OF_HOUR, 2)
                        .optionalStart()
                        .appendLiteral(':')
                        .appendValue(SECOND_OF_MINUTE, 2)
                        .optionalStart()
                        .appendLiteral('.')
                        .appendValue(MILLI_OF_SECOND, 3)
                        .appendLiteral("+0000")
                        .toFormatter();
    }

    public static void main(String[] args) {
        String timestamp = "2020-12-03T05:35:59.398+0000";
        final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss");
        LocalDateTime localDateTime = LocalDateTime.parse(timestamp, INPUT_NON_STANDARD_FORMAT);
        System.out.println(localDateTime.format(dateTimeFormatter));
    }
}

Вывод

2020-12-03 05:35:59
person Tushar    schedule 03.12.2020
comment
Остерегайтесь большого регистра букв шаблона формата. Заглавная буква YYYY подарит вам сюрпризы к Новому году. - person Ole V.V.; 04.12.2020