用于在C++中将串行日期 (Excel) 转换为年-月-日的算法
Algorithm for converting Serial Date (Excel) to Year-Month-Day in C++
这篇文章提供了一个非常整洁和纯粹的C++算法,用于将串行日期(Excel)转换为其明确的年-月-日表示(和返回)。为方便起见,让我粘贴一个压缩版本:
void ExcelSerialDateToDMY(int nSerialDate, int& nDay, int& nMonth, int& nYear)
{
// Modified Julian to DMY calculation with an addition of 2415019
int l = nSerialDate + 68569 + 2415019;
int n = int(( 4 * l ) / 146097);
l = l - int(( 146097 * n + 3 ) / 4);
int i = int(( 4000 * ( l + 1 ) ) / 1461001);
l = l - int(( 1461 * i ) / 4) + 31;
int j = int(( 80 * l ) / 2447);
nDay = l - int(( 2447 * j ) / 80);
l = int(j / 11);
nMonth = j + 2 - ( 12 * l );
nYear = 100 * ( n - 49 ) + i + l;
}
int DMYToExcelSerialDate(int nDay, int nMonth, int nYear)
{
// DMY to Modified Julian calculated with an extra subtraction of 2415019.
return int(( 1461 * ( nYear + 4800 + int(( nMonth - 14 ) / 12) ) ) / 4) +
int(( 367 * ( nMonth - 2 - 12 * ( ( nMonth - 14 ) / 12 ) ) ) / 12) -
int(( 3 * ( int(( nYear + 4900 + int(( nMonth - 14 ) / 12) ) / 100) ) ) / 4) +
nDay - 2415019 - 32075;
}
例如
2019-06-22 <--> 43638
2000-01-28 <--> 36553
1989-09-21 <--> 32772
上面的帖子来自 2002 年,所以我想知道是否有更好的替代实现。我所说的"更好"是指例如更快、更短或更不晦涩。甚至是算法,它们可能会提供一定数量的预计算(例如,记录所需年份范围的 1 月 1 日序列日期,例如 1900 到 2200,然后执行快速查找)。
你展示的算法非常好。 在我的平台(clang++ -O3)上,它们生成没有分支(管道停滞器)的目标代码,也没有对远处内存的访问(缓存未命中)。 作为一对,有效期范围从-4800-03-01到未来数百万年(大量范围)。 在整个范围内,他们模拟公历。
以下是一些非常相似的替代算法。 一个区别是,你的纪元是 1900-01-01,而我介绍的纪元是 1970-01-01。 但是,通过这些纪元(25569 天)的差异来调整纪元非常容易,如下所示:
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 - 25569;
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);
}
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 - 25569);
}
这些算法在向前和向后(包括 -4800-03-01 之前)都有效数百万年。 虽然这个额外的范围不会给你带来太多好处,因为公历直到 1582-10-15 才开始。
我使用clang++ -O3 -S
在 macOS 上编译了这两对算法,我拥有的集合生成的目标代码略小(约 10%)。 尽管它们都很小,无分支且无缓存未命中,但试图通过衡量性能来验证这种好处将是一项具有挑战性的工作。
我没有发现任何一组的可读性优于另一组。 然而,对于那些好奇这些算法如何工作的人来说,这对算法确实带有令人恼火的详尽推导,以及单元测试以确保算法在 +/-100 万年的范围内工作。
通过在两种算法中设置const int era = 5
,将有效性范围限制为 [2000-03-01, 2400-02-29],可以在上述算法中获得非常轻微的性能。 我还没有对此选项进行性能测试。 我希望这样的增益是在噪音水平上。
或者,通过不考虑era
的负值来限制[0000-03-01,数百万年向前]的范围,可能会有一些微小的性能优势:
在civil_from_days
:
const int era = z / 146097;
在days_from_civil
:
const int era = y / 400;
- 防止主数据类型C++的隐式转换
- 模板参数替换失败,并且未完成隐式转换
- 努力将整数转换为链表。不知道我在这里做错了什么
- HEX值到wchar_t字符(UTF-8)的转换
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- 将 Qvector<uint8_t> 转换为 QString
- 如何在cuSparse中使用cusparseXcoo2csr从coo转换为csc
- 有关插入适配器的错误。[错误]请求从 'back_insert_iterator<vector<>>' 类型转换为非标量类型
- 在c++中使用nlohmann从类到json的转换
- 从"int*"强制转换为"unsigned int"会丢失精度错误
- 将Integer转换为4字节的unsined字符矢量(按大端字节顺序)
- 处理小于cpu数据总线的数据类型.(c++转换为机器代码)
- 如何使用OpenCV将RBG图像转换为HSV,并将H、S和V值保存为C++中的3个独立图像
- 复制列表初始化的隐式转换的等级是多少
- 如何将字符串(2019年10月3日星期四18:45:10)转换为cpp中的time_t格式?
- 用于在C++中将串行日期 (Excel) 转换为年-月-日的算法
- put_time具有转换说明符,该转换指定符,该指定符在一个月的单个数字上输出没有前面字符的单位天数
- 从1601年1月1日起,将100纳秒间隔的uint64时间转换为日期时间字符串
- 将2003年8月17日转换为boost::gregori::date
- 我如何将一年中的某一天(1-365)转换为其等效日期(即2013年1月5日)C++