首页 > 编程语言 >[底层原理] C/C++获取时间(将时间戳转换为年月日)?

[底层原理] C/C++获取时间(将时间戳转换为年月日)?

时间:2024-08-24 18:24:37浏览次数:10  
标签:int days C++ tm mon year yday 年月日 底层

前言

大家都知道,计算机中存储的时间是一个整数,在现在的编程语言中,可以很方便地将时间戳(整数)转换为字符串,但是如果没有这些我们该如何自己计算出呢?

刚好以前研究过Nginx的源代码,我以他的代码为例,说明其背后的数学原理。当然在工程实践中,没有必要花时间自己实现转换的函数,所以本文用作一些底层原理的研究,说明计算机的背后其实是数学的原理组成的。

我这里采用C语言,如果是其他语言,也是类似的转换方法。

 本文分为两个部分源代码和源代码解析。

环境:Visual Studio 2022,Win11

为了方便后面解析,说明我写代码当天时间是:2024年08月24日,周六

源代码

为了能够在一个单独的console环境中展示,那么我对源代码的命名放在了一起,以下是一个演示的完整代码:

#include <iostream>
#include <ctime>
using namespace std;
typedef struct tm             ngx_tm_t;
typedef intptr_t        ngx_int_t;
void
ngx_gmtime(time_t t, ngx_tm_t* tp)
{
    ngx_int_t  sec, min, hour, mday, mon, year, wday, yday, days;

    days = t / 86400;

    /* Jaunary 1, 1970 was Thursday */
    wday = (4 + days) % 7;

    t %= 86400;
    hour = t / 3600;
    t %= 3600;
    min = t / 60;
    sec = t % 60;

    /* the algorithm based on Gauss's formula */

    days = days - (31 + 28) + 719527;

    year = days * 400 / (365 * 400 + 100 - 4 + 1);
    yday = days - (365 * year + year / 4 - year / 100 + year / 400);

    mon = (yday + 31) * 12 / 367;
    mday = yday - (mon * 367 / 12 - 31);

    mon += 2;

    if (yday >= 306) {

        /*
         * there is no "yday" in Win32 SYSTEMTIME
         *
         * yday -= 306;
         */

        year++;
        mon -= 12;

        if (mday == 0) {
            /* Jaunary 31 */
            mon = 1;
            mday = 31;

        }
        else if (mon == 2) {

            if ((year % 4 == 0) && (year % 100 || (year % 400 == 0))) {
                if (mday > 29) {
                    mon = 3;
                    mday -= 29;
                }

            }
            else if (mday > 28) {
                mon = 3;
                mday -= 28;
            }
        }
        /*
         *  there is no "yday" in Win32 SYSTEMTIME
         *
         *  } else {
         *      yday += 31 + 28;
         *
         *      if ((year % 4 == 0) && (year % 100 || (year % 400 == 0))) {
         *          yday++;
         *      }
         */
    }

    tp->tm_sec = (int)sec;
    tp->tm_min = (int)min;
    tp->tm_hour = (int)hour;
    tp->tm_mday = (int)mday;
    tp->tm_mon = (int)mon;
    tp->tm_year = (int)year;
    tp->tm_wday = (int)wday;
}


int main()
{
    std::time_t now = std::time(nullptr);
 
    ngx_tm_t t;
    ngx_gmtime(now, &t);

    // 中国属于东八区,所以我在hour加了一个8
    std::cout << t.tm_year << "年" << t.tm_mon << "月" << t.tm_mday  << "日 " 
              << t.tm_hour + 8 << ":" << t.tm_min << ":" << t.tm_sec;

    getchar();

}

效果如下,和我的预期一致:

 

源代码解析

先看第11行如下,86400属于什么呢?我直接问gpt,说明是一天的秒数,经过计算确实如此。1 天 = 24 × 60 × 60 秒 = 86400秒。第一步nginx算出了当天到1970年的第一天差距了多少天,

days = t / 86400;

debug查看days的值为19959,也就是说2024年8月24日减去1970年1月1日为19959天:

14行:

wday代表今天是星期几,因为1970年的7月1日为星期四,所以使用上面得到的天数加上4然后余7,得到了wday为6。

wday = (4 + days) % 7;

 

16-17行,执行16行代码后,t 的值会被更新为 t 除以 86400 的余数。这实际上是将时间戳 t 转换为了从当天午夜(00:00:00)开始计算的秒数。换句话说,这行代码去除了时间戳中的日期部分,只保留了时间部分(以秒为单位)。

所以17行除一个小时的秒数,1600秒,得到今天是几点钟了,

t %= 86400;
hour = t / 3600;

debug发现是10点,因为计算机内部时间戳是UTC格式,中国属于东八区,所以其实是18点 ,我后续输出的时候对小时加了一个8。

18-20行,这个时候,t已经是当天过了多少秒了,分钟和秒数以此类推

t %= 3600; // 获取除开小时的秒数
min = t / 60; // 获取当前的分钟数
sec = t % 60; // 获取秒数

到这里已经获取了小时,分钟,秒数、星期几,接下来就是获取年、月、日。

24行

days = days - (31 + 28) + 719527;

经过查找资料才明白 719527 是从公元前1年3月1日到1970年3月1日的天数。所以这行代码是将days改为从公元当天开始到现在的天数。

后续的代码,我后面再继续研究,因为哪个公式看不看懂,也搜索不到。

标签:int,days,C++,tm,mon,year,yday,年月日,底层
From: https://blog.csdn.net/weixin_39445116/article/details/141502505

相关文章

  • 【C++】类与对象篇三
    【C++】类与对象篇三一.运算符重载1运算符重载2赋值运算符重载3前置++和后置++重载4.const成员5.取地址及const取地址操作符重载一.运算符重载1运算符重载C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数函数名字为:关键字o......
  • 解决 C/C++ 程序执行一闪而过的方法
    作者:一去、二三里个人微信号:iwaleon微信公众号:高效程序员在VS编写控制台程序的时候,包括使用其他IDE(VisualC++)编写C/C++程序,经常会看到程序的执行结果一闪而过,要解决这个问题,可以在代码的最后加上system("pause")、getchar()、cin.get()。推荐方法比较常用的做......
  • C++11
    类型推导类型推导是C++的一种特性,允许编译器自动推导变量的类型,而不需要显式地制定类型。autoauto用于让编译器自动推导变量类型,常见用法:基本示例:autox=10;与容器一起使用:vector<string>names={"Alice","Bob"};for(autoit=names.begin();it!=names.en......
  • C++调用Python和numpy第三方库计算MFCC音频特征实现封装发布
    目录项目简介程序/数据集下载环境准备执行步骤1.新建python虚拟环境2.虚拟环境运行下python代码3.迁移虚拟环境4.编写Cmakelists.txt5.编写C++代码6.编译项目7.测试项目简介深度学习程序的边缘部署以性能绝佳的C++为主(⊙﹏⊙),但遇到项目开发周期短,则以功能优先,一些复杂的算法和......
  • 从零开始学习C++之结构体
    前言之前讲过变量,讲了数据类型(如int等),而结构体就相当于创造一个类型。定义结构体首先,写上一个神圣不可侵犯的(bushi)struct。好了,不开玩笑了。在程序外围定义(一般写在命名空间后面)。struct名字{ 含有的东西。};一定一定要有分号!!!例:定义存储坐标的结构体structzuo......
  • 从零开始学习C++之函数
    前言作者看着200行的主函数大模拟返回了WA,陷入了沉思......咋办?把每个模块包装一下就知道哪里出错了,所以,今天讲函数。(太生硬了吧。。。)定义一个函数格式:返回值类型函数名(变量*n){ 代码}例如:intadd(inta,intb){ returna+b;}注:定义函数中的每个变量必......
  • 从零开始学习C++
    updateon2024/8/24前言配置环境基本编码格式入门输入输出循环变量与数组if判断语句(分支结构)基础函数......
  • 埃筛C++写法
    埃筛的作用是找素数(质数),以质数的倍数一定是合数为重心思路。比如说2是质数,但2的倍数(除了自己)都是合数。3是质数,但3的倍数(除了自己)都是合数。我们针对这个特性,可以用打标法实现。p[x]表示x是否为质数。voidPrime(){ memset(P,true,sizeof(P)); for(inti......
  • C++相关知识
     string倒排reverse#include<iostream>#include<string>#include<algorithm>intmain(){std::stringstr="Hello,World!";std::reverse(str.begin(),str.end());std::cout<<str<<std::endl;r......
  • C++11新特性(四):库特性
    C++11新特性库特性std::move用于实现移动语义的函数,完成左值到右值的转换,参见C++11新特性(一)std::forward用于实现完美转发的函数,直接将参数的类型传递到参数中,右值不会退化为左值std::threadthread是引入的线程库,用于创建线程,并发编程。std::to_string()to_string能够完......