Как я могу рассчитать количество секунд до следующей субботы в C ?

#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 парами ввода:

  1. Суббота 2019-03-02 с 22:02:05 по восточному времени по понедельник 2019-03-04 00:00:00 по восточному времени
  2. Суббота 2019-03-09 22:02:05 EST — понедельник 2019-03-11 00:00:00 EDT
  3. Суббота 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:

  1. Ошибка в вычислении количества дней.
  2. Ошибка в вычислении количества секунд.
  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 , нам просто нужно вычесть их, чтобы получить желаемое количество секунд. Есть два способа сделать это вычитание:

  1. В local_time . Это будет измерять время в «календарных секундах», как определено локальным time_zone . В этом календаре все дни равны 24 часам.

  2. 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. Пожалуйста, просто отредактируйте это в свой актуальный вопрос. Переполнение стека — это не форум, поэтому добавление новых «сообщений» — это не то, как вы обновляете вещи. ИТАК, есть вопрос и ответ (ы), поэтому вы добавили это как ответ на свой вопрос (что, очевидно, неверно). Вы можете удалить это и просто обновить свой исходный вопрос.