/**************************************************************************** * Copyright (c) Bob Toxen 2026. All rights reserved. Permission is * * granted to run on up to 20 computers owned by oneself or one's employer. * * Permission is granted to make changes or translations provided that * * such changed versions, whose copyright is retained by Bob Toxen, are * * sent electronically in ASCII to Bob Toxen & that this copyright is kept. * **************************************************************************** */ #include #include #include #include #define ONE_YEAR_ROUGH ((time_t)(3600L * 24 * 365)) /* One year, in seconds, without leap years. */ #define ONE_YEAR ((3600L * (time_t)(24 * 365.25))) /* One year, in seconds, with leap years. */ #define OK 0 #define OOPS (!OK) struct tm *when = 0; void exit(int); /* Print the time in the future, handling time library routine error returns. */ int outtime(char *msg, time_t *t0, time_t *today0, time_t *old_inc0, int dashq0) { char *s = 0; if (dashq0 != 1) { printf("%s: years inc=", msg); fflush(stdout); /* We flush output a lot in case the program crashes. */ printf("%'13lld,\n years in the future=%'13lld, ", (long long) (*old_inc0 / ONE_YEAR), (long long) ((*t0 - *today0) / ONE_YEAR)); fflush(stdout); } if (when) when->tm_year = 13; when = localtime(t0); if (!when) { if (!dashq0) fprintf(stderr, "\nERROR: Invalid seconds since Epoch in localtime\n"); fflush(stderr); goto oops; } s = asctime(when); if (!s) { if (!dashq0) fprintf(stderr, "\nERROR: Invalid broken out time in asctime\n"); fflush(stderr); goto oops; } /* * If -q then since quiet then must be called an * additional time (with) -q being 2 with previous data * (before max year exceeded). * * asctime() appends a newline so we don't in the * printf. */ if (dashq0 != 1) { printf("%s", s); } fflush(stdout); return OK; oops:; if (!dashq0) perror("Error code"); fflush(stderr); return OOPS; } int main(int argc, char **argv) { int dashq = 0; short s = 0; int i = 0; long int l = 0; long long int ll = 0; char c = 0; time_t today = 0; time_t t = 0; time_t t2 = 0; time_t old_t = 0; time_t inc = 0; time_t old_inc = 0; setlocale(LC_NUMERIC, "en_US"); if (--argc >= 1 && !strcmp(*++argv, "-q")) dashq = 1; printf("Copyright (c) Bob Toxen 2026. All rights reserved.\n"); if (!dashq) printf("-q to quiet the output\n"); printf("\nThis program will analyze your *nix for the well-known bug if the\n" "seconds since 01/01/1970 exceeds a signed 32-bit (4-byte) integer.\n" "If it outputs abnormal output for years beyond 2038 then your computer\n" "will fail at that time, about 12 years from now.\n" "\n" "Many *nix systems were fixed decades ago so that this variable became\n" "an unsigned 32-bit int, which can keep time until 2106.\n" "More recently most systems went to a signed 64-bit int.\n\n"); printf("Note that most Unix and Linux distributions corrected the time\n" "problem by approximately 2014 to work until 2106 (using an unsigned\n" "32-bit number) or well beyond if using a 64-bit number but maybe the code\n" "will fail before the largest 64-bit signed (292,271,022,989 years)\n" "or unsigned number is exceeded.\n\n" "If it is not fixed in your version, well, good luck.\n\n"); printf("\nBut wait! There's more! The original NTP, the network time protocol has a\nsimilar problem and will fail in 2036 because it uses an unsigned 32-bit\nint for seconds since 1900 (since *nix was around that long ago due\nto time travelers) but we are not programmed to respond in that area.\n\n"); fflush(stdout); /* Print # bytes for different types of ints. */ printf("sizeof char=%d, sizeof short=%d, sizeof int=%d, sizeof long=%d, sizeof long long=%d,\n sizeof time_t=%d\n\n", sizeof c, sizeof s, sizeof i, sizeof l, sizeof ll, sizeof t); switch (sizeof (time_t)) { case 4: printf("So sorry, you still have the old 32-bit time_t so your universe will end in"); fflush(stdout); printf("\n 2038 or 2106. Stay tuned.\n"); break; case 8: printf("You have the new improved 64-bit time_t so you will be good until the y10k bug\n (if it is present in your application code).\n"); break; default: printf("Your time_t claims to be a %d-bit size, which is bizarre!\n", 8 * sizeof (time_t)); } fflush(stdout); today = time(&t); /* Time in seconds since epoch (01/01/1970). */ printf("Current years and seconds since the epoch: %'lld %'lld\n", (long long) (t / ONE_YEAR_ROUGH), (long long) (t)); fflush(stdout); outtime("Today", &today, &today, &old_inc, 0); fflush(stdout); printf(" seconds/year:%'lld, seconds/year including leap year:%'lld,\n delta seconds:%'lld, delta hours:%'lld\n\n", (long long) ONE_YEAR_ROUGH, (long long) ONE_YEAR, (long long) (ONE_YEAR - ONE_YEAR_ROUGH), (long long) ((ONE_YEAR - ONE_YEAR_ROUGH) / 3600)); fflush(stdout); inc = ONE_YEAR; /* One year, in seconds. */ t2 = (time_t) 0x7FFFFFFF; outtime("Biggest signed 32-bit long value for type time_t (secs since\n 01/01/1970)", &t2, &today, &old_inc, 0); t2 = (time_t) 0xFFFFFFFF; outtime("Biggest unsigned 32-bit long value for type time_t (secs since\n 01/01/1970)", &t2, &today, &old_inc, 0); putchar('\n'); fflush(stdout); if (when->tm_year + 1900 < 2038) { printf("time_t still is the original signed 32-bit so your universe will end in early\n2038. Time to upgrade. No point looking farther into the future.\nSo sorry.\n"); fflush(stdout); return (0); } /* * See how far in the future long long integer (time_t type) and time routines will work. * Infinite loops are bad, prevent if invoked as "./mytime < /dev/null". */ for (i=0; i<10000; i++) { if (outtime("Time", &t, &today, &old_inc, dashq) == OOPS) { if (inc == ONE_YEAR) { if (dashq == 1) { dashq++; outtime("Latest year that libc time routines appear to\n handle", &old_t, &today, &inc, dashq); } /* Her Majesty is the Queen of England who lived very very long. */ printf("\nWelcome to the Restaurant at the end of the universe. Hello, your Majesty.\n\n"); fflush(stdout); /* We do this after normal end of the universe search in case the software crashes. */ old_inc = (time_t) 0; t2 = (time_t) 0x7FFFFFFFFFFFFFFFLL; outtime("Biggest signed 64-bit long value for type time_t (secs since 01/01/1970)", &t2, &today, &old_inc, 0); putchar('\n'); fflush(stdout); /* * We do this after normal end of the universe search in case the software crashes. * If negative (Dec 31 18:59:59 1969) then time_t is signed 64-bit number. */ t2 = (time_t) 0xFFFFFFFFFFFFFFFFLL; outtime("Biggest unsigned 64-bit long value for type time_t (secs since 01/01/1970)", &t2, &today, &old_inc, 0); putchar('\n'); fflush(stdout); return OK; } inc = ONE_YEAR; t = old_t; t += inc; } else { old_t = t; t += inc; old_inc = inc; inc *= 10; } } return OK; }