我如何计算到C++下周六的秒数

How can I calculate the number of seconds until next Saturday in C++?

本文关键字:C++ 周六 何计算 计算      更新时间:2023-10-16

我正在尝试制作一个计算器,该计算器将确定特定日期开始之前的秒数。

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

我想我可以计算出我们除了星期六之外的几天,然后将其转换为秒,但问题是可能会增加额外的时间(我想知道午夜的时间,一天的开始)。

有人知道这怎么可能吗?

#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 秒(要么工作)。

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

  • "一天的开始"是否在特定时区? 在UTC中?

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

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

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

  1. 星期六 2019-03-02 22:02:05 EST 至 星期一 2019-03-04
  2. 00:00:00 EST
  3. 星期六 2019-03-09 22:02:05 EST 至 星期一 2019-03-11 00:00:00 EDT
  4. 星期六 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 个输入对,正确答案为 93475s,或 1 天和 7075s,或 1 天和 01:57:55。

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

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

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

Howard Hinnant 的免费开源时区库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_secondszoned_seconds是秒精度的zoned_time<Duration>专业化。

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

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

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

today_local是通过获得nowlocal_time并将其地板设置为日精度而发现的日精度local_time

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

delta_days是达到目标weekdaywd必须添加到today_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_datenow,我们只需要减去它们即可获得所需的秒数。 有两种方法可以进行此减法:

  1. local_time. 这将以本地time_zone定义的"历法秒"来测量时间。 在此日历中,所有日子都是 24h。

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

笔记:

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

当我写这篇文章时:

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

输出:

128459s

1Howard Hinnant 的时区库现在是 C++20 规范的一部分,有一些细微的更改,例如将其放入namespace std::chrono中。

试试这个,

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

我已经用这个解决方案更新了原始问题,我不确定是否有更简单的方法可以做到这一点......所以我愿意接受建议。

这是添加的代码...

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