2015/05/29

Recent entries from same category

  1. Zenn で Twitter bot 作成入門を書いた。
  2. プログラマーのための新しい情報共有コミュニティ Zenn で本を書いてみた。
  3. Windows ユーザは cmd.exe で生きるべき 2020年版
  4. Let's Encrypt を簡単操作できる CLI、Lego が MyDNS に対応した。
  5. golang でメモ専用コマンド「memo」作った。

まず2038年問題というのをご存じでしょうか

2038年問題 - Wikipedia

2038年問題(にせんさんじゅうはちねんもんだい)は、2038年1月19日3時14分7秒(UTC、以下同様)を過ぎると、コンピュータが誤動作する可能性があるとされる年問題。

http://ja.wikipedia.org/wiki/2038%E5%B9%B4%E5%95%8F%E9%A1%8C

C言語で epoch を扱う time_t が32bit OS上でオーバーフローし日本時間2038/01/19 12:14:07の次に1901/12/14 05:45:52が来てしまうという問題です。まぁそもそも2038年にもなって32bit OSを使っている側が悪いと言えばアレですが。

実際にどんな事が起きるかは、32bit OS上で以下を実行すれば分かる。

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

void
test(time_t tmr) {
  struct tm* t;
  char buf[2038];
  t = localtime(&tmr);
  strftime(buf, sizeof(buf), "%Y/%m/%d %H:%M:%S", t);
  puts(buf);
}

int
main(int argc, char* argv[]) {
  int i;
  time_t tmr = 2147483647;

  test(tmr); /* 2038/01/19 12:14:07 */
  tmr += 1;
  test(tmr); /* 1901/12/14 05:45:52 */
  return 0;
}

実行結果

$ ./a.out
2038/01/19 12:14:07
1901/12/14 05:45:52

でも待てよ...と思っていたら同じ事を考えてる人がいた。

Cのライブラリの2038年問題と、その対策方法(いや結局time_tは使わないってのもあり)

...という方法でラップすれば2106年迄使う事が出来ます。

http://www.tensyo.com/urame/date/Y2038.htm

これを使えば time_t がオーバーフローした後でも localtime が正しく動く!誰も困らない!2037年頃にオッサンが招集されて狭い部屋に閉じ込められるという事案も発生しない!という事で作ってみた。

mattn/localtime2038 - GitHub
https://github.com/mattn/localtime2038
#ifdef __i386__
#define _GNU_SOURCE
#include <time.h>
#include <dlfcn.h>

static struct tm* (*__localtime)(const time_t *timer);

__attribute__((constructor))
void
wrap_localtime() {
  __localtime = dlsym(RTLD_NEXT, "localtime");
}

struct tm *
localtime(const time_t *timer) {
  struct tm* t;
  unsigned long tmr = *timer;
  int yadd = 0;

  /* 1970 - 2101 */
  if (tmr >= (60uL*60uL*24uL)*47847uL) {
    yadd += 40;
    tmr -= (60uL*60uL*24uL)*14609uL;
  }
  /* 1970 - 2100 */
  if (tmr >= (60uL*60uL*24uL)*47482uL) {
    yadd += 6;
    tmr -= (60uL*60uL*24uL)*2191uL;
  }

  while (tmr >= ((60uL*60uL*24uL)*24837uL)) {
    yadd  +=28;
    tmr -= (60uL*60uL*24uL)*10227uL;
  }
  t = __localtime((time_t*)&tmr);
  t->tm_year += yadd;
  return t;
}
#endif

これを使って LD_PRELOAD を付けて実行すれば

$ LD_PRELOAD=./liblocaltime2038.so ./a.out
2038/01/19 12:14:07
2038/01/19 12:14:08

おぉぉぉぉ...これで2038年にオッサンが過労死する事もなくなりますね。良かったですね。

ジョークです。ちゃんと64bitに移行しましょう。

メールで指摘を頂き、2100年の処理が抜けていたのを修正(2016/10/26)
Posted at by