[英]strftime - convert week number ISO8601 to Gregorian standard C++
[英]Calculate Gregorian date from week number in C/C++
我正在使用公歷,我想實施 IS0 8601 周,但我偶然發現了計算任何周數的日期的問題。 例如,ISO 日期2010-W01-1
應返回2010 2009-W01-1
1 月 4 日,而2009-W01-1
應返回2008 年 12 月 29 日。
// Get the date for a given year, week and weekday(1-7)
time_t *GetDateFromWeekNumber(int year, int week, int dayOfWeek)
{
// Algorithm here
}
編輯:我還沒有找到任何可以在線運行的算法,嘗試了很多,但我現在有點卡住了。
當前接受的答案給出了 2017 年第 1 周(以及 2017 年每周)的錯誤答案。 函數GetDayAndMonthFromWeekInYear
輸入 2017 和 1 for year
和weekInYear
應該在month
輸出 1 和在dayInMonth
輸出 2,表明 2017-W01 從 2017-01-02 星期一開始,但它輸出的是公歷日期 2017- 01-01.
這個免費的開源 C++11/14 庫使用以下語法輸出從 ISO 周到公歷的正確日期轉換:
#include "date/date.h"
#include "date/iso_week.h"
#include <iostream>
int
main()
{
using namespace iso_week::literals;
std::cout << date::year_month_day{2017_y/1_w/mon} << '\n';
}
2017-01-02
由於該庫是開源的,因此可以輕松檢查所用算法的源代碼(“iso_week.h”和“date.h”)。 算法也很高效,不使用迭代。
一般方法是使用以下算法將字段2017_y/1_w/mon
轉換為自 1970-01-01 以來的連續天數:
CONSTCD14
inline
year_weeknum_weekday::operator sys_days() const NOEXCEPT
{
return sys_days{date::year{int{y_}-1}/date::dec/date::thu[date::last]}
+ (date::mon - date::thu) + weeks{unsigned{wn_}-1} + (wd_ - mon);
}
然后使用此算法將連續天數轉換為year/month/day
字段類型:
CONSTCD14
inline
year_month_day
year_month_day::from_sys_days(const sys_days& dp) NOEXCEPT
{
static_assert(std::numeric_limits<unsigned>::digits >= 18,
"This algorithm has not been ported to a 16 bit unsigned integer");
static_assert(std::numeric_limits<int>::digits >= 20,
"This algorithm has not been ported to a 16 bit signed integer");
auto const z = dp.time_since_epoch().count() + 719468;
auto const era = (z >= 0 ? z : z - 146096) / 146097;
auto const doe = static_cast<unsigned>(z - era * 146097); // [0, 146096]
auto const yoe = (doe - doe/1460 + doe/36524 - doe/146096) / 365; // [0, 399]
auto const y = static_cast<sys_days::rep>(yoe) + era * 400;
auto const doy = doe - (365*yoe + yoe/4 - yoe/100); // [0, 365]
auto const mp = (5*doy + 2)/153; // [0, 11]
auto const d = doy - (153*mp+2)/5 + 1; // [1, 31]
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4146) // unary minus operator applied to unsigned type, result still unsigned
#endif
auto const m = mp + (mp < 10 ? 3 : -9u); // [1, 12]
#ifdef _MSVC_VER
#pragma warning(pop)
#endif
return year_month_day{date::year{y + (m <= 2)}, date::month(m), date::day(d)};
}
后一種算法在此處詳細記錄。
也許你應該看看boost::date_time::gregorian 。 使用它,您可以編寫這樣的函數:
#include <boost/date_time/gregorian/gregorian.hpp>
// Get the date for a given year, week and weekday(0-6)
time_t *GetDateFromWeekNumber(int year, int week, int dayOfWeek)
{
using namespace boost::gregorian;
date d(year, Jan, 1);
int curWeekDay = d.day_of_week();
d += date_duration((week - 1) * 7) + date_duration(dayOfWeek - curWeekDay);
tm tmp = to_tm(d);
time_t * ret = new time_t(mktime(&tmp));
return ret;
}
不幸的是,他們的日期格式與您的不同 - 他們從星期日開始計算星期幾,即Sunday = 0, Monday = 1, ..., Saturday = 6
。 如果它不能滿足您的需求,您可以使用這個稍微改變的函數:
#include <boost/date_time/gregorian/gregorian.hpp>
// Get the date for a given year, week and weekday(1-7)
time_t *GetDateFromWeekNumber(int year, int week, int dayOfWeek)
{
using namespace boost::gregorian;
date d(year, Jan, 1);
if(dayOfWeek == 7) {
dayOfWeek = 0;
week++;
}
int curWeekDay = d.day_of_week();
d += date_duration((week - 1) * 7) + date_duration(dayOfWeek - curWeekDay);
tm tmp = to_tm(d);
time_t * ret = new time_t(mktime(&tmp));
return ret;
}
編輯:
經過一番思考,我找到了一種無需使用 boost 即可實現相同功能的方法。 這是代碼:
警告:下面的代碼已損壞,請勿使用!
// Get the date for a given year, week and weekday(1-7)
time_t *GetDateFromWeekNumber(int year, int week, int dayOfWeek)
{
const time_t SEC_PER_DAY = 60*60*24;
if(week_day == 7) {
week_day = 0;
week++;
}
struct tm timeinfo;
memset(&timeinfo, 0, sizeof(tm));
timeinfo.tm_year = year - 1900;
timeinfo.tm_mon = 0;
timeinfo.tm_mday = 1;
time_t * ret = new time_t(mktime(&timeinfo)); // set all the other fields
int cur_week_day = timeinfo.tm_wday;
*ret += sec_per_day * ((week_day - cur_week_day) + (week - 1) * 7);
return ret;
}
編輯2:
是的, EDIT 中的代碼完全損壞了,因為我沒有花足夠的時間來理解周數是如何分配的。
由 F#
open System
open System.Globalization
//wday: 1-7, 1:Monday
let DateFromWeekOfYear y w wday =
let dt = new DateTime(y, 1, 4) //first week include 1/4
let dow = if dt.DayOfWeek = DayOfWeek.Sunday then 7 else int dt.DayOfWeek //to 1-7
let dtf = dt.AddDays(float(wday - dow))
GregorianCalendar().AddWeeks(dtf, w - 1)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.