如何使用C ++计算到下一个星期六的秒数?

时间:2019-04-11 01:36:47

标签: c++ date time epoch

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

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

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

有人知道怎么可能吗?

#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秒(无论是工作还是工作)。

3 个答案:

答案 0 :(得分: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到美国东部时间2019-03-11星期一美国东部时间
  3. 美国东部夏令时间2019-03-16 22:02:05至2019-03-18星期一美国东部时间

这是连续三周计算从星期六深夜到星期一开始之间的时间,由于夏令时生效,中间一周跨越了UTC偏移量更改。

问题中的代码为所有输入对提供了7074s。采用hh:mm:ss格式,即01:57:54。距离正确答案大约1天,对于第一对和第三对输入,答案是93475s,或者1天和7075s,或者1天和01:57:55。

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

我没有在问题内的代码中查找所有问题,但似乎至少存在以下3个问题:

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

Howard Hinnant's free, open-source, time zone library 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_secondszoned_time<Duration>的特化,具有秒精度。

zoned_time是本地时间点和time_zone的配对。可以等效地将其视为UTC时间点和time_zone的配对。无论哪种情况,都可以使用local_timesys_timesys_time是UTC)进行构造,并且可以提取local_timesys_time ,它们通过time_zone相关联。

此示例中的time_zonecurrent_zone()一起使用,它检测到计算机的当前本地时区设置。

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

today_local是日精度local_time,是通过获取now的{​​{1}}并将其下限为日精度来找到的。

本地local_time可以直接从weekday构建。

today_local是一个人必须添加到delta_days中才能达到today_wd的目标weekday的天数。 wd减法本质上是循环的:它总是导致[0,6]范围内的天数,并且独立于工作日的基础编码。例如,即使星期六编码为6,星期日编码为0,星期日也总是在星期六之后1天。

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

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

现在我们有了target_datetarget_date,我们只需要减去它们就可以得到所需的秒数。有两种方法可以执行此减法操作:

  1. now中。这将以本地local_time所定义的“日历秒”来度量时间。在此日历中,全天都是24小时。

  2. time_zone中。这将在减法之前转换为UTC,从而测量“物理秒”。在上述情况2中,它将检测到23h星期日。

注意:

  • 该代码不包含任何明确的转换常量,例如60、24或86400。
  • 该代码不会麻烦计算当天经过的秒数。
  • 该代码独立于sys_time编码,因此大大简化了。
  • 该代码完全避免使用过时的C计时API,因此大大简化了。

在我写这篇文章的时候:

weekday

输出:

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

1 Howard Hinnant's time zone library现在是C ++ 20规范草案的一部分,并做了一些细微的更改,例如将其放入128459s 中。

答案 1 :(得分:0)

尝试一下

#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";

}

答案 2 :(得分:-1)

我已经使用此解决方案更新了原始问题,不确定是否有更简单的方法来实现此目的...所以我愿意征求建议。

这是添加的代码...

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;
}