18

I am using the struct timespec structure and here it is:

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

Thing is, user will be entering the values for each of these individual members, and i want to put a check a max. value the user can enter.

Can I take the max. value of time_t as int max value? i.e INT_MAX for tv_sec and LONG_MAX (defined in limits.h) for the tv_nsec? What will be the minimum acceptable values for both? Is it zero? I guess negative values can't be accepted? Just to add, these values will be using in a timer.

P.S: Where is the typedef for time_t? Could not find it in time.h.

mpromonet
  • 11,326
  • 43
  • 62
  • 91
RajSanpui
  • 11,556
  • 32
  • 79
  • 146
  • You can't even rely on `time_t` being an integral type; according to POSIX, it may be floating-point. http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_types.h.html – Fred Foo Apr 11 '11 at 07:26
  • 3
    @larsmans: your link says: *"`time_t` shall be an integer type."* – jfs Aug 22 '15 at 06:50
  • [POSIX 2004](https://pubs.opengroup.org/onlinepubs/009695399/basedefs/sys/types.h.html) says: "time_t and clock_t shall be integer or real-floating types." — This means that time_t being integral is a quite new requirement and should not be relied upon for some years. – Roland Illig Nov 01 '20 at 09:48

9 Answers9

38

Since people here are answering how to set the maximum time_t value, and make further guesswork as to its type, I thought I'd add the c++ way to do it:

#include <limits>
...
time_t maxTime = std::numeric_limits<time_t>::max();
swalog
  • 4,403
  • 3
  • 32
  • 60
  • This gives maxTime = -9223372036854775808, which does work for a largest possible date. – myk May 13 '17 at 00:41
  • 4
    @myk On what platform/compiler/system do you get that result? I don't think that result is in line with the standard... – skyking Jul 03 '17 at 05:17
  • 1
    @skyking Wow, I can't recreate the result from my previous post. When I try now on fedora 26 (64 bit) + gcc, I get maxTime = 9223372036854775807, which looks like a valid answer. I was using fedora 25 (64-bit) + gcc at the time of my previous post. – myk Jul 18 '17 at 14:40
5

I would not care so much about what goes into a time_t, but about what is reasonable. On any system that I have seen, a time_tcan encode timespans anywhere from 63 years to 1011 years (pretty much every system I know uses 64 bit numbers ever since these geniusses came up with the Y2K world-will-end thing in 1999, it remains to be seen who will notice the much bigger "event" when the year 2038 goes past).

If you reasonably expect that your program will be running for no more than 50 years, reject any value greater than 50*365*86400, or simply saturate the value. I don't expect any of the programs that I write now to be in use in 50 years (though I will not live to verify that).
On the other hand, if your system does use a 32 bit time_t, then it does not matter anyway, because the system time will have overflown in 50 years either way, so one can't construct a meaningful time anyway without shifting epoch.

If you ask "how long do you want to pause?" and the user says "250 years", I would deem it not truly incorrect program behaviour if you said "yeah right, 50 will do, too". Because, hey, the difference really isn't observable.

Damon
  • 67,688
  • 20
  • 135
  • 185
  • 1
    The issue also arises with programs that do computations on dates, which can be in the future. Such programs should be able to work with dates in the distant future on platforms that support them. But on platforms with a limited `time_t`, the program should behave in a reasonable way if the dates are too large. There was an [issue with Mutt](https://dev.mutt.org/trac/ticket/3880) with dates in the future, until [my solution](https://stackoverflow.com/a/39782264/3782797) was implemented. – vinc17 Jul 03 '17 at 14:39
  • @vinc17 Yep. Also those of us who program concerning the past, or dates in fictional / gaming worlds... that involve history, etc. – Dronz Jul 02 '18 at 00:02
  • "reject any value greater than 50*365*86400" is reasonable if the units of `time_t` are seconds. If `time_t` is say in milliseconds, of course it would be too small. So now this answer begets a new task: determine `time_t` units. IOWs, the idea here is good, but lacks important portable implementation details. – chux - Reinstate Monica Jul 27 '18 at 14:37
  • @chux: It's a pity the standard doesn't really say anything about it. Though I guess it's kinda "reasonable" to assume seconds. Out of curiosity, have you actually seen (not heard of, anecdotically, in the 1960, on a non-existent architecture) a system where the assumption of seconds is wrong? Of course _POSIX_ indeed does require seconds, only just _C_ doesn't... – Damon Jul 28 '18 at 08:32
  • @Damon "have you actually seen" 10-25 yrs ago a system counted ms in 64-bit another used FP yet still in seconds. Also came across an East Asian system that counted seconds (32-bit), yet included leap seconds - also see [TAI](https://en.wikipedia.org/wiki/International_Atomic_Time). Another integer system that counted seconds yet with a 2000 epoch. Yet the only novelty with the last 10 yrs has been 32 unsigned, 40 int (64 bit restricted range), 64 int. I suspect between now and [2038](https://en.wikipedia.org/wiki/Year_2038_problem) some other novelties will arise. – chux - Reinstate Monica Jul 28 '18 at 13:03
4

Unfortunately the ISO C standard (currently C11) does not provide any way to get the maximum value of time_t. So, unless one uses tools like Autoconf providing information, one needs to make some assumptions.

Assuming that time_t is an integer type without padding bits (which is the case on most platforms nowadays, if not all), one can probably take:

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

which is the maximum representable value for a signed integer type (but the fact that a value is representable in time_t does not mean that it is supported by the system as a time_t value).

One may also want to detect whether time_t is an integer type. The ISO C standard specifies that time_t is a real type (Clause 7.27.1). By definition, a real type is either an integer type or a real floating type (float, double or long double, and possibly others added in future versions of the standard, as mentioned in Clause 6.11.1). Thus, if time_t is not an integer type, it is necessarily a real floating type. As a consequence, one can detect whether time_t is an integer type with the test (time_t) 1 / 2 == 0.

Note: The C standard does not strictly require that (T) 1 / 2 be different from 0 if T is a floating type, but if this is not the case, I suspect that such platforms would have serious issues with floating-point calculations.

vinc17
  • 2,829
  • 17
  • 23
  • Except it's not safe to assume that. The standard both allows `time_t` to be a non-integral type and have padding bits. Also I don't actually think that the standard guarantees excludes the possibility that `(T)1/2==0` for other arithmetic types than integral types (that would mean that you can't detect whether `time_t` is an integer type either`). – skyking Jul 03 '17 at 05:10
  • 2
    @skyking I know that it is not safe (hence the "Assuming" in my answer), but the point is that it seems that no safe ways exist in just ISO C (without using tools like Autoconf). So the best one can do is to provide a solution that works on most machines in practice. That's still better than the accepted answer, which says "A time_t is simply a long int." (this may be wrong, even in practice). I'll update my answer to be clearer on these points (and `(T)1/2==0` too. – vinc17 Jul 03 '17 at 13:37
2

Taken from ISO/IEC 9899:TC3 §7.23

  1. The types declared are size_t (described in 7.17); clock_t and time_t which are arithmetic types capable of representing times; and struct tm which holds the components of a calendar time, called the broken-down time .

  2. The range and precision of times representable in clock_t and time_t are implementation-defined

Therefore you cannot make any assumption on its max value based on the C standard.

If you need to write portable code, you'll probably use the autotols. Autoconf offers the AC_CHECK_SIZEOF macro that can help you dealing with the architecture-specific data limits.

Ottavio Campana
  • 4,088
  • 5
  • 31
  • 58
2

According to Wikipedia, time_t may be an integer or floating point number, but is usually a 32-bit or 64-bit signed integer. I think the largest safe value you can assume is INT_MAX. For time_t at least negative numbers are legal and refer to before 1 January 1970.

sverre
  • 6,768
  • 2
  • 27
  • 35
  • 3
    I can't recommend comparing against INT_MAX without a great deal of caution as this cuts off at 2038 which isn't far away. This choice should only be made if you're guaranteed to be running on a 32-bit system, AND if you can guarantee the software doesn't have to deal with dates more than 26 years away AND you can guarantee that noone will be using the software by the time it has to deal with this dates. Otherwise its a bad idea. – frankster Oct 03 '12 at 11:02
1

This should give you the maximum value of a time_t on any POSIX machine and any architecture, as long time_t is an unsigned integer (not floating point) type. It is written to be agnostic to the machine's word length and any padding bits. Compile with cc -DDEBUG to get some diagnostics:

#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: */
0

A time_t is simply a long int.
It's defined in (on my Ubuntu linux system) /usr/include/time.h, however the definition stretches back all the way to /usr/include/bits/types.h, where __SLONGWORD_TYPE (which is what __TIME_T_TYPE is defined to) is defined.

The problem with simply checking if a value is greater than, say, LONG_MAX, is that once a value exceeds this value it will automatically wrap around and become negative. Thus you can't check to see if anything is greater than this value - the macro is defined as the largest value this type can take.

You don't really want a user to input these values - unless by 'user' you mean 'developer'. The only real "safe" way to test this would be to let the user input a string (c-style, of course) and then run two checks:
1) Check to see if the user entered more digits than is allowed (a cheap trick is int(log10(number)) + 1 to count the amount of digits in a number).
2) If this is equal to the amount of digits, start comparing digit-by-digit. You can compare digit-by-digit by using a little bit of modulo arithmetic.

This is really the safest way to check whether or not the user inputs a number that's far too large. You won't run into any overflow issues this way, though it is terrifically tedious. Hope this helps.

Ben Stott
  • 2,218
  • 17
  • 23
  • @Ben: Can't we do, 0?? – RajSanpui Apr 11 '11 at 07:38
  • No, as < 0 is a valid value - time_t represents the time before/after the Unix epoch (1st of January 1970). In any case, a user can input a value sufficiently high that it could wrap from positive back to positive again (say something like 2*LONG_MAX should do it) – Ben Stott Apr 11 '11 at 07:41
  • @Ben: Accepted. But in case of a timer, we can do it? Because in timers, the values will be always +ve, as for example: a timer can run from 2.36 to 4.68 seconds. It can't be a -ve time. – RajSanpui Apr 11 '11 at 07:49
  • 2
    Sure - this fixes the first issue (<0 as a valid value) however a user could still feasibly enter 2*LONG_MAX + 12 (though this is unlikely). This will wrap around to somewhere near 12 even though the value that was entered was ridiculously high. – Ben Stott Apr 11 '11 at 07:54
  • @Ben: ok, in that case counting the no of digits (what you suggested) seems a good one, and other which comes to my mind (but works only for timers) is, if the user enters a large value to wrap around to a +ve one, (-ve are discarded) then we check timer_startTime > timerEndTime. If not, we throw an error. What do you say? – RajSanpui Apr 11 '11 at 08:02
  • start_time = 2*LONG_MAX + 12, end_time = 2*LONG_MAX + 13 still breaks that one (admittedly, that's a ridiculously trite set of input, but it's technically possible....) Really, the only foolproof option is checking digits. Worst comes to worst you'll have to run 11 checks (and 11 mod operations), which shouldn't cost too many cpu cycles anyway.... – Ben Stott Apr 11 '11 at 08:06
  • That's a nice test case to break my code :-). So counting the digits remains the only one. Thanks Ben, it was really useful. – RajSanpui Apr 11 '11 at 08:10
  • 21
    "A time_t is simply a long int." - maybe on your system - this is not guaranteed by any standard – M.M Feb 27 '16 at 10:11
  • 3
    This is wrong, `time_t` is `long long` on gnux32 systems. – o11c Apr 21 '16 at 19:11
  • 6
    The standard does **not** guarantee `time_t` to be a `long int`, it doesn't even guarantee it to be of integral type. – skyking Jul 03 '17 at 05:06
  • @BenStott Also `<0` need not be a valid `time_t`. The standard allows for `time_t` being unsigned for example... – skyking Jul 03 '17 at 11:38
  • @skyking POSIX guarantees that `time_t` is an integer as an extension to C99 – osvein Apr 15 '20 at 22:08
  • @osvein As far as I can see C99 also only put the requirement that time_t is an arithmetic type. – skyking Apr 16 '20 at 04:59
-1

For LINUX ctime may accept any time_t that will keep resulting year lower or equal to INT_MAX limit, that is for 64bit system:

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

Will produce the following output: Tue Dec 31 23:59:59 2147483647

Stephen Rauch
  • 47,830
  • 31
  • 106
  • 135
Borland P
  • 1
  • 3
-1
# 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;
}