在CentOS上的mktime()中的错误?

我一直在玩mktime,我注意到一种奇怪的,不一致的行为.

我提供的日期不是在DST(夏令时)期间,但是当tm_isdst设置为1时,mktime通常做的是将tm_isdst更改为0,并相应地调整时间,移动1小时.

然而,在大约1928年至1933年的时间段内(找不到不同的范围),行为是不同的. tm_isdst字段设置为0,但时间不会更改.这在执行时间计算等时会产生奇怪的现象.

我有一个很小的测试程序,对于给定的输入日期打印:原始struct tm,在调用mktime之后的struct tm,mktime结果和struct tm,它是在mktime结果上调用localtime的结果(应代表同一时刻)时间作为原始的).

输出是:

2013-01-01 12:00:00 (off=0,dst=1) -> 2013-01-01 11:00:00 (off=-28800,dst=0) ->   1357066800 -> 2013-01-01 11:00:00 (off=-28800,dst=0)
1927-01-01 12:00:00 (off=0,dst=1) -> 1927-01-01 11:00:00 (off=-28800,dst=0) ->  -1356930000 -> 1927-01-01 11:00:00 (off=-28800,dst=0)
1929-01-01 12:00:00 (off=0,dst=1) -> 1929-01-01 12:00:00 (off=-28800,dst=0) ->  -1293768000 -> 1929-01-01 12:00:00 (off=-28800,dst=0)
1932-01-01 12:00:00 (off=0,dst=1) -> 1932-01-01 12:00:00 (off=-28800,dst=0) ->  -1199160000 -> 1932-01-01 12:00:00 (off=-28800,dst=0)
1934-01-01 12:00:00 (off=0,dst=1) -> 1934-01-01 11:00:00 (off=-28800,dst=0) ->  -1136005200 -> 1934-01-01 11:00:00 (off=-28800,dst=0)

看到2013年,1927年,1934年,小时改变,dst设置为0.但在1929年和1932年,小时没有改变,但dst是.

有点奇怪的是,在tzinfo中没有关于那个时间范围的事情 – 洛杉矶的zdump显示最接近的变化发生在1919年和1942年.

这是在CentOS上,内核2.6.32-358.11.1.el6.x86_64,glibc-2.12-1.107.el6.x86_64.

进一步调查似乎在MacOSX上按预期(一致)工作.所以这看起来像mktime()中的一个bug给我,但也许我错过了一些东西.

测试测试程序如下,也可用here

#include <time.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

char* printtm(struct tm tm)
{
  static char buf[100];
  sprintf(buf,"%04d-%02d-%02d %02d:%02d:%02d (off=%ld,dst=%d)",tm.tm_year + 1900,tm.tm_mon + 1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec,tm.tm_gmtoff,tm.tm_isdst);
  return buf;
}

void test(int y,int m,int d,int hh,int mm,int ss,int isdst)
{
  // Prepare tm structs
  struct tm tm,tm2;
  memset(&tm,sizeof(tm));
  memset(&tm2,sizeof(tm));
  tm.tm_year = y - 1900;
  tm.tm_mon = m - 1;
  tm.tm_mday = d;
  tm.tm_hour = hh;
  tm.tm_min = mm;
  tm.tm_sec = ss;
  tm.tm_isdst = isdst;
  // Convert tm -> t -> tm and print
  printf("%s -> ",printtm(tm));
  time_t t = mktime(&tm);
  printf("%s -> ",printtm(tm));
  printf("%12ld -> ",t);
  localtime_r(&t,&tm2);
  printf("%s\n",printtm(tm));
}

int main()
{
  setenv("TZ",":America/Los_Angeles",1);
  tzset();

  test(2013,07,01,12,1);
  test(2013,1);
  test(1927,1);
  test(1929,1);
  test(1932,1);
  test(1934,1);
  return 0;
}
source to glibc’s mktime你可以找到这个:
/* tm.tm_isdst has the wrong value.  Look for a neighboring
   time with the right value,and use its UTC offset.

   Heuristic: probe the adjacent timestamps in both directions,looking for the desired isdst.  This should work for all real
   time zone histories in the tz database.  */

/* Distance between probes when looking for a DST boundary.  In
   tzdata2003a,the shortest period of DST is 601200 seconds
   (e.g.,America/Recife starting 2000-10-08 01:00),and the
   shortest period of non-DST surrounded by DST is 694800
   seconds (Africa/Tunis starting 1943-04-17 01:00).  Use the
   minimum of these two values,so we don't miss these short
   periods when probing.  */
int stride = 601200;

/* The longest period of DST in tzdata2003a is 536454000 seconds
   (e.g.,America/Jujuy starting 1946-10-01 01:00).  The longest
   period of non-DST is much longer,but it makes no real sense
   to search for more than a year of non-DST,so use the DST
   max.  */
int duration_max = 536454000;

/* Search in both directions,so the maximum distance is half
   the duration; add the stride to avoid off-by-1 problems.  */
int delta_bound = duration_max / 2 + stride;

如果你做数学运算,你会发现delta_bound是268828200秒,相当于大约8年半.这几乎完全是zdump日期(1919年和1942年)与神秘转换时期(1928年和1933年)之间的差异.每个人都在25小时30分钟之内休息.我没有更深入地找到这个神奇数字的原因.

在不理解整个事情的情况下,我认为评论基本上意味着当你处于从1919年到1942年的那个长期非DST延伸的中间部分时,该算法试图在你提供的那个附近找到一个有效的dst = 1时间戳.错误的dst = 1,并在找到之前放弃,因为它没有任何意义.在中间部分之外的年份也可能没有多大意义,但作为与Jujuy相关的调整参数的副作用表现不同.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


linux下开机自启: 在/etc/init.d目录下新建文件elasticsearch 并敲入shell脚本: 注意, 前两行必须填写,且要注释掉。 第一行为shell前行代码,目的告诉系统使用shell。 第二行分别代表运行级别、启动优先权、关闭优先权,且后面添加开机服务会用到。 shell脚本
1、因为在centos7中/etc/rc.d/rc.local的权限被降低了,所以需要赋予其可执行权 chmod +x /etc/rc.d/rc.local 2、赋予脚本可执行权限假设/usr/local/script/autostart.sh是你的脚本路径,给予执行权限 chmod +x /usr
最简单的查看方法可以使用ls -ll、ls-lh命令进行查看,当使用ls -ll,会显示成字节大小,而ls- lh会以KB、MB等为单位进行显示,这样比较直观一些。 通过命令du -h –max-depth=1 *,可以查看当前目录下各文件、文件夹的大小,这个比较实用。 查询当前目录总大小可以使用d
ASP.NET Core应用程序发布linux在shell中运行是正常的。可一但shell关闭网站也就关闭了,所以要配置守护进程, 用的是Supervisor,本文主要记录配置的过程和过程遇到的问题 安装Supervisor&#160;1 yum install python-setuptools
设置时区(CentOS 7) 先执行命令timedatectl status|grep &#39;Time zone&#39;查看当前时区,如果不是时区(Asia/Shanghai),则需要先设置为中国时区,否则时区不同会存在时差。 #已经是Asia/Shanghai,则无需设置 [root@xia
vim&#160;/etc/sysconfig/network-scripts/ifcfg-eth0 BOOTPROTO=&quot;static&quot; ONBOOT=yes IPADDR=192.168.8.106 NETMASK=255.255.252.0 GATEWAY=192.168.
一、安装gcc依赖 由于 redis 是用 C 语言开发,安装之前必先确认是否安装 gcc 环境(gcc -v),如果没有安装,执行以下命令进行安装 [root@localhost local]# yum install -y gcc 二、下载并解压安装包 [root@localhost local
第一步 On CentOS/RHEL 6.*: $ sudo rpm -Uvh http://li.nux.ro/download/nux/dextop/el6/x86_64/nux-dextop-release-0-2.el6.nux.noarch.rpm On CentOS/RHEL 7: $
/// &lt;summary&gt; /// 取小写文件名后缀 /// &lt;/summary&gt; /// &lt;param name=&quot;name&quot;&gt;文件名&lt;/param&gt; /// &lt;returns&gt;返回小写后缀,不带“.”&lt;/ret
which nohup .bash_profile中并source加载 如果没有就安装吧 yum provides */nohup nohup npm run start &amp; nohup ./kibana &amp;
1.1 MySQL安装 1.1.1 下载wget命令 yum -y install wget 1.1.2 在线下载mysql安装包 wget https://dev.mysql.com/get/mysql57-community-release-el7-8.noarch.rpm 1.1.3 安装My
重启 reboot shutdown -r now init 6 关闭 init 0 shutdown -h now shutdown -h 20:25 #8点25关机查看内存 free CPU利用率 top 日期 date 设置时间 date 033017002015 #月日时间年 日历 cal
1、firewalld的基本使用 启动: systemctl start firewalld 关闭: systemctl stop firewalld 查看状态: systemctl status firewalld 开机禁用 : systemctl disable firewalld 开机启用 :
1 下载并安装MySQL官方的&#160;Yum Repository wget -i -c http://dev.mysql.com/get/mysql57-community-release-el7-10.noarch.rpm 使用上面的命令就直接下载了安装用的Yum Repository,大概
CentOS6.x CentOS6中转用Upstrat代替以前的init.d/rcX.d的线性启动方式。 一、相关命令 通过initctl help可以查看相关命令 [root@localhost ~]# initctl help Job commands: start Start job. sto
1、使用命令:df -lk 找到已满磁盘 2、使用命令:du --max-depth=1 -h 查找大文件,删除
ifconfig:查看网卡信息 网卡配置文件位置: /etc/sysconfig/network-scripts/文件夹 nmtui:配置网卡 netstat -tlunp:查看端口信息 端口信息存储位置: /etc/services文件 route:查看路由信息 wget:下载网路文件,例如 wg
ps -ef:查看所有进程,&#160;ps -ef |grap firewalld 查看与firewalld相关的进程 which :查看进程:which firewalld kill 进程id:杀掉进程 kill 640,强制杀:kill -9 640 man:查看帮助,例如 man ps 查看
useradd:添加用户 useradd abc,默认添加一个abc组 vipw:查看系统中用户 groupadd:添加组groupadd ccna vigr:查看系统中的组 gpasswd:将用户abc添加到ccna组 gpasswd -a abc ccna groups abc:查看用户abc属