本文介绍了数学将1970年以来的秒转换为日期,反之亦然的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我自1970年1月1日00:00以来的秒数为n,以纳秒为单位,我想将它转换为月/日/年/星期。

I have seconds since Jan 1 1970 00:00 as an int64 in nanoseconds and I'm trying to convert it into month/day/year/day of week.

这是很容易做到迭代,我有工作,但我想做它的公式。我正在寻找实际的数学。

It's easy to do this iteratively, I have that working but I want to do it formulaically. I'm looking for the actual math.

推荐答案

旧问题的新答案:

这个新答案的原理:现有答案不显示从纳秒到年/月/日的转换算法(例如,它们使用源隐藏的库),或者在算法中使用迭代

Rationale for this new answer: The existing answers either do not show the algorithms for the conversion from nanoseconds to year/month/day (e.g. they use libraries with the source hidden), or they use iteration in the algorithms they do show.

这个答案没有任何迭代。

This answer has no iteration whatsoever.

,并在令人难以置信的细节中解释。它们还在+/-百万年的范围内进行单位测试正确性(超过您的需要)。

The algorithms are here, and explained in excruciating detail. They are also unit tested for correctness over a span of +/- a million years (way more than you need).

算法不计算闰秒。如果你需要它,它可以做,但需要一个表查找,并且该表随时间增长。

The algorithms don't count leap seconds. If you need that, it can be done, but requires a table lookup, and that table grows with time.

日期算法只处理天单位,而不是纳秒。要将天数转换为纳秒,请乘以 86400 * 1000000000 (注意确保使用64位算术)。要将纳秒转换为天,请除以相同的量。或者更好地使用C ++ 11 < chrono> 库。

The date algorithms deal only with units of days, and not nanoseconds. To convert days to nanoseconds, multiply by 86400*1000000000 (taking care to ensure you're using 64 bit arithmetic). To convert nanoseconds to days, divide by the same amount. Or better yet, use the C++11 <chrono> library.

1。 days_from_civil

// Returns number of days since civil 1970-01-01.  Negative values indicate
//    days prior to 1970-01-01.
// Preconditions:  y-m-d represents a date in the civil (Gregorian) calendar
//                 m is in [1, 12]
//                 d is in [1, last_day_of_month(y, m)]
//                 y is "approximately" in
//                   [numeric_limits<Int>::min()/366, numeric_limits<Int>::max()/366]
//                 Exact range of validity is:
//                 [civil_from_days(numeric_limits<Int>::min()),
//                  civil_from_days(numeric_limits<Int>::max()-719468)]
template <class Int>
constexpr
Int
days_from_civil(Int y, unsigned m, unsigned d) 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");
    y -= m <= 2;
    const Int era = (y >= 0 ? y : y-399) / 400;
    const unsigned yoe = static_cast<unsigned>(y - era * 400);      // [0, 399]
    const unsigned doy = (153*(m + (m > 2 ? -3 : 9)) + 2)/5 + d-1;  // [0, 365]
    const unsigned doe = yoe * 365 + yoe/4 - yoe/100 + doy;         // [0, 146096]
    return era * 146097 + static_cast<Int>(doe) - 719468;
}

code> civil_from_days

// Returns year/month/day triple in civil calendar
// Preconditions:  z is number of days since 1970-01-01 and is in the range:
//                   [numeric_limits<Int>::min(), numeric_limits<Int>::max()-719468].
template <class Int>
constexpr
std::tuple<Int, unsigned, unsigned>
civil_from_days(Int z) 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");
    z += 719468;
    const Int era = (z >= 0 ? z : z - 146096) / 146097;
    const unsigned doe = static_cast<unsigned>(z - era * 146097);          // [0, 146096]
    const unsigned yoe = (doe - doe/1460 + doe/36524 - doe/146096) / 365;  // [0, 399]
    const Int y = static_cast<Int>(yoe) + era * 400;
    const unsigned doy = doe - (365*yoe + yoe/4 - yoe/100);                // [0, 365]
    const unsigned mp = (5*doy + 2)/153;                                   // [0, 11]
    const unsigned d = doy - (153*mp+2)/5 + 1;                             // [1, 31]
    const unsigned m = mp + (mp < 10 ? 3 : -9);                            // [1, 12]
    return std::tuple<Int, unsigned, unsigned>(y + (m <= 2), m, d);
}

code> weekday_from_days

// Returns day of week in civil calendar [0, 6] -> [Sun, Sat]
// Preconditions:  z is number of days since 1970-01-01 and is in the range:
//                   [numeric_limits<Int>::min(), numeric_limits<Int>::max()-4].
template <class Int>
constexpr
unsigned
weekday_from_days(Int z) noexcept
{
    return static_cast<unsigned>(z >= -4 ? (z+4) % 7 : (z+5) % 7 + 6);
}

这些算法是为C ++ 14编写的。如果你有C ++ 11,删除 constexpr 。如果你有C ++ 98/03,删除 constexpr noexcept static_assert s。

These algorithms are written for C++14. If you have C++11, remove the constexpr. If you have C++98/03, remove the constexpr, the noexcept, and the static_asserts.

注意这三种算法中任何一种都没有迭代。

Note the lack of iteration in any of these three algorithms.

他们可以这样使用:

#include <iostream>

int
main()
{
    int64_t z = days_from_civil(2015LL, 8, 22);
    int64_t ns = z*86400*1000000000;
    std::cout << ns << '\n';
    const char* weekdays[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
    unsigned wd = weekday_from_days(z);
    int64_t y;
    unsigned m, d;
    std::tie(y, m, d) = civil_from_days(ns/86400/1000000000);
    std::cout << y << '-' << m << '-' << d << ' ' << weekdays[wd] << '\n';
}

其输出:

1440201600000000000
2015-8-22 Sat

算法在公共领域。根据需要使用它们。如果需要,还有几个更有用的日期算法(例如 weekday_difference

The algorithms are in the public domain. Use them however you want. The date algorithms paper has several more useful date algorithms if needed (e.g. weekday_difference is both remarkably simple and remarkably useful).

这些算法被包装在一个。

These algorithms are wrapped up in an open source, cross platform, type-safe date library if needed.

或闰秒支持,则会在时区库 href =http://howardhinnant.github.io/date/date.html =nofollow>日期库。

If timezone or leap second support is needed, there exists a timezone library built on top of the date library.

更新:同一应用程式中的不同区域

查看。

更新:

这是一个很好的问题,从下面的注释。

This is a good question from the comments below.

em>答案:有一些陷阱。还有一些好处。很高兴知道它们是什么。

Answer: There are some pitfalls. And there are some benefits. It is good to know what they both are.

几乎来自操作系统的每个时间源都基于。 是自1970-01-01之后的时间排除闰秒。这包括诸如C time(nullptr)和C ++ std :: chrono :: system_clock :: now(),以及POSIX gettimeofday clock_gettime 。这不是标准指定的事实(除了它由POSIX指定),但它是事实上的标准。

Almost every source of time from an OS is based on Unix Time. Unix Time is a count of time since 1970-01-01 excluding leap seconds. This includes functions like the C time(nullptr) and the C++ std::chrono::system_clock::now(), as well as the POSIX gettimeofday and clock_gettime. This is not a fact specified by the standard (except it is specified by POSIX), but it is the de facto standard.

所以如果你的秒数(纳秒,忽略闰秒,在转换为 {year,month,day,hours,minutes,seconds,nanoseconds} 等字段类型时忽略闰秒是完全正确的。事实上,在这样的上下文中考虑闰秒会实际上引入错误。

So if your source of seconds (nanoseconds, whatever) neglects leap seconds, it is exactly correct to ignore leap seconds when converting to field types such as {year, month, day, hours, minutes, seconds, nanoseconds}. In fact to take leap seconds into account in such a context would actually introduce errors.

因此,知道你的时间来源是很好的,特别是要知道它是否也忽略闰秒作为。

So it is good to know your source of time, and especially to know if it also neglects leap seconds as Unix Time does.

如果您的时间源忽略闰秒,您可以 获得正确的答案。你只需要知道已经插入的闰秒集。 。

If your source of time does not neglect leap seconds, you can still get the correct answer down to the second. You just need to know the set of leap seconds that have been inserted. Here is the current list.

例如,如果您从包含 闰秒的1970-01-01 00:00:00 UTC计算秒数,您知道这代表现在(目前是2016-09-26 ),现在和1970-01-01之间插入的闰秒的当前数量为26.因此,您可以从计数中减去26,然后按照这些算法,获得确切的结果。

For example if you get a count of seconds since 1970-01-01 00:00:00 UTC which includes leap seconds and you know that this represents "now" (which is currently 2016-09-26), the current number of leap seconds inserted between now and 1970-01-01 is 26. So you could subtract 26 from your count, and then follow these algorithms, getting the exact result.

可以自动执行闰秒 - 为您的计算。例如,要获取UTC时间介于2016-09-26 00:00:00 UTC和1970-01-01 00:00:00 UTC(包括闰秒)之间的秒数,您可以这样做:

This library can automate leap-second-aware computations for you. For example to get the number of seconds between 2016-09-26 00:00:00 UTC and 1970-01-01 00:00:00 UTC including leap seconds, you could do this:

#include "chrono_io.h"
#include "tz.h"
#include <iostream>

int
main()
{
    using namespace date;
    auto now  = to_utc_time(sys_days{2016_y/sep/26});
    auto then = to_utc_time(sys_days{1970_y/jan/1});
    std::cout << now - then << '\n';
}

其输出:

1474848026s

忽略闰秒()如下所示:

Neglecting leap seconds (Unix Time) looks like:

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

int
main()
{
    using namespace date;
    using namespace std::chrono_literals;
    auto now  = sys_days{2016_y/sep/26} + 0s;
    auto then = sys_days{1970_y/jan/1};
    std::cout << now - then << '\n';
}

其输出:

1474848000s

>

这个即将到来的新年(2017-01-01),我们将插入27 闰秒。

This upcoming New Years (2017-01-01) we will insert the 27 leap second.

在1958-01-01和1970-01-01之间插入了10个闰秒,但是小于第二次,而不仅仅是在12月或6月底。有关确切插入了多少时间和确切何时是粗略的文档,我还没有能够跟踪一个可靠的来源。

Between 1958-01-01 and 1970-01-01 10 "leap seconds" were inserted, but in units smaller than a second, and not just at the end of Dec or Jun. Documentation on exactly how much time was inserted and exactly when is sketchy, and I have not been able to track down a reliable source.

原子计时服务在1955年开始实验,第一个基于原子的国际时间标准TAI有一个纪元为1958-01-01 00:00:00 GMT(现在是UTC)。在此之前,我们最好的是基于石英的时钟,不够精确到担心闰秒。

Atomic time keeping services began experimentally in 1955, and the first atomic-based international time standard TAI has an epoch of 1958-01-01 00:00:00 GMT (what is now UTC). Prior to that the best we had was quartz-based clocks which were not accurate enough to worry about leap seconds.

这篇关于数学将1970年以来的秒转换为日期,反之亦然的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-09 10:01