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

news2024/9/22 21:24:51

前言

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

刚好以前研究过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改为从公元当天开始到现在的天数。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2071704.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

docker系列12:Dockerfile实战

传送门 docker系列1&#xff1a;docker安装 docker系列2&#xff1a;阿里云镜像加速器 docker系列3&#xff1a;docker镜像基本命令 docker系列4&#xff1a;docker容器基本命令 docker系列5&#xff1a;docker安装nginx docker系列6&#xff1a;docker安装redis docker系…

红黑树、B+Tree、B—Tree

红黑树 B-Tree 这三个通常都是把内存全部加载到内存里&#xff0c;然后再内存中进行处理的&#xff0c;数据量通常不会很大。 内存一般容量都在GB级别&#xff0c;比如说现在常见的4G、8G或者16G。 如果要处理的数据规模非常大&#xff0c;大到内存根本存不下的时候。这个时候…

基于微信小程序靓丽内蒙古APP(源码+定制+辅导)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

验证码功能的思路和做法

验证码登录的思路和流程 步骤 1.导入依赖 <dependency><groupId>com.github.axet</groupId><artifactId>kaptcha</artifactId><version>0.0.9</version> </dependency> 2.写一个验证码的配置类 package com.lzy.config;im…

IM即时通讯软件,企业即时通讯系统就选WorkPlus

在现代企业中&#xff0c;高效的沟通和协作是推动业务发展的关键。随着科技的不断进步&#xff0c;团队成员和企业之间的沟通已经超越了传统的邮件和电话方式&#xff0c;转向了更实时、更便捷的方式&#xff0c;即即时通讯软件。在众多即时通讯软件中&#xff0c;WorkPlus作为…

滑动窗口解决子串问题

问题解析&#xff1a; 以这道题为例子&#xff1a;. - 力扣&#xff08;LeetCode&#xff09;找长度最小的子数组&#xff0c;子数组和必须大于条件中的target 暴力解法&#xff1a;左右指针列举出每一种子数组的可能&#xff0c;每种可能去求子数组的和&#xff0c;找到最小的…

17 深入理解 C 语言 main 函数:返回值意义、命令行参数接收、跨环境差异及CMD乱码解决

目录 1 main 主函数 2 main 函数的返回值 2.1 返回值的意义 2.2 默认返回值 2.3 返回值类型 3 main 函数的参数 3.1 参数内容 3.2 案例&#xff1a;循环遍历主函数的参数 3.3 不传递参数 3.4 powershell 环境下传参 3.5 cmd 环境下传参 3.6 解决 cmd 输出乱码问题 …

pytorch深度学习基础 7 (简单的线性拟合+检验模型在验证集上的效果)

我们之前做的目的都是评估训练的损失&#xff0c;训练的损失Loss告诉我们&#xff0c;我们的模型是否能够完全拟合训练集&#xff0c;也就是说我们的模型是否有足够的能力处理数据中的相关信息。但是我们之前都是评价训练的好坏&#xff0c;并没有引入验证集。接下来我们就需要…

Java基础——自学习使用(多态)

一、多态的定义 父类的引用指向子类的对象。 B继承A&#xff0c;A abnew B();——父类引用指向子类的对象。 二、创建对象了解多态的内部结构 &#xff08;1&#xff09;父类即A类对象的内存结构图 &#xff08;2&#xff09;子类即B类对象的内存结构图 由于B中重写了父类A中…

EazyDraw for Mac 矢量图绘制设计软件

Mac分享吧 文章目录 效果一、下载软件二、开始安装1、双击运行软件&#xff0c;将其从左侧拖入右侧文件夹中&#xff0c;等待安装完毕2、应用程序显示软件图标&#xff0c;表示安装成功 三、运行测试安装完成&#xff01;&#xff01;&#xff01; 效果 一、下载软件 下载软件…

SSRF和CSRF实战复现

文章目录 SSRFWeb-Hacking-Lab-master1、Centos未授权访问2、Ubuntu未授权访问3、Ubuntu传入公钥访问4、ssrf_redis_lab_pickle_redis_lab CSRF:windphp SSRF SSRF(Server-Side Request Forgery:服务器端请求伪造) 是一种由攻击者构造形成由服务端发起请求的一个安全漏洞。 f…

第三课《排序》

前言 排序是将一组数据&#xff0c;按照指定的顺序或要求来进行排列的过程。是数据结构相关课程和内容较为重要和核心的内容之一&#xff0c;常常作为考试题和面试题目来考察学生和面试者&#xff0c;因此熟练掌握经典的排序算法原理和代码实现是非常重要的 本文介绍了几大较为…

AJAX(5)——Promise

Promise Promise对象用于表示一个异步操作的最终完成或失败及其结果值 语法&#xff1a; //创建Promise对象const p new Promise((resolve, reject) > {//执行异步代码setTimeout(() > {// resolve(成功结果)reject(new Error(失败结果))}, 2000)})//获取结果p.then(r…

坚鹏讲人才第13期:个人数字化转型——个人与时代的共赢之选

坚鹏讲人才第13期&#xff1a;个人数字化转型——个人与时代的共赢之选 在这个日新月异的时代&#xff0c;数字化转型已经成为当今时代的必然趋势&#xff0c;它不仅改变了我们的生活方式&#xff0c;也正在改变着各行各业的运营模式。数字化时代&#xff0c;不仅需要数字化企…

网络udp及ipc内存共享

大字符串找小字符串 调试 1. 信号处理函数注册&#xff1a;•一旦使用 signal 函数注册了信号处理函数&#xff0c;该函数就会一直有效&#xff0c;直到程序结束或者显式地取消注册。2. 注册多次的影响&#xff1a;•如果多次注册同一信号的处理函数&#xff0c;最后一次注册的…

快9月了刚结束基础,武忠祥强化vs张宇18讲应该如何选择?

快9月了&#xff0c;最近有一部分同学刚结束基础&#xff0c;在后台提问&#xff1a;强化到底该学武忠祥还是张宇18讲&#xff1f;其实这个问题&#xff0c;如果你是6月份开始强化&#xff0c;很好回答&#xff0c;但是现在已经快9月份了&#xff0c;很多同学都开始做真题了&am…

代码随想录 刷题记录-16 贪心算法(1)贪心理论基础及习题

一、理论基础 什么是贪心 贪心的本质是选择每一阶段的局部最优&#xff0c;从而达到全局最优。 贪心的套路&#xff08;什么时候用贪心&#xff09; 贪心算法并没有固定的套路。 所以唯一的难点就是如何通过局部最优&#xff0c;推出整体最优。 靠自己手动模拟&#xff0c…

深度学习 回归问题

1. 梯度下降算法 深度学习中, 梯度下降算法是是一种很重要的算法. 梯度下降算法与求极值的方法非常类似, 其核心思想是求解 x ′ x x′, 使得 x ′ x x′ 在取 x ⋆ x^{\star} x⋆ 时, 可以使得 l o s s 函数 loss函数 loss函数 的值最小. 其中, 在求解 x ′ x x′ 的过…

罗德与施瓦茨RS、UPV 音频分析仪 250KHZ 双通道分析仪UPL

罗德与施瓦茨 UPV 音频分析仪的规格包括&#xff1a; 模拟 双通道分析仪&#xff1a;带宽高达 250 kHz 生成正弦波信号&#xff1a;单通道最高 185 kHz&#xff08;需要 B1&#xff09;和双通道最高 80 kHz FFT本底噪声&#xff1a;< -140dB 固有频率响应&#xff08;20 …

链动 2+1 模式小程序 AI 智能名片商城源码培训邀约策略研究

摘要&#xff1a;本文深入剖析链动 21 模式小程序 AI 智能名片商城源码的培训邀约策略&#xff0c;从该源码的价值出发&#xff0c;阐述邀约的重要性&#xff0c;并详细介绍具体的邀约策略&#xff0c;旨在为相关培训活动提供切实可行的指导&#xff0c;提高邀约成功率&#xf…