Ваша настоящая проблема заключается в том, что ваш серверный REST сервис плохо спроектирован.
ISO 8601
Прежде всего, значения даты и времени, которые будут передаваться, должны быть в стандарте *Формат 1007 * ISO 8601 , а не некоторая локализованная строка представления.
Стандартные форматы по умолчанию используются в классах java.time при синтаксическом анализе / генерации текста.
java.time
Никогда не используйте ужасный класс Date
.Этот класс, а также Calendar
, SimpleDateFormat
и тому подобное, был вытеснен несколько лет назад классами java.time , определенными в JSR 310.
Date.from (Instant.now ())
Никогда не смешивайте ужасные устаревшие классы даты и времени (Date
) с их заменами (Instant
), современные java.time классы,Смешивать их не нужно и сбивает с толку.
Классы java.time полностью заменяют своих предшественников.
Время всегда одинаковое, меняются только дни.И API также возвратит дату, которая находится в будущем.
Если вы хотите только обмениваться значениями даты, без времени суток и без часового пояса или смещения, используйте LocalDate
класс и заменить ISO 8601 в формате ГГГГ-ММ-ДД, например 2018-03-11
.Звоните LocalDate.parse
и LocalDate::toString
.
long разностьInDays = TimeUnit.DAYS.convert (diff, TimeUnit.MILLISECONDS);
Представление количества дней в виде миллисекунд без учета временизона или смещение от UTC безрассудно.Дни не всегда 24 часа.Это может быть 23, 23,5, 25 или другое количество часов.
Если вы хотите использовать UTC, чтобы всегда иметь 24-часовой рабочий день, так и скажите.Представьте свою дату-время с указанием часового пояса или смещения.Например, стандартный формат: 2018-03-11T00:00Z
, где Z
в конце означает UTC и произносится «Zulu».
Таким образом, вся ваша проблема может быть уменьшена до этой однострочной.
ChronoUnit.DAYS.between(
LocalDate.now( ZoneId.of( "America/Montreal" ) ) , // Get the current date as seen in the wall-clock time used by the people of a particular region (a time zone).
LocalDate.parse( "2019-01-23" ) // Parse a string in standard ISO 8601 format for a date-only value.
) // Returns a `long` integer number of days elapsed.
Unzoned
Если вы не в состоянии устранить все эти грязные проблемы дизайна, тогда давайте продолжим, пытаясь использовать эти грязные данные.
Сначала исправьте am
/ pm
, который должен быть в верхнем регистре.
String input = "March 13, 2018 12:00 pm".replace( " am" , " AM" ).replace( " pm" , " PM" );
Определите шаблон форматирования, чтобы соответствоватьВаша строка ввода.
Укажите Locale
, чтобы определить человеческий язык и культурные нормы, используемые при переводе текста.
Locale locale = Locale.US;
DateTimeFormatter f = DateTimeFormatter.ofPattern( "MMMM d, uuuu HH:mm a" );
Синтаксически проанализировать как LocalDateTime
, поскольку в вводе отсутствуетуказатель часового пояса или смещения от UTC.
LocalDateTime ldt = LocalDateTime.parse( input , f );
ldt.toString (): 2018-03-13T12: 00
A LocalDateTime
преднамеренноне имеет понятия о часовом поясе или смещении от UTC.Так что этот класс не может представлять момент, является не точкой на временной шкале.
Если вы хотите использовать общие 24-часовые дни без учета реальности аномалий во время настенных часов, используемых различными людьми в разных местах, таких как Летнее время (DST) , мыМожно продолжать использовать этот класс.
Получить текущую дату, как видно на часах настенного времени, использованных людьми, на которых ориентировано ваше приложение (часовой пояс).
Часовой пояс имеет решающее значение при определении даты.В любой момент времени дата меняется по всему земному шару в зависимости от зоны.Например, через несколько минут после полуночи в Париж Франция - это новый день, в то время как "вчера" в Монреаль Квебек .
Если часовой пояс не указан,JVM неявно применяет свой текущий часовой пояс по умолчанию.Это значение по умолчанию может измениться в любой момент во время выполнения (!), Поэтому ваши результаты могут отличаться.Лучше указать в качестве аргумента ваш желаемый / ожидаемый часовой пояс .
Укажите правильное имя часового пояса в формате continent/region
, например America/Montreal
, Africa/Casablanca
или Pacific/Auckland
.Никогда не используйте 2-4 буквенные сокращения, такие как EST
или IST
, поскольку они не истинные часовые пояса, не стандартизированы и даже не уникальны (!).
ZoneId z = ZoneId.of( "America/Montreal" ) ;
LocalDate today = LocalDate.now( z ) ;
Если уВы хотите использовать текущий часовой пояс JVM по умолчанию, запросить его и передать в качестве аргумента.Если опущено, текущее значение по умолчанию JVM применяется неявно.Лучше быть явным, поскольку значение по умолчанию может быть изменено в любой момент во время выполнения любым кодом в любом потоке любого приложения в JVM.
ZoneId z = ZoneId.systemDefault() ; // Get JVM’s current default time zone.
Получить полдень в эту дату,ни в одном конкретном часовом поясе.
LocalDateTime ldtTodayNoon = LocalDateTime.of( today , LocalTime.NOON ) ;
Количество прошедших дней.
long daysElapsed =
ChronoUnit.DAYS.between(
ldtTodayNoon ,
ldt
)
;
Конечно, мы могли бы сделать это, используя только LocalDate
, а не LocalDateTime
, но я следовал за вашей формулировкой проблемы, как написано.
Обратите внимание, что в данном примере строка представляет дату в прошлом.Таким образом, наше число дней будет отрицательным.
Зонировано
Если вы хотите учесть аномалии, наблюдаемые в некоторые даты в некоторых зонах, то вы должны были правильно представить момент, как обсуждалось выше., с индикатором часового пояса или смещения от UTC.
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = ldt.atZone( z ) ;
ZonedDateTime zdtNow = ZonedDateTime.now( z ) ;
Или, возможно, вы хотите сегодня полдень в нужном часовом поясе.Если полдень не является действительным временем суток в эту дату в этой зоне, класс ZonedDateTime
изменится.Обязательно прочитайте ZonedDateTime.of
JavaDoc, чтобы понять алгоритм этой корректировки.
LocalDate today = LocalDate.now( z ) ;
ZonedDateTime zdtTodayNoon = ZonedDateTime.of( today , LocalTime.NOON , z ) ;
Рассчитать прошедшее время, основываясь на долях секунды или целых календарных днях.
Duration d = Duration.between( zdtTodayNoon , zdt ) ; // For a calculation based in whole seconds plus a fractional second in nanoseconds without regard for a calendar, just using generic 24-hour days.
Period p = Period.between( zdtTodayNoon , zdt ) ; // For a calculation based in whole days, for a number of years-months-days based on calendar dates.
Если вы настаиваете на отслеживании на счет миллисекунд, позвоните по номеру Duration::toMillis
.
long millisecondsElapsed = d.toMillis() ; // Entire duration as a total number of milliseconds, ignoring any microseconds or nanos.
Все это уже много раз было рассмотрено в переполнении стека.Вы можете узнать больше и увидеть больше примеров, выполнив поиск по этим java.time именам классов.
About java.time
The 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?
ThreeTen-Extra Проект расширяет java.time дополнительными классами.Этот проект является полигоном для возможных будущих дополнений к java.time.Здесь вы можете найти некоторые полезные классы, такие как Interval
, YearWeek
, YearQuarter
и more .