0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Linux 0.01 の mktime の仕組みを読む:1900→1970年変換の理解

Posted at

目的

Linux 0.01 のカーネルには、CMOS などから取得した日付・時刻(1900 年始まり)を UNIX 時間(1970 年1月1日0時0分0秒からの経過秒数)に変換するコードがあります。

この記事では、その変換の仕組みを理解することを目的としています。

仕組み

Linux 0.01 の kernel/mktime.c にある kernel_mktime() は、CMOS などから取得した日付・時刻を UNIX 時間(1970年1月1日0時0分0秒からの経過秒数) に変換する関数です。

1. 年の計算

tm->tm_yearは1900 年からの年数を表すので、UNIX時間の基準である1970年からの差を計算します:

year = tm->tm_year - 70;

2. 閏年の補正

1 年は通常365日ですが、4年ごとに閏年(366 日)があります。

res = YEAR * year + DAY * ((year + 1) / 4);

ここで(year + 1)/4は閏年の日数を加算しています。

3. 月の加算

月ごとの累積日数をmonth[]配列で計算して加算します。
2月以降かつ閏年でない場合は1日分を引いて調整します:

if (tm->tm_mon > 1 && ((year + 2) % 4))
    res -= DAY;

4. 日・時・分・秒の加算

残りの tm_mday, tm_hour, tm_min, tm_sec を秒に換算して足し込みます:

res += DAY * (tm->tm_mday - 1);
res += HOUR * tm->tm_hour;
res += MINUTE * tm->tm_min;
res += tm->tm_sec;

結果として、与えられた日付・時刻を UNIX 時間に変換して返します。

動作

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

#define MINUTE 60
#define HOUR   (60 * MINUTE)
#define DAY    (24 * HOUR)
#define YEAR   (365 * DAY)

/* 
その月が始まるまでに経過した秒数の累積値
一旦毎年閏年と仮定する 
*/
static int month[12] = {
    0,                                                                 // 1月
    DAY * (31),                                                        // 2月
    DAY * (31 + 29),                                                   // 3月
    DAY * (31 + 29 + 31),                                              // 4月
    DAY * (31 + 29 + 31 + 30),                                         // 5月
    DAY * (31 + 29 + 31 + 30 + 31),                                    // 6月
    DAY * (31 + 29 + 31 + 30 + 31 + 30),                               // 7月
    DAY * (31 + 29 + 31 + 30 + 31 + 30 + 31),                          // 8月
    DAY * (31 + 29 + 31 + 30 + 31 + 30 + 31 + 31),                     // 9月
    DAY * (31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30),                // 10月
    DAY * (31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31),           // 11月
    DAY * (31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30)       // 12月
};

long kernel_mktime(struct tm *tm)
{
    long res;
    int year;

    year = tm->tm_year - 70;

    res = YEAR * year + DAY * ((year + 1) / 4);
    res += month[tm->tm_mon];

    if (tm->tm_mon > 1 && ((year + 2) % 4))
        res -= DAY;

    // 残りの tm_mday, tm_hour, tm_min, tm_sec を秒に換算して足し込みます
    res += DAY * (tm->tm_mday - 1);
    res += HOUR * tm->tm_hour;
    res += MINUTE * tm->tm_min;
    res += tm->tm_sec;

    return res;
}

int main(void)
{
    struct tm t;

    /* :2024-01-20 12:34:56 */
    t.tm_year = 2024 - 1900;  // struct tm は1900から始まる
    t.tm_mon  = 0;            // 0=1月,11=12月
    t.tm_mday = 20;
    t.tm_hour = 12;
    t.tm_min  = 34;
    t.tm_sec  = 56;

    long sec = kernel_mktime(&t);

    printf("Unix time = %ld\n", sec);

    return 0;
}

test@test-fujitsu:~/kaihatsu$ gcc test.c -o test
test@test-fujitsu:~/kaihatsu$ ./test 
Unix time = 1705754096
test@test-fujitsu:~/kaihatsu$ date +%s -d "2024-01-20 12:34:56"
1705725296
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?