2015-05-03 2 views
2

Я пытаюсь распечатать дату в удобном для чтения формате для максимального значения time_t. Следующий код, кажется, отлично работает на 32-битных машинах (инициализация m_time с помощью 0x7fffffff), но он выводит значение null для теоретически самого высокого значения на машине с 64 бит. Это ограничение времени или я что-то упускаю?Человекочитаемая строка из 64 бит времени_t значение

сборник: gcc -Wall -g3 главный.c -o время_test
хост-машина: x86_64.

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

int main(int argc, char** argv) { 
    time_t m_time = 0x7fffffffffffffff; 
    time_t current_time; 
    time(&current_time); 

    printf("time_t info: sizeof [%ld] bytes or [%ld] bits.\n", sizeof(time_t), sizeof(time_t) *8); 
    printf("m_time val: [%ld]-> %s\n", m_time, ctime(&m_time)); 
    printf("current_time val: [%ld]-> %s\n", current_time, ctime(&current_time)); 
    return 0; 
} 

Выход:

time_t info: sizeof [8] bytes or [64] bits. 
m_time val: [9223372036854775807]-> (null) 
current_time val: [1430678274]-> Sun May 3 15:37:54 2015 

ТКС.

+1

«При возникновении ошибки эти функции возвращают« NULL »и устанавливают« errno »в соответствующее значение». Что говорит 'perror'? – zneak

+0

Действительно, есть ошибка. Ошибка: неверный аргумент. – pbn

+0

Прагматически, если 'time_t' имеет более 40 бит (например, 64 бит), вам не важно максимально отображаемое время. Вы и все, кто читает этот форум (и все наши великие дети), будут мертвы, компьютеры, на которых запущена ваша программа, будут уничтожены, и в это время C больше не будет существовать. Проблема [Y2038] (http://en.wikipedia.org/wiki/Year_2038_problem) практически не имеет эквивалента в 64 бита. Так что только особый случай 'time_t' - 32 бита. –

ответ

4

BTW, ctime (& ctime(3)) документирована как дает строку года, представленной четыре цифр (в общей сложности 26 байт). Таким образом, максимальное время в году 9999 (конечно, меньше максимального time_t на машине с 64 бит time_t).

Кроме того, как я уже говорил, прагматично, если time_t имеет более 40 бит (например, 64 бит), вам не важно максимально отображаемое время. Вы и все, кто читает этот форум (и все наши великие дети), будут мертвы, компьютеры, на которых запущена ваша программа, будут уничтожены, и в это время C больше не будет существовать. Y2038 problem практически не имеют эквивалентов в 64 бита. Так что только особый случай, когда time_t - 32 бит.

Очень маловероятно, чтобы любая программа C имела бы значение после 3000 года; программное обеспечение, аппаратные средства, стандарты и человеческие технические знания не длятся так долго ...

POSIX ctime documentation говорит явно:

Attempts to use ctime() or ctime_r() for times before the Epoch or for times beyond the year 9999 produce undefined results. Refer to asctime .

BTW, musl-libc, кажется, соответствует стандартному: its time/__asctime.c (косвенно вызывается ctime) имеет хороший комментарий:

if (snprintf(buf, 26, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n", 
    __nl_langinfo(ABDAY_1+tm->tm_wday), 
    __nl_langinfo(ABMON_1+tm->tm_mon), 
    tm->tm_mday, tm->tm_hour, 
    tm->tm_min, tm->tm_sec, 
    1900 + tm->tm_year) >= 26) 
{ 
    /* ISO C requires us to use the above format string, 
    * even if it will not fit in the buffer. Thus asctime_r 
    * is _supposed_ to crash if the fields in tm are too large. 
    * We follow this behavior and crash "gracefully" to warn 
    * application developers that they may not be so lucky 
    * on other implementations (e.g. stack smashing..). 
    */ 
    a_crash(); 
} 

и GNU glibc имеет I п его time/asctime.c файл:

/* We limit the size of the year which can be printed. Using the %d 
    format specifier used the addition of 1900 would overflow the 
    number and a negative vaue is printed. For some architectures we 
    could in theory use %ld or an evern larger integer format but 
    this would mean the output needs more space. This would not be a 
    problem if the 'asctime_r' interface would be defined sanely and 
    a buffer size would be passed. */ 
if (__glibc_unlikely (tp->tm_year > INT_MAX - 1900)) 
    { 
    eoverflow: 
    __set_errno (EOVERFLOW); 
    return NULL; 
    } 

int n = __snprintf (buf, buflen, format, 
      (tp->tm_wday < 0 || tp->tm_wday >= 7 ? 
      "???" : ab_day_name (tp->tm_wday)), 
      (tp->tm_mon < 0 || tp->tm_mon >= 12 ? 
      "???" : ab_month_name (tp->tm_mon)), 
      tp->tm_mday, tp->tm_hour, tp->tm_min, 
      tp->tm_sec, 1900 + tp->tm_year); 
if (n < 0) 
return NULL; 
if (n >= buflen) 
    goto eoverflow; 

Поэтому я считаю, что и GNU Glibc и MUSL-Libc лучше, чем реализация MacOSX (в цитируемой в zneak's answer) на этом аспекте. Стандартам требуется ctime, чтобы получить 26 байтов. Кроме того, POSIX 2008 маркирует ctime как устаревшее, новый код должен использовать strftime (см. Также strftime(3)).

+0

Странно, ['asctime'] (http://pubs.opengroup.org/onlinepubs/9699919799/functions/asctime.html#) (эта же ссылка) не упоминает год 9999 , и вместо этого говорит, что результат не определен, если 'tm_year' больше, чем' {INT_MAX} -1990'. Что может вызвать UB, так это то, что буфер результата определяется как 26 байтов. Это не проблема с 'asctime_r' и не должна быть с' ctime_r'. – zneak

2

Чтобы понять это, лучше всего найти реализацию и посмотреть, что она делает. Я загрузил Apple Libc tarball for OS X 10.1.1 (ссылка которого находится на this page), и обнаружил, что ctime определен в stdtime/FreeBSD/localtime.c.

Функция выглядит следующим образом:

char * 
ctime(timep) 
const time_t * const timep; 
{ 
/* 
** Section 4.12.3.2 of X3.159-1989 requires that 
** The ctime function converts the calendar time pointed to by timer 
** to local time in the form of a string. It is equivalent to 
**  asctime(localtime(timer)) 
*/ 
#ifdef __LP64__ 
    /* 
    * In 64-bit, the timep value may produce a time value with a year 
    * that exceeds 32-bits in size (won't fit in struct tm), so localtime 
    * will return NULL. 
    */ 
    struct tm *tm = localtime(timep); 

    if (tm == NULL) 
     return NULL; 
    return asctime(tm); 
#else /* !__LP64__ */ 
    return asctime(localtime(timep)); 
#endif /* __LP64__ */ 
} 

С second-hand reference, по-видимому, struct tm быть определены в терминах целых чисел, а tm_year поле смещение от 1900 г. Если предположить, что соответствие с, даже не- Соответствующий ctime не может принять временную метку после года 2 + 1900-1.

Вот программа, которая находит (и тесты) наибольшее временную метку ctime будет принимать с реализацией от Apple:

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

int main(int argc, char** argv) { 
    struct tm t = { 
     .tm_sec = 59, 
     .tm_min = 59, 
     .tm_hour = 23, 
     .tm_mday = 31, 
     .tm_mon = 11, 
     .tm_year = INT_MAX, 
    }; 
    time_t max = mktime(&t); 

    printf("Maximum time: %li\n", max); 
    printf("ctime max: %s\n", ctime(&max)); 
    max++; 
    printf("ctime max+1: %s\n", ctime(&max)); 
} 

Выход:

Maximum time: 67768036191694799
ctime max: Wed Dec 31 23:59:59 2147485547
ctime max+1: (null)

Это 56-разрядное число, поэтому Максимальный год: 64-бит time_t может содержать (хотя struct tm не может), вероятно, между 547 608 814 485 и 549 756 300 032, или как 36-кратный возраст Вселенной. Другими словами, это будет немного.

Для чего это стоит, реализация Apple не соответствует. В стандарте говорится, что вывод ctime должен соответствовать 26 байтам, включая символ новой строки и нулевой символ. Для соответствующей реализации это означает, что год должен быть в пределах -999 и 9999.

Смежные вопросы