Синтаксический анализ даты в Kotlin работает неправильно

#java #kotlin #java-time #localdatetime #datetimeformatter

Вопрос:

Поэтому я искал, как правильно анализировать входящее время и дату, проблема в том, что эта строка также содержит зону, которая, по-видимому, по какой-то причине не может быть проанализирована. Чтобы привести пример строки входящей даты и времени: 2021-10-05T10:00:00.0000000

Теперь я попытался сделать следующее:

 var dateTimeString = "2021-10-05T10:00:00.0000000"
var formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
var date = LocalDate.parse(dateTimeString, formatter)
 

Я попытался заменить Z ничем и ZZZZ, но это не сработало, я предполагаю, что это не работает из-за отсутствия знака плюс или минус. К вашему сведению, я получаю дату в этом формате из-за API Microsoft Graph при извлечении событий календаря.

Есть идеи о том, как правильно отформатировать эту дату?

Изменить: Это взято из Microsoft Graph. В основном они дают как дату в качестве объекта:

 "start": {
    "dateTime": "2021-10-05T10:00:00.0000000",
    "timeZone": "UTC"
}
 

Это страница документации, которая объясняет этот объект даты: тип ресурса dateTimeTimeZone.

Обновить:

Наконец-то я смог решить эту проблему с датой, и я сделал следующее:

 var inputDateTime = "2021-10-05T10:00:00.0000000"
var inputTimeZone = "UTC"
var zonedDateTime = ZonedDateTime.parse(
    inputDateTime,
    DateTimeFormatter.ISO_DATE_TIME.withZone(ZoneId.of(inputTimeZone))
).withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime()
 

Таким образом, дата будет правильно преобразована в нужный часовой пояс и в нужную дату/время.

Комментарии:

1. С чем именно вы пытаетесь разобраться Z ? Я не вижу никакого смещения зоны "2021-10-05T10:00:00.0000000" .

2. часовой пояс-это последние 4 цифры

3. Там нет знака «плюс» и «минус», как бы это соотнеслось с допустимым часовым поясом? Хорошо, 0000 будет UTC, но как насчет других зон?

4. Я знаю, но это исходит от Microsoft Graph, это не то, что я выдумываю. В основном они дают как дату в качестве объекта: "start": {"dateTime": "2021-10-05T10:00:00.0000000", "timeZone": "UTC"}

5. Это страница документации, которая объясняет этот объект даты: docs.microsoft.com/en-us/graph/api/resources/…

Ответ №1:

Как я вижу из предоставленных вами документов https://docs.microsoft.com/en-us/graph/api/resources/datetimetimezone?view=graph-rest-1.0

 dateTime    String  A single point of time in a combined date and time representation ({date}T{time}; for example, 2017-08-29T04:00:00.0000000).
timeZone    String  Represents a time zone, for example, "Pacific Standard Time". See below for more possible values.
 

dateTime объект не имеет закодированной зоны. И все 7 нулей представляют собой доли секунды. В таком случае это нормально ISO_DATE_TIME , и вам не нужно создавать свой собственный форматер.

Следующий код должен работать

 var dateTimeString = "2021-10-05T10:00:00.0000000"
var date = LocalDate.parse(dateTimeString, DateTimeFormatter.ISO_DATE_TIME)
 

Комментарии:

1. Спасибо вам за это, когда я впервые столкнулся с таким форматом, обычно даты содержат только 3 цифры, обозначающие доли секунд, поэтому я предположил, что остальные 4 цифры должны быть смещением часового пояса

Ответ №2:

В вашей строке даты и времени нет информации о часовом поясе

Ваша строка даты и времени 2021-10-05T10:00:00.0000000 не содержит информации о часовом поясе. Он .0000000 представляет долю секунды и на данный момент java.time способен обрабатывать до 9 цифр точности (т. е. точность в наносекунду).

Поскольку он не содержит информации о часовом поясе, он представляет собой локальную дату-время и, следовательно, должен быть проанализирован LocalDateTime .

Вам не нужна строка DateTimeFormatter для даты и времени

Современный API для определения даты и времени основан на стандарте ISO 8601 и не требует DateTimeFormatter явного использования объекта, если строка даты и времени соответствует стандартам ISO 8601. Ваша строка даты и времени уже находится в формате ISO 8601.

ДЕМОНСТРАЦИЯ:

 import java.time.LocalDateTime;

public class Main {
    public static void main(String args[]) {
        var dateTimeString = "2021-10-05T10:00:00.0000000";
        var ldt = LocalDateTime.parse(dateTimeString);
        System.out.println(ldt);
    }
}
 

Выход:

 2021-10-05T10:00
 

ONLINE DEMO

Как ZonedDateTime выйти из LocalDateTime экземпляра?

Вы можете использовать LocalDateTime#atZone для преобразования вложения a ZoneId в LocalDateTime результат в a ZonedDateTime .

 ZonedDateTime zdt = ldt.atZone(ZoneId.of("Etc/UTC"));
 

Примечание: Если вам нужен Instant , вы можете получить его с этого момента ZonedDateTime , например

 Instant instant = zdt.toInstant();
 

An Instant представляет собой мгновенную точку на временной шкале, обычно представленную во времени UTC. Z На выходе отображается обозначение часового пояса для смещения нулевого часового пояса. Он расшифровывается как Zulu и указывает Etc/UTC часовой пояс (который имеет смещение часового 00:00 пояса в часах).

Узнайте больше о современном API даты и времени* из трейла: Дата и время.


* Если вы работаете в проекте Android, и ваш уровень API Android по-прежнему не соответствует Java-8, проверьте API Java 8 , доступные через десугаринг. Обратите внимание, что Android 8.0 Oreo уже предоставляет поддержку java.time .

Ответ №3:

В том случае, если последние 4 цифры в вашем примере String не представляют часовой пояс:

Проанализируйте его без форматирования (нет необходимости, так как это был бы идеальный стандарт ISO, если бы последние 4 цифры были просто дополнительными долями секунды), но также учитывайте часовой пояс, который вы получаете с помощью JSON:

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

fun main() {
    // the time String from JSON
    var dateTimeString = "2021-10-05T10:00:00.0000000"
    // the zone from JSON (may not always work, but does with UTC)
    var timeZone = "UTC"
    // create the zone from the timezone String
    var zoneId = ZoneId.of(timeZone)
    // then parse the time String using plain LocalDateTime and add the zone afterwards
    var zdt: ZonedDateTime = LocalDateTime.parse(dateTimeString).atZone(zoneId)
    // print some results
    println("Full ZonedDateTime: ${zdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME)}")
    println("(Local)Date only:   ${zdt.toLocalDate()}")
}
 
 Full ZonedDateTime: 2021-10-05T10:00:00Z[UTC]
(Local)Date only:   2021-10-05
 

Пожалуйста, обратите внимание, что анализ часовых поясов, поддерживаемых в настоящее время Windows, не будет работать так просто (кроме как из UTC ), но часовых поясов, поддерживаемых API календаря (в основном), достаточно для создания a java.time.ZoneId .

Ответ №4:

В качестве дополнения: В вашей документации упоминается Pacific Standard Time строка часового пояса, которая может быть частью вашего часового пояса MS Graph. Я не думаю, что другие ответы могут справиться с этим, поэтому я хотел бы показать, как вы справляетесь с этим на Java.

 private static final DateTimeFormatter ZONE_FORMATTER
        = DateTimeFormatter.ofPattern("zzzz", Locale.ENGLISH);

static ZoneId parseZone(String timeZoneString) {
    try {
        return ZoneId.from(ZONE_FORMATTER.parse(timeZoneString));
    } catch (DateTimeParseException dtpe) {
        return ZoneId.of(timeZoneString);
    }
}
 

Я полагаю, что это обрабатывает строки, упомянутые в документации, а также UTC в вашем вопросе. Я демонстрирую только три разных:

     String[] msTimeZoneStrings = { "UTC", "Pacific Standard Time", "Pacific/Honolulu" };
    for (String zoneString : msTimeZoneStrings) {
        ZoneId zid = parseZone(zoneString);
        System.out.format("%-21s parsed to %s%n", zoneString, zid);
    }
 

Выход:

 UTC                   parsed to UTC
Pacific Standard Time parsed to America/Los_Angeles
Pacific/Honolulu      parsed to Pacific/Honolulu
 

В форматере, который я использую zzzz , указано название часового пояса, например Pacific Standard Time . Мой метод синтаксического анализа сначала пробует этот форматер. Это терпит неудачу для UTC и. Pacific/Honolulu Когда DateTimeParseException он пойман, метод затем пытается ZoneId.of использовать метод, также используемый в других ответах. Он обрабатывает UTC и все идентификаторы часовых поясов в формате регион/город, указанном в документации.

Объедините ZoneId полученные из моего метода с проанализированными LocalDateTime , чтобы получить ZonedDateTime способ, который деХаар уже показывает в своем ответе.