У этого вопроса есть пара тонких вопросов:
- Находится ли «начало дня» в определенном часовом поясе? В UTC?
При чтении кода в вопросе выясняется, что «начало дня» определяется настройкой локального часового пояса компьютера.
- Поскольку мы учитываем часовые пояса, я полагаю, что нам также необходимо учитывать летнее время. Например, если в 2:00 в определенный день происходит переход в течение 1 часа в течение 1 часа, то в этот день прошедшее время с 1:00 до 3:00 составляет 1 час, тогда как в «нормальные» дни это 2 часа. , Если только мы не собираемся считать «местные секунды», а не «физические секунды». Вопрос не совсем ясен в этом вопросе ...
Я протестировал код, приведенный в вопросе, используя «America / New_York» в качестве местного часового пояса с 3 входными парами:
- Суббота 2019-03-02 22:02:05 EST до понедельника 2019-03-04 00:00:00 EST
- Суббота 2019-03-09 22:02:05 EST до понедельника 2019-03-11 00:00:00 ПО ВОСТОЧНОМУ ВРЕМЕНИ
- Суббота 2019-03-16 22:02:05 ПО ВОСТОЧНОМУ ВРЕМЕНИ до понедельника 2019-03-18 00:00:00 ПО ВОСТОЧНОМУ ВТОРОМУ
Это вычисление времени между поздним субботним вечером и началом понедельника в течение 3 недель подряд, когда средняя неделя колеблется в изменении смещения UTC из-за перехода на летнее время.
Код в вопросе дает 7074 для всех входных пар. В формате чч: мм: сс, то есть 01:57:54. Это примерно 1 день от правильного ответа, который для 1-й и 3-й пар ввода составляет 93475 с, или 1 день и 7075 с, или 1 день и 01: 57: 55.
Правильный ответ для второго случая, потому что воскресенье длится всего 23 часа, на 1 час меньше, или 89875 с.
Я не отследил все проблемы в коде в вопросе, но, кажется, есть по крайней мере эти 3:
- Непостоянная ошибка в подсчете количества дней.
- Непостоянная ошибка в подсчете количества секунд.
- Пренебрегать изменениями смещения UTC, которые могут произойти в местном часовом поясе.
Бесплатная библиотека с открытым исходным кодом Говарда Хиннанта для часовых поясов 1 может быть использована для исправления следующих проблем:
#include "date/tz.h"
#include <chrono>
#include <iostream>
std::chrono::seconds
GetSecondsUntilNextWeekDay(date::weekday wd,
date::sys_seconds tp =
date::floor<std::chrono::seconds>(std::chrono::system_clock::now()))
{
using namespace std::chrono;
using namespace date;
auto zone = current_zone();
zoned_seconds now = {zone, tp};
auto today_local = floor<days>(now.get_local_time());
weekday today_wd{today_local};
auto delta_days = wd - today_wd;
if (delta_days == days{0} && now.get_local_time() != today_local)
delta_days += weeks{1};
zoned_seconds target_date = {zone, today_local + delta_days};
return target_date.get_sys_time() - now.get_sys_time();
}
int
main()
{
using namespace date;
using namespace std::chrono;
zoned_seconds zt{current_zone(), local_days{March/9/2019} + 22h + 2min + 5s};
std::cout << GetSecondsUntilNextWeekDay(Monday, zt.get_sys_time()) << '\n';
}
Этот код работает путем создания zoned_seconds
как для времени ввода, так и для целевого времени (начало целевого дня недели, определенного местным часовым поясом).
zoned_seconds
- это специализация zoned_time<Duration>
с точностью до секунды.
A zoned_time
- это пара точек местного времени и time_zone
. Это можно эквивалентно представить как соединение момента времени UTC и time_zone
. В любом случае можно использовать либо local_time
, либо sys_time
(sys_time
- это UTC), и можно извлечь как local_time
, так и sys_time
, которые связаны через time_zone
.
В этом примере time_zone
находится с current_zone()
, который определяет текущую настройку местного часового пояса компьютера.
now
представляет собой пару входных данных sys_time
(sys_seconds
- псевдоним для секундной точности sys_time
) и текущего местного часового пояса компьютера.
today_local
- это дневная точность local_time
, полученная путем получения now
s local_time
и получения дневной точности.
Местный weekday
может быть построен непосредственно из today_local
.
delta_days
- количество дней, которое нужно добавить к today_wd
, чтобы достичь цели weekday
из wd
. Вычитание weekday
имеет циклический характер: оно всегда приводит к числу дней в диапазоне [0, 6] и не зависит от основного кодирования дней недели. Например, воскресенье всегда 1 день после субботы, даже если суббота кодируется как 6, а воскресенье как 0.
Если текущее значение weekday
совпадает с целевым значением weekday
, и если это не первая секунда целевого значения weekday
, то мы хотим вычислить начало этого же дня на следующей неделе. В противном случае now
является целевой датой, и эта функция вернет 0 секунд.
Учитывая количество дней (delta_days
) и Текущий день (today_local
), целевую дату можно вычислить с помощью today_local + delta_days
. Это точность дней local_time
, которая неявно преобразуется в точность секунд и указывает на начало дня. Это в паре с местным часовым поясом для построения target_date
.
Теперь, когда у нас есть target_date
и now
, нам просто нужно вычесть их, чтобы получить желаемое количество секунд. Есть два способа сделать это вычитание:
В local_time
. Это будет измерять время в «календарных секундах», как определено местным time_zone
. В этом календаре все дни 24 часа.
В sys_time
. Это преобразуется в UTC до вычитания и, таким образом, измеряет «физические секунды». Это обнаруживает 23-е воскресенье в случае 2 выше.
Примечания:
- Код не содержит явных констант преобразования, таких как 60, 24 или 86400.
- Код не беспокоит вычисление истекших секунд текущего дня.
- Код не зависит от кодировки
weekday
и поэтому значительно упрощен.
- Код полностью избегает устаревшего API синхронизации C и поэтому значительно упрощен.
Как я пишу это:
std::cout << GetSecondsUntilNextWeekDay(Saturday) << '\n';
Выходы:
128459s
1 Библиотека часовых поясов Говарда Хиннанта теперь является частью черновой спецификации C ++ 20 с небольшими изменениями, такими как добавление в namespace std::chrono
.