Why cannot I set the date of my GNU/Linux machines to the Epoch?

Both on a Raspberry Pi and on a Ubuntu 16.04 x86_64 machine I have access to, running

# date -s @0

returns

date: cannot set date: Invalid argument
thu 1 jan 1970 01:00:00 CET

and I wonder:

  • what is the reasoning behind the impossibility,
  • whether there is a programmatic way of resetting the system time to Epoch.

Here is Solutions:

We have many solutions to this problem, But we recommend you to use the first solution because it is tested & true solution that will 100% work for you.

Solution 1

I can’t reproduce “Invalid argument” for exactly the epoch on an older system, but:

# strace date -s '@-1'
…
clock_settime(CLOCK_REALTIME, {4294967295, 0}) = -1 EINVAL (Invalid argument)
settimeofday({4294967295, 0}, NULL)     = -1 EINVAL (Invalid argument)
write(2, "date: ", 6date: )                   = 6
write(2, "cannot set date", 15cannot set date)         = 15
write(2, ": Invalid argument", 18: Invalid argument)      = 18
write(2, "\n", 1

(And then date goes on to display the date that it wanted to set, even though it hasn’t actually set it.)

In plain English, date is calling the kernel to set the date to one second before the epoch, and the kernel tells it that the proposed date is invalid.

Now in the trace above, you see 4294967295 as the number of seconds instead of -1. That number is 2^32-1 and I ran this on a 32-bit machine. This is actually not the source of the problem: it’s a display problem in strace, which doesn’t know whether the value is supposed to be signed or not. In fact the number of seconds is a signed integer of type time_t. Glibc 2.23 on Linux defines time_t as __time_t in /usr/include/time.h, __time_t as __TIME_T_TYPE in /usr/include/bits/types.h __TIME_T_TYPE as __SYSCALL_SLONG_TYPE which is __SQUAD_TYPE in /usr/include/bits/typesizes.h, and __SQUAD_TYPE as a 64-bit signed type in /usr/include/bits/types.h. On the kernel side, as of 4.4, the definition of the type of the argument of the settimeofday call is either struct timespec where the seconds are a __kernel_time_t which is a signed 64-bit type on 64-bit machines, or a time64_t which is a signed 64-bit type on 32-bit machines. So the kernel does see -1 as -1 and not some large positive number.

The reason you can’t go back before the epoch is that the Linux kernel explicitly refuses to do so. The settimeofday system call returns EINVAL (“Invalid argument”) if timeval_valid rejects the given time structure, and that function rejects number of seconds that are negative.

Different versions of the kernel have code that’s organized differently, but I don’t think the behavior has changed.

I couldn’t see any reason why a time of exactly 0 would be rejected and it worked in the VM where I tested it.

The reason for putting this sanity check is presumably that there’s code out there that stores the time since the epoch as an unsigned integer, and thus cannot cope with dates that are before the epoch. Some code might interpret 0 as “the date isn’t known”, so it would make sense to reject 0 too, but Linux doesn’t actually do that, and it would only last for a second anyway.

Times before the epoch can certainly be represented and manipulated. A lot of software needs to track the time of past events. The timezone database that virtually every operating system uses has historical data going back to when timezones were codified (around the time railways started to take on). But you can’t tell a Linux computer that the current time is before 1970.

Solution 2

This can happen if the current timezone’s idea of epoch is before the (UTC) Unix epoch. Such times just can’t be represented.

Note: Use and implement solution 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply