#include <time.h>
char *asctime(const struct tm *tm);
char *asctime_r(const struct tm *restrict tm,
char buf[restrict 26]);
char *ctime(const time_t *timep);
char *ctime_r(const time_t *restrict timep,
char buf[restrict 26]);
struct tm *gmtime(const time_t *timep);
struct tm *gmtime_r(const time_t *restrict timep,
struct tm *restrict result);
struct tm *localtime(const time_t *timep);
struct tm *localtime_r(const time_t *restrict timep,
struct tm *restrict result);
time_t mktime(struct tm *tm);
glibc 向けの機能検査マクロの要件 (feature_test_macros(7) 参照):
asctime_r(), ctime_r(), gmtime_r(), localtime_r():
_POSIX_C_SOURCE
|| /* glibc <= 2.19: */ _BSD_SOURCE || _SVID_SOURCE
関数 asctime() と mktime() は 年・月・日などに分離された要素別の時刻を引数とします。
要素別の時刻は tm(3type)で定義されている tm 構造体に保持されます。
ctime(t) 関数の呼び出しは、 asctime(localtime(t)) と等価です。カレンダー時刻 t を
"Wed Jun 30 21:49:08 1993\n"
という形式のヌル終端された文字列へ変換します。曜日の略称は "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" です。月の略称は "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" です。戻り値は、静的 (static) に割り当てられた文字列へのポインターで、この文字列は、日付・時刻関数のいずれかが呼び出されると上書きされることがあります。またこの関数は tzset(3) として呼び出された時、大域変数 tzname, timezone, daylight に現在のタイムゾーンの情報を設定します。リエントラント版である ctime_r() も同様ですが、文字列はユーザーが用意したバッファに格納されます。バッファのサイズは 少なくとも 26 バイト以上必要です。この関数は tzname, timezone, and daylight を設定する必要はありません。
関数 gmtime() は、カレンダー時刻 timep を 協定世界時 (UTC) での要素別の時刻へ変換します。年が整数型に収まらない場合、NULL を返します。戻り値は静的に確保された構造体を指しており、この後で日付や時刻に関する関数のいずれかが呼び出されると上書きされる可能性があります。gmtime_r() も同様ですが、データはユーザーが用意した構造体に格納されます。
関数 localtime() は、カレンダー時刻 timep を ユーザーが指定したタイムゾーンでの時刻要素別の表現へ変換します。この関数は tzset(3) を呼び出したかのように振舞い、大域変数 tzname、timezone と daylight に現在のタイムゾーンの情報を設定します。返り値は静的に確保された構造体を指しており、この後で 日付や時刻に関する関数のいずれかが呼び出されると 上書きされる可能性があります。localtime_r() も同様ですが、データはユーザーが用意した構造体に格納されます。この関数は tzname, timezone, and daylight を設定する必要はありません。
関数 asctime() は、要素別の時刻 tm を ctime() と同じ形式のヌル終端された文字列へ変換します。戻り値は静的に割り当てられた文字列へのポインタです。この文字列は、日付・時刻関数のいずれかが呼び出されると上書きされることがあります。リエントラント版である asctime_r() も同様ですが、文字列はユーザーが用意したバッファに格納される。バッファのサイズは 少なくとも 26 バイト以上必要です。
関数 mktime() は、(ローカルタイムで記述されている) 要素別の時刻を カレンダー時刻へ変換します。この際、呼び出し元がフィールド tm_wday と tm_yday で指定した値は無視します。mktime() は、フィールド tm_isdst で指定された値により、 tm 構造体で渡された時刻で夏時間 (daylight saving time; DST) が有効になっているかを知ります。正の値は夏時間が有効です。値が 0 の場合は、DST が無効です。負の値であれば、 mktime() は (タイムゾーン情報とシステムのデータベースを使って) 指定された時刻で夏時間が有効かどうかを判断する必要があることを意味します。
mktime() は tm 構造体の各フィールドを以下のように修正します。tm_wday と tm_yday には他のフィールドの内容から求めた値を設定します。構造体の要素が有効な範囲にない場合、正規化されます(たとえば、10 月 40 日は 11 月 9 日に変更されます)。tm_isdst には (最初の値にかかわらず) 正の値か 0 が設定されます。正の値 または 0 は指定された時間で夏時間が有効/無効であることを示します。 関数 mktime() を呼び出すと、 大域変数 tzname、timezone と daylight が現在のタイムゾーンに設定されます。
要素別の時刻をカレンダー時刻 (紀元 (Epoch) からの秒数) で表現できない場合、 mktime() は (time_t) (-1) を返し、要素別の時刻の構造体メンバーを変更しません。
成功すると、 gmtime_r() と localtime_r() は result によってポイントされる構造体のアドレスを返します。
成功すると、asctime() と ctime() は、文字列へのポインタを返します。
成功すると、asctime_r() and ctime_r() は、buf によってポイントされる文字列へのポインタを返します。
成功すると、mktime() は、カレンダー時間 (エポックからの秒数) を タイプ time_tで表現する値で返します。
エラーの場合、mktime() は値として (time_t)\ -1 を返し、tm->tm_wday メンバは変更されません。その他の関数は、エラーの場合に NULL を返します。エラーの場合、errno はエラーを示すように設定されます。
| インターフェース | 属性 | 値 |
| asctime() | Thread safety |
MT-Unsafe race:asctime locale
|
| asctime_r() | Thread safety |
MT-Safe locale
|
| ctime() | Thread safety |
MT-Unsafe race:tmbuf
race:asctime env locale
|
| ctime_r(), gmtime_r(), localtime_r(), mktime() | Thread safety |
MT-Safe env locale
|
| gmtime(), localtime() | Thread safety |
MT-Unsafe race:tmbuf env locale
|
glibc を含む多くの実装では、 tm_mday に 0 を指定すると前月の最終日を意味していると解釈されます。
POSIX.1 によると、 localtime() はあたかも tzset(3) が呼ばれたかのように振舞うことが要求されますが、localtime_r() にはこの要件はありません。移植性が必要なコードでは、 localtime_r() の前に tzset(3) を呼び出しておくべきです。
POSIX.1-2001 では、「関数 asctime(), ctime(), gmtime(), localtime() は、要素別の時刻の構造体か char 型の配列かのどちらかの静的オブジェクトを返すべきである。これらのオブジェクト型のいずれかへのポインタを返す関数を実行すると、これらのいずれかへの以前の呼び出しで返された値が指す同じ型のオブジェクト内の情報が上書きされる可能性がある。」となっています。このことは glibc の実装で起こりえます。
mktime() における非負の tm_isdst の扱いは十分に規定されておらず、指定された時間に対して正しくない値を渡すと、不特定の結果が生成されます。mktime() は、DST がいつ有効であるかを認識する数少ない関数の1つであるため、正しい値を提供することは困難な場合があります。これに対する1つの回避策は、mktime() を2回呼び出すことです。1回は tm_isdst を 0 に設定し、もう1回は tm_isdst を正の値に設定して、それを変更した呼び出しからの結果を破棄します。どちらの呼び出しも tm_isdst を変更しない場合、指定された時間はおそらく DST が開始または終了するフォールバック期間中に発生し、両方の結果は有効ですが、2つの異なる時間を表します。両方の呼び出しがそれを変更する場合、それはフォールフォワード遷移、または指定された時間が存在しないその他の理由を示している可能性があります。
タイムゾーンと夏時間の指定は地方政府に任されており、頻繁に変更され、mktime が結果を文書化する機能を超えた不連続性が含まれる場合があります。たとえば、タイムゾーン定義の変更により、対応する DST が変更されずに時計の時刻が繰り返されたりスキップされたりする可能性があります。
次のシェルセッションは、プログラムの実行例を示しています。
$ TZ=UTC ./a.out 1969 12 31 23 59 59 0; -1 $ $ export TZ=Europe/Madrid; $ $ ./a.out 2147483647 2147483647 00 00 00 00 -1; a.out: mktime: Value too large for defined data type $ $ ./a.out 2024 08 23 00 17 53 -1; 1724365073 $ ./a.out 2024 08 23 00 17 53 0; a.out: my_mktime: Invalid argument 1724368673 $ ./a.out 2024 08 23 00 17 53 1; 1724365073 $ $ ./a.out 2024 02 23 00 17 53 -1; 1708643873 $ ./a.out 2024 02 23 00 17 53 0; 1708643873 $ ./a.out 2024 02 23 00 17 53 1; a.out: my_mktime: Invalid argument 1708640273 $ $ ./a.out 2023 03 26 02 17 53 -1; a.out: my_mktime: Invalid argument 1679793473 $ $ ./a.out 2023 10 29 02 17 53 -1; a.out: my_mktime: Name not unique on network 1698542273 $ ./a.out 2023 10 29 02 17 53 0; 1698542273 $ ./a.out 2023 10 29 02 17 53 1; 1698538673 $ $ ./a.out 2023 02 29 12 00 00 -1; a.out: my_mktime: Invalid argument 1677668400
#include <err.h>
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define is_signed(T) ((T) -1 < 1)
static time_t my_mktime(struct tm *tp);
int
main(int argc, char *argv[])
{
char **p;
time_t t;
struct tm tm;
if (argc != 8) {
fprintf(stderr, "Usage: %s yyyy mm dd HH MM SS isdst\n", argv[0]);
exit(EXIT_FAILURE);
}
p = &argv[1];
tm.tm_year = atoi(*p++) - 1900;
tm.tm_mon = atoi(*p++) - 1;
tm.tm_mday = atoi(*p++);
tm.tm_hour = atoi(*p++);
tm.tm_min = atoi(*p++);
tm.tm_sec = atoi(*p++);
tm.tm_isdst = atoi(*p++);
errno = 0;
tm.tm_wday = -1;
t = my_mktime(&tm);
if (tm.tm_wday == -1)
err(EXIT_FAILURE, "mktime");
if (errno == EINVAL || errno == ENOTUNIQ)
warn("my_mktime");
if (is_signed(time_t))
printf("%jd\n", (intmax_t) t);
else
printf("%ju\n", (uintmax_t) t);
exit(EXIT_SUCCESS);
}
static time_t
my_mktime(struct tm *tp)
{
int e, isdst;
time_t t;
struct tm tm;
unsigned char wday[sizeof(tp->tm_wday)];
e = errno;
tm = *tp;
isdst = tp->tm_isdst;
memcpy(wday, &tp->tm_wday, sizeof(wday));
tp->tm_wday = -1;
t = mktime(tp);
if (tp->tm_wday == -1) {
memcpy(&tp->tm_wday, wday, sizeof(wday));
return -1;
}
if (isdst == -1)
tm.tm_isdst = tp->tm_isdst;
if ( tm.tm_sec != tp->tm_sec
|| tm.tm_min != tp->tm_min
|| tm.tm_hour != tp->tm_hour
|| tm.tm_mday != tp->tm_mday
|| tm.tm_mon != tp->tm_mon
|| tm.tm_year != tp->tm_year
|| tm.tm_isdst != tp->tm_isdst)
{
errno = EINVAL;
return t;
}
if (isdst != -1)
goto out;
tm = *tp;
tm.tm_isdst = !tm.tm_isdst;
tm.tm_wday = -1;
mktime(&tm);
if (tm.tm_wday == -1)
goto out;
if (tm.tm_isdst != tp->tm_isdst) {
errno = ENOTUNIQ;
return t;
}
out:
errno = e;
return t;
}