【问题标题】:Maximum values for time_t (struct timespec)time_t (struct timespec) 的最大值
【发布时间】:2011-08-02 20:10:25
【问题描述】:

我正在使用struct timespec 结构,这里是:

struct timespec {
           time_t tv_sec;                /* Seconds */
           long   tv_nsec;               /* Nanoseconds */
};

事情是,用户将输入每个成员的值,我想检查一下最大值。用户可以输入的值。

我可以取最大值吗? time_t 的值作为 int 最大值?即INT_MAX 用于tv_secLONG_MAX(在limits.h 中定义)用于tv_nsec?两者的最小可接受值是多少?是零吗?我猜不能接受负值?补充一下,这些值将在计时器中使用。

P.S:time_t 的 typedef 在哪里?未能及时找到。h。

【问题讨论】:

  • 你甚至不能依赖time_t 是一个整数类型;根据 POSIX,它可能是浮点数。 pubs.opengroup.org/onlinepubs/9699919799/basedefs/…
  • @larsmans:您的链接显示:time_t 应为整数类型。”
  • POSIX 2004 说:“time_t 和 clock_t 应为整数或实浮点类型。” — 这意味着 time_t 是整数是一个相当新的要求,不应依赖几年。

标签: c linux time


【解决方案1】:

time_t 只是一个 long int。
它在(在我的 Ubuntu linux 系统上)/usr/include/time.h 中定义,但是定义一直延伸到 /usr/include/bits/types.h,其中__SLONGWORD_TYPE(这就是__TIME_T_TYPE被定义为) 被定义。

简单地检查一个值是否大于LONG_MAX 的问题在于,一旦一个值超过了这个值,它就会自动回绕并变成负数。因此,您无法检查是否有任何东西大于该值 - 宏被定义为该类型可以采用的最大值。

您并不真正希望用户输入这些值 - 除非“用户”是指“开发人员”。唯一真正“安全”的测试方法是让用户输入一个字符串(当然是 c 风格),然后运行两个检查:
1) 检查用户输入的位数是否超过允许的位数(一个便宜的技巧是 int(log10(number)) + 1 来计算数字中的位数)。
2)如果这等于位数,开始逐位数比较。您可以使用一点模运算来逐位比较。

这确实是检查用户是否输入了太大的数字的最安全方法。这样您不会遇到任何溢出问题,尽管它 非常乏味。 希望这会有所帮助。

【讨论】:

  • @Ben:我们不能这样做吗,0??
  • 当然 - 这解决了第一个问题(
  • "time_t 只是一个 long int。" - 可能在您的系统上 - 任何标准都不能保证这一点
  • 这是错误的,time_t 在 gnux32 系统上是 long long
  • 标准保证time_tlong int,甚至不保证它是整数类型。
【解决方案2】:

根据 Wikipedia,time_t 可能是整数或浮点数,但通常是 32 位或 64 位有符号整数。我认为您可以假设的最大安全值是INT_MAX。对于time_t,至少负数是合法的,并且指的是 1970 年 1 月 1 日之前。

【讨论】:

  • 我不建议在不非常谨慎的情况下与 INT_MAX 进行比较,因为它在不远的 2038 年截止。仅当您保证在 32 位系统上运行,并且您可以保证该软件不必处理超过 26 年之后的日期并且您可以保证没有人会使用时,才应该做出此选择软件到它必须处理这个日期的时候。否则这是个坏主意。
【解决方案3】:

我不会太在意 time_t 的内容,而是在乎什么是合理的。在我见过的任何系统上,time_t可以编码从 63 年到 1011 年的时间跨度(自从这些天才提出 Y2K 以来,我所知道的几乎每个系统都使用 64 位数字世界将在 1999 年结束的事情,到 2038 年过去时谁会注意到更大的“事件”还有待观察)。

如果您合理地期望您的程序将运行不超过 50 年,请拒绝任何大于 50*365*86400 的值,或者简单地使该值饱和。我不希望我现在编写的任何程序在 50 年内都可以使用(尽管我不会活着证实这一点)。
另一方面,如果您的系统确实使用了 32 位的time_t,那么无论如何都无所谓,因为无论哪种方式,系统时间都会在 50 年内溢出,因此无法构建无论如何,这是一个有意义的时间,而不是改变时代。

如果你问“你想暂停多久?”并且用户说“250 年”,如果您说“是的,50 年也可以”,我认为这并不是真正不正确的程序行为。因为,嘿,差异真的是无法观察到的。

【讨论】:

  • 这个问题也出现在对日期进行计算的程序上,这可能是在将来。这样的程序应该能够在支持它们的平台上处理遥远未来的日期。但是在time_t 有限的平台上,如果日期太大,程序应该以合理的方式运行。在my solution 实施之前,有一个issue with Mutt 的日期是未来的。
  • @vinc17 是的。还有我们这些编程关于过去的人,或者虚构/游戏世界中的日期......涉及历史等。
  • "拒绝任何大于50*365*86400的值"如果time_t的单位是秒是合理的。如果time_t 以毫秒为单位,当然它太小了。所以现在这个答案引发了一项新任务:确定time_t 单位。 IOW,这里的想法很好,但缺少重要的可移植实现细节。
  • @chux:很遗憾标准并没有真正说明它。虽然我想假设几秒钟有点“合理”。出于好奇,您是否真的见过(有趣的是,在 1960 年,在不存在的架构上没有听说过)一个以秒为单位的假设是错误的系统?当然 POSIX 确实需要几秒钟,只是 C 不需要...
  • @Damon“你真的见过吗”10-25 年前,一个系统以 64 位计算 ms 另一个使用的 FP 但仍以秒为单位。还遇到了一个计算秒数(32 位)的东亚系统,但包括闰秒 - 另见 TAI。另一个以 2000 纪元计算秒数的整数系统。然而,过去 10 年唯一的新奇事物是 32 无符号、40 整数(64 位受限范围)、64 整数。我怀疑从现在到2038 之间会出现一些其他新奇事物。
【解决方案4】:

由于这里的人们正在回答如何设置最大 time_t 值,并对其类型进行进一步猜测,我想我会添加 c++ 方法来做到这一点:

#include <limits>
...
time_t maxTime = std::numeric_limits<time_t>::max();

【讨论】:

  • 这给出了 maxTime = -9223372036854775808,这确实适用于尽可能大的日期。
  • @myk 你在什么平台/编译器/系统上得到这个结果?我觉得这个结果不符合标准……
  • @skyking 哇,我无法重新创建上一篇文章的结果。当我现在在 fedora 26(64 位)+ gcc 上尝试时,我得到 maxTime = 9223372036854775807,这看起来像是一个有效的答案。我在上一篇文章时使用的是 fedora 25(64 位)+ gcc。
【解决方案5】:

取自ISO/IEC 9899:TC3§7.23

  1. 声明的类型是size_t(在7.17中描述); clock_ttime_t 是能够表示时间的算术类型;和 struct tm 包含日历时间的组成部分,称为 故障时间。

  2. clock_ttime_t 中表示的时间范围和精度是实现定义的

因此,您不能根据 C 标准对其最大值做出任何假设。

如果您需要编写可移植的代码,您可能会使用自动工具。 Autoconf 提供了 AC_CHECK_SIZEOF 宏,可以帮助您处理特定于架构的数据限制。

【讨论】:

    【解决方案6】:

    很遗憾,ISO C 标准(当前为 C11)没有提供任何方法来获得 time_t 的最大值。因此,除非使用 Autoconf 等工具提供信息,否则需要做出一些假设。

    假设time_t 是一种没有填充位的整数类型(现在大多数平台上都是这种情况,如果不是全部的话),可能会采取:

    (((time_t) 1 << (sizeof(time_t) * CHAR_BIT - 2)) - 1) * 2 + 1
    

    这是有符号整数类型的最大可表示值(但在time_t 中可表示的值并不意味着系统支持它作为time_t 值)。

    可能还想检测time_t 是否为整数类型。 ISO C 标准规定 time_t 是一个实类型(第 7.27.1 条)。根据定义,实数类型是整数类型或实数浮点类型floatdoublelong double,可能还会在未来版本中添加其他类型标准,如第 6.11.1 条所述)。因此,如果time_t 不是整数类型,它必然是真正的浮点类型。因此,可以通过测试(time_t) 1 / 2 == 0 来检测time_t 是否为整数类型。

    注意:如果T 是浮点类型,C 标准并不严格要求(T) 1 / 2 不同于0,但如果不是这种情况,我怀疑这样的平台会出现严重的浮点问题计算。

    【讨论】:

    • 除非假设是不安全的。该标准都允许time_t 为非整数类型并具有填充位。此外,我实际上并不认为标准保证排除了 (T)1/2==0 用于除整数类型之外的其他算术类型的可能性(这意味着您无法检测 time_t 是否也是整数类型)。
    • @skyking 我知道它不安全(因此在我的回答中“假设”),但关键是似乎在 ISO C 中不存在安全的方法(不使用像 Autoconf 这样的工具)。因此,最好的办法是提供一种在实践中适用于大多数机器的解决方案。这仍然比接受的答案要好,后者说“A time_t 只是一个长整数”。 (即使在实践中,这也可能是错误的)。我会更新我的答案,以便在这些方面更清楚(以及(T)1/2==0
    【解决方案7】:

    对于 LINUX,ctime 可以接受任何time_t,这将使结果年份低于或等于 INT_MAX 限制,即对于 64 位系统:

    time_t maxtime = 67767976233521999;
    printf("%s\n", ctime(&maxtime));
    

    将产生以下输出:Tue Dec 31 23:59:59 2147483647

    【讨论】:

      【解决方案8】:
      # include <sys/time.h>
      # include <time.h>
      # include <unistd.h>
      # include <stdlib.h>
      # include <stdio.h> /* printf() */
      # include <inttypes.h>  /* format spec PRIX64: ll/l + X on 32/64-bit arch */
      # include <limits.h>    /* INT_MAX */
      
      /* This is portable, more or less */
      time_t
      approx_time_t_max()
      {
        struct tm tm;
        time_t t0, t1;
      
        tm.tm_isdst = -1;
        tm.tm_min = tm.tm_sec = 59;
        tm.tm_mday = tm.tm_hour = 23;
      
        fprintf (stderr, "Please be patient, this takes loooong...(hours -- 
      occasionally showing the year)...");
      for (tm.tm_year = 99999 - 1900; tm.tm_year <= INT_MAX; tm.tm_year++) {
        if (tm.tm_year % 49999 == 0)
          fprintf (stderr, "%d", tm.tm_year - 1900);
        else if (tm.tm_year % 2503 == 0)
          fprintf (stderr, ".");
        for (tm.tm_mon = 0; tm.tm_mon <= 11; tm.tm_mon++)
      #if 0
          for (tm.tm_mday = 21; tm.tm_mday <= 31; tm.tm_mday++)
            if (tm.tm_mon == 1 && tm.tm_mday > 28)
              continue;
            else
              //for (tm.tm_hour = 20; tm.tm_hour <= 23; tm.tm_hour++)
                //for (tm.tm_min = 0; tm.tm_min <= 59; tm.tm_min++)
                  //for (tm.tm_sec = 0; tm.tm_sec <= 59; tm.tm_sec++)
      #endif /* commented out */
                    if ((t0 = mktime (&tm)) == (time_t)(-1))
                      goto out;
                    else t1 = t0;
        }
      out: /* NOP */ ;
      
        /* FIXME not portable: time_t can be a floating point type,
         * or any integer type other than long or long long.
         */
        fprintf (stderr, "time_t_max = 0x%"PRIX64"\n", t1);
        return t1;
      }
      

      【讨论】:

        【解决方案9】:

        只要 time_t 是无符号整数(不是浮点)类型,这应该会在任何 POSIX 机器和任何架构上为您提供 time_t 的最大值。它被写入与机器的字长和任何填充位无关。使用cc -DDEBUG 编译以获得一些诊断:

        #include <time.h>
        #include <stdint.h>     /* uintmax_t */
        #include <stdio.h>      /* fprintf() */
        #include <limits.h>     /* CHAR_BIT, UINT_MAX */
        #include <inttypes.h>   /* format spec PRIX64:
                                 * ll/l + X on 32/64-bit arch */
        
        /* The following two routines (should) work for any integer
         * representation in either 2's or 1's complement and for any
         * #bits per byte (CHAR_BIT could be <> 8).
         */
        
        /* Count the bits set in any unsigned integer type (plus any signed
         * iff using 2's complement integer representation).
         * Returns the precision (width - padding bits - sign bit) iff given
         * the xxx_MAX value of any integer type, signed or unsigned.
         * From SEI CERT C Coding Standard:
         * Rules for Developing Safe, Reliable, and Secure Systems (2016)  
         */
        size_t popcount (uintmax_t num)
        {
            size_t cnt = 0;
            
            while (num != 0) {
                if (num % 2 == 1)
                    cnt++;
                num >>= 1;
            }
            return cnt;
        }
        #define PRECISION(max_value)    popcount(max_value)
        #define SIGN_BIT                (1)
        #ifndef MIN
        #define MIN(a, b)               ((a) < (b)? (a): (b))
        #endif
        /* Get the maximum value of a time_t from it's storage width.
         * On error: returns (time_t)(-1) iff time_t is longer than an 
         * intmax_t (which would mean it's a floating point type and
         * longer than an intmax_t).
         * ASSERTION: time_t is a signed integer type,
         * i.e. not (unsigned, but the bit pattern of (-1) treated special).  
         */
        time_t get_time_t_max (void)
        {
            time_t t0, t1 = (time_t)(-1);
            size_t size = sizeof(time_t);
            size_t prec;
            uintmax_t max;
        
            if (sizeof(time_t) > sizeof(uintmax_t))
                return t1;
          
            /* Get an uintmax_t with all bits set that could be in a time_t.
             * We can not do this calculation with a time_t as long we do
             * not know its precision (overflow could occur).
             */
            prec = MIN (PRECISION (UINTMAX_MAX), CHAR_BIT * sizeof(time_t));
            max = (uintmax_t) 1 << (prec - 1);
            max = max|(max - 1);
        
            t0 = max;       /* maybe truncation happens here */
          
            /* Now account for any padding bits */
            prec = PRECISION(t0) - SIGN_BIT;
            t0 = (time_t) 1 << (prec - 1);
            t1 = t0|(t0 - 1);
          
        #ifdef DEBUG
            fprintf (stderr, "time_t_max\t= 0x%"PRIX64"\n", (uint64_t) t1);
            fprintf (stderr, "sizeof(time_t)\t= %3zd byte\n", size);
            fprintf (stderr, "precision\t= %3zd bit\n", prec);
            fprintf (stderr, "padding\t\t= %3zd bit\n",
                        CHAR_BIT*size - prec - SIGN_BIT);
            fprintf (stderr, "bits per byte\t= %3d bit\n", CHAR_BIT);
        #endif /* DEBUG */
            return t1;
        }
        #undef SIGN_BIT
        /*! vi: set ai tabs=8 shiftwidth=4: */
        

        【讨论】:

          猜你喜欢
          • 2015-05-09
          • 2012-07-29
          • 2015-10-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-05-11
          • 2017-03-29
          • 2015-05-06
          相关资源
          最近更新 更多