Is any version of OS X/macOS vulnerable to the Year 2038 problem?

All versions before OS X 10.6 "Snow Leopard" have the year 2038 problem. Most installs of 10.6 and all installs of 10.7 "Lion" fixed the main cause of the problem. It's almost gone, but the year 2038 bug might survive in a few apps.

My old PowerPC Mac runs OS X 10.4.11 "Tiger". I went to Date & Time in System Preferences, and tried to enter a date after 2038, but it reset the date to December 31, 2037. It knows that something's wrong in 2038.

screenshot of Date & Time in OS X 10.4.11

OS X is derived from Unix by way of BSD. Apps for OS X use BSD system calls for things like opening files and connecting to the internet. BSD has a long history and some of its system calls come from the 1980s. One of its system calls is gettimeofday(), which first appeared in 4.1cBSD. UC Berkeley released 4.1cBSD in 1982, more than half a century before 2038. The time from gettimeofday() is an integer of type time_t. It counts seconds, where zero is the Unix epoch of 1970-01-01 00:00:00 UTC.

In my old PowerPC Mac, time_t is a signed 32-bit integer. This causes the year 2038 problem. A signed 32-bit time_t has a range of -2147483648 to 2147483647. This can only hold times from 1901-12-13 20:45:52 UTC to 2038-01-19 03:14:07 UTC. When this overflows, gettimeofday() will flip the date from Monday the 19th of January 2038 to Friday the 13th of December 1901.

The fix is to transition from 32-bit time_t to 64-bit time_t. For OS X, this transition happened along with another transition from 32-bit pointers to 64-bit pointers, but 64-bit pointers require a 64-bit processor. Apple has only provided 64-bit time_t for 64-bit processors. It is possible for 32-bit processors to handle 64-bit time_t, but Apple never provided that feature.

Apple's compilers have supported 64-bit integers on 32-bit processors since OS X 10.0 "Cheetah", when time_t had 32 bits and off_t had 64 bits. In Unix and BSD, off_t is the size of a file, or an entire disk. A 32-bit off_t would limit OS X to disks under 2 GiB. Apple defined off_t as 64 bits, so OS X worked with larger disks. Apple defined time_t as 32 bits in 10.0, so a transition to 64-bit time_t needed to happen later.

For my old PowerPC Mac, the header file <ppc/_types.h> defines __darwin_time_t as long. For Intel Mac, the header <i386/_types.h> does the same. In OS X, long has the same size as a pointer. So when pointers became 64 bits, long also became 64 bits, and time_t also became 64 bits, fixing the main cause of the year 2038 problem.

Apple's 64-Bit Transition Guide says, "Prior to OS X v10.6, all applications that shipped with the operating system were 32-bit applications. Beginning in v10.6, applications that ship with the operating system are generally 64-bit applications." I know from Wikipedia that 10.6 required an Intel processor and 10.7 required a 64-bit Intel processor. A Mac running 10.6 on a 32-bit Intel processor must have had 32-bit apps. I conclude that most Macs running 10.6 and all Macs running 10.7 have 64-bit apps with 64-bit pointers and 64-bit time_t. Apple released 10.7 in 2011, well before January 2038.

With 64-bit time_t, the year 2038 bug is almost gone, but it might survive in a few apps. Old 32-bit apps might still run on newer systems. Also, 64-bit apps might contain coding mistakes or outdated designs, causing them to convert a 64-bit time_t to 32 bits. This is a problem for all systems, not only macOS.


There is also Mach time. The kernel of OS X mixes BSD with Mach. Most apps get the time from BSD using gettimeofday(), but there is a way to go around BSD and get the time from Mach. This is more difficult: the program would call mach_host_self() to get the host port, then host_get_clock_service() to get the CALENDAR_CLOCK, and finally clock_get_time().

In my old Mac running 10.4 "Tiger", <mach/clock_types.h> declares the time as an unsigned int (inside struct mach_timespec). This is an unsigned 32-bit integer, with range from 0 to 4294967295, which is from 1970-01-01 00:00:00 UTC to 2106-02-07 06:28:15 UTC. This would exchange the year 2038 problem for the year 2106 problem. It can't reach year 29,940.

I suspect that host_get_clock_service() was obsolete since 10.0 "Cheetah", because Apple provided faster ways to get the time. These were BSD's gettimeofday() for calendar time, and Apple's mach_absolute_time() for monotonic time. The preference for gettimeofday() put the problem in 2038, not 2106.


Well, as far as El Capitan 10.11.6 is in question, answer is not as easy as using Perl script.

If we try this

macmladen@buk $ touch -t 205012121212 my
macmladen@buk $ ls -al
total 104
drwx------  7 root root    4096 Dec 25 13:42 .
drwxr-xr-x 23 root root    4096 Dec  4 17:06 ..
-rw-r--r--  1 root root       0 Dec 12  2050 my

So that proves macOS in lowest, system layer is safe from Y2038 bug. That means the system as such will not fail and will work correctly.

However, on application level, that is not always the case.

Trying to push date to 2040, System preferences Date&Time responded by setting it to 01.01.2038 (you'll have to uncheck automatically setting)

enter image description here

That means that it depends on application how will they react to Y2038 bug.


El Capitan is not susceptible:

#!/usr/bin/perl
use POSIX;
$ENV{'TZ'} = "GMT";
for ($clock = 2147483641; $clock < 2147483651; $clock++)
{
print ctime($clock);
}

This perl script provides the output below:

Family-iMac:~ dude$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.11.6
BuildVersion:   15G1004
Family-iMac:~ dude$ /Users/dude/Desktop/2038.pl 
Tue Jan 19 03:14:01 2038
Tue Jan 19 03:14:02 2038
Tue Jan 19 03:14:03 2038
Tue Jan 19 03:14:04 2038
Tue Jan 19 03:14:05 2038
Tue Jan 19 03:14:06 2038
Tue Jan 19 03:14:07 2038
Tue Jan 19 03:14:08 2038
Tue Jan 19 03:14:09 2038
Tue Jan 19 03:14:10 2038

Information gleaned and script compacted to bare bones from this very interesting site.