Я пытаюсь проанализировать datetimeoffset. Это из столбца таблицы базы данных SQL Server, который был создан как datetimeoffset, длина = 10, точность = 34, масштаб = 7.
Пример из одной из записей: 2024-07-16 22:30:00.0000000 -04:00
В этом случае я хотел бы вернуть -4 часа, а также преобразовать его в местное время пользователя.
К сожалению, я не могу использовать для этого T-SQL. нравится (datename(tz, value)
У меня есть следующее:
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
public class Main {
public static void main(String[] args) throws Exception {
DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss:SSS z");
//Date string with zone information
//String dateString = "08/03/2019T16:20:17:717 UTC+05:30";
String dateString = "2024-07-16 22:30:00.0000000 -04:00";
//Instance with given zone
ZonedDateTime zdtInstanceAtOffset = ZonedDateTime.parse(dateString, DATE_TIME_FORMATTER);
//Instance in UTC
ZonedDateTime zdtInstanceAtUTC = zdtInstanceAtOffset.withZoneSameInstant(ZoneOffset.UTC);
//Formatting to string
String dateStringInUTC = zdtInstanceAtUTC.format(DATE_TIME_FORMATTER);
System.out.println("Hello world!");
System.out.println(zdtInstanceAtOffset);
System.out.println(zdtInstanceAtUTC);
System.out.println(dateStringInUTC);
//Convert ZonedDateTime to instant which is in UTC
System.out.println(zdtInstanceAtOffset.toInstant());
}
}
Я получаю эту ошибку:
Исключение в потоке "main" java.time.format.DateTimeParseException: текст '2024-09-17 00:00:00.0000000 -04:00' не удалось проанализировать по индексу 2
Какие-либо предложения?




Ваш вопрос предполагает план. Предполагаемый план таков:
Вы поступили неправильно. Вышеупомянутый план — плохой план. Следовательно, ваш вопрос неактуален. Это вопрос о части плохого плана.
Я также собираюсь предположить кое-что: что где-то в этой строке у вас есть resultSet.getTimestamp или что-то подобное (где resultSet — это экземпляр JDBC ResultSet), и вы обнаружили (правильно), что это значение проблематично.
Правильный план состоит в том, чтобы вместо этого сделать следующее:
OffsetDateTime odt = resultSet.getObject(colIdxOrName, OffsetDateTime.class);
И возьмите все необходимое из этого одта. Вы можете просто запросить смещение напрямую. Таким образом, преобразование строк не происходит вообще, вы не зависите от определений tzdata ни вашего механизма базы данных, ни вашего экземпляра JDK, а работа с API значительно улучшается.
ZoneOffset offset = odt.getOffset() ;
JDBC 4.2+ требует, чтобы драйверы JDBC поддерживали .getObject(colIdxOrName, OffsetDateTime.class) (наряду с ODT, а также Instant и LocalDate). Они верны, а getDate и getTimestamp неверны. Никогда не следует использовать getDate и getTimestamp, поскольку возвращаемые ими типы SQL расширяются java.util.Date, а j.u.Date не работает. В том смысле, что j.u.Date делает именно то, что говорит его спецификация, но его спецификация является чистым и абсолютным безумием. Начнем с того, что название — полная ложь; j.u.Date экземпляры явно не представляют даты. Они представляют мгновения; это старый API-эквивалент java.time.Instant. Вот почему все методы, связанные с датой (например, .getYear()), устарели.
Исправление состоит в том, чтобы никогда не использовать j.u.Date, что вы делаете, используя вместо этого соответствующие типы в java.time (есть причина, по которой Java поставляется с новым API даты/времени: потому что старый очень плохой, сломанный и, конечно, очень неожиданный) .
По... причинам у JDBC нет метода .getLocalDateTime() и никогда не будет. .getObject(X, LocalDateTime.class) это так. Официально. В обоих смыслах: базы данных должны поддерживать это, и JDBC считает, что это канонический способ получить LDT из базы данных (то же самое относится к ODT, Instant и LD). Наверное потому, что иначе количество .getX() методов никогда бы не закончилось.
Это исходит из API. Я должен использовать API. Неуместные/бесполезные комментарии не нужны!
g00se опубликовал этот ответ в качестве комментария:
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.n z");
OffsetDateTime odt = OffsetDateTime.parse("2024-09-17 00:00:00.0000000 -04:00", dtf);
System.out.println(odt);
ZoneOffset o = odt.getOffset();
System.out.printf("Offset is %s%n", o);
OffsetDateTime odtLocal = odt.withOffsetSameInstant(ZonedDateTime.now().getOffset());
System.out.printf("As local OffsetDateTime is %s%n", odtLocal);
Это работает отлично. Но мне, вероятно, не следует это поощрять, поскольку я не верю, что вас заставляют работать со строками.
Я рад, что вы сохранили мой комментарий, но публикация его в качестве ответа просто поощряет неправильный подход. Если вы хотите использовать мои комментарии в качестве ответов, хотя бы сделайте правильное форматирование;)
100% согласен с вашим комментарием. Для тех, кто сталкивается с той же проблемой, но из вопроса «а как мне это сделать в базе данных?», подход тот же, если не подробности. В частности, используйте
datepart(tzoffset, @yourValue), чтобы получить смещение напрямую, а не пытаться проанализировать его из строкового представления.