Ответ Беры близок к правильному, но выбирает не совсем правильные java.time классы и методы.
ZonedDateTime
& Instant
, не LocalDateTime
Неверно используется класс LocalDateTime
, в котором отсутствует понятие часового пояса или смещения от UTC.Таким образом, LocalDateTime
не не представляет определенный момент на временной шкале.
Вместо этого используйте ZonedDateTime
, чтобы представить конкретный момент со временем настенных часов, используемым людьмиконкретный регион (часовой пояс).
Часовой пояс имеет решающее значение при определении даты и времени суток.В любой момент времени дата меняется по всему земному шару в зависимости от зоны.Например, через несколько минут после полуночи в Париж Франция - это новый день, в то время как "вчера" в Монреаль Квебек .
Если часовой пояс не указан,JVM неявно применяет свой текущий часовой пояс по умолчанию.Это значение по умолчанию может измениться в любой момент во время выполнения (!), Поэтому ваши результаты могут отличаться.Лучше явно указать в качестве аргумента желаемый / ожидаемый часовой пояс .
Укажите правильное имя часового пояса в формате continent/region
, например America/Montreal
, Africa/Casablanca
или Pacific/Auckland
.Никогда не используйте 3-4-буквенное сокращение, такое как EST
или IST
, поскольку они не истинные часовые пояса, не стандартизированы и даже не уникальны (!).
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime now = ZonedDateTime.now( z ) ;
Если вы хотите использовать текущий часовой пояс JVM по умолчанию, запросите его и передайте в качестве аргумента.Если опущено, текущее значение по умолчанию JVM применяется неявно.Лучше быть точным, так как значение по умолчанию может быть изменено в любой момент во время выполнения любым кодом в любом потоке любого приложения в JVM.
ZoneId z = ZoneId.systemDefault() ; // Get JVM’s current default time zone.
Извлечение части только для датыбез времени суток и без часового пояса.Любой java.time класс с именем «Local…» означает, что он «не зонирован», без какой-либо концепции зоны или смещения.
LocalDate ld = now.toLocalDate(); // Extract date-only without time-of-day and without offset-from-UTC.
Избегайте думать / говорить о «полуночи» как таковойстановится скользкой концепцией.Вместо этого сосредоточьтесь на «первом моменте дня».Итак, нам нужен первый момент завтрашнего дня.
Определите «завтра».
LocalDate tomorrow = ld.plusDays( 1 ) ;
Пусть java.time определит первый момент.Не думайте, что это будет время суток 00:00:00.Аномалии, такие как переход на летнее время (DST), означают, что день может начинаться в такое время, как 01:00:00.Укажите часовой пояс, разумеется, для обработки любых аномалий, влияющих на жителей этого региона, этого часового пояса.
ZonedDateTime tomorrowStart = tomorrow.atStartOfDay( z ) ;
Рассчитайте промежуток времени от настоящего момента до начала завтрашнего дня.Класс Duration
представляет промежуток времени, не привязанный к временной шкале.
Duration d = Duration.between( now , tomorrowStart ) ;
Кстати, это может помочь вашему пониманию увидеть эти значения в UTC.Как программист (или системный администратор), вы должны думать в UTC.Забудьте о своем собственном приходском часовом поясе во время работы.
Класс Instant
представляет момент на временной шкале в UTC с разрешением наносекунды (до девяти (9) цифр десятичной дроби).
Instant instantNow = now.toInstant() ;
Instant instantTomorrowStart = tomorrowStart.toInstant() ;
Duration d = Duration.between( instantNow , instantTomorrowStart ) ;
Эта пара Instant
объектов представляет те же самые моменты, что и пара ZonedDateTime
объектов.Те же точки на временной шкале, но разное время на настенных часах.Таким образом, результирующий расчет Duration
такой же.
ScheduledExecutorService::schedule
, а не scheduleAtFixedRate
Другой ответ правильный, предлагая ScheduledExecutorService
запланировать задачу для запуска наопределенное время.
Но другие отвечают на звонкиscheduleAtFixedRate
, который многократно выполняет одну и ту же задачу (a Runnable
или Callable
).Но Вопрос определяет требование, чтобы задача выполнялась в первый момент дня, каждый день.Это время дня может меняться в разные даты из-за аномалий, таких как Летнее время (DST) или других изменений в смещение от UTC , используемых конкретным часовой пояс. Эти изменения происходят на удивление часто, так как политики всего мира демонстрируют склонность к этим изменениям.Так что, если вы действительно хотите проверить первый момент дня, вам нужно пересчитывать продолжительность ожидания каждый раз, когда вы запускаете задачу.
Таким образом, вы должны попросить службу исполнения выполнить эту задачу только один раз.И в конце этой задачи запланируйте еще один запуск на следующий день.
Настройте службу исполнителя и сохраните ссылку на нее где-нибудь в своем приложении.Класс Executors
предоставляет множество таких услуг.Получить счетчик SECONDS
или NANOS
из общего метода Duration::get
.Использование наносекунд немного глупо, поскольку исполнитель не гарантированно работает с такой точностью.При этом всегда учитывается фактор выдумки, поскольку нагрузка на ядра вашего ЦП, многозадачное планирование вашей операционной системы, многопоточность вашей JVM могут повлиять, когда ваша задача действительно будет запущена.Поэтому я использую секунды здесь.
Кстати, немного сероборода совета ... Полночь - это час колдовства на компьютерах.Это когда многие операционные системы и приложения участвуют в операциях очистки и переноса, таких как создание новых файлов журнала.Некоторые программисты и системные администраторы предпочитают запускать некоторое время после полуночи или позже в середине ночи, например, в 2 или 3 часа ночи.
ScheduledExecutorService executorService = Executors.newSingleThreadExecutor() ;
Рассчитать задержку до первого выполнения.
long countUntilNextTaskRun = d.getSeconds() ;
Запланируйте задачу, позвонив по номеру ScheduledExecutorService.schedule
и указав время ожидания.
executorService.schedule( myRunnableOrCallable , countUntilNextTaskRun , unit ) ;
Возможно, вы захотите захватить ScheduledFuture
для последующего использования.
ScheduledFuture<?> scheduledFuture = executorService.schedule( myRunnableOrCallable , countUntilNextTaskRun , TimeUnit.SECONDS ) ;
Эта задача будет выполняться только один раз.После того, как эта задача выполнит свою основную работу, она также запланирует следующий запуск, используя тот же код, что и выше.Рассчитайте время до следующего начала дня в этом конкретном часовом поясе.
Жизненный цикл
Служба исполнителя хранится в памяти вашего приложения.Это имеет два важных значения:
- Обязательно выключите службу исполнителя.В противном случае пул потоков и запланированная задача могут остаться в памяти даже после закрытия приложения (в зависимости от реализации вашей JVM и хост-ОС).Таким образом, ваше приложение должно всегда определять, когда оно закрывается / завершается, и закрывать службу исполнителя.
- Служба исполнителя не сохраняется после закрытия / выхода приложения.Поэтому, когда ваше приложение запускается, ваш код инициализации / запуска должен заново установить новую службу исполнителя.
Если ваши бизнес-требования требуют, чтобы вы наверстали упущенное в дни выполнения заданий, вам также потребуется запрограммировать задание на постоянную запись даты и времени (Instant
) когда он в последний раз бежал.Затем вы вычисляете, сколько дней вы пропустили, используя те же классы и методы, которые обсуждались выше.
О java.time
java.time фреймворк встроен в Java 8 и более поздние версии.Эти классы вытесняют проблемные старые устаревшие классы даты и времени, такие как java.util.Date
, Calendar
, & SimpleDateFormat
.
Проект Joda-Time , теперь в режиме обслуживания , рекомендует перейти на классы java.time .
Чтобы узнать больше, см. Oracle Tutorial .И поиск переполнения стека для многих примеров и объяснений.Спецификация JSR 310 .
Yoвы можете обмениваться java.time объектами напрямую с вашей базой данных.Используйте драйвер JDBC , совместимый с JDBC 4.2 или более поздней версии.Нет необходимости в строках, нет необходимости в java.sql.*
классах.
Где получить классы java.time?