将天数转换为年数的高效算法(包括闰年)

14

问题

我正在编写一个用于在C++中保存日期的类,但是我遇到了以下问题:

我有一个自参考日期(在我的例子中是公元1年1月1日)以来过去的天数N,包括自参考日以来经过的闰年天数。如何高效地将这个数字转换为年份Y、月份M和日期D

我希望尽可能高效地执行此操作,因此最佳实现显然具有O(1)的复杂度。

接下来的章节将解释我已经学到的一些知识。

闰年

为了确定一年是否为闰年,有几个规则:

  1. 可以被4整除的年份为闰年
  2. 规则1的例外:可以被100整除的年份不是闰年
  3. 规则2的例外:可以被400整除的年份是闰年

代码实现如下:

bool IsLeapYear(int year)
{
    // Corrected after Henrick's suggestion
    if (year % 400 == 0) return true;
    if ((year % 4 == 0) && (year % 100 != 0)) return true;
    return false;
}

一种高效的计算某年之前有多少个闰年的方法是:

int LeapDaysBefore(int year)
{
    // Years divisible by 4, not divisible by 100, but divisible by 400
    return ((year-1)/4 - (year-1)/100 + (year-1)/400);
}

计算月份

找到年份后,可以计算距离当前年份还有多少天,并将此数字从N中减去。这将给出一年中的日期。

通过保留每个月开始的日期编号的表格,我们可以轻松地计算月份。 我还创建了一个函数,如果年份是闰年且月份大于或等于2,则会添加1。

// What day each month starts on (counting from 0)
int MonthDaySt[] = { 0, 31, 59, 90, 120, 151, 181, 212, 
    243, 273, 304, 334, 365 };

int MonthDayStart(int month, bool leap)
{
   if (leap && month >= 2) return MonthDaySt[month]+1;
   return MonthDaySt[month];
}

我的想法

我的算法非常复杂,大致如下:

void GetDate(int N, int &Y, int &M, int &D)
{
    int year_days;

    // Approximate the year, this will give an year greater or equal
    // to what year we are looking for.
    Y = N / 365 + 1;

    // Find the actual year, counting leap days
    do {
        Y--;

        // Calculate the actual number of days until the
        // approximate year
        year_days = Y * 365 + LeapDaysBefore(year);

    } while (year_days > N);

    // Add 1, because we start from year 1 AD (not 0)
    Y++;

    // Calculate month
    uint64_t diff = N - year_days; // Will give us the day of the year
    bool leap = IsLeapYear(Y);  // Is current year leap?

    // Use table to find month
    M = 0;
    while (MonthDayStart(M, leap) <= diff && M <= 12)
        M++;

    // Calculate day
    D = diff - MonthDayStart(M - 1, leap) + 1;
}

这个函数可能有一些bug(例如,当N为0时它不能正常工作)。

其他注意事项

我希望我的算法仍然是正确的,因为我对原始算法进行了一些修改以回答这个问题。如果我漏掉了什么,或者出现了错误,请告知我以便进行修改。对于问题的长度,我感到抱歉。


你想过将年数除以400,然后考虑每100年,再找到确切的年份吗? - nhahtdh
2
请参考源代码:http://emr.cs.uiuc.edu/home/reingold/calendar-book/index.shtml - High Performance Mark
@stefan 在当前的开发阶段很难预测,但其中一个用途将是用于日志记录(该类实现了日期和时间),因此我猜它会经常被调用。 - Tibi
1
使用boost::gregorian::date可能是从这里开始的最佳方式。 - Alexandre C.
无需迭代(且高效)的用于此操作的算法:http://howardhinnant.github.io/date_algorithms.html - Howard Hinnant
显示剩余6条评论
11个回答

5
以下是一些提示。注意:在本练习中,我将假设当 N=0 时,Y % 400 == 0
1. 每个 400 年周期内有固定的天数,为 (400 * 365) + 100 + 1 - 4
其中,+100 是为了计算闰年,+1 是为了计算每 400 年的闰年,-4 是因为每 100 年没有闰年。
因此,你的第一行代码将是:
GetDate(int N, int &Y, int &M, int &D) {
  const int DAYS_IN_400_YEARS = (400*365)+97;
  int year = (N / DAYS_IN_400_YEARS) * 400;
  N = N % DAYS_IN_400_YEARS;

2: 如果你把3月1日视为一年的开始,将会让生活变得轻松很多。

3:(1)的代码基础上,我们可以计算出年份。记住,每个四百年以闰年开始。因此,你可以使用以下公式来完成年份的计算:

  const int DAYS_IN_100_YEARS = (100*365) + 24;
  year += 100 * (N / DAYS_IN_100_YEARS) + (N < DAYS_IN_100_YEARS ? 1 : 0); // Add an extra day for the first leap year that occurs every 400 years.
  N = N - (N < DAYS_IN_100_YEARS ? 1 : 0);
  N = N % DAYS_IN_400_YEARS;

4: 现在你已经解决了年份问题,其他的很容易(只需要记住(2),整个过程就变得简单了)。

或者,您可以使用boost::date


有一件事我不太明白...为什么 DAYS_IN_100_YEARS = (100*365) - 24?难道不应该是 +24 吗?而且当你将其加到年份时,应该乘以 100 - Tibi
1
最佳答案是因为它真正帮助我解决了问题,而不像其他答案一样添加了另一个问题。 - Tibi

5

用一个老笑话的结尾来说,“我不会从这里开始”。

在了解“现代”之前,您需要阅读有关日历变化的各种信息,例如1752年发生了什么。


格里高利历的采用只发生在1752年的英国帝国。其他地方的采用则在不同年份或在某些地方经历了几年时间的变化。 - Some programmer dude
朱利安-格里高利历问题。如果我们假设一切都在格里高利历中,我认为这不是一个问题。转换也不太复杂。 - nhahtdh
1
我真的很想知道这个笑话是什么! - El Ronnoco
这超出了应用程序的目的,我为方便起见选择了公元1年1月1日。 - Tibi
1
@Tibi - 问题在于这并不方便,因为你需要决定你所说的 0001 年 1 月 1 日到底是什么意思。它不是历史日期,所以你必须自己创造。1970 年或 2000 年作为日志日期的基准年有什么问题吗? - Bo Persson

2

多年来,我尝试解决公历日期问题,但都失败了。我大约在15年前开发了这段代码,它仍然表现良好。由于我很久以前写过这些代码,所以是用本地C编写的,但很容易编译成C++程序。如果您喜欢,可以将其包装在一个日期类中。

我的代码基于将所有闰年规则组合成一个400年周期。根据公历闰年规则,每个400年周期恰好有146,097天。

我采用的一种优化方法是将1月和2月移到前一年的末尾。这使得闰日(如果存在)始终落在当年的最后一天。这使我能够建立一个表(dayOffset),其中提供了从3月1日开始的天数距离。因为闰日会落在年底,所以这个表对于闰年和非闰年都是准确的。

我将从头文件开始。

#if !defined( TIMECODE_H_ )
#define TIMECODE_H_ 1

#if defined(__cplusplus)
extern "C" {
#endif

int dateCode( int month, int dayOfMonth, int year );

void decodeDate( int *monthPtr, int *dayOfMonthPtr, int *yearPtr, int dateCode );

int dayOfWeek( int dateCode );

int cardinalCode( int nth, int weekday, int month, int year );

enum Weekdays { eMonday, eTuesday, eWednesday, eThursday, eFriday, eSaturday, eSunday };

#if defined(__cplusplus)
}
#endif

#endif

这个API包含四个方法:dateCode(),用于计算公历日期的日期代码;decodeDate(),用于从日期代码计算公历月份、日期和年份;dayOfWeek(),返回日期代码对应的星期几;cardinalCode(),返回指定月份中“基数”日的日期代码(例如,2014年8月第二个星期三)。

下面是实现代码:

#include <math.h>

enum
{
   nbrOfDaysPer400Years = 146097,
   nbrOfDaysPer100Years = 36524,
   nbrOfDaysPer4Years = 1461,
   nbrOfDaysPerYear = 365,
   unixEpochBeginsOnDay = 135080
};

const int dayOffset[] =
{
   0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337, 366
};

/* ------------------------------------------------------------------------------------ */
int mod( int dividend, int divisor, int* quotientPtr )
{
   *quotientPtr = (int)floor( (double)dividend / divisor );
   return dividend - divisor * *quotientPtr;
}

/* ------------------------------------------------------------------------------------ */
int dateCode( int month, int dayOfMonth, int year )
{
   int days;
   int temp;
   int bYday;
   /*
   we take the approach of starting the year on March 1 so that leap days fall
   at the end. To do this we pretend Jan. - Feb. are part of the previous year.
   */
   int bYear = year - 1600;
   bYday = dayOffset[ mod( month - 3, 12, &temp ) ] + dayOfMonth - 1;
   bYear += temp;

   bYear = mod( bYear, 400, &days );
   days *= nbrOfDaysPer400Years;

   bYear = mod( bYear, 100, &temp );
   days += nbrOfDaysPer100Years * temp;

   bYear = mod( bYear, 4, &temp );
   days += nbrOfDaysPer4Years * temp + nbrOfDaysPerYear * bYear + bYday -
      unixEpochBeginsOnDay;

   return days;
}

/* ------------------------------------------------------------------------------------ */
int dayOfWeek( int dateCode )
{
   int temp;
   return mod( dateCode + 3, 7, &temp );
}

/* ------------------------------------------------------------------------------------ */
void decodeDate( int *monthPtr, int *dayOfMonthPtr, int *yearPtr, int dateCode )
{
   int diff;
   int diff2;
   int alpha;
   int beta;
   int gamma;
   int year;
   int temp;

   /* dateCode has the number of days relative to 1/1/1970, shift this back to 3/1/1600 */
   dateCode += unixEpochBeginsOnDay;
   dateCode = mod( dateCode, nbrOfDaysPer400Years, &temp );
   year = 400 * temp;
   dateCode = mod( dateCode, nbrOfDaysPer100Years, &temp );
   /* put the leap day at the end of 400-year cycle */
   if ( temp == 4 )
   {
      --temp;
      dateCode += nbrOfDaysPer100Years;
   }
   year += 100 * temp;
   dateCode = mod( dateCode, nbrOfDaysPer4Years, &temp );
   year += 4 * temp;
   dateCode = mod( dateCode, nbrOfDaysPerYear, &temp );
   /* put the leap day at the end of 4-year cycle */
   if ( temp == 4 )
   {
      --temp;
      dateCode += nbrOfDaysPerYear;
   }
   year += temp;

   /* find the month in the table */
   alpha = 0;
   beta = 11;
   gamma = 0;
   for(;;)
   {
      gamma = ( alpha + beta ) / 2;
      diff = dayOffset[ gamma ] - dateCode;
      if ( diff < 0 )
      {
         diff2 = dayOffset[ gamma + 1 ] - dateCode;
         if ( diff2 < 0 )
         {
            alpha = gamma + 1;
         }
         else if ( diff2 == 0 )
         {
            ++gamma;
            break;
         }
         else
         {
            break;
         }
      }
      else if ( diff == 0 )
      {
         break;
      }
      else
      {
         beta = gamma;
      }
   }

   if ( gamma >= 10 )
   {
      ++year;
   }
   *yearPtr = year + 1600;
   *monthPtr = ( ( gamma + 2 ) % 12 ) + 1;
   *dayOfMonthPtr = dateCode - dayOffset[ gamma ] + 1;
}

/* ------------------------------------------------------------------------------------ */
int cardinalCode( int nth, int weekday, int month, int year )
{
   int dow1st;
   int dc = dateCode( month, 1, year );
   dow1st = dayOfWeek( dc );
   if ( weekday < dow1st )
   {
      weekday += 7;
   }
   if ( nth < 0 || nth > 4 )
   {
      nth = 4;
   }
   dc += weekday - dow1st + 7 * nth;
   if ( nth == 4 )
   {
      /* check that the fifth week is actually in the same month */
      int tMonth, tDayOfMonth, tYear;
      decodeDate( &tMonth, &tDayOfMonth, &tYear, dc );
      if ( tMonth != month )
      {
         dc -= 7;
      }
   }
   return dc;
}

一个立即显而易见的效率问题是mod()函数。正如你所期望的,它提供了两个整数被除数的商和余数。C/C++提供了模运算符(%),似乎是更好的选择; 然而,标准没有指定这个操作应该如何处理负被除数。(有关更多信息,请参见这里。)

可能有一种便携式的解决方案,使用高效的整数运算; 然而,我在这里选择了一个稍微不那么高效但保证在所有平台上正确的解决方案。

日期代码只是相对于基准日期的天数偏移量。我选择了1600年3月1日,因为它是一个400年的公历周期的开始,早到足以使我们遇到的所有日期都产生一个正整数的日期代码。然而,在基准日期之前使用日期代码也没有错。由于我们使用的是稳定/可移植的模运算,所有的数学运算对于负日期代码都有效。

有些人不喜欢我的非标准基准日期,所以我决定采用标准的Unix纪元,它从1970年1月1日开始。我定义了unixEpochBeginsOnDay来偏置日期代码以从所需的日期开始。如果您想使用不同的基准日期,您可以用您喜欢的一个值替换这个值。

计算日期代码就像将月份、日和年传递给dateCode()一样简单:

int dc = dateCode( 2, 21, 2001 );  // returns date code for 2001-Feb-21

我已经编写了 dateCode,使其可以接受超出月份和日数范围的值。你可以将月份看作是给定年份的一月后的整数个月加一。以下是一些测试示例:

assert(dateCode( 14, 1, 2000 ) == dateCode( 2, 1, 2001 ));
assert(dateCode( 5, 32, 2005 ) == dateCode( 6, 1, 2005 ));
assert(dateCode( 0,  1, 2014 ) == dateCode(12, 1, 2013 ));

使用非规范的月份和日期值调用dateCode,然后使用decodeDate进行转换,是将日期规范化的有效方法。例如:

int m, d, y;
decodeDate( &m, &d, &y, dateCode( 8, 20 + 90, 2014 ));
printf("90 days after 2014-08-20 is %4d-%02d-%02d\n", y, m, d);

输出应该是:
2014-08-20后的90天是2014-11-18。
decodeDate()始终为月份和dayOfMonth生成规范值。
dayOfWeek()仅返回dateCode的模数7,但我必须将dateCode偏置3,因为1970年1月1日是星期四。如果您喜欢从周一开始而不是从其他日期开始,则修复Weekdays枚举并根据需要更改偏差。
cardinalCode()提供了这些方法的有趣应用程序。第一个参数提供了月份的第几周(“nth”),第二个参数提供了工作日。因此,要查找2007年8月的第四个星期六,您将:
int m, d, y;
decodeDate( &m, &d, &y, cardinalCode( 3, eSaturday, 8, 2007 ) );
printf( "%d/%02d/%d\n", m, d, y );

这将产生答案:

2007年8月25日

请注意,上面示例中的第n个参数3指定了第四个星期六。我曾经考虑过这个参数应该是基于0还是1。出于某种原因,我选择了:0=第一个,1=第二个,2=第三个等。即使最短的月份每个工作日也有四次出现。值4具有特殊含义。人们会期望它返回所请求工作日的第五次出现;然而,由于该月份可能有也可能没有第五次出现,我决定返回该月份的最后一次出现。

例如,要显示明年每个月的最后一个星期一:

int i, m, d, y;
for (i=1; i <= 12; ++i) {
    decodeDate( &m, &d, &y, cardinalCode( 4, eMonday, i, 2015 ) );
    printf( "%d/%02d/%d\n", m, d, y );
}

最后一个例子,展示了使用cardinalCode()的一种用途,显示距离下次大选的天数:

#include <stdio.h>
#include <time.h> /* only needed for time() and localtime() calls */
#include "datecode.h"

void main()
{
   int eYear, eday, dc;
   int eY, eM, eD;
   time_t now;
   struct tm bdtm;

   time(&now);
   if (localtime_r(&now, &bdtm) == NULL) {
       printf("Error\n");
       return 1;
   }
   eYear = bdtm.tm_year + 1900;
   dc = dateCode(bdtm.tm_mon + 1, bdtm.tm_mday, eYear);
   if ((eYear % 2) != 0) {
       ++eYear;
   }
   for(;;) {
       eday = cardinalCode(0, eTuesday, 11, eYear);
       if (eday >= dc) break;
       eYear += 2;    /* move to the next election! */
   }
   decodeDate(&eM, &eD, &eY, eday);
   printf("Today is %d/%02d/%d\neday is %d/%02d/%d, %d days from today.\n",
           bdtm.tm_mon + 1, bdtm.tm_mday, bdtm.tm_year + 1900,
           eM, eD, eY, eday - dc);
}

1

显然,瓶颈在于年份计算。我建议您这样做。当您初始化日历时,通过将天数除以365来粗略地近似年份。之后,预先列出所有在此估计之前的闰年列表。这应该相当快,因为您不需要计算所有闰年,只需每次添加4年即可。同时,在进行计算时,记录其中有多少个。实际上,您甚至可以按较大的包(即每400年有100个闰年)计算它们,但是您需要仔细检查闰年例外情况,以免跳过其中一些。

在此结束时,您将拥有年份的粗略估计以及之前所有闰年的数量。现在,您可以非常容易地计算精确的年份,而无需迭代任何内容:

leapYearCount * 366 + (lastCalculatedYear - leapYearCount) * 365

更正一下,每400年有97个闰年。排除可被100整除的年份,再加上可被400整除的年份,总共为97个。 - Tibi
@Tibi,是的,you will need to check for the the leap year exceptions carefully, not to skip some of them. 这部分就是关于这个的:P - SingerOfTheFall

1

这个

bool IsLeapYear(int year) 
{ 
    if ((year % 4 == 0) && (year % 100 != 0) && (year % 400 == 0)) return true; 
    else return false; 
}

是不正确的。它会返回false,而对于2000来说更好的做法是:

bool IsLeapYear(int year) 
{ 
    if (year % 400 == 0) return true; 
    if ((year % 4 == 0) && (year % 100 != 0)) return true; 
    return false; 
}

我认为我知道原因,因为这些情况不能同时发生:(year % 100 != 0) && (year % 400 == 0)。我已经纠正了原问题。 - Tibi

1
让我简化问题,不考虑例外情况进行解释。每4年会出现一个闰年,如果你有365*5天,必须有一个闰年(除非应用例外2)。您可以使用除法来获得闰年的数量(如果忽略例外情况)。
然后,您可以轻松地使用除法和余数来获得非闰年/月/日。
对于解决“例外1”,使用相同的基本直觉(如果年数是100的倍数,则还要检查“例外2”)。
  1. 可被4整除的年份为闰年
  2. 规则1的例外:可被100整除的年份不是闰年
  3. 规则2的例外:可被400整除的年份为闰年

那么您的意思是我应该忽略异常来近似一年,然后找到真正的年份? - Tibi
不,永远不要忽略异常。我只是向您展示了如何使用第一条规则获取闰年的数量。现在困难的任务是检查如何“交叉”规则以确保两个异常都得到保证。 - Nuno Aniceto
@NunoAniceto:实际上,你可以有很多现实的忽略情况 - 如果纪元秒数在[-1,999,999,999至3,999,999,999]的范围内(基本上是“[-20亿,+40亿]”(考虑到我们距离17亿的纪元还有4.5个月,这已经是一个巨大的范围了),它被1906年和2096年所包围。为什么这很重要呢?因为只有一个模100年落在这个范围内,那就是“Y2K”,由于第二例外规则而闰年。也就是说,在-20亿至+40亿之间的所有纪元都可以安全地使用第一条模4规则进行计算(顺便说一句,在这190年的时间跨度内恰好有48个闰年)。 - RARE Kpop Manifesto
当然,这是基于“纪元秒”而不是“YYYY”本身作为起点的假设。 - RARE Kpop Manifesto

1
我有一个距离参考日期(在我的情况下是公元1年1月1日)已经过去了N天的数字...在这种情况下,适用4-100-400规则和查找月份长度的“效率”不是您主要面临的问题。请还要注意将今天的格里高利历应用于其出现之前的日期所固有的多个问题,并且格里高利历的推广并非一致。(*) Wikipedia是一个涉及到这个主题非常复杂但很好的起点 good。(*): 根据不同国家,推广时间分别为1582年10月15日至1923年2月15日,或根本没有进行推广。

我使用(预推算的)公历,因为它是最简单的方式,我甚至不想去烦恼其他日历。此外,我选择了参考日期是为了方便起见,第“0”天意味着第一年的第一天。 - Tibi
如果是在日历引入之前的年份,这个结果就会出错,但这已经超出了我的应用程序的目的。 - Tibi
@Tibi:如果公历改革前的日期不在考虑范围内,为什么要选择公元1年作为第一行的参考日期呢? - DevSolar
我并没有说公历改革前的日期超出了范围,我的意思是对于我的应用程序来说,它们不必100%准确。公历引入后的日期是准确的,但在此之前的日期不需要完全准确。我正在尝试创建一个游戏,根据情节可能需要一些更早的日期,但我还没有决定故事情节。这不是一个历史数据库或任何需要100%精确的东西。 - Tibi

0
为了加快年份的计算,您可以建立一个查找表。
int[] YearStartDays =
{
    0,                     // 1 AD
    365,                   // 2 AD
    365 + 365,             // 3 AD
    365 + 365 + 365,       // 4 AD
    365 + 365 + 365 + 366, // 5 AD (4 was a leap year)
    /* ... */
};

然后你可以在这个数组中进行二分查找,这是O(log N),而不是你当前的年份查找算法的O(N)。


我想到了这种方法,由于闰年规则每400年重复一次,所以它可能可行。然而,它以速度为代价来换取内存。 - Tibi
一个问题是第4年不是闰年,因为闰年还没有被发明出来。甚至有人质疑是否存在第4年。 :-) - Bo Persson

0
bool IsLeapYear(int year)
{
    boost::gregorian::date d1(year, 1, 1);
    boost::gregorian::date d2 = d1 + boost::gregorian::years(1);
    boost::gregorian::date_duration diff = d2 - d1;
    return diff.days() != 365;
}

这并没有回答完整的问题,它只改变了函数的一部分。 - Mike Precup

0

这是我的做法:

class DatePoint {
public:
    double seconds;
    uint8_t minutes;
    uint8_t hours;
    uint8_t weekday;
    uint8_t day;
    uint8_t month;
    uint16_t year;
    DatePoint(double SecondsSinceEpoch);
    double GetSecondsSinceEpoch();
};

DatePoint::DatePoint(double SecondsSinceEpoch) {

    double DaysSinceEpoch = std::floor(SecondsSinceEpoch / 86400.0);
    double SecondsOfDay = SecondsSinceEpoch - (DaysSinceEpoch * 86400.0);
    double MinutesOfDay = std::floor(SecondsOfDay / 60.0);
    double HoursOfDay = std::floor(MinutesOfDay / 60.0);
    int DaysSinceEpoch_int = static_cast<int>(DaysSinceEpoch);
    DaysSinceEpoch_int = DaysSinceEpoch_int + (97 + (400 * 365)) * 5 - 11017; // 11,017 days between 01/01/1970 and 01/03/2000
    
    int Quotient = (DaysSinceEpoch_int / (97 + (400 * 365))) * 400;
    int Mod = DaysSinceEpoch_int - (97 + (400 * 365)) * (Quotient / 400);
    Quotient = Quotient + (Mod / (24 + (100 * 365)) - Mod / ((24 + (100 * 365)) * 4)) * 100;
    Mod = Mod - (24 + (100 * 365)) * (Mod / (24 + (100 * 365)) - Mod / ((24 + (100 * 365)) * 4));
    Quotient = Quotient + (Mod / (1 + (4 * 365))) * 4;
    Mod = Mod - (1 + (4 * 365)) * (Mod / (1 + (4 * 365)));
    Quotient = Quotient + Mod / 365 - Mod / (4 * 365);
    Mod = Mod - (Mod / 365 - Mod / (4 * 365)) * 365;
    Quotient = Quotient + Mod / 306;

    seconds = SecondsOfDay - (MinutesOfDay * 60.0);
    minutes = static_cast<uint8_t>(MinutesOfDay - (HoursOfDay * 60.0));
    hours = static_cast<uint8_t>(HoursOfDay);
    weekday = 1 + static_cast<uint8_t>((DaysSinceEpoch_int + 2) % 7);
    day = static_cast<uint8_t>(Mod - ((((100 * Mod + 52) / 3060) * 306 + 5) / 10) + 1);
    month = static_cast<uint8_t>(((((100 * Mod + 52) / 3060) + 2) % 12) + 1);
    year = static_cast<uint16_t>(Quotient);

};

double DatePoint::GetSecondsSinceEpoch() {
    double SecondsSinceEpoch = seconds + minutes * 60.0 + hours * 3600.0;
    int StdMonth = (static_cast<int>(month) + 9) % 12;
    int StdYear = static_cast<int>(year) - (StdMonth / 10);
    int Days = StdYear * 365 + (StdYear/4) - (StdYear/100) + (StdYear/400) + ((StdMonth*306 +5)/10) + static_cast<int>(day) - 1;
    Days = Days - (97 + (400 * 365)) * 5 + 11017; // 11,017 days between 01/01/1970 and 01/03/2000
    SecondsSinceEpoch = SecondsSinceEpoch + Days * 86400.0;
    return SecondsSinceEpoch;
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接