【问题标题】:How can I calculate the number of seconds until next Saturday in C++?如何在 C++ 中计算下周六之前的秒数?
【发布时间】:2019-09-01 13:20:59
【问题描述】:

我正在尝试制作一个计算器,它可以确定距离特定日期开始的秒数。

我目前能够确定当前日期和时间,但我不确定如何确定距离我想要的未来一天开始的秒数(例如星期六)。

我想我可以计算距星期六还有多少天,然后将其转换为秒,但问题是可能会增加额外的时间(我想知道午夜的时间,一天的开始)。

有人知道这怎么可能吗?

#pragma once
#include <string>
#include <ctime>

class DateTime
{
public:
   DateTime();

   DateTime(
      const time_t& specific_datetime);

   const std::string GetWeekDay() const;

   const std::string GetMonth() const;

   int GetDate() const;

   const int GetSecondsUntilNextWeekDay(
      const std::string& day_name) const;

private:

   const int GetWeekDayNumberFromName(
      const std::string& name) const;

   tm m_time;
   const time_t m_datetime;

   DateTime(const DateTime &rhs);
   DateTime &operator=(const DateTime &rhs);
};
#include "DateTime.h"

DateTime::DateTime() :
   m_datetime(std::time(NULL))
{
   localtime_s(&m_time, &m_datetime);
}

DateTime::DateTime(
   const time_t& specific_datetime) :
   m_datetime(specific_datetime)
{
   localtime_s(&m_time, &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::string& 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::string& 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 秒(要么工作要么工作)。

【问题讨论】:

    标签: c++ date time epoch


    【解决方案1】:

    这个问题有几个微妙的问题:

    • “一天的开始”是否在特定时区? UTC 时间?

    通过阅读问题中的代码,“一天的开始”似乎是由计算机的本地时区设置定义的。

    • 由于我们考虑了时区,我认为我们还需要考虑夏令时。例如,如果在特定日期的 2:00 有一个 1 小时的“Spring Forward”过渡,那么在那一天从 1:00 到 3:00 的经过时间是 1 小时,而在“正常”日是 2 小时.除非我们打算计算“本地秒数”而不是“物理秒数”。这个问题上的问题不是很清楚......

    我测试了问题中给出的代码,使用“America/New_York”作为具有 3 个输入对的本地时区:

    1. 美国东部标准时间 2019 年 3 月 2 日星期六 22:02:05 至美国东部标准时间 2019 年 3 月 4 日星期一 00:00:00
    2. 美国东部时间 2019 年 3 月 9 日星期六 22:02:05 至美国东部时间 2019 年 3 月 11 日星期一 00:00:00
    3. 美国东部时间 2019 年 3 月 16 日星期六 22:02:05 至美国东部时间 2019 年 3 月 18 日星期一 00:00:00

    这是连续 3 周计算周六深夜和周一开始之间的时间,由于夏令时生效,中周跨越 UTC 偏移量变化。

    问题中的代码为所有输入对提供 7074s。在 hh:mm:ss 格式中,即 01:57:54。这大约比正确答案差 1 天,对于第 1 和第 3 个输入对,正确答案是 93475s,或 1 天和 7075s,或 1 天和 01:57:55。

    第二种情况的正确答案,因为星期日只有 23 小时,少了 1 小时,即 89875 秒。

    我没有追踪问题中代码中的所有问题,但似乎至少有以下 3 个:

    1. 天数计算中的一个错误。
    2. 秒数计算中的一个错误。
    3. 忽略本地时区可能发生的 UTC 偏移量变化。

    Howard Hinnant's free, open-source, time zone library1 可用于纠正这些问题:

    #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_secondszoned_secondszoned_time&lt;Duration&gt; 的特化,具有秒精度。

    zoned_time 是本地时间点和time_zone 的配对。它可以等效地被认为是 UTC 时间点和time_zone 的配对。在任何一种情况下,都可以使用local_timesys_timesys_time 是UTC)进行构造,并且可以提取local_timesys_time,它们通过time_zone 关联。

    本例中的time_zone 与检测计算机当前本地时区设置的current_zone() 一起找到。

    now 是输入 sys_timesys_seconds 是秒精度 sys_time 的别名)和计算机当前本地时区的配对。

    today_local 是日精度local_time,通过获取nowlocal_time 并将其设置为日精度。

    本地的weekday可以直接从today_local构造。

    delta_days 是必须添加到 today_wd 的天数才能达到目标 weekdaywdweekday 减法本质上是循环的:它总是导致 [0, 6] 范围内的天数,并且与工作日的底层编码无关。例如,星期日总是比星期六晚 1 天,即使星期六编码为 6,星期日编码为 0。

    如果当前weekday 与目标weekday 相同,并且如果这不是目标weekday 的第一秒,那么我们要计算到下周同一天的开始。否则now 为目标日期,此函数将返回 0 秒。

    给定天数 (delta_days) 和当前日期 (today_local),可以使用 today_local + delta_days 计算目标日期。这是一个天精度local_time,它将隐式转换为秒精度并指向一天的开始。这与本地时区配对构造target_date

    现在我们有了target_datenow,我们只需将它们相减即可得到所需的秒数。减法有两种方法:

    1. local_time。这将以本地time_zone 定义的“日历秒”为单位测量时间。在这个日历中,所有的日子都是 24 小时。

    2. sys_time。这会在减法之前转换为 UTC,从而测量“物理秒数”。这会在上述情况 2 中检测到 23 小时星期日。

    注意事项:

    • 代码不包含显式转换常量,例如 60、24 或 86400。
    • 代码不会计算当天经过的秒数。
    • 代码独立于weekday 编码,因此大大简化。
    • 代码完全避免了过时的 C 计时 API,因此大大简化了。

    在我写这篇文章时:

    std::cout << GetSecondsUntilNextWeekDay(Saturday) << '\n';
    

    输出:

    128459s
    

    1Howard Hinnant's time zone library 现在是 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(&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::string& 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::string& 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;
        }
        

        【讨论】:

        • 请将其编辑为您的实际问题。 Stack Overflow 不是一个论坛,所以添加新的“帖子”并不是你更新内容的方式。 SO 有一个问题和答案,因此您已将其添加为问题的答案(其中 obvs 不正确)。您可以删除它并简单地更新您的原始问题。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-03-08
        • 1970-01-01
        • 1970-01-01
        • 2015-05-05
        • 1970-01-01
        相关资源
        最近更新 更多