#c #date #time #epoch
#c #Дата #время #эпоха
Вопрос:
Я пытаюсь создать калькулятор, который будет определять количество секунд до начала определенного дня.
В настоящее время я могу определить текущий день и время, но я не уверен, как определить количество секунд до начала будущего дня, который я хочу (скажем, в субботу, например).
Я думаю, я мог бы рассчитать, сколько дней у нас осталось до субботы, а затем преобразовать это в секунды, но проблема в том, что может быть добавлено дополнительное время (я хочу знать время в полночь, начало дня).
Кто-нибудь знает, как это было бы возможно?
#pragma once
#include <string>
#include <ctime>
class DateTime
{
public:
DateTime();
DateTime(
const time_tamp; specific_datetime);
const std::string GetWeekDay() const;
const std::string GetMonth() const;
int GetDate() const;
const int GetSecondsUntilNextWeekDay(
const std::stringamp; day_name) const;
private:
const int GetWeekDayNumberFromName(
const std::stringamp; name) const;
tm m_time;
const time_t m_datetime;
DateTime(const DateTime amp;rhs);
DateTime amp;operator=(const DateTime amp;rhs);
};
#include "DateTime.h"
DateTime::DateTime() :
m_datetime(std::time(NULL))
{
localtime_s(amp;m_time, amp;m_datetime);
}
DateTime::DateTime(
const time_tamp; specific_datetime) :
m_datetime(specific_datetime)
{
localtime_s(amp;m_time, amp;m_datetime);
}
const std::string DateTime::GetWeekDay() const
{
switch (m_time.tm_wday)
{
case 0:
return "Sunday";
case 1:
return "Monday";
case 2:
return "Tuesday";
case 3:
return "Wednesday";
case 4:
return "Thursday";
case 5:
return "Friday";
case 6:
return "Saturday";
default:
return "Sunday";
}
}
const std::string DateTime::GetMonth() const
{
switch (m_time.tm_mon)
{
case 0:
return "January";
case 1:
return "February";
case 2:
return "March";
case 3:
return "April";
case 4:
return "May";
case 5:
return "June";
case 6:
return "July";
case 7:
return "August";
case 8:
return "September";
case 9:
return "October";
case 10:
return "November";
case 11:
return "December";
default:
return "January";
}
}
int DateTime::GetDate() const
{
return m_time.tm_mday;
}
int DateTime::GetYear() const
{
return 1900 m_time.tm_year;
}
const int DateTime::GetSecondsUntilNextWeekDay(
const std::stringamp; day_name) const
{
// Calculate how many seconds left for today...
const int todays_hours_left = 23 - m_time.tm_hour;
const int todays_minutes_left = (todays_hours_left * 60) (59 - m_time.tm_min);
const int todays_seconds_left = (todays_minutes_left * 60) (59 - m_time.tm_sec);
int overlap_seconds = 0;
// Calculate how many days until the desired date.
int current_day_number = m_time.tm_wday;
const int desired_day_number = GetWeekDayNumberFromName(day_name);
if (desired_day_number >= 0)
{
if (desired_day_number <= current_day_number)
{
// Find out how many days left in the week, add them to how many days until the today.
const int max_day = 6;
const int days_remaining = max_day - current_day_number;
overlap_seconds = (days_remaining * 24 * 60 * 60);
current_day_number = 0;
}
const int days_left = desired_day_number - current_day_number;
const int hours_left = days_left * 24;
const int minutes_left = hours_left * 60;
const int seconds_left = (minutes_left * 60) - (86400 - todays_seconds_left) overlap_seconds;
return seconds_left;
}
return -1;
}
const int DateTime::GetWeekDayNumberFromName(
const std::stringamp; name) const
{
if (name == "Sunday")
{
return 0;
}
else if (name == "Monday")
{
return 1;
}
else if (name == "Tuesday")
{
return 2;
}
else if (name == "Wednesday")
{
return 3;
}
else if (name == "Thursday")
{
return 4;
}
else if (name == "Friday")
{
return 5;
}
else if (name == "Saturday")
{
return 6;
}
return -1;
}
#include <iostream>
#include "DateTime.h"
int main()
{
DateTime date_time;
const int seconds = date_time.GetSecondsUntilNextWeekDay("Monday");
std::cout << "Seconds Till Date: " << seconds;
}
Я ожидал бы, что если бы я хотел узнать, сколько времени осталось до начала четверга с текущего времени (9:00 вечера в среду), он вернет 3 часа или 10800 секунд (либо или работает).
Ответ №1:
У этого вопроса есть несколько тонких проблем:
- Является ли «начало дня» в определенном часовом поясе? В UTC?
Прочитав код в вопросе, выясняется, что «начало дня» определяется настройкой локального часового пояса компьютера.
- Поскольку мы учитываем часовые пояса, я полагаю, нам также необходимо учитывать переход на летнее время. Например, если в определенный день в 2:00 происходит 1-часовой переход «Вперед по пружине», то в этот день время, прошедшее с 1:00 до 3:00, равно 1 часу, тогда как в «обычные» дни оно равно 2 часам. Если только мы не намерены считать «локальные секунды», а не «физические секунды». Вопрос не совсем ясен по этому вопросу…
Я протестировал код, приведенный в вопросе, используя «America / New_York» в качестве местного часового пояса с 3 парами ввода:
- Суббота 2019-03-02 с 22:02:05 по восточному времени по понедельник 2019-03-04 00:00:00 по восточному времени
- Суббота 2019-03-09 22:02:05 EST — понедельник 2019-03-11 00:00:00 EDT
- Суббота 2019-03-16 с 22:02:05 EDT по понедельник 2019-03-18 00:00:00 EDT
Это вычисление времени между поздней ночью субботы и началом понедельника в течение 3 недель подряд, при этом средняя неделя охватывает изменение смещения UTC из-за перехода на летнее время. вступающий в силу.
Код в вопросе дает 7074 секунды для всех входных пар. В формате hh: mm: ss, то есть 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} amp;amp; 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';
}
Этот код работает путем создания a zoned_seconds
как для времени ввода, так и для целевого времени (начало целевого дня недели, определенного местным часовым поясом).
zoned_seconds
это специализация zoned_time<Duration>
с точностью до секунд.
A zoned_time
— это соединение локальной временной точки и a time_zone
. Это можно эквивалентно рассматривать как сопряжение временной точки UTC и time_zone
. В любом случае можно построить, используя либо a local_time
, либо a sys_time
( sys_time
это UTC), и можно извлечь как a local_time
, так и a sys_time
, которые связаны через time_zone
.
time_zone
В этом примере найден параметр, с current_zone()
помощью которого определяется текущая настройка локального часового пояса компьютера.
now
это сопоставление входных sys_time
данных ( sys_seconds
псевдоним для секундной точности sys_time
) и текущего локального часового пояса компьютера.
today_local
определяется ли точность дня путем получения ‘s и приведения ее к точности дня. local_time
now
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 часам. -
In
sys_time
. This преобразуется в UTC до вычитания и, таким образом, измеряет «физические секунды». Это определяет воскресенье 23h в случае 2 выше.
Примечания:
- Код не содержит явных констант преобразования, таких как 60, 24 или 86400.
- Код не утруждает себя вычислением прошедших секунд текущего дня.
- Код не зависит от
weekday
кодировки и, следовательно, значительно упрощен. - Код полностью избегает устаревшего C timing API и, таким образом, значительно упрощен.
Когда я пишу это:
std::cout << GetSecondsUntilNextWeekDay(Saturday) << 'n';
Выводит:
128459s
Библиотека часовых поясов Говарда Хиннанта 1 теперь является частью спецификации C 20 с незначительными изменениями, такими как включение ее в namespace std::chrono
.
Ответ №2:
Попробуйте это,
#include <ctime>
#include <chrono>
#include <iostream>
using std::chrono::system_clock;
int main()
{
std::time_t current = std::time(nullptr);
tm* timeinfo = localtime(amp;current);
int wday=timeinfo->tm_wday;
int hour=timeinfo->tm_hour;
int min=timeinfo->tm_min;
int sec=timeinfo->tm_sec;
int seconds_passed = sec (60 * min) (60 * 60 * hour); // get the time in seconds passed after 12 AM
// days till next saturday.
switch (wday)
{
case 1:
wday = 5;
break;
case 2:
wday = 4;
break;
case 3:
wday = 3;
break;
case 4:
wday = 2;
break;
case 5:
wday = 1;
break;
case 6:
wday = 7;
break;
case 0:
wday = 6;
break;
}
std::chrono::system_clock::time_point t = std::chrono::system_clock::now();
std::chrono::system_clock::time_point new_t = t std::chrono::hours(wday * 24); // get saturdays timestamp
std::time_t time = system_clock::to_time_t(new_t) - seconds_passed; // subtract seconds_passed from saturdays timestamp
int seconds = time - current ; //seconds until next saturday
std::cout <<"Number of seconds until next saturday "<< seconds <<"n";
}
Ответ №3:
Я обновил исходный вопрос этим решением, я не уверен, есть ли более простой способ сделать это… итак, я открыт для предложений.
Вот добавленный код…
const int DateTime::GetSecondsUntilNextWeekDay(
const std::stringamp; day_name) const
{
// Calculate how many seconds left for today...
const int todays_hours_left = 23 - m_time.tm_hour;
const int todays_minutes_left = (todays_hours_left * 60) (59 - m_time.tm_min);
const int todays_seconds_left = (todays_minutes_left * 60) (59 - m_time.tm_sec);
int overlap_seconds = 0;
// Calculate how many days until the desired date.
int current_day_number = m_time.tm_wday;
const int desired_day_number = GetWeekDayNumberFromName(day_name);
if (desired_day_number >= 0)
{
if (desired_day_number <= current_day_number)
{
// Find out how many days left in the week, add them to how many days until the today.
const int max_day = 6;
const int days_remaining = max_day - current_day_number;
overlap_seconds = (days_remaining * 24 * 60 * 60);
current_day_number = 0;
}
const int days_left = desired_day_number - current_day_number;
const int hours_left = days_left * 24;
const int minutes_left = hours_left * 60;
const int seconds_left = (minutes_left * 60) - (86400 - todays_seconds_left) overlap_seconds;
return seconds_left;
}
return -1;
}
const int DateTime::GetWeekDayNumberFromName(
const std::stringamp; name) const
{
if (name == "Sunday")
{
return 0;
}
else if (name == "Monday")
{
return 1;
}
else if (name == "Tuesday")
{
return 2;
}
else if (name == "Wednesday")
{
return 3;
}
else if (name == "Thursday")
{
return 4;
}
else if (name == "Friday")
{
return 5;
}
else if (name == "Saturday")
{
return 6;
}
return -1;
}
Комментарии:
1. Пожалуйста, просто отредактируйте это в свой актуальный вопрос. Переполнение стека — это не форум, поэтому добавление новых «сообщений» — это не то, как вы обновляете вещи. ИТАК, есть вопрос и ответ (ы), поэтому вы добавили это как ответ на свой вопрос (что, очевидно, неверно). Вы можете удалить это и просто обновить свой исходный вопрос.