2

我有:

  • 年份编号(可以是任何年份)
  • 月份数(从一月到十二月)
  • 周数(第 1、第 2、第 3、第 4、最后)
  • 一个工作日(周日、周一、周二、周三、周四、周五、周六)

我需要获得一个日期编号 [从 1 到 ~31] -“YYYY-MM- DD ”(ISO8601)。

有没有办法使用 boost posix time 或使用其他一些 C++ 库来计算这个?

IE

  • 2014 年,三月的第一个星期日 - 那将是 2014-03-02
  • 2014 年,12 月的第 4 个星期三 - 那将是 2014-12-24

谢谢。

4

3 回答 3

1

我写了一个轻量级的C 库,可以做你想做的事,有趣的部分在这里,你可以看到算法是微不足道的。

#include <stdio.h>
#include <stdint.h>
#include "dt_dow.h"
#include "dt_accessor.h"

const struct test {
    int      year;
    int      month; /* Month of the year [1=Jan, 12=Dec] */
    int      nth;   /* Occurrence within month           */
    dt_dow_t dow;   /* Day of the week [1=Mon, 7=Sun]    */
    int      dom;   /* Expected day of the month [1, 31] */
} tests[] =  {
    { 2014,  3,  1, DT_SUNDAY,     2 },
    { 2014,  4, -1, DT_TUESDAY,   29 },
    { 2014,  4, -2, DT_MONDAY,    21 },
    { 2014,  4, -5, DT_TUESDAY,    1 },
    { 2014,  4,  1, DT_TUESDAY,    1 },
    { 2014, 12,  4, DT_WEDNESDAY, 24 },
};

int 
main() {
    int i, ntests;

    ntests = sizeof(tests) / sizeof(*tests);
    for (i = 0; i < ntests; i++) {
        const struct test t = tests[i];

        {
            int dom = dt_dom(dt_from_nth_dow_in_month(t.year, t.month, t.nth, t.dow));

            if (t.dom != dom) {
                printf("dt_dom(dt_from_nth_dow_in_month(%d, %d, %d, %d))\n", 
                  t.year, t.month, t.nth, t.dow);
                printf("  got: %d\n", dom);
                printf("  exp: %d\n", t.dom);
            }
        }
    }
    return 0;
}

如果您不想使用上面的库/代码,这里是一个重新实现。

#include <stdio.h>
#include <assert.h>
#include <stdint.h>
#include <stdbool.h>

bool
leap_year(int y) {
    return ((y % 4) == 0 && (y % 100 != 0 || y % 400 == 0));
}

int
days_in_month(int y, int m) {
    static const int T[2][13] = {
        { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
        { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
    };
    assert(m >=  1);
    assert(m <= 12);
    return T[leap_year(y)][m];
}

/* Computes the day of the week [1=Mon, 7=Sun] from the given year, month, day. */
int
ymd_to_dow(int y, int m, int d) {
    static const int T[13] = { 0, 6, 2, 1, 4, 6, 2, 4, 0, 3, 5, 1, 3 };

    assert(y >=  1);
    assert(m >=  1);
    assert(m <= 12);
    assert(d >=  1);

    y -= m < 3;
    return 1 + (y + y/4 - y/100 + y/400 + T[m] + d) % 7;
}

int
dom_from_nth_dow_in_month(int y, int m, int nth, int dow) {
    int dim, dom;

    assert(y   >=  1);
    assert(m   >=  1);
    assert(m   <= 12);
    assert(dow >=  1);
    assert(dow <=  7);

    dim = days_in_month(y, m);
    if (nth > 0) {
        dom = 1;
        dom += (dow - ymd_to_dow(y, m, dom) + 7) % 7;
        dom += --nth * 7;
        if (dom <= dim)
            return dom;
    }
    else if (nth < 0) {
        dom = dim;
        dom -= (ymd_to_dow(y, m, dom) - dow + 7) % 7;
        dom -= ++nth * -7;
        if (dom >= 1)
            return dom;
    }
    return -1;
}

const struct test {
    int year;
    int month; /* Month of the year [1=Jan, 12=Dec] */
    int nth;   /* Occurrence within month           */
    int dow;   /* Day of the week [1=Mon, 7=Sun]    */
    int dom;   /* Expected day of the month [1, 31] */
} tests[] =  {
    { 2014,  3,  1, 7,  2 },
    { 2014,  4, -1, 2, 29 },
    { 2014,  4, -2, 1, 21 },
    { 2014,  4, -5, 2,  1 },
    { 2014,  4,  1, 2,  1 },
    { 2014, 12,  4, 3, 24 },
};

int 
main() {
    int i, ntests;

    ntests = sizeof(tests) / sizeof(*tests);
    for (i = 0; i < ntests; i++) {
        const struct test t = tests[i];

        {
            int dom = dom_from_nth_dow_in_month(t.year, t.month, t.nth, t.dow);

            if (t.dom != dom) {
                printf("dom_from_nth_dow_in_month(%d, %d, %d, %d))\n", 
                  t.year, t.month, t.nth, t.dow);
                printf("  got: %d\n", dom);
                printf("  exp: %d\n", t.dom);
            }
        }
    }
    return 0;
}
于 2014-04-15T23:09:38.327 回答
1

老问题的新答案。

使用这个免费的开源日期库:

http://howardhinnant.github.io/date_v2.html

2014 年,三月的第一个星期日 - 那将是 2014-03-02

#include "date.h"
#include <iostream>

int
main()
{
    using namespace date;
    std::cout << year_month_day{sun[1]/mar/2014} << '\n';
}

输出:

2014-03-02

该类型date::year_month_day具有名为year,month和的吸气剂day。它很容易使用,并且在上面的链接中有完整的文档。

2014 年,12 月的第 4 个星期三 - 那将是 2014-12-24

using namespace date;
std::cout << year_month_day{wed[4]/dec/2014} << '\n';

2014-12-24

但这不是 2014 年 12 月的最后一个星期三,而是:

std::cout << year_month_day{wed[last]/dec/2014} << '\n';

2014-12-31

这是图书馆的视频演示

于 2015-12-20T01:07:24.970 回答
0

干得好陈森!这可能真的很有用。但是因为我可以自由使用 boost - 这是我到目前为止所做的:

// DateFromWeekNumAndDay
// year     - Year YYYY.
// weekNum  - From 0 to 4.
// weekDay  - Week day starts from Sunday - 0 to Saturday - 6.
boost::gregorian::date DateFromWeekNumAndDay(boost::gregorian::date::year_type year, unsigned short weekNum, boost::date_time::weekdays weekDay)
{
   boost::gregorian::date date(year, boost::gregorian::Apr, 1);
   boost::gregorian::date::day_of_week_type d = date.day_of_week();

   date += boost::gregorian::date_duration(weekNum * 7) + boost::gregorian::date_duration(weekDay - d);

   return date;
}

据我测试 - 一切似乎都很好。我从这篇文章中采用了这种方法。因此,感谢用户kebs的链接,非常感谢用户Mikhail Melnik的“GetDateFromWeekNumber”函数示例。

于 2014-04-16T05:23:51.010 回答