[Java] Передача даты с формы в базу
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
ДаноAngular, PrimeNG, Spring Boot, JDBC, PostgreSQLНадоПередавать дату с формы в базу и обратноПодготовка
create database test_date;
CREATE TABLE test_table (
test_date date NULL,
test_timestamp timestamp NULL,
test_timestamptz timestamptz NULL,
id serial2,
CONSTRAINT test_table_pk PRIMARY KEY (id)
);
java.util.DateРешение 1Надо сохранять только дату без времени. Использую колонку с типом date.Выбираю на форме дату 2020-12-22. На сервер отправится 2020-12-21T21:00:00.000Z. Это текущее время по UTC, так как браузер в зоне +3. Java сделает запрос
statement.setObject(1, entity.getTestDate(), Types.DATE)
insert into test_table (test_date) values ('2020-12-22 +03')
Java отбрасывает время и передает автоматически таймзону (по умолчанию зона сервера или -Duser.timezone=Europe/Moscow). Postgres не учитывает зону для типа данных date. Будет сохранено 2020-12-22. При чтении из базы вернется эта же дата. В Json попадет
{ "testDate": "2020-12-22" }
Браузер прочитает такой формат, как начало дня по UTC.
new Date('2020-12-22')
new Date('2020-12-22T00:00:00.000+00:00')
Tue Dec 22 2020 03:00:00 GMT+0300 (Moscow Standard Time)
Т.е на форме отображается 2020-12-22 03:00 или просто без времени 2020-12-22. Все верно.Я встречал ситуацию , когда Chrome и Firefox интерпретировали дату без времени по-разному. Кто-то как начало дня по локальному времени. В данный момент такое не воспроизводится на обновленных версиях. Документация говорит, что сейчас такой формат стандартизирован. Но если строка отличается от формата 2020-12-22T00:00:00.000+00:00, то поведение не гарантировано.Ошибка всплывет только если начнет тестировать пользователь, который восточнее часового пояса сервера. Например Europe/Samara (+4). Выберет на форме 2020-12-22. На сервер отправится 2020-12-21T20:00:00.000Z (2020-12-22 00:00 +4). Сервер (работает в зоне +3) переведет это в 2020-12-21T23:00:00.000+03:00, отбросит время и сохранит как
insert into test_table (test_date) values ('2020-12-21 +03')
При чтении сервер отдаст 2020-12-21, что превратится в 2020-12-21 04:00. На форме видим 2020-12-21. Ошибка.Решение 2При сохранении в БД указать временную зону пользователя, а не зону сервера.
statement.setDate(1, new java.sql.Date(entity.getTestDate().getTime()),
Calendar.getInstance(TimeZone.getTimeZone(userZoneId)));
Получить её можно отдельным параметром в запросе. Для этого в JS можно выполнить.
Intl.DateTimeFormat().resolvedOptions().timeZone;
Можно попробовать с полифилом новый API Temporal.now().timeZone().id. Этот параметр должен содержать зону по умолчанию. На старых браузерах может не работать или возвращать неправильную зону. Запрос на сервер:
{"testDate":"2020-12-21T20:00:00.000Z","zoneId":"Europe/Samara"}
Сохранение:
insert into test_table (test_date) values ('2020-12-22 +04')
Зная зону, драйвер преобразовал 2020-12-21T23:00:00.000+03:00 в 2020-12-22T00:00:00.000+04:00, и сформировал строку 2020-12-22 +04. При чтении получится 2020-12-22 -> 2020-12-22 04:00:00. На форме видим 2020-12-22. Все верно.Теперь протестируем ситуацию, когда пользователь к западу от UTC. Например America/Chicago (-6). Сохранится выбранная дата 2020-12-22. При чтении сервер отдаст её обратно, но она превратится в 2020-12-21 18:00 по местному времени пользователя и отобразится как 2020-12-21.Решение 3Надо , чтобы сервер отдавал дату с временем 2020-12-22T00:00:00.000 и без зоны, тогда это будет преобразовано браузером в начало дня по местному времени. Для этого сделаю сериалайзер даты
import java.text.SimpleDateFormat;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
public class DateSerializer extends JsonSerializer<Date> {
private final static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
@Override
public void serialize(Date value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString(format.format(value));
}
}
Теперь все верно. Выбранная пользователем дата сохраняется в БД правильно. И возвращается на форму правильно. Проверено на разных таймзонах. Можно на прод.С прода приходит баг. Пользователи видят неправильную дату. И смещение не на один день, а вообще не та дата.Решение 4Метод java.text.SimpleDateFormat.format() не потокобезопасный. А я создал его один раз на все приложение. Надо для каждой сериализации делать свой экземпляр.
private static final String format = "yyyy-MM-dd'T'HH:mm:ss";
@Override
public void serialize(Date value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString(new SimpleDateFormat(format).format(value));
}
Получился сервис со странным интерфейсом для сохранения даты. В другом клиенте придется передавать не только дату, но и рассчитывать время и передавать зону, для которой это время рассчитано. Если я захочу сохранить 2020-12-22, то мне надо сначала определится с зоной. Если это +3, то надо передавать
{"testDate":"2020-12-21T21:00:00.000Z","zoneId":"Europe/Moscow"}
или
{"testDate":"2020-12-22T00:00:00.000+03:00","zoneId":"Europe/Moscow"}
В последнем варианте вообще получилось дублирование нужной информации о смещении. Надо убрать из запроса зону и оставить дату в формате 2020-12-22. Если придет 2020-12-21T21:00:00.000Z, то игнорировать время с зоной - сохранять как 2020-12-21). Возвращаться результат должен тоже без времени.Решение 5Убираю свой сериалайзер из java. На фронте надо обработать 2020-12-22 без времени, чтобы это было начало дня по локальному времени.
const ymd: string[] = obj.testDate.split('-');
const date: Date = new Date(ymd);
Это удобно и оно работает в Chrome и Firefox. Но конструктор с параметром Array не описан в стандарте. Поэтому параметр будет преобразован в строку и передан в Date.parse(). А этот метод стандартно работает только для 2020-12-22.Поэтому напишу по стандарту
const ymd: number[] = obj.testDate.split('-').map((s: string) => Number(s));
const date: Date = new Date(ymd[0], ymd[1] - 1, ymd[2])
Следующим шагом надо отбрасывать время при сохранении. Это уже делает JDBC. Но это вызывает ошибку для пользователей с востока. Так как время отбрасывается от даты по серверному времени. Поэтому время надо отбрасывать до конвертации строки в дату по серверному времени. Тут появляется ещё проблема: браузер отправляет дату, как начало дня по времени UTC. Т.е код надо писать ещё перед отправкой на фронте.
public saveEntity(entity: TestEntity): Observable<number> {
const date: Date = entity.testDate;
const testDate: string = [date.getFullYear(), date.getMonth() + 1, date.getDate()]
.map(n => String(n).padStart(2, '0')).join('-');
const body: any = Object.assign({}, entity, {testDate});
return this.http.post<number>(CONTROLLER, body);
}
Такой вариант работает, для пользователей из всех зон работает. И не надо ничего программировать на сервере.Потом на проекте появляется разработчик из Чикаго. И тестирует приложение у себя. На сервер отправляется 2020-12-22. Сервер превращает это в 2020-12-21 18:00:00 по местному времени. И сохраняет
insert into test_table (test_date) values ('2020-12-21 -06')
Ошибка. Решение работает только для сервера к востоку от UTC.Решение 6Самое простое решение - захардкодить зону приложения.
System.setProperty("user.timezone", "UTC")
Но не совсем правильное. Что делать, если в приложении уже куча логики зависит от того, что сервер находится где-то по местному времени на западе? Проблема в том, что Jackson воспринимает полученную дату как начало дня по UTC. A я хотел, чтобы дата была началом дня для сервера.Тогда надо захардкодить эту зону и указать Jackson, что для конвертации надо использовать зону сервера.
public static final String APP_TIMEZONE = "America/Chicago";
public static void main(String[] args) {
System.setProperty("user.timezone", APP_TIMEZONE);
SpringApplication.run(TestDateApplication.class, args);
}
import com.fasterxml.jackson.annotation.JsonFormat;
public class TestEntity {
@JsonFormat(timezone = TestDateApplication.APP_TIMEZONE,
pattern = "yyyy-MM-dd")
private Date testDate;
Так как браузер теперь передает только дату без времени. То можно ограничить интерфейс и не позволять формат с временем. Если кто-то начнет передавать время, значит возможна ошибка с временными зонами.Решение 7Jackson пропускает такие даты, не учитывая время. Поэтому надо писать свой десериалайзер. Он будет выбрасывать исключение, если строка длиннее заданного формата.
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
public class DateDeserializer extends JsonDeserializer<Date> {
private static final String format = "yyyy-MM-dd";
@Override
public Date deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
if (p.hasToken(JsonToken.VALUE_STRING)) {
String text = p.getText().trim();
if (text.length() != format.length()) {
throw new InvalidFormatException(p, "Wrong date", text, Date.class);
}
try {
Date result = new SimpleDateFormat(format).parse(text);
return result;
} catch (ParseException e) {
throw new InvalidFormatException(p, "Wrong date", text, Date.class);
}
}
return (Date) ctxt.handleUnexpectedToken(Date.class, p);
}
}
Если сохранить дату на востоке, а открыть на западе, то она будет одинакова. Но на западе может быть еще только вчера. Это может быть принято за ошибку. Зависит от задачи. Если речь о дате рождения, то ошибки нет. Если о дате публикации новости, то читатель на западе увидит новость из будущего. Это может выглядеть странно.Решение 8Для такого случая придется сохранять время вместе с датой. От времени зависит, одинаковая дата для разных часовых поясов в это время или разная. Для сохранения можно использовать два типа: timestamp или timestamp with time zone. Зону мне хранить, вроде бы, не надо, поэтому сделаю timestamp.
private static final String COLUMN_LABEL = "test_timestamp";
entity.setTestDate(rs.getTimestamp(COLUMN_LABEL));
statement.setTimestamp(1, new Timestamp(entity.getTestDate().getTime()));
С фронта поступит дата 2020-12-21T20:00:00.000Z. Будет передана в базу как
insert into test_table (test_timestamptz) values ('2020-12-21 14:00:00-06')
И сохранена в базе как время 2020-12-21 14:00:00. На фронт придет время с указанием зоны 2020-12-21T20:00:00.000+00:00 и будет показано локальное время. Работает.Беда придет, если изменится таймзона сервера. Время в базе сохранено по зоне сервера. При чтении на сервере с другой таймзоной будет неправильное время. Из 2020-12-21 14:00:00 на сервере Europe/Moscow получится 2020-12-21T11:00:00.000+00:00. А должно было быть 2020-12-21T20:00:00.000+00:00.Решение 9Либо сервер должен быть всегда в одной зоне. Либо надо хранить даты в одной зоне и явно это указывать. Так как сервер был раньше в America/Chicago и время сохранено в такой зоне, то я укажу эту зону
private static final String COLUMN_TIMEZONE = "America/Chicago";
entity.setTestDate(rs.getTimestamp(COLUMN_LABEL,
Calendar.getInstance(TimeZone.getTimeZone(COLUMN_TIMEZONE))));
statement.setTimestamp(1, new Timestamp(entity.getTestDate().getTime()),
Calendar.getInstance(TimeZone.getTimeZone(COLUMN_TIMEZONE)));
Чтобы было легче дебажить, лучше сделать зону UTC. И в базе перевести время на UTC.
update test_table
set test_timestamp =
(test_timestamp at time zone 'America/Chicago') at time zone 'UTC';
private static final String COLUMN_TIMEZONE = "UTC";
С фронта поступит дата 2020-12-21T20:00:00.000Z. Будет передана в базу как
insert into test_table (test_timestamp) values ('2020-12-21 20:00:00+00')
И сохранена в базе как время 2020-12-21 20:00:00. При чтении сервер получит 2020-12-21 14:00:00 по своему времени (-6). На фронт придет время с указанием зоны 2020-12-21T20:00:00.000+00:00 и будет показано локальное время.Решение 10Получилось тоже самое, что можно было сделать сразу, используя timestamp with time zone. Данный тип не хранит зону. Он хранит время для зоны UTC и автоматически конвертирует его во время для другой зоны. Поэтому код можно переписать без указания зоны при сохранении и чтении.
private static final String COLUMN_LABEL = "test_timestamptz";
entity.setTestDate(rs.getTimestamp(COLUMN_LABEL));
statement.setTimestamp(1, new Timestamp(entity.getTestDate().getTime()));
С фронта поступит дата 2020-12-21T20:00:00.000Z. Будет передана в базу как
insert into test_table (test_timestamptz) values ('2020-12-21 14:00:00-06')
И сохранена в базе независимо от зоны сервера как время 2020-12-21T20:00:00.000Z. При чтении сервер получит 2020-12-21 14:00:00 по своему времени. На фронт придет время с указанием зоны 2020-12-21T20:00:00.000+00:00 и будет показано локальное время.Решение 11Надо чтобы время показывалось всегда то, что ввели, и не зависело от временной зоны браузера или сервера. Для передачи по сети буду использовать формат без указания зоны. Для хранения буду использовать колонку timestamp.
public saveEntity(entity: TestEntity): Observable<number> {
const date: Date = entity.testDate;
const testDate: string = [date.getFullYear(), date.getMonth() + 1, date.getDate()]
.map(n => String(n).padStart(2, '0')).join('-')
+ 'T' + [date.getHours(), date.getMinutes(), date.getSeconds()]
.map(n => String(n).padStart(2, '0')).join(':');
const body: any = Object.assign({}, entity, {testDate});
return this.http.post<number>(CONTROLLER, body);
}
@JsonDeserialize(using = DateDeserializer.class)
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = TestDateApplication.APP_TIMEZONE)
private Date testDate;
В десериалайзере:
Date result = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").parse(text);
При выборе на форме 2020-12-22 14:14 отправится 2020-12-22T14:14:00. Это воспринимается как локальное время. И будет отправлено в базу.
insert into test_table (test_timestamp) values ('2020-12-22 14:14:00+04')
Так как тип колонки timestamp without time zone, то переданная зона просто будет отброшена, и дополнительной конвертации не будет. При чтении все также. Отобразится тоже, что сохраняли, в любом часовом поясе.
new Date('2020-12-22T14:14:00')
Tue Dec 22 2020 14:14:00 GMT-0600 (Central Standard Time)
Time APIРешение 1Сохраняю только дату. Колонка date. Тип поля DTO LocalDate.
private LocalDate testDate;
statement.setObject(1, entity.getTestDate(), Types.DATE);
entity.setTestDate(rs.getObject("test_date", LocalDate.class));
Выберу на форме 2020-12-22. От браузера придет 2020-12-21T21:00:00.000Z. Jackson превратит это в LocalDateTime для зоны UTC и отбросит время.
insert into test_table (test_date) values ('2020-12-21'::date)
В браузер вернется 2020-12-21. Неверно.
new Date('2020-12-21')
Mon Dec 21 2020 03:00:00 GMT+0300 (Moscow Standard Time)
Решение 2Надо делать десериализацию с таймзоной сервера.
public LocalDate deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
if (p.hasToken(JsonToken.VALUE_STRING)) {
String text = p.getText().trim();
try {
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
.appendZoneId()
.toFormatter();
LocalDate result = ZonedDateTime.parse(text, formatter)
.withZoneSameInstant(ZoneId.systemDefault())
.toLocalDate();
return result;
} catch (Exception e) {
throw new InvalidFormatException(p, "Wrong date", text, Date.class);
}
}
return (LocalDate) ctxt.handleUnexpectedToken(LocalDate.class, p);
}
Теперь сохраняется введенная дата.
insert into test_table (test_date) values ('2020-12-22'::date)
Но это не будет работать для пользователя восточнее сервера.Решение 3Можно пойти путем передачи зоны пользователя. Тогда надо сменить тип поля на тип с временем: LocalDateTime.
String zoneId = entity.getZoneId();
statement.setObject(1,
ZonedDateTime.of(entity.getTestDate(), ZoneId.systemDefault())
.withZoneSameInstant(ZoneId.of(zoneId))
.toLocalDate(),
Types.DATE);
entity.setTestDate(
LocalDateTime.of(rs.getObject(COLUMN_LABEL, LocalDate.class), LocalTime.MIN));
Обратно с сервера вернется дата уже с временем 2021-12-22T00:00:00. Поэтому это решение будет работать и для пользователей с запада от UTC.Решение 4Если формировать дату без времени на фронте, то на сервере можно оставить только поле с типом LocalDate. И больше не делать дополнительных конвертаций.
statement.setObject(1, entity.getTestDate(), Types.DATE);
entity.setTestDate(rs.getObject(COLUMN_LABEL, LocalDate.class));
Это решение работает, если даже переместить сервер в другую зону.Чтобы предотвратить ошибку из-за передачи даты в формате ISO по времени UTC, достаточно указать формат. На дополнительные символы будет ругаться.
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate testDate;
Решение 5Чтобы сохранить время можно использовать LocalDateTime. Но для конвертации строки 2020-12-21T20:00:00.000Z в локальное время нужен десериалайзер с использованием ZoneDateTime. Поэтому буду использовать сразу его.
statement.setObject(1,
entity.getTestDate()
.withZoneSameInstant(ZoneId.systemDefault())
.toLocalDateTime(),
Types.TIMESTAMP);
entity.setTestDate(
ZonedDateTime.of(
rs.getObject(COLUMN_LABEL, LocalDateTime.class),
ZoneId.systemDefault()
)
);
При изменении зоны сервера даты поедут.Решение 6Укажу явно зону UTC для хранения времени.
private static final String COLUMN_TIMEZONE = "UTC";
statement.setObject(1,
entity.getTestDate()
.withZoneSameInstant(ZoneId.of(COLUMN_TIMEZONE))
.toLocalDateTime(),
Types.TIMESTAMP);
entity.setTestDate(
ZonedDateTime.of(
rs.getObject(COLUMN_LABEL, LocalDateTime.class),
ZoneId.of(COLUMN_TIMEZONE)
)
);
Решение 7Теперь можно перейти на тип колонки timestamptz.Чтобы сохранить ZonedDateTime в такую колонку, можно использовать LocalDateTime, но обязательно сконвертировав в зону сервера. Потому что JDBC сам добавит в запрос смещение на основе зоны сервера. Postgres его учтет для конвертации в UTC.
statement.setObject(1,
entity.getTestDate()
.withZoneSameInstant(ZoneId.systemDefault())
.toLocalDateTime());
insert into test_table (test_timestamptz) values ('2020-12-21 23:30:00+03'::timestamp)
Можно сохранять OffsetDateTime. Тогда будет передано то, что пришло из браузера.
statement.setObject(1, entity.getTestDate().toOffsetDateTime());
insert into test_table (test_timestamptz)
values ('2020-12-21 20:30:00+00'::timestamp with time zone)
Читать из базы драйвер позволяет только в OffsetDateTime.
entity.setTestDate(
rs.getObject(COLUMN_LABEL, OffsetDateTime.class).toZonedDateTime()
);
Поэтому поле DTO можно сразу переделать в OffsetDateTime.Решение 8Для сохранение выбранного времени и отображения его независимо от зоны браузера достаточно передавать на сервер локальное время.
public saveEntity(entity: TestEntity): Observable<number> {
const date: Date = entity.testDate;
const testDate: string =
[date.getFullYear(), date.getMonth() + 1, date.getDate()]
.map(n => String(n).padStart(2, '0')).join('-')
+ 'T'
+ [date.getHours(), date.getMinutes(), date.getSeconds()]
.map(n => String(n).padStart(2, '0')).join(':');
const body: any = Object.assign({}, entity, {testDate});
return this.http.post<number>(CONTROLLER, body);
}
На сервере использовать LocalDateTime и timestamp. Дополнительная настройка Jackson не нужна. При передаче времени с зоной он будет ругаться.
statement.setObject(1, entity.getTestDate());
entity.setTestDate(rs.getObject(COLUMN_LABEL, LocalDateTime.class));
ЗаключениеЧтобы избежать некоторых ошибок, надо изначально договорится о некоторых вещах. Формат даты в запросе и ответе. Зона, в которой будет запущен сервер. Зона, в которой хранится время.Дополнительные ошибки могут появится, из-за устаревшей tzdata. PostgreSQL имеет свою tzdata. Если есть колонки timestamptz, то эта база используется. Надо обновлять минорные релизы. PostgreSQL может быть собран с флагом with-system-tzdata. Тогда надо обновлять системные зоны. Java имеет свою tzdata. Надо тоже обновлять. Можно отдельно от всей jre. Joda-time имеет свою tzdata.Все решения доступны в репозитории. Там две ветки.
===========
Источник:
habr.com
===========
Похожие новости:
- [Java] Еще одна p2p overlay сеть
- [Программирование, Java, Компиляторы] Java HotSpot JIT компилятор — устройство, мониторинг и настройка (часть 1)
- [Java, C++, Разработка под Android] Android interop with SWIG (a guide). From simple to weird. Part 1 — simple
- [Разработка веб-сайтов, JavaScript, HTML, ReactJS] React.js — формошлепство или работа с формами при помощи пользовательских хуков
- [Ненормальное программирование, JavaScript, Google Chrome, PDF] Пугающие эксперименты с PDF: запускаем «Арканоид» в документе (перевод)
- [JavaScript, TensorFlow] Фронтендер пишет нейронки. Уровень сложности «хочу на ручки»
- [JavaScript, Программирование, Atlassian] Как я подружил BPMN и Bitbucket
- [Java] Фреймворк Camel: сравнение компонентов HTTP и AHC
- [Программирование, Разработка игр, WebGL, Прототипирование, Godot] Как собрать паука в Godot, Unigine или PlayCanvas
- [Java, Облачные сервисы, Распределённые системы, Kubernetes] Почему JVM —это ОС и больше чем Кубер
Теги для поиска: #_java, #_java, #_java
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 17:30
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
ДаноAngular, PrimeNG, Spring Boot, JDBC, PostgreSQLНадоПередавать дату с формы в базу и обратноПодготовка create database test_date;
CREATE TABLE test_table (
test_date date NULL, test_timestamp timestamp NULL, test_timestamptz timestamptz NULL, id serial2, CONSTRAINT test_table_pk PRIMARY KEY (id) ); statement.setObject(1, entity.getTestDate(), Types.DATE)
insert into test_table (test_date) values ('2020-12-22 +03')
{ "testDate": "2020-12-22" }
new Date('2020-12-22')
new Date('2020-12-22T00:00:00.000+00:00') Tue Dec 22 2020 03:00:00 GMT+0300 (Moscow Standard Time) insert into test_table (test_date) values ('2020-12-21 +03')
statement.setDate(1, new java.sql.Date(entity.getTestDate().getTime()),
Calendar.getInstance(TimeZone.getTimeZone(userZoneId))); Intl.DateTimeFormat().resolvedOptions().timeZone;
{"testDate":"2020-12-21T20:00:00.000Z","zoneId":"Europe/Samara"}
insert into test_table (test_date) values ('2020-12-22 +04')
import java.text.SimpleDateFormat;
import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; public class DateSerializer extends JsonSerializer<Date> { private final static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); @Override public void serialize(Date value, JsonGenerator gen, SerializerProvider serializers) throws IOException { gen.writeString(format.format(value)); } } private static final String format = "yyyy-MM-dd'T'HH:mm:ss";
@Override public void serialize(Date value, JsonGenerator gen, SerializerProvider serializers) throws IOException { gen.writeString(new SimpleDateFormat(format).format(value)); } {"testDate":"2020-12-21T21:00:00.000Z","zoneId":"Europe/Moscow"}
{"testDate":"2020-12-22T00:00:00.000+03:00","zoneId":"Europe/Moscow"}
const ymd: string[] = obj.testDate.split('-');
const date: Date = new Date(ymd); const ymd: number[] = obj.testDate.split('-').map((s: string) => Number(s));
const date: Date = new Date(ymd[0], ymd[1] - 1, ymd[2]) public saveEntity(entity: TestEntity): Observable<number> {
const date: Date = entity.testDate; const testDate: string = [date.getFullYear(), date.getMonth() + 1, date.getDate()] .map(n => String(n).padStart(2, '0')).join('-'); const body: any = Object.assign({}, entity, {testDate}); return this.http.post<number>(CONTROLLER, body); } insert into test_table (test_date) values ('2020-12-21 -06')
System.setProperty("user.timezone", "UTC")
public static final String APP_TIMEZONE = "America/Chicago";
public static void main(String[] args) { System.setProperty("user.timezone", APP_TIMEZONE); SpringApplication.run(TestDateApplication.class, args); } import com.fasterxml.jackson.annotation.JsonFormat;
public class TestEntity { @JsonFormat(timezone = TestDateApplication.APP_TIMEZONE, pattern = "yyyy-MM-dd") private Date testDate; import java.io.IOException;
import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.exc.InvalidFormatException; public class DateDeserializer extends JsonDeserializer<Date> { private static final String format = "yyyy-MM-dd"; @Override public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { if (p.hasToken(JsonToken.VALUE_STRING)) { String text = p.getText().trim(); if (text.length() != format.length()) { throw new InvalidFormatException(p, "Wrong date", text, Date.class); } try { Date result = new SimpleDateFormat(format).parse(text); return result; } catch (ParseException e) { throw new InvalidFormatException(p, "Wrong date", text, Date.class); } } return (Date) ctxt.handleUnexpectedToken(Date.class, p); } } private static final String COLUMN_LABEL = "test_timestamp";
entity.setTestDate(rs.getTimestamp(COLUMN_LABEL)); statement.setTimestamp(1, new Timestamp(entity.getTestDate().getTime())); insert into test_table (test_timestamptz) values ('2020-12-21 14:00:00-06')
private static final String COLUMN_TIMEZONE = "America/Chicago";
entity.setTestDate(rs.getTimestamp(COLUMN_LABEL, Calendar.getInstance(TimeZone.getTimeZone(COLUMN_TIMEZONE)))); statement.setTimestamp(1, new Timestamp(entity.getTestDate().getTime()), Calendar.getInstance(TimeZone.getTimeZone(COLUMN_TIMEZONE))); update test_table
set test_timestamp = (test_timestamp at time zone 'America/Chicago') at time zone 'UTC'; private static final String COLUMN_TIMEZONE = "UTC";
insert into test_table (test_timestamp) values ('2020-12-21 20:00:00+00')
private static final String COLUMN_LABEL = "test_timestamptz";
entity.setTestDate(rs.getTimestamp(COLUMN_LABEL)); statement.setTimestamp(1, new Timestamp(entity.getTestDate().getTime())); insert into test_table (test_timestamptz) values ('2020-12-21 14:00:00-06')
public saveEntity(entity: TestEntity): Observable<number> {
const date: Date = entity.testDate; const testDate: string = [date.getFullYear(), date.getMonth() + 1, date.getDate()] .map(n => String(n).padStart(2, '0')).join('-') + 'T' + [date.getHours(), date.getMinutes(), date.getSeconds()] .map(n => String(n).padStart(2, '0')).join(':'); const body: any = Object.assign({}, entity, {testDate}); return this.http.post<number>(CONTROLLER, body); } @JsonDeserialize(using = DateDeserializer.class)
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = TestDateApplication.APP_TIMEZONE) private Date testDate; Date result = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").parse(text);
insert into test_table (test_timestamp) values ('2020-12-22 14:14:00+04')
new Date('2020-12-22T14:14:00')
Tue Dec 22 2020 14:14:00 GMT-0600 (Central Standard Time) private LocalDate testDate;
statement.setObject(1, entity.getTestDate(), Types.DATE); entity.setTestDate(rs.getObject("test_date", LocalDate.class)); insert into test_table (test_date) values ('2020-12-21'::date)
new Date('2020-12-21')
Mon Dec 21 2020 03:00:00 GMT+0300 (Moscow Standard Time) public LocalDate deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException { if (p.hasToken(JsonToken.VALUE_STRING)) { String text = p.getText().trim(); try { DateTimeFormatter formatter = new DateTimeFormatterBuilder() .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME) .appendZoneId() .toFormatter(); LocalDate result = ZonedDateTime.parse(text, formatter) .withZoneSameInstant(ZoneId.systemDefault()) .toLocalDate(); return result; } catch (Exception e) { throw new InvalidFormatException(p, "Wrong date", text, Date.class); } } return (LocalDate) ctxt.handleUnexpectedToken(LocalDate.class, p); } insert into test_table (test_date) values ('2020-12-22'::date)
String zoneId = entity.getZoneId();
statement.setObject(1, ZonedDateTime.of(entity.getTestDate(), ZoneId.systemDefault()) .withZoneSameInstant(ZoneId.of(zoneId)) .toLocalDate(), Types.DATE); entity.setTestDate( LocalDateTime.of(rs.getObject(COLUMN_LABEL, LocalDate.class), LocalTime.MIN)); statement.setObject(1, entity.getTestDate(), Types.DATE);
entity.setTestDate(rs.getObject(COLUMN_LABEL, LocalDate.class)); @JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate testDate; statement.setObject(1,
entity.getTestDate() .withZoneSameInstant(ZoneId.systemDefault()) .toLocalDateTime(), Types.TIMESTAMP); entity.setTestDate( ZonedDateTime.of( rs.getObject(COLUMN_LABEL, LocalDateTime.class), ZoneId.systemDefault() ) ); private static final String COLUMN_TIMEZONE = "UTC";
statement.setObject(1, entity.getTestDate() .withZoneSameInstant(ZoneId.of(COLUMN_TIMEZONE)) .toLocalDateTime(), Types.TIMESTAMP); entity.setTestDate( ZonedDateTime.of( rs.getObject(COLUMN_LABEL, LocalDateTime.class), ZoneId.of(COLUMN_TIMEZONE) ) ); statement.setObject(1,
entity.getTestDate() .withZoneSameInstant(ZoneId.systemDefault()) .toLocalDateTime()); insert into test_table (test_timestamptz) values ('2020-12-21 23:30:00+03'::timestamp)
statement.setObject(1, entity.getTestDate().toOffsetDateTime());
insert into test_table (test_timestamptz)
values ('2020-12-21 20:30:00+00'::timestamp with time zone) entity.setTestDate(
rs.getObject(COLUMN_LABEL, OffsetDateTime.class).toZonedDateTime() ); public saveEntity(entity: TestEntity): Observable<number> {
const date: Date = entity.testDate; const testDate: string = [date.getFullYear(), date.getMonth() + 1, date.getDate()] .map(n => String(n).padStart(2, '0')).join('-') + 'T' + [date.getHours(), date.getMinutes(), date.getSeconds()] .map(n => String(n).padStart(2, '0')).join(':'); const body: any = Object.assign({}, entity, {testDate}); return this.http.post<number>(CONTROLLER, body); } statement.setObject(1, entity.getTestDate());
entity.setTestDate(rs.getObject(COLUMN_LABEL, LocalDateTime.class)); =========== Источник: habr.com =========== Похожие новости:
|
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 17:30
Часовой пояс: UTC + 5