2

我正在寻找确定 NTP 时间戳向量的日期分量的最快方法。

在这种情况下,输入是自 1900 年 1 月 1 日以来测量的 NTP 时间戳或秒数,通过添加 2208988800 或减去如果采用其他方式,往返 unix 时间是微不足道的。我需要将时间戳分解为其他 API 的日期组件,这些 API 只接受日期作为其组件,特别是年、月和日。

使用time.h( glibc) 中的 ANSI C 方法,我可以轻松导出组件,但对于较大的向量来说太慢了。较小的向量可能包含 172800 个值,但更现实的是,我希望能够尽快处理具有 1314000 个值的向量。

我在想我可以自己处理时间并通过迭代地减去每个日期组件的第二个除数输入的时间戳来消除少量glibc开销,直到我到达日期,基本上是什么glibc,但没有时区和一些额外的(小)高架。

我很快发现以这种方式去做仍然很慢。

我正在处理这样的事情:

typedef struct simple_time_ {
    int year;
    int month;
    int day;
} simple_time;

size_t time_cruncher(const time_t *ntp_vector, simple_time *out, size_t len)
{
    size_t i;
    time_t corrected;
    struct tm cal;
    for(i=0;i<len;i++) {
        corrected = ntp_vector[i] - 2208988800; /* NTP Offset */
        gmtime_r(&corrected, &cal);
        simple_time[i].year = cal.tm_year + 1900;
        simple_time[i].month = cal.tm_mon + 1;
        simple_time[i].day = cal.tm_mday;
    }
    return i;
}

是否有潜伏的算法可以帮助我更快地推导出计算?像 Zeller 的算法,但从秒到日期组件?

谢谢!

4

2 回答 2

1

鉴于您的许多连续时间戳很可能在同一个月内,请利用这一点。

Pseudo code
1) Calculate the y,m,d for the given timestamp 't' as you have above.
2) now calculate the t0 = gmtime(y, m, 1) and t1 = gmtime(y, m+1, 1).  (Dec + 1 is OK)
3) As long as your timestamps are t0 <= t < t1, a simple add/divide is need to determine the day of the month.
4) Should 't' fall outside the range,  go to step 1.

还可以确定当天的开始和结束时间,看看这是否适用于下一个时间戳。

于 2013-06-27T21:26:14.990 回答
1

如果您的时间戳在 1901 到 2099 的日期范围内,您可以利用每四年是闰年这一事实(这可以进一步扩展到包括 1900 年以内的日期,因为它不是闰年)。

我从我正在处理的日期 c 库中派生了这段代码,我正在使用Zeller 的 congruence中的“移位月份”方法。

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

void
dt_to_ymd(unsigned int n, int *yp, int *mp, int *dp) {
    unsigned int z, c;
    int y, m, d;

    assert(n <= 73107); /* 2100-02-28 */

    z = n + 307;                    /* 1 + 306 (306 days between March 1 and December 31) */
    y = (4 * z) / 1461;
    c = z - (1461 * y - 1) / 4;     /* day of the year [1, 366]  */
    m = (5 * c + 456) / 153;        /* month of the year [1, 12] */
    d = c - (153 * m - 457) / 5;    /* day of the month [1, 31]  */

    if (m > 12)
        y++, m -= 12;

    if (yp) *yp = y + 1899;
    if (mp) *mp = m;
    if (dp) *dp = d;
}

const struct test {
    int year;
    int month;
    int day;
    uint64_t timestamp;
} tests[] =  {
    { 1900,  1,  1,          0 },
    { 1970,  1,  1, 2208988800 },
    { 1972,  1,  1, 2272060800 },
    { 1999, 12, 31, 3155587200 },
    { 2013,  7,  4, 3581884800 },
    { 2100,  2, 28, 6316444800 },
};

int 
main() {
    int i, ntests;

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

        {
            unsigned int n;
            int y, m, d;

            n = t.timestamp / 86400;
            dt_to_ymd(n, &y, &m, &d);
            if (t.year != y || t.month != m || t.day != d) {
                printf("dt_to_ymd(%u)\n", n);
                printf("  got: %.4d-%.2d-%.2d\n", y, m, d);
                printf("  exp: %.4d-%.2d-%.2d\n", t.year, t.month, t.day);
            }
        }
    }
    return 0;
}
于 2013-07-04T13:01:29.567 回答