Создание акции, которая начинается в 9 утра для всех магазинов по всему миру - PullRequest
0 голосов
/ 21 ноября 2018

Скажем, например, что продвижение в магазине начинается в 9 утра для всех магазинов по всему миру.Это означает, что он начинается в 9:00 CST для магазинов в Чикаго, 9:00 PST для магазинов в Сиэтле и 9:00 по Гринвичу для магазинов в Великобритании.

В нашей таблице promotions на Postgres мы установим время началадля этой акции как 09: 00: 00.

В каждом магазине есть компьютер с веб-браузером, который просматривает доступные акции.Необходимо передать свое локальное время на сервер, чтобы сервер мог вернуть все рекламные акции за это местное время.Таким образом, нам нужно найти способ записать местное время в JavaScript, кодировать его, отправить его в бэкэнд Java, восстановить его, а затем сравнить со временем начала в таблице promotions.

Местное время, конечно, зависит от часового пояса.Если в Чикаго 9 утра, то магазин в Чикаго должен сообщить серверу, что сейчас 9 утра.Бесполезно отправлять время UTC без указания часового пояса.

Вопрос: Какой хороший способ записать местное время (на основе часового пояса) в JavaScript, закодировать его,отправить его в бэкэнд Java, восстановить его как Java Date, а затем сравнить этот Java Date со временем начала продвижения в 9 утра в базе данных Postgres?

Мой (неудовлетворительный) подход: Лучшее, что я могу придумать, - это отправить время UTC в миллисекундах, используя метод JavaScript Date.getTime, вместе со смещением часового пояса, которое можно рассчитать в минутах с помощью метода JavaScript Date.getTimezoneOffset и преобразовать в миллисекунды.Вычитая смещение часового пояса из времени UTC в миллисекундах, мы можем затем создать объект Java Date из полученной разницы.Если в Чикаго 9 утра, то, надеюсь, Java Date будет хранить 9 утра.Что немного странно в этом подходе, так это то, что Java Date будет на самом деле хранить 9:00 UTC, даже если он представляет 9:00 CST.Это только одна из причин, почему я не удовлетворен таким подходом.Можете ли вы придумать что-нибудь лучше?

Ответы [ 2 ]

0 голосов
/ 22 ноября 2018

Postgres

В Postgres, когда вы имеете в виду 9 AM везде или 9 AM где-либо, на определенную дату, используйте тип данных столбца TIMESTAMP WITHOUT TIME ZONE.Любой часовой пояс или смещение от UTC, включенное во вход, игнорируется, а дата и время дня принимаются как есть (без регулировки) и сохраняются.В этом типе данных намеренно отсутствует какое-либо понятие часовой пояс или смещение от UTC .

Для времени суток без даты используйте данные TIME WITHOUT TIME ZONEтип.Postgres также предлагает TIME WITH TIME ZONE только потому, что это требуется спецификацией SQL;этот тип WITH не имеет смысла и никогда не должен использоваться.

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

Java

В бэкэнде Java используйте только современные java.time классы.Эти годы назад вытеснили ужасные старые классы даты и времени в комплекте с самыми ранними версиями Java.

Если вы еще не используете Java 8 или более позднюю версию, найдите почти все те же функции в обратном порте Java 6 & 7 в проекте ThreeTen-Backport .Стоит того, чтобы добавить эту библиотеку в ваш проект.От тех же самых замечательных людей, которые принесли вам классы java.time и проект Joda-Time, все под руководством одного и того же человека Стивена Колебурна.

LocalDateTime

В java.time используйте класс LocalDateTime для случаев, когда вы имеете в виду 9 утра где угодно / везде на определенную дату.Как и TIMESTAMP WITHOUT TIME ZONE в Postgres, в этом классе намеренно отсутствует какое-либо понятие зоны или смещения.

LocalDateTime ldt = LocalDateTime.of( 2018 , 1 , 23 , 15 , 0 , 0 , 0 ) ;  // 3 PM on 23rd of January this year.

LocalTime

Если вы имеете в виду только время дня, без датыиспользуйте класс LocalTime.

LocalTime lt = LocalTime.of( 15 , 0 ) ;  // 3 PM.

JDBC

Начиная с JDBC 4.2 и более поздних версий вы можете обмениваться объектами java.time с базой данных через getObjectи setObject методы.

LocalDateTime ldt = myResultSet.getObject( … , LocalDateTime.class ) ;

Если ваши драйверы JDBC еще не обновлены до 4.2, вернитесь к ужасным старым классам прежних версий, но немедленно преобразуйте в классы java.time .

Учитывая, что у унаследованных классов отсутствует класс для даты плюс время суток без часового пояса, мы должны подделать его.Используйте java.sql.Timestamp, который представляет момент в UTC с разрешением наносекунд, и просто игнорируйте тот факт, что он находится в UTC.

java.sql.Timestamp ts = myResultSet.getTimestamp( … ) ;

Для Java 8 и более поздних версий выполните преобразование с использованиемновые методы добавлены к старым классам.Сначала преобразуйте в java.time.Instant, который также представляет момент в UTC с разрешением наносекунд.Затем преобразуйте в LocalDateTime, эффективно удалив понятие UTC.

Instant instant = ts.toInstant() ;  // Convert from legacy class to modern one.
LocalDateTime ldt = LocalDateTime.ofInstant( instant , ZoneOffset.UTC ) ;  // Remove the concept of UTC (or any other offset or zone) from our data.

Для Java 6 и 7 с использованием библиотеки ThreeTen-Backport используйтеметоды преобразования в их полезности DateTimeUtils class.

org.threeten.bp.Instant instant = org.threeten.bp.DateTimeUtils.toInstant( ts ) ;  // Convert from legacy class to modern.
org.threeten.bp.LocalDateTime ldt = LocalDateTime.ofInstant( instant , ZoneOffset.UTC ) ;  // Remove the concept of UTC (or any other offset or zone) from our data.

ZonedDateTime

Классы Local… по определению не имеют реального значения, пока вы не поместите их вконтекст часового пояса.LocalDateTime - это , а не момент, не представляет точку на временной шкале.

Укажите собственное имя часового пояса вформат continent/region, например America/Montreal, Africa/Casablanca или Pacific/Auckland.Никогда не используйте 2-4-буквенное сокращение, такое как PST или BST или EST или IST, поскольку они не истинные часовые пояса, не стандартизированы и даже не уникальны (!).

LocalDateTime ldt = 
    LocalDateTime.of(
        LocalDate.of( 2018 , Month.January , 23 ) ,
        LocalTime.of( 9 , 0 ) 
    )
;

ZoneId zLosAngeles = ZoneId.of( "America/Los_Angeles" ) ;  // Seattle time zone.
ZonedDateTime zdtSeattle = ldt.atZone( zLosAngeles ) ;

ZoneId zChicago = ZoneId.of( "America/Chicago" ) ;  
ZonedDateTime zdtChicago = ldt.atZone( zChicago ) ;

ZoneId zLondon = ZoneId.of( "Europe/London" ) ;  
ZonedDateTime zdtLondon = ldt.atZone( zLondon ) ;

Там у нас есть три ZonedDateTime объекта: zdtSeattle, zdtChicago и zdtLondon.Это все 9 утра 23 января в начале этого года.Поймите, что это три совершенно разных момента, каждый на несколько часов раньше, когда вы идете на восток.У них у всех одинаковое время на настенных часах (9 утра 23 числа), но они представляют собой три разные точки на временной шкале.

JavaScript

Хотя я недостаточно хорошо знаю JavaScript, чтобы сказать наверняка,Я сомневаюсь, что у вас есть какая-нибудь библиотека для работы с датой и временем.Платформа java.time является ведущей в отрасли.

Что касается разработки пользовательского интерфейса веб-клиента, я использую Vaadin , так что это не проблема: чистая Java на внутреннем сервере автоматически генерирует HTML / CSS / DOM / JavaScript, необходимый длявеб-браузер.

найти способ захвата местного времени в JavaScript

Что касается определения текущего часового пояса по умолчанию на клиентском компьютере, я не эксперт,но, насколько я помню, браузеры не возвращают именованный часовой пояс, только смещение от UTC.См. Ответ Мэтта Джонсона для возможного решения.В конечном счете, в любом приложении (на рабочем столе или в Интернете), если необходим правильный часовой пояс, вы должны попросить или подтвердить желаемый / ожидаемый часовой пояс с пользователем .И может быть целесообразно всегда указывать где-нибудь в вашем пользовательском интерфейсе, какой часовой пояс используется вашим приложением.

Если вам нужно обмениваться значениями даты и времени между вашим бэкэндом Java и кодом JavaScript в front-end, у вас есть два варианта выбора:

  • ISO 8601
  • Count-from-epoch

ISO 8601

Стандарт ISO 8601 определяет множество текстовых форматов для обмена значениями даты и времени.Они продуманно разработаны, чтобы избежать двусмысленности.Их легко разобрать на машине и легко прочитать людям в разных культурах.

Классы java.time по умолчанию используют эти форматы при генерации / разборе строк.

Count-from-Epoch

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

Ссылочная дата эпохи - это точка времени, используемая в качестве базовой линии.Тогда некоторый отсчет вперед или назад делается с некоторой гранулярностью.

Одна большая проблема заключается в том, что десятков ссылок эпохи используются различными системами.Классы java.time по умолчанию используют эпоху Unix первого момента 1970 года в UTC, 1970-01-01T00: 00Z.

Другая проблема заключается в том, что существует множество гранулярностей, таких как целые секунды, миллисекунды, микросекунды и наносекунды.Программисты должны четко задокументировать / сообщить, какая гранулярность находится в игре.

Если бы вы отправляли свои три момента открытия для ваших трех магазинов в JavaScript в качестве отсчета времени, вы бы отправляли три разных числа.

long millisecondsSeattle = zdtSeattle.toInstant().toEpochMilli() ;
long millisecondsChicago = zdtChicago.toInstant().toEpochMilli() ;
long millisecondsLondon = zdtLondon.toInstant().toEpochMilli() ;

В результате получается три разных числа для трех разных моментов.


О java.time

java.time Framework встроен в Java 8 и более поздние версии.Эти классы вытесняют проблемные старые устаревшие классы даты и времени, такие как java.util.Date, Calendar, & SimpleDateFormat.

Проект Joda-Time , теперь в режиме обслуживания , рекомендует перейти на классы java.time .

Чтобы узнать больше, см. Oracle Tutorial .И поиск переполнения стека для многих примеров и объяснений.Спецификация: JSR 310 .

Вы можете обмениваться java.time объектами непосредственно с вашей базой данных.Используйте драйвер JDBC , совместимый с JDBC 4.2 или более поздней версии.Нет необходимости в строках, нет необходимости в java.sql.* классах.

Где получить классы java.time?

  • Java SE 8 , Java SE 9 , Java SE 10, Java SE 11 и более поздние версии - часть стандартного Java API с связанной реализацией.
    • Java 9 добавляет некоторые незначительные функции и исправления.
  • Java SE 6 и JavaSE 7
    • Большинство функций java.time перенесено в Java 6 & 7 в ThreeTen-Backport .
  • Android
    • Более поздние версии реализации связки Android классов java.time .
    • Для более ранних версий Android (<26) <a href="https://github.com/JakeWharton/ThreeTenABP" rel="nofollow noreferrer"> ThreeTenABP проект адаптируется ThreeTen-Backport (упомянуто выше).См. Как использовать ThreeTenABP… .

ThreeTen-Extra Проект расширяет java.time дополнительными классами.Этот проект является полигоном для возможных будущих дополнений к java.time.Здесь вы можете найти несколько полезных классов, таких как Interval, YearWeek, YearQuarter и more .

0 голосов
/ 21 ноября 2018

Вам не нужно фиксировать местное время пользователя, а просто его идентификатор часового пояса IANA, например, "America/Los_Angeles".Это может затем использоваться в вашем внутреннем коде Java в API, которые принимают часовой пояс.

В большинстве современных браузеров вы можете получить идентификатор часового пояса следующим образом:

Intl.DateTimeFormat().resolvedOptions().timeZone

Если вам требуется поддержка старых браузеров, есть несколько библиотек, которые будут использовать этот API-интерфейс Intl, когда он будет доступен, но в противном случае вернутся к обоснованному предположению. Подробнее об этом здесь .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...