首先我们要明白,“时间”和“时区”是两个东西。

  时间是指从某个时间点开始到另一个时间点经过的“长度”,是“纵向”距离,一般在linux系统内有两个主要的时间,一是始于1970年(unix元年)至今的距离,二是系统启动后至今的距离。前者一般是由不断电的硬件维护(RTC)或者其他专门服务器授时(NTP),可修改;后者只能前进无法后退,不能修改

  时区则是指世界范围内(国家/洲际之间)昼夜交替不同而导致的每天的相对时间不同造成的区域划分,是“横向”的“偏移”。比如北京会比纽约早12小时看到日出。时区一般以1小时划分,全球就有24个时区

  linux操作系统内核只有“时间”概念,没有“时区”概念。对于linux操作系统本身来说,启动后时间为0,表示当前就是1970年。我们通过读取RTC或NTP时间后,会通过系统调用更新这个时间,然后cpu会自动对时间进行累加。后面其他进程就可以获取正确的时间了。

  应用进程从系统内核读取到自1970年来经历过的时间后就会有一个问题,就是同样经过了一百年整,有些地区现处于旭日东升,另一些地区确黑夜蔽日。因此就要靠时区对这个时间进行一个“合理的描述”,比如在北京,现在就应该处于正午,日头正浓,而同样的时间点在纽约,大家就进入梦乡了。

  这样大家更加理解了时区概念,它并不存在,只是为了让全球在24小时的尺度内(由于地球自转)对于同样一个时间点发生的日照情况比较合理。

  

  接下来我们着重说说应用进程如何使用时间和时区。

  一般我们会将这个过程分为3步,第一步是从系统获取时间(1970年来秒数),第二步是从文件系统读取时区配置(对于现代操作系统,还可能是GPS定位或通过出口网络进行ISP运营商定位等)以获取偏移量(也即一个秒数,可能为负),第三步则是将两者相加并格式化输出。

  (这里还体现了我们程序编写中的一个数据和显示分离的思维,数据还是那个数据(1970年至今秒数),但展示是因人而异的)

  

  

  上面代码中的tzset十分重要,它一般会从环境变量或/etc/localtime文件中读取时区配置并设置到进程全局变量。然后localtime,strftime等函数则会参考全局变量来计算当前地区应该显示的时间。

  (tzset函数可以只调用一次,除非确定了配置更改,也可以不调用,因为strftime等函数内部有判定,如果进程对tzset函数调用次数为0,则会主动调用一次)

  我们具体讲讲linux下时间日期的格式化输出和其背后的运行逻辑。

  首先我们要认识一个前提:就是tzset为什么必须调用?

  其实道理很简单,因为c库没有自动运行的权利。它不会在你加载*c.so时就自动调用某个函数,甚至遑论读取文件系统的文件,这个操作具有一定的侵入性,也会造成一些不必要的浪费(万一你的程序不需要格式化时间日期呢)。

  我们根据一个实例来跟踪一下。

  我们一般在系统内查看当前时间是使用date命令,以下是一般用法。

  

  我们跟踪一下date命令的代码(以busybox为例)

  

  

  

  我们可以看到,其实重要的就是time/localtime/strftime几个函数。

  我们接着跟踪一下。

  

  

  可以发现,localtime调用了tzset_internal。而实际上,tzset也是调用的tzset_internal。

  

  tzset后,时区的偏移量就有了。后续就可以compute了

   

  综上所述,其实所谓的时区并不复杂,就是一个秒数偏移量,用于不同地域的格式化输出。

  我们再最后稍微深入分析一下tzset的内部实现。

  

  可以看到,tzset首先从环境变量/编译时宏定义/运行时系统文件等地方读取配置进行分析。

   

  当然,最终更新到的全局变量在外部也可访问

  

  从tzset代码也可以看出,一般我们配置时区还是以配置文件为主。配置文件一般在/etc/localtime。

  

  当然,这个配置文件是二进制的,并不容易编辑。

  

  如果只需要单独进程具有正确的时区,可以使用setenv("TZ", "CST-8", 1);tzset();来更新。

  setenv中还有一个坑,就是我们为什么设置的是CST-8(CST是什么意思我就不赘述了,大家百度一下即可,其实也没什么特别的意思,写ABC没区别,它只是你为当前地区取得别名),不是说中国在东8区吗,东为正,应该是+8啊!

  这里是一个思维误区,就是我们设置的是UTC时区,即是“计算出UTC零点的算法”。所以CST=UTC+(+8小时) 或 UTC=CST-(+8小时)。

  最后修改时间 2024-08-04 14:13:54

  ps:虽然linux设置和获取系统时间现在的函数settimeofday和gettimeofday可以传递时区,但我还是建议不使用这部分功能,还是只用获取的unix秒数部分以及/etc/localtime配置文件。

  毕竟官方已经建议settimeofday中的tz传递NULL

  

   

  最后修改时间 2024-08-05 11:47:06

  linux常用的时间日期函数如下:

  time :用于从操作系统获取Unix时间戳

  settimeofday : 设置unix时间戳到操作系统(stime已弃用)

  mktime : 从本地时间(加了偏移量)转为unix时间戳

  localtime : 从unix时间戳转为本地时间

  如果我们的进程需要负责设置系统时间,一般是两种办法,一是从服务器获取unix时间戳直接通过settimeofday设置;二是使用时区和本地时间,先将时区通过setenv或/etc/localtime方式保存,然后tszet,而后通过mktime将本地时间转为unix时间戳,再通过settimeofday设置。

  mktime/localtime都会在内部使用时区配置(tzset)。

  以下是一个设置系统时间的参考代码:

#include <unistd.h>
#include <sys/time.h> #include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <sstream>
#include <iomanip>
#include <iostream>
#include <chrono> int main() { setenv("TZ", "CST-8", 1);
tzset(); std::tm when = {0};
std::stringstream ss_when;
ss_when <<"2024-08-09 12:00:23";
ss_when >> std::get_time(&when, "%Y-%m-%d %H:%M:%S");
std::time_t t = mktime(&when);
timeval tv = {0};
tv.tv_sec = t;
::settimeofday(&tv, nullptr);
std::system("hwclock -wu"); return 0;
}

  (对于c++来说,从字符串转为tm要方便很多)

  注意:tm和time_t转换就涉及到时区

  最后修改时间 2024-08-09 22:23:48

linux下时间时区详解的更多相关文章

  1. Linux下ps命令详解 Linux下ps命令的详细使用方法

    http://www.jb51.net/LINUXjishu/56578.html Linux下的ps命令比较常用 Linux下ps命令详解Linux上进程有5种状态:1. 运行(正在运行或在运行队列 ...

  2. Linux下rar命令详解

    Linux下rar命令详解 用法: rar <命令> -<选项1> ….-<选项N> < 操作文档> <文件…> <@文件列表…> ...

  3. linux下tar命令详解

     linux下tar命令详解    tar是Linux环境下最常用的备份工具之一.tar(tap archive)原意为操作磁带文件,但基于Linux的文件操作机制,同样也可适用于普通的磁盘文件.ta ...

  4. Linux下top命令详解

    Linux下top命令详解 top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器.top是一个动态显示过程,即可以通过用户按键来不断刷 ...

  5. Linux下桥接模式详解一

    注册博客园已经好长时间,一直以来也没有在上面写过文章,都是随意的记录在了未知笔记上,今天开始本着分享和学习的精神想把之前总结的笔记逐步分享到博客园,和大家一起学习,一起进步吧! 2016-09-20  ...

  6. linux下IPTABLES配置详解 (防火墙命令)

    linux下IPTABLES配置详解 -A RH-Firewall-1-INPUT -p tcp -m state --state NEW -m tcp --dport 24000 -j ACCEPT ...

  7. Linux下chkconfig命令详解(转)

    Linux下chkconfig命令详解 chkconfig命令主要用来更新(启动或停止)和查询系统服务的运行级信息.谨记chkconfig不是立即自动禁止或激活一个服务,它只是简单的改变了符号连接. ...

  8. Linux知识积累(4) Linux下chkconfig命令详解

    Linux下chkconfig命令详解 chkconfig命令主要用来更新(启动或停止)和查询系统服务的运行级信息.谨记chkconfig不是立即自动禁止或激活一个服务,它只是简单的改变了符号连接. ...

  9. 转载的 Linux下chkconfig命令详解

    Linux下chkconfig命令详解 chkconfig命令主要用来更新(启动或停止)和查询系统服务的运行级信息.谨记chkconfig不是立即自动禁止或激活一个服务,它只是简单的改变了符号连接. ...

  10. 转 linux下cat命令详解

    linux下cat命令详解 http://www.cnblogs.com/perfy/archive/2012/07/23/2605550.html 简略版: cat主要有三大功能:1.一次显示整个文 ...

随机推荐

  1. python调用第三方java包实例

    先看结果: 对于python与java的互调,我一开始是用的py4j,但是后来发现在使用方法的时候,不知道如何在python中导入jar包,然后网上的资料也比较少.后来想不出来办法,又看到有Jpype ...

  2. Java反射初探123456789

    牛逼的框架都反射,不要问我为什么,因为我也不知道 可能是因为生成了class文件没法实例化,所以只能反射创建对象,但是在spring中,ioc就是反射实现的控制反转 看Spring4.x企业级开发实战 ...

  3. Vue使用v-for 循环生成tabs 标签页

    实现最终效果: template代码: activeName:默认第一个显示的tab <el-tabs v-model="activeName" type="car ...

  4. SQLserver 数据库自定义函数

    起源 最近项目开发上使用的SQLserver数据库是2008版本,由于08版本的数据是没有字符串合并(STRING_AGG)这个函数(2017版本及以上支持)的,只有用stuff +for xml p ...

  5. 2024-08-28:用go语言,给定一个从1开始、长度为n的整数数组nums,定义一个函数greaterCount(arr, val)可以返回数组arr中大于val的元素数量。 按照以下规则进行n次

    2024-08-28:用go语言,给定一个从1开始.长度为n的整数数组nums,定义一个函数greaterCount(arr, val)可以返回数组arr中大于val的元素数量. 按照以下规则进行n次 ...

  6. TrueType 和 OpenType 的关系

    OpenType 和 TrueType 都是字体文件格式,它们用于在数字设备中存储和渲染文本.虽然这两种格式都广泛使用,但它们在设计和功能上有一些重要区别. TrueType 是由苹果公司和微软公司在 ...

  7. Linux 扩展磁盘分区

    之前安装 Ubuntu 虚拟机时使用的都是默认配置,虚拟机硬盘分配了 60 GB.后来想要扩容,特此记录一下扩容过程. 在操作前请做好备份 首先在 VMware 中修改虚拟硬盘大小. 然后进入虚拟机, ...

  8. 使用 nuxi upgrade 升级现有nuxt项目版本

    title: 使用 nuxi upgrade 升级现有nuxt项目版本 date: 2024/9/10 updated: 2024/9/10 author: cmdragon excerpt: 摘要: ...

  9. 【合合TextIn】智能文档处理系列—电子文档解析技术全格式解析

    一.引言 在当今的数字化时代,电子文档已成为信息存储和交流的基石.从简单的文本文件到复杂的演示文档,各种格式的电子文档承载着丰富的知识与信息,支撑着教育.科研.商业和日常生活的各个方面.随着信息量的爆 ...

  10. CSS – Clip Path

    前言 我是在搞 1 side box-shadow 发现这个功能的. 平常很少做特效, 所以对这个好功能缺乏认识. 这篇大概记入一下先, 以后有认真用再补上细节. 参考 Youtube – Aweso ...