C++ 数学将 1970 年以来的秒数转换为日期,反之亦然

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7960318/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 17:48:29  来源:igfitidea点击:

Math to convert seconds since 1970 into date and vice versa

c++datemath

提问by David

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.

自 1970 年 1 月 1 日 00:00 以来,我有几秒作为 int64 以纳秒为单位,我正在尝试将其转换为月/日/年/星期几。

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.

迭代地做到这一点很容易,我有这个工作,但我想公式化地做到这一点。我正在寻找实际的数学。

回答by Howard Hinnant

New answer for old question:

旧问题的新答案:

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.

算法不计算闰秒。如果您需要,它可以完成,但需要查找表,并且该表会随着时间的推移而增长。

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.

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

There are three date algorithms from this paper that are needed to answer this question.

回答这个问题需要本文中的三种日期算法。

1.days_from_civil:

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

2.civil_from_days:

2.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);
}

3.weekday_from_days:

3.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);
}

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.

这些算法是为 C++14 编写的。如果您有 C++11,请删除constexpr. 如果你有C ++ 98/03,取出constexprnoexcept,和static_assert秒。

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

请注意这三种算法中的任何一种都缺乏迭代。

They can be used like this:

它们可以这样使用:

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

which outputs:

输出:

1440201600000000000
2015-8-22 Sat

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

这些算法属于公共领域。随心所欲地使用它们。如果需要,日期算法论文有几个更有用的日期算法(例如weekday_difference,既非常简单又非常有用)。

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

如果需要这些算法都包含在一个开源、跨平台、类型安全的日期库中

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

如果需要时区或闰秒支持,则存在建立在日期库之上的时区

Update: Different local zones in same app

更新:同一应用程序中的不同本地区域

See how to convert among different time zones.

了解如何在不同时区之间进行转换

Update:Are there any pitfalls to ignoring leap seconds when doing date calculations in this manner?

更新:以这种方式进行日期计算时忽略闰秒是否有任何陷阱?

This is a good question from the comments below.

这是下面评论中的一个好问题。

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

答:有一些陷阱。还有一些好处。很高兴知道他们俩是什么。

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

几乎每个操作系统的时间源都基于Unix TimeUnix 时间是自 1970-01-01 以来的时间计数,不包括闰秒。这包括像 Ctime(nullptr)和 C++这样的函数std::chrono::system_clock::now(),以及 POSIXgettimeofdayclock_gettime. 这不是标准规定的事实(除非是 POSIX 规定的),但它是事实上的标准。

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 introduceerrors.

因此,如果您的秒源(纳秒,无论如何)忽略了闰秒,那么在转换为{year, month, day, hours, minutes, seconds, nanoseconds}. 事实上,在这种情况下考虑闰秒实际上会引入错误。

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

因此,最好了解您的时间来源,尤其是了解它是否也像Unix 时间那样忽略闰秒。

If your source of time does notneglect leap seconds, you can stillget 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.

如果您的时间来源忽略闰秒,您仍然可以得到精确到秒的正确答案。您只需要知道已插入的闰秒集。 这是当前列表

For example if you get a count of seconds since 1970-01-01 00:00:00 UTC which includesleap 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 thenfollow these algorithms, getting the exact result.

例如,如果您获得自 1970-01-01 00:00:00 UTC 以来的秒数,其中包括闰秒,并且您知道这代表“现在”(当前为 2016-09-26),则当前闰秒数从现在到 1970-01-01 之间插入的秒数是 26。所以你可以从你的计数中减去 26,然后按照这些算法,得到准确的结果。

This librarycan 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 includingleap seconds, you could do this:

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

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

int
main()
{
    using namespace date;
    auto now  = clock_cast<utc_clock>(sys_days{2016_y/September/26});
    auto then = clock_cast<utc_clock>(sys_days{1970_y/January/1});
    std::cout << now - then << '\n';
}

which outputs:

输出:

1474848026s

Neglecting leap seconds (Unix Time) looks like:

忽略闰秒(Unix Time)看起来像:

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

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

which outputs:

输出:

1474848000s

For a difference of 26s.

对于26s.

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

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

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.

在 1958-01-01 和 1970-01-01 之间插入了 10 个“闰秒”,但单位小于 1 秒,而不仅仅是在 12 月底或 6 月。有关插入时间和确切时间的文档是粗略的,我一直无法找到可靠的来源。

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.

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

回答by Alexey Frunze

The Single Unix Specification gives a formula for Seconds since the Epoch:

单一 Unix 规范给出了自 Epoch 以来的秒数公式:

A value that approximates the number of seconds that have elapsed since the Epoch. A Coordinated Universal Time name (specified in terms of seconds (tm_sec), minutes (tm_min), hours (tm_hour), days since January 1 of the year (tm_yday), and calendar year minus 1900 (tm_year)) is related to a time represented as seconds since the Epoch, according to the expression below.

If the year is <1970 or the value is negative, the relationship is undefined. If the year is >=1970 and the value is non-negative, the value is related to a Coordinated Universal Time name according to the C-language expression, where tm_sec, tm_min, tm_hour, tm_yday, and tm_year are all integer types:

tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
    (tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
    ((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400

The relationship between the actual time of day and the current value for seconds since the Epoch is unspecified.

How any changes to the value of seconds since the Epoch are made to align to a desired relationship with the current actual time is implementation-defined. As represented in seconds since the Epoch, each and every day shall be accounted for by exactly 86400 seconds.

Note: The last three terms of the expression add in a day for each year that follows a leap year starting with the first leap year since the Epoch. The first term adds a day every 4 years starting in 1973, the second subtracts a day back out every 100 years starting in 2001, and the third adds a day back in every 400 years starting in 2001. The divisions in the formula are integer divisions; that is, the remainder is discarded leaving only the integer quotient.

一个近似于自 Epoch 以来经过的秒数的值。协调世界时名称(以秒 (tm_sec)、分钟 (tm_min)、小时 (tm_hour)、自一年 1 月 1 日以来的天数 (tm_yday) 和日历年减去 1900 (tm_year) 指定)与时间相关根据以下表达式,表示为自纪元以来的秒数。

如果年份 <1970 或值为负,则关系未定义。如果年份 >=1970 且值为非负数,则根据 C 语言表达式,该值与协调世界时名称相关,其中 tm_sec、tm_min、tm_hour、tm_yday 和 tm_year 均为整数类型:

tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
    (tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
    ((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400

自 Epoch 以来的实际时间与当前秒数之间的关系未指定。

自 Epoch 以来对秒值的任何更改如何与当前实际时间保持所需的关系是实现定义的。以 Epoch 以来的秒数表示,每一天都应准确地占 86400 秒。

注意:表达式的最后三项在闰年之后的每一年加上一天,从 Epoch 以来的第一个闰年开始。第一项从 1973 年开始每 4 年加一天,第二项从 2001 年开始每 100 年减一天,第三项从 2001 年开始每 400 年加一天。 公式中的除法是整数除法; 也就是说,余数被丢弃,只留下整数商。

You'll need to convert month and day of month to tm_yday to use this formula and that too should be done taking into account leap years. The rest in the formula is trivial.

您需要将月份和月份的日期转换为 tm_yday 才能使用此公式,并且考虑到闰年也应该这样做。公式中的其余部分是微不足道的。

Try to figure out from this how to get back date and time from seconds.

尝试从中找出如何从秒中获取日期和时间。

EDIT:

编辑

I've implemented a convertor in integer arithmetic in this answer.

我在这个答案中实现了一个整数算术转换器。

See a test run at ideone.

在 ideone 上查看测试运行

回答by user3735867

This code works...

此代码有效...

Usage: uint32_t getSecsSinceEpoch(1970, month, day, years_since_epoch, hour, minute, second);

用法:uint32_t getSecsSinceEpoch(1970, month, day, years_since_epoch, hour, minute, second);

Example: timestamp = getSecsSinceEpoch(1970, 6, 12, (2014 - 1970), 15, 29, 0)

示例:时间戳 = getSecsSinceEpoch(1970, 6, 12, (2014 - 1970), 15, 29, 0)

Returns: 1402586940

退货:1402586940

You can verify at www.epochconverter.com.

您可以在 www.epochconverter.com 上进行验证。

Took about 20 mins to write it and most of that was spent arguing with a friend as to whether I should include leap-seconds, nano-seconds, etc. Blech.

写了大约 20 分钟,其中大部分时间都花在与朋友争论我是否应该包括闰秒、纳秒等。Blech。

Have fun...

玩得开心...

Dr. Bryan Wilcutt

布莱恩·威尔卡特博士

#define DAYSPERWEEK (7)
#define DAYSPERNORMYEAR (365U)
#define DAYSPERLEAPYEAR (366U)

#define SECSPERDAY (86400UL) /* == ( 24 * 60 * 60) */
#define SECSPERHOUR (3600UL) /* == ( 60 * 60) */
#define SECSPERMIN (60UL) /* == ( 60) */

#define LEAPYEAR(year)          (!((year) % 4) && (((year) % 100) || !((year) % 400)))

const int _ytab[2][12] = {
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};

/****************************************************
* Class:Function    : getSecsSomceEpoch
* Input     : uint16_t epoch date (ie, 1970)
* Input     : uint8 ptr to returned month
* Input     : uint8 ptr to returned day
* Input     : uint8 ptr to returned years since Epoch
* Input     : uint8 ptr to returned hour
* Input     : uint8 ptr to returned minute
* Input     : uint8 ptr to returned seconds
* Output        : uint32_t Seconds between Epoch year and timestamp
* Behavior      :
*
* Converts MM/DD/YY HH:MM:SS to actual seconds since epoch.
* Epoch year is assumed at Jan 1, 00:00:01am.
****************************************************/
uint32_t getSecsSinceEpoch(uint16_t epoch, uint8_t month, uint8_t day, uint8_t years, uint8_t hour, uint8_t minute, uint8_t second)
{
unsigned long secs = 0;
int countleap = 0;
int i;
int dayspermonth;

secs = years * (SECSPERDAY * 365);
for (i = 0; i < (years - 1); i++)
{   
    if (LEAPYEAR((epoch + i)))
      countleap++;
}
secs += (countleap * SECSPERDAY);

secs += second;
secs += (hour * SECSPERHOUR);
secs += (minute * SECSPERMIN);
secs += ((day - 1) * SECSPERDAY);

if (month > 1)
{
    dayspermonth = 0;

    if (LEAPYEAR((epoch + years))) // Only counts when we're on leap day or past it
    {
        if (month > 2)
        {
            dayspermonth = 1;
        } else if (month == 2 && day >= 29) {
            dayspermonth = 1;
        }
    }

    for (i = 0; i < month - 1; i++)
    {   
        secs += (_ytab[dayspermonth][i] * SECSPERDAY);
    }
}

return secs;
}

回答by David Schwartz

bool FloatToTime(float seconds_since_epoch, bool local_time, struct tm *timest)
{
   struct tm *ret;
   time_t t=(time_t) seconds_since_epoch;
   if (local_time) ret=localtime(&t);
      else ret=gmtime(&t);
   if(ret==NULL) return false;
   memcpy(timest, ret, sizeof(struct tm));
   return true;
}

Pass it the seconds as the first parameter. The second parameter should be true for local time, false for GMT. The third parameter is a pointer to a structure to hold the response.

将秒数作为第一个参数传递给它。第二个参数对于本地时间应该是 true,对于 GMT 应该是 false。第三个参数是一个指向保存响应的结构的指针。

The return structures are (from the man page):

返回结构是(来自手册页):

tm_sec: The number of seconds after the minute, normally in the range 0 to 59, but can be up to 60 to allow for leap seconds.

tm_min: The number of minutes after the hour, in the range 0 to 59.

tm_hour: The number of hours past midnight, in the range 0 to 23.

tm_mday: The day of the month, in the range 1 to 31.

tm_mon: The number of months since January, in the range 0 to 11.

tm_year: The number of years since 1900.

tm_wday: The number of days since Sunday, in the range 0 to 6.

tm_yday: The number of days since January 1, in the range 0 to 365.

tm_isdst: A flag that indicates whether daylight saving time is in effect at the time described. The value is positive if daylight saving time is in effect, zero if it is not, and negative if the information is not available.

tm_sec:分钟后的秒数,通常在 0 到 59 的范围内,但最多可以达到 60 以允许闰秒。

tm_min:小时后的分钟数,范围为 0 到 59。

tm_hour:午夜过后的小时数,范围为 0 到 23。

tm_mday:月份中的第几天,取值范围为 1 到 31。

tm_mon:自一月以来的月数,范围为 0 到 11。

tm_year:自 1900 年以来的年数。

tm_wday:从星期日开始的天数,范围为 0 到 6。

tm_yday:自 1 月 1 日以来的天数,范围为 0 到 365。

tm_isdst:指示夏令时是否在所述时间生效的标志。如果夏令时有效,则值为正,否则为零,如果信息不可用,则值为负。

回答by Martin Beckett

Depends on which time you want gmtimeor localtimethen just read the struct_tm

取决于您想要gmtimelocaltime 的时间,然后读取struct_tm

回答by Seth Carnegie

There are plenty of functions to do this, see http://www.cplusplus.com/reference/clibrary/ctime/, namely strftime.

有很多功能可以做到这一点,请参见http://www.cplusplus.com/reference/clibrary/ctime/,即strftime.

回答by gred

First of all, do not store your seconds as a float. If you need micro/nanoseconds, store them separately. You're going to need integers to do these calculations.

首先,不要将秒数存储为浮点数。如果您需要微/纳秒,请将它们分开存储。您将需要整数来进行这些计算。

It depends on your time zone (DST rules, leap years, leap seconds), but I would say first get the number of days by integer dividing by 86400. Then find out what's left over, by modulo dividing by 86400. Now you can figure out how many years have passed by first integer dividing the number of days by 365, and then subtracting the number of leap days from the remaining days (calculated by modulo dividing the number of days by 365). You'll also want to subtract the number of leap seconds from the number of remaining seconds (already calculated). If that subtraction drives those numbers below zero, then subtract from the next biggest denomination. Then you can calculate the day of month using explicit logic for your calendar. Make sure to add an hour (or whatever the DST offset is) if you land in DST.

这取决于你的时区(夏令时规则,闰年,闰秒),但我会说先用整数除以 86400 得到天数。然后找出剩下的,通过模除以 86400。现在你可以计算了通过第一个整数将天数除以 365,然后从剩余天数中减去闰日数(通过模数除以 365 计算得出)已经过去了多少年。您还需要从剩余秒数(已计算)中减去闰秒数。如果该减法使这些数字低于零,则从下一个最大面额中减去。然后,您可以使用日历的显式逻辑计算月份中的哪一天。如果您在 DST 着陆,请确保添加一个小时(或任何 DST 偏移量)。

Personally, I would just use Boost.Date_Time, since it does all this and more (probably with fewer mistakes than you or I would make in the first few iterations), but I figured I'd take a shot at your question...

就我个人而言,我只会使用Boost.Date_Time,因为它可以完成所有这些以及更多(可能比你或我在前几次迭代中犯的错误更少),但我想我会试一试你的问题......

回答by Shahbaz

BEFORE

    for (i = 0; i < (years - 1); i++)
    {   
        if (LEAPYEAR((epoch + i)))
        countleap++;
    }

LATER:

之后:

    for (i = 0; i < years; i++)
 {   
   if (LEAPYEAR((epoch + i)))
    countleap++;
 }

After the correction the code worked for me.

更正后,代码对我有用。

回答by Syr

I needed to implement conversion to Unix time at a low-end 8-bit MCU without HW multiplier. Below is the C# code that requires only a general 8-bit multiplication and a division by constant values 4 and 100. Both on 32-bit (long) operand. The C# code can be easily ported to the final framework. It gives the same result as DateTimeOffset.ToUnixTimeSeconds()from .NET.

我需要在没有硬件乘法器的低端 8 位 MCU 上实现到 Unix 时间的转换。下面的 C# 代码只需要一般的 8 位乘法和除以常数值 4 和 100。两者都在 32 位(长)操作数上。C# 代码可以很容易地移植到最终的框架中。它给出与.NET 中的DateTimeOffset.ToUnixTimeSeconds()相同的结果。

static long UnixTime ( int sec, int min, int hour, int day, int month, int year )
{
  // Cumulative days for each previous month of the year
  int[] mdays = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
  // Year is to be relative to the epoch start
  year -= 1970;
  // Compensation of the non-leap years
  int minusYear = 0;
  // Detect potential lead day (February 29th) in this year?
  if ( month >= 3 )
  {
    // Then add this year into "sum of leap days" computation
    year++;
    // Compute one year less in the non-leap years sum
    minusYear = 1;
  }

  return 
    // + Seconds from computed minutes
    60 * (
      // + Minutes from computed hours
      60 * (
        // + Hours from computed days
        24 * (
          // + Day (zero index)
          day - 1
          // + days in previous months (leap day not included)
          + mdays[month - 1]
          // + days for each year divisible by 4 (starting from 1973)
          + ( ( year + 1 ) / 4 )
          // - days for each year divisible by 100 (starting from 2001)
          - ( ( year + 69 ) / 100 )
          // + days for each year divisible by 400 (starting from 2001)
          + ( ( year + 369 ) / 100 / 4 )
          // + days for each year (as all are non-leap years) from 1970 (minus this year if potential leap day taken into account)
          + ( 5 * 73 /*=365*/ ) * ( year - minusYear )
          // + Hours
        ) + hour
        // + Minutes
      ) + min 
      // + Seconds
    ) + sec;
}

Hope it helps.

希望能帮助到你。

Edited:

编辑:

Below is the optimized code for 8-bit PIC MCU and CC5X compiler.

下面是 8 位 PIC MCU 和 CC5X ​​编译器的优化代码。

uns32 unixTime;

...
  // Test data returning 0xFfFfFfFf UnixTime
  uns8 year = 2106 - 1970;
  uns8 month = 2;
  uns8 day = 7;
  uns8 hour = 6;
  uns8 min = 28;
  uns8 sec = 15;

  // See original C# code below

  //### Compute days
  // ( 5 * 73 /*=365*/ ) * year
  unixTime = year;
  mulUnixTime( 5 );
  mulUnixTime( 73 );

  // if ( month >= 3 ) year++;
  if ( month > 3 )
    year++;

  // if ( year > 130 ) => minus 1 total days ( year-=4 makes a result of the next division by 4 less by 1)
  if ( year > 130 )
    year -= 4;
  // + ( ( year + 1 ) / 4 )
  addUnixTime( ( year + 1 ) / 4 );
  // + mdays[month - 1]
  addUnixTime( daysInMonths( month ) );
  // + day - 1
  addUnixTime( day - 1 );
  //### Compute hours
  // Hours from computed days
  mulUnixTime( 24 );
  // + Hours
  addUnixTime( hour );
  //### Compute minutes
  // Minutes from computed hours 
  mulUnixTime( 60 );
  // + Minutes
  addUnixTime( min );
  //### Compute seconds
  // Seconds from computed minutes
  mulUnixTime( 60 );
  // + Seconds
  addUnixTime( sec );
...

void mulUnixTime( uns8 mul )
{
  unixTime *= mul;
}

void addUnixTime( uns8 add )
{
  unixTime += add;
}

uns8 daysInMonths( uns8 month @ W )
{
  skip( month );
#pragma computedGoto 1
  return 0xFF;// Dummy value for month 0
  return   0; // January
  return  31; // February
  return  59; // ...
  return  90;
  return 120;
  return 151;
  return 181;
  return 212;
  return 243;
  return 273;
  return 304; // ...
  return 334; // December
#pragma computedGoto 0
}


/*
 static long UnixTime ( int sec, int min, int hour, int day, int month, int year )
  {
    // Cumulative days for each previous month of the year
    int[] mdays = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
    // Year is to be relative to the epoch start
    year -= 1970;
    // Compensation of the non-leap years
    int minusYear = 0;
    // Detect potential lead day (February 29th) in this year?
    if ( month >= 3 )
    {
      // Then add this year into "sum of leap days" computation
      year++;
      // Compute one year less in the non-leap years sum
      minusYear = 1;
    }

    return
      // + Seconds from computed minutes
      60 * (
        // + Minutes from computed hours
        60 * (
          // + Hours from computed days
          24L * (
            // + Day (zero index)
            day - 1
            // + days in previous months (leap day not included)
            + mdays[month - 1]
            // + days for each year divisible by 4 (starting from 1973)
            + ( ( year + 1 ) / 4 )
            // - days after year 2000
            - ( ( year > 130 ) ? 1 : 0 )
            // + days for each year (as all are non-leap years) from 1970 (minus this year if potential leap day taken into account)
            + ( 5 * 73 ) * ( year - minusYear )
          // + Hours
          ) + hour
        // + Minutes
        ) + min
      // + Seconds
      ) + sec;
  }
*/