Index: src/libbu/CMakeLists.txt
===================================================================
--- src/libbu/CMakeLists.txt	(revision 63680)
+++ src/libbu/CMakeLists.txt	(working copy)
@@ -98,7 +98,7 @@
   xdr.c
   )
 
-BRLCAD_ADDLIB(libbu "${LIBBU_SOURCES}" "${CMAKE_THREAD_LIBS_INIT};${TCL_LIBRARY};${WINSOCK_LIB};${PSAPI_LIB};${M_LIBRARY}")
+BRLCAD_ADDLIB(libbu "${LIBBU_SOURCES}" "${CMAKE_THREAD_LIBS_INIT};${TCL_LIBRARY};${WINSOCK_LIB};${PSAPI_LIB};${M_LIBRARY};y2038")
 set_target_properties(libbu PROPERTIES VERSION 20.0.1 SOVERSION 20)
 get_directory_property(BRLCAD_TCL_BUILD DIRECTORY ${BRLCAD_SOURCE_DIR}/src/other DEFINITION BRLCAD_TCL_BUILD)
 if("${BRLCAD_TCL_BUILD}" STREQUAL "ON")
@@ -109,6 +109,7 @@
 BRLCAD_ADDDATA(nonie.r.12 vfont)
 
 add_subdirectory(tests)
+add_subdirectory(y2038)
 
 set(bu_ignore_files
   bu_internals.h
Index: src/libbu/date-time.c
===================================================================
--- src/libbu/date-time.c	(revision 63680)
+++ src/libbu/date-time.c	(working copy)
@@ -27,6 +27,8 @@
 #include "bu/parallel.h"
 #include "bu/vls.h"
 
+#include "y2038/time64.h"
+
 void
 bu_utctime(struct bu_vls *vls_gmtime, const int64_t time_val)
 {
@@ -33,7 +35,7 @@
     static const char *nulltime = "0000-00-00T00:00:00Z";
     struct tm loctime;
     struct tm* retval;
-    time_t some_time;
+    Time64_T some_time;
     int fail = 0;
 
     if (!vls_gmtime)
@@ -41,8 +43,8 @@
 
     BU_CK_VLS(vls_gmtime);
 
-    some_time = (time_t)time_val;
-    if (some_time == (time_t)(-1)) {
+    some_time = (Time64_T)time_val;
+    if (some_time == (Time64_T)(-1)) {
 	/* time error: but set something, an invalid "NULL" time. */
 	bu_vls_sprintf(vls_gmtime, nulltime);
 	return;
@@ -51,7 +53,7 @@
     memset(&loctime, 0, sizeof(loctime));
 
     bu_semaphore_acquire(BU_SEM_DATETIME);
-    retval = gmtime(&some_time);
+    retval = gmtime64(&some_time);
     if (retval)
 	loctime = *retval; /* struct copy */
     else
Index: src/libbu/y2038/CMakeLists.txt
===================================================================
--- src/libbu/y2038/CMakeLists.txt	(revision 0)
+++ src/libbu/y2038/CMakeLists.txt	(working copy)
@@ -0,0 +1,5 @@
+set(Y2038_SOURCES
+  time64.c
+  )
+
+BRLCAD_ADDLIB(y2038 "${Y2038_SOURCES}" "")
Index: src/libbu/y2038/LICENSE
===================================================================
--- src/libbu/y2038/LICENSE	(revision 0)
+++ src/libbu/y2038/LICENSE	(working copy)
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (c) 2007-2010 Michael G Schwern
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
Index: src/libbu/y2038/README.txt
===================================================================
--- src/libbu/y2038/README.txt	(revision 0)
+++ src/libbu/y2038/README.txt	(working copy)
@@ -0,0 +1,67 @@
+This is an implementation of POSIX time.h which solves the year 2038
+bug on systems where time_t is only 32 bits.  It is implemented in
+bog-standard ANSI C.  The latest version can be found at
+http://y2038.googlecode.com/
+
+It makes use of the system's native 32 bit functions to perform time
+zone and daylight savings time calculations and thus does *not* need
+to ship its own time zone table.
+
+Here is a list of the currently implemented functions in time64.h and
+their POSIX time.h equivalents.
+
+  time64.h                      time.h
+  --------                      ------
+  localtime64_r                 localtime_r
+  localtime64                   localtime
+  gmtime64_r                    gmtime_r
+  gmtime64                      gmtime
+  asctime64_r                   asctime_r
+  asctime64                     asctime
+  ctime64_r                     ctime_r
+  ctime64                       ctime
+  timelocal64                   mktime
+  mktime64                      mktime
+  timegm64                      timegm (a GNU extension)
+
+To install, simply copy time64.c and time64.h into your project and
+make use of the functions.
+
+To test, run "make test".  You must have Perl, prove (which comes with
+a recent version of the Test::Harness Perl module) and bzdiff
+installed to run the full test suite.  It will do a number of unit
+tests, plus test against a large table of known good values in
+different time zones.
+
+
+Limitations, Issues, etc...
+---------------------------
+
+localtime64_r() gets its time zone and daylight savings time
+information by mappping the future year back to a similar one between
+2010 and 2037, safe for localtime_r().  The calculations are accurate
+according to current time zone and daylight savings information, but
+may become inaccurate if a change is made that takes place after 2010.
+But its better to be off by an hour than 137 years.
+
+Future versions will probe for a 64 bit safe system localtime_r() and
+gmtime_r() and use that.  You can manually set the safe range of your
+system in time64_config.h.
+
+The maximum date is still limited by your tm struct.  Most 32 bit
+systems use a signed integer tm_year which means the practical upper
+limit is the year 2147483647 which is somewhere around 2**54.  You can
+use a 64 bit clean tm struct by setting USE_TM64 in time64_config.h
+
+
+Portability
+-----------
+
+I would like to add some configuration detection stuff in the future, but
+for now all I can do is document the assumptions...
+
+This code assumes that long longs are 64 bit integers which is technically
+in violation of the C standard.  This can be changed in time64.h by
+changing the Time64_T and Int64 typedefs.
+
+There are a number of configuration options in time64.h.
Index: src/libbu/y2038/time64.c
===================================================================
--- src/libbu/y2038/time64.c	(revision 0)
+++ src/libbu/y2038/time64.c	(working copy)
@@ -0,0 +1,886 @@
+/* 
+
+Copyright (c) 2007-2010  Michael G Schwern
+
+This software originally derived from Paul Sheer's pivotal_gmtime_r.c.
+
+The MIT License:
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+*/
+
+/*
+
+Programmers who have available to them 64-bit time values as a 'long
+long' type can use localtime64_r() and gmtime64_r() which correctly
+converts the time even on 32-bit systems. Whether you have 64-bit time 
+values will depend on the operating system.
+
+localtime64_r() is a 64-bit equivalent of localtime_r().
+
+gmtime64_r() is a 64-bit equivalent of gmtime_r().
+
+*/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+#include "time64.h"
+#include "time64_limits.h"
+
+/* Max/min for mktime() */
+/* MODIFIED: Assign to each field so as not to depend on system field
+   ordering
+*/
+static struct tm SYSTEM_MKTIME_MAX;
+static struct tm SYSTEM_MKTIME_MIN;
+
+static int mktime_maxmin_initialized = 0;
+
+void initialize_mktime_maxmin()
+{
+  SYSTEM_MKTIME_MAX.tm_sec = 7;
+  SYSTEM_MKTIME_MAX.tm_min = 14;
+  SYSTEM_MKTIME_MAX.tm_hour = 19;
+  SYSTEM_MKTIME_MAX.tm_mday = 18;
+  SYSTEM_MKTIME_MAX.tm_mon = 0;
+  SYSTEM_MKTIME_MAX.tm_year = 138;
+  SYSTEM_MKTIME_MAX.tm_wday = 1;
+  SYSTEM_MKTIME_MAX.tm_yday = 17;
+  SYSTEM_MKTIME_MAX.tm_isdst = 0;
+#ifdef HAS_TM_TM_GMTOFF
+  SYSTEM_MKTIME_MAX.tm_gmtoff = -28800;
+#endif
+#ifdef HAS_TM_TM_ZONE
+  SYSTEM_MKTIME_MAX.tm_zone = "PST";
+#endif
+
+  SYSTEM_MKTIME_MIN.tm_sec = 52;
+  SYSTEM_MKTIME_MIN.tm_min = 45;
+  SYSTEM_MKTIME_MIN.tm_hour = 12;
+  SYSTEM_MKTIME_MIN.tm_mday = 13;
+  SYSTEM_MKTIME_MIN.tm_mon = 11;
+  SYSTEM_MKTIME_MIN.tm_year = 1;
+  SYSTEM_MKTIME_MIN.tm_wday = 5;
+  SYSTEM_MKTIME_MIN.tm_yday = 346;
+  SYSTEM_MKTIME_MIN.tm_isdst = 0;
+#ifdef HAS_TM_TM_GMTOFF
+  SYSTEM_MKTIME_MIN.tm_gmtoff = 2880;
+#endif
+#ifdef HAS_TM_TM_ZONE
+  SYSTEM_MKTIME_MIN.tm_zone = "PST";
+#endif
+
+  mktime_maxmin_initialized = 1;
+}
+
+/* Spec says except for stftime() and the _r() functions, these
+   all return static memory.  Stabbings! */
+static struct TM   Static_Return_Date;
+static char        Static_Return_String[35];
+
+static const char days_in_month[2][12] = {
+    {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
+    {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
+};
+
+static const short julian_days_by_month[2][12] = {
+    {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
+    {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335},
+};
+
+static char wday_name[7][4] = {
+    "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
+
+static char mon_name[12][4] = {
+    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+static const short length_of_year[2] = { 365, 366 };
+
+/* Some numbers relating to the gregorian cycle */
+static const Year     years_in_gregorian_cycle   = 400;
+#define               days_in_gregorian_cycle      ((365 * 400) + 100 - 4 + 1)
+static const Time64_T seconds_in_gregorian_cycle = days_in_gregorian_cycle * 60LL * 60LL * 24LL;
+
+/* Year range we can trust the time funcitons with */
+#define MAX_SAFE_YEAR 2037
+#define MIN_SAFE_YEAR 1971
+
+/* 28 year Julian calendar cycle */
+#define SOLAR_CYCLE_LENGTH 28
+
+/* Year cycle from MAX_SAFE_YEAR down. */
+static const short safe_years_high[SOLAR_CYCLE_LENGTH] = {
+    2016, 2017, 2018, 2019,
+    2020, 2021, 2022, 2023,
+    2024, 2025, 2026, 2027,
+    2028, 2029, 2030, 2031,
+    2032, 2033, 2034, 2035,
+    2036, 2037, 2010, 2011,
+    2012, 2013, 2014, 2015
+};
+
+/* Year cycle from MIN_SAFE_YEAR up */
+static const int safe_years_low[SOLAR_CYCLE_LENGTH] = {
+    1996, 1997, 1998, 1971,
+    1972, 1973, 1974, 1975,
+    1976, 1977, 1978, 1979,
+    1980, 1981, 1982, 1983,
+    1984, 1985, 1986, 1987,
+    1988, 1989, 1990, 1991,
+    1992, 1993, 1994, 1995,
+};
+
+/* This isn't used, but it's handy to look at */
+static const char dow_year_start[SOLAR_CYCLE_LENGTH] = {
+    5, 0, 1, 2,     /* 0       2016 - 2019 */
+    3, 5, 6, 0,     /* 4  */
+    1, 3, 4, 5,     /* 8       1996 - 1998, 1971*/
+    6, 1, 2, 3,     /* 12      1972 - 1975 */
+    4, 6, 0, 1,     /* 16 */
+    2, 4, 5, 6,     /* 20      2036, 2037, 2010, 2011 */
+    0, 2, 3, 4      /* 24      2012, 2013, 2014, 2015 */
+};
+
+/* Let's assume people are going to be looking for dates in the future.
+   Let's provide some cheats so you can skip ahead.
+   This has a 4x speed boost when near 2008.
+*/
+/* Number of days since epoch on Jan 1st, 2008 GMT */
+#define CHEAT_DAYS  (1199145600 / 24 / 60 / 60)
+#define CHEAT_YEARS 108
+
+#define IS_LEAP(n)      ((!(((n) + 1900) % 400) || (!(((n) + 1900) % 4) && (((n) + 1900) % 100))) != 0)
+#define WRAP(a,b,m)     ((a) = ((a) <  0  ) ? ((b)--, (a) + (m)) : (a))
+
+#ifdef USE_SYSTEM_LOCALTIME
+#    define SHOULD_USE_SYSTEM_LOCALTIME(a)  (       \
+    (a) <= SYSTEM_LOCALTIME_MAX &&              \
+    (a) >= SYSTEM_LOCALTIME_MIN                 \
+)
+#else
+#    define SHOULD_USE_SYSTEM_LOCALTIME(a)      (0)
+#endif
+
+#ifdef USE_SYSTEM_GMTIME
+#    define SHOULD_USE_SYSTEM_GMTIME(a)     (       \
+    (a) <= SYSTEM_GMTIME_MAX    &&              \
+    (a) >= SYSTEM_GMTIME_MIN                    \
+)
+#else
+#    define SHOULD_USE_SYSTEM_GMTIME(a)         (0)
+#endif
+
+/* Multi varadic macros are a C99 thing, alas */
+#ifdef TIME_64_DEBUG
+#    define TIME64_TRACE(format) (fprintf(stderr, format))
+#    define TIME64_TRACE1(format, var1)    (fprintf(stderr, format, var1))
+#    define TIME64_TRACE2(format, var1, var2)    (fprintf(stderr, format, var1, var2))
+#    define TIME64_TRACE3(format, var1, var2, var3)    (fprintf(stderr, format, var1, var2, var3))
+#else
+#    define TIME64_TRACE(format) ((void)0)
+#    define TIME64_TRACE1(format, var1) ((void)0)
+#    define TIME64_TRACE2(format, var1, var2) ((void)0)
+#    define TIME64_TRACE3(format, var1, var2, var3) ((void)0)
+#endif
+
+
+static int is_exception_century(Year year)
+{
+    int is_exception = ((year % 100 == 0) && !(year % 400 == 0));
+    TIME64_TRACE1("# is_exception_century: %s\n", is_exception ? "yes" : "no");
+
+    return(is_exception);
+}
+
+
+/* Compare two dates.
+   The result is like cmp.
+   Ignores things like gmtoffset and dst
+*/
+int cmp_date( const struct TM* left, const struct tm* right ) {
+    if( left->tm_year > right->tm_year )
+        return 1;
+    else if( left->tm_year < right->tm_year )
+        return -1;
+
+    if( left->tm_mon > right->tm_mon )
+        return 1;
+    else if( left->tm_mon < right->tm_mon )
+        return -1;
+
+    if( left->tm_mday > right->tm_mday )
+        return 1;
+    else if( left->tm_mday < right->tm_mday )
+        return -1;
+
+    if( left->tm_hour > right->tm_hour )
+        return 1;
+    else if( left->tm_hour < right->tm_hour )
+        return -1;
+
+    if( left->tm_min > right->tm_min )
+        return 1;
+    else if( left->tm_min < right->tm_min )
+        return -1;
+
+    if( left->tm_sec > right->tm_sec )
+        return 1;
+    else if( left->tm_sec < right->tm_sec )
+        return -1;
+
+    return 0;
+}
+
+
+/* Check if a date is safely inside a range.
+   The intention is to check if its a few days inside.
+*/
+int date_in_safe_range( const struct TM* date, const struct tm* min, const struct tm* max ) {
+    if( cmp_date(date, min) == -1 )
+        return 0;
+
+    if( cmp_date(date, max) == 1 )
+        return 0;
+
+    return 1;
+}
+
+
+/* timegm() is not in the C or POSIX spec, but it is such a useful
+   extension I would be remiss in leaving it out.  Also I need it
+   for localtime64()
+*/
+Time64_T timegm64(const struct TM *date) {
+    Time64_T days    = 0;
+    Time64_T seconds = 0;
+    Year     year;
+    Year     orig_year = (Year)date->tm_year;
+    int      cycles  = 0;
+
+    if( orig_year > 100 ) {
+        cycles = (orig_year - 100) / 400;
+        orig_year -= cycles * 400;
+        days      += (Time64_T)cycles * days_in_gregorian_cycle;
+    }
+    else if( orig_year < -300 ) {
+        cycles = (orig_year - 100) / 400;
+        orig_year -= cycles * 400;
+        days      += (Time64_T)cycles * days_in_gregorian_cycle;
+    }
+    TIME64_TRACE3("# timegm/ cycles: %d, days: %lld, orig_year: %lld\n", cycles, days, orig_year);
+
+    if( orig_year > 70 ) {
+        year = 70;
+        while( year < orig_year ) {
+            days += length_of_year[IS_LEAP(year)];
+            year++;
+        }
+    }
+    else if ( orig_year < 70 ) {
+        year = 69;
+        do {
+            days -= length_of_year[IS_LEAP(year)];
+            year--;
+        } while( year >= orig_year );
+    }
+
+    days += julian_days_by_month[IS_LEAP(orig_year)][date->tm_mon];
+    days += date->tm_mday - 1;
+
+    seconds = days * 60 * 60 * 24;
+
+    seconds += date->tm_hour * 60 * 60;
+    seconds += date->tm_min * 60;
+    seconds += date->tm_sec;
+
+    return(seconds);
+}
+
+
+static int check_tm(struct TM *tm)
+{
+    /* Don't forget leap seconds */
+    assert(tm->tm_sec >= 0);
+    assert(tm->tm_sec <= 61);
+
+    assert(tm->tm_min >= 0);
+    assert(tm->tm_min <= 59);
+
+    assert(tm->tm_hour >= 0);
+    assert(tm->tm_hour <= 23);
+
+    assert(tm->tm_mday >= 1);
+    assert(tm->tm_mday <= days_in_month[IS_LEAP(tm->tm_year)][tm->tm_mon]);
+
+    assert(tm->tm_mon  >= 0);
+    assert(tm->tm_mon  <= 11);
+
+    assert(tm->tm_wday >= 0);
+    assert(tm->tm_wday <= 6);
+    
+    assert(tm->tm_yday >= 0);
+    assert(tm->tm_yday <= length_of_year[IS_LEAP(tm->tm_year)]);
+
+#ifdef HAS_TM_TM_GMTOFF
+    assert(tm->tm_gmtoff >= -24 * 60 * 60);
+    assert(tm->tm_gmtoff <=  24 * 60 * 60);
+#endif
+
+    return 1;
+}
+
+
+/* The exceptional centuries without leap years cause the cycle to
+   shift by 16
+*/
+static Year cycle_offset(Year year)
+{
+    const Year start_year = 2000;
+    Year year_diff  = year - start_year;
+    Year exceptions;
+
+    if( year > start_year )
+        year_diff--;
+
+    exceptions  = year_diff / 100;
+    exceptions -= year_diff / 400;
+
+    TIME64_TRACE3("# year: %lld, exceptions: %lld, year_diff: %lld\n",
+          year, exceptions, year_diff);
+
+    return exceptions * 16;
+}
+
+/* For a given year after 2038, pick the latest possible matching
+   year in the 28 year calendar cycle.
+
+   A matching year...
+   1) Starts on the same day of the week.
+   2) Has the same leap year status.
+
+   This is so the calendars match up.
+
+   Also the previous year must match.  When doing Jan 1st you might
+   wind up on Dec 31st the previous year when doing a -UTC time zone.
+
+   Finally, the next year must have the same start day of week.  This
+   is for Dec 31st with a +UTC time zone.
+   It doesn't need the same leap year status since we only care about
+   January 1st.
+
+   MODIFIED: Compile better by renaming safe_year variable to not
+   shadow funtion.
+*/
+static int safe_year(const Year year)
+{
+    int ret;
+    Year year_cycle;
+
+    if( year >= MIN_SAFE_YEAR && year <= MAX_SAFE_YEAR ) {
+        return (int)year;
+    }
+
+    year_cycle = year + cycle_offset(year);
+
+    /* safe_years_low is off from safe_years_high by 8 years */
+    if( year < MIN_SAFE_YEAR )
+        year_cycle -= 8;
+
+    /* Change non-leap xx00 years to an equivalent */
+    if( is_exception_century(year) )
+        year_cycle += 11;
+
+    /* Also xx01 years, since the previous year will be wrong */
+    if( is_exception_century(year - 1) )
+        year_cycle += 17;
+
+    year_cycle %= SOLAR_CYCLE_LENGTH;
+    if( year_cycle < 0 ) 
+        year_cycle = SOLAR_CYCLE_LENGTH + year_cycle;
+
+    assert( year_cycle >= 0 );
+    assert( year_cycle < SOLAR_CYCLE_LENGTH );
+    if( year < MIN_SAFE_YEAR )
+        ret = safe_years_low[year_cycle];
+    else if( year > MAX_SAFE_YEAR )
+        ret = safe_years_high[year_cycle];
+    else
+        assert(0);
+
+    TIME64_TRACE3("# year: %lld, year_cycle: %lld, safe_year: %d\n",
+          year, year_cycle, ret);
+
+    assert(ret <= MAX_SAFE_YEAR && ret >= MIN_SAFE_YEAR);
+
+    return ret;
+}
+
+
+void copy_tm_to_TM64(const struct tm *src, struct TM *dest) {
+    if( src == NULL ) {
+        memset(dest, 0, sizeof(*dest));
+    }
+    else {
+#       ifdef USE_TM64
+            dest->tm_sec        = src->tm_sec;
+            dest->tm_min        = src->tm_min;
+            dest->tm_hour       = src->tm_hour;
+            dest->tm_mday       = src->tm_mday;
+            dest->tm_mon        = src->tm_mon;
+            dest->tm_year       = (Year)src->tm_year;
+            dest->tm_wday       = src->tm_wday;
+            dest->tm_yday       = src->tm_yday;
+            dest->tm_isdst      = src->tm_isdst;
+
+#           ifdef HAS_TM_TM_GMTOFF
+                dest->tm_gmtoff  = src->tm_gmtoff;
+#           endif
+
+#           ifdef HAS_TM_TM_ZONE
+                dest->tm_zone  = src->tm_zone;
+#           endif
+
+#       else
+            /* They're the same type */
+            memcpy(dest, src, sizeof(*dest));
+#       endif
+    }
+}
+
+
+void copy_TM64_to_tm(const struct TM *src, struct tm *dest) {
+    if( src == NULL ) {
+        memset(dest, 0, sizeof(*dest));
+    }
+    else {
+#       ifdef USE_TM64
+            dest->tm_sec        = src->tm_sec;
+            dest->tm_min        = src->tm_min;
+            dest->tm_hour       = src->tm_hour;
+            dest->tm_mday       = src->tm_mday;
+            dest->tm_mon        = src->tm_mon;
+            dest->tm_year       = (int)src->tm_year;
+            dest->tm_wday       = src->tm_wday;
+            dest->tm_yday       = src->tm_yday;
+            dest->tm_isdst      = src->tm_isdst;
+
+#           ifdef HAS_TM_TM_GMTOFF
+                dest->tm_gmtoff  = src->tm_gmtoff;
+#           endif
+
+#           ifdef HAS_TM_TM_ZONE
+                dest->tm_zone  = src->tm_zone;
+#           endif
+
+#       else
+            /* They're the same type */
+            memcpy(dest, src, sizeof(*dest));
+#       endif
+    }
+}
+
+
+/* Simulate localtime_r() to the best of our ability */
+struct tm * fake_localtime_r(const time_t *time, struct tm *result) {
+    const struct tm *static_result = localtime(time);
+
+    assert(result != NULL);
+
+    if( static_result == NULL ) {
+        memset(result, 0, sizeof(*result));
+        return NULL;
+    }
+    else {
+        memcpy(result, static_result, sizeof(*result));
+        return result;
+    }
+}
+
+
+/* Simulate gmtime_r() to the best of our ability */
+struct tm * fake_gmtime_r(const time_t *time, struct tm *result) {
+    const struct tm *static_result = gmtime(time);
+
+    assert(result != NULL);
+
+    if( static_result == NULL ) {
+        memset(result, 0, sizeof(*result));
+        return NULL;
+    }
+    else {
+        memcpy(result, static_result, sizeof(*result));
+        return result;
+    }
+}
+
+
+static Time64_T seconds_between_years(Year left_year, Year right_year) {
+    int increment = (left_year > right_year) ? 1 : -1;
+    Time64_T seconds = 0;
+    int cycles;
+
+    if( left_year > 2400 ) {
+        cycles = (left_year - 2400) / 400;
+        left_year -= cycles * 400;
+        seconds   += cycles * seconds_in_gregorian_cycle;
+    }
+    else if( left_year < 1600 ) {
+        cycles = (left_year - 1600) / 400;
+        left_year += cycles * 400;
+        seconds   += cycles * seconds_in_gregorian_cycle;
+    }
+
+    while( left_year != right_year ) {
+        seconds += length_of_year[IS_LEAP(right_year - 1900)] * 60 * 60 * 24;
+        right_year += increment;
+    }
+
+    return seconds * increment;
+}
+
+
+Time64_T mktime64(struct TM *input_date) {
+    struct tm safe_date;
+    struct TM date;
+    Time64_T  time;
+    Year      year = input_date->tm_year + 1900;
+
+    /* MODIFIED: this check added, used to make sure that the mktime()
+       max and min structures are initialized now that they are not
+       initialized through a struct literal (see above).
+    */
+    if (!mktime_maxmin_initialized)
+    {
+        initialize_mktime_maxmin();
+    }
+    if( date_in_safe_range(input_date, &SYSTEM_MKTIME_MIN, &SYSTEM_MKTIME_MAX) )
+    {
+        copy_TM64_to_tm(input_date, &safe_date);
+        time = (Time64_T)mktime(&safe_date);
+
+        /* Correct the possibly out of bound input date */
+        copy_tm_to_TM64(&safe_date, input_date);
+        return time;
+    }
+
+    /* Have to make the year safe in date else it won't fit in safe_date */
+    date = *input_date;
+    date.tm_year = safe_year(year) - 1900;
+    copy_TM64_to_tm(&date, &safe_date);
+
+    time = (Time64_T)mktime(&safe_date);
+
+    /* Correct the user's possibly out of bound input date */
+    copy_tm_to_TM64(&safe_date, input_date);
+
+    time += seconds_between_years(year, (Year)(safe_date.tm_year + 1900));
+
+    return time;
+}
+
+
+/* Because I think mktime() is a crappy name */
+Time64_T timelocal64(struct TM *date) {
+    return mktime64(date);
+}
+
+
+struct TM *gmtime64_r (const Time64_T *in_time, struct TM *p)
+{
+    int v_tm_sec, v_tm_min, v_tm_hour, v_tm_mon, v_tm_wday;
+    Time64_T v_tm_tday;
+    int leap;
+    Time64_T m;
+    Time64_T time = *in_time;
+    Year year = 70;
+    int cycles = 0;
+
+    assert(p != NULL);
+
+    /* Use the system gmtime() if time_t is small enough */
+    if( SHOULD_USE_SYSTEM_GMTIME(*in_time) ) {
+        time_t safe_time = (time_t)*in_time;
+        struct tm safe_date;
+        GMTIME_R(&safe_time, &safe_date);
+
+        copy_tm_to_TM64(&safe_date, p);
+        assert(check_tm(p));
+
+        return p;
+    }
+
+#ifdef HAS_TM_TM_GMTOFF
+    p->tm_gmtoff = 0;
+#endif
+    p->tm_isdst  = 0;
+
+#ifdef HAS_TM_TM_ZONE
+    p->tm_zone   = "UTC";
+#endif
+
+    v_tm_sec =  (int)(time % 60);
+    time /= 60;
+    v_tm_min =  (int)(time % 60);
+    time /= 60;
+    v_tm_hour = (int)(time % 24);
+    time /= 24;
+    v_tm_tday = time;
+
+    WRAP (v_tm_sec, v_tm_min, 60);
+    WRAP (v_tm_min, v_tm_hour, 60);
+    WRAP (v_tm_hour, v_tm_tday, 24);
+
+    v_tm_wday = (int)((v_tm_tday + 4) % 7);
+    if (v_tm_wday < 0)
+        v_tm_wday += 7;
+    m = v_tm_tday;
+
+    if (m >= CHEAT_DAYS) {
+        year = CHEAT_YEARS;
+        m -= CHEAT_DAYS;
+    }
+
+    if (m >= 0) {
+        /* Gregorian cycles, this is huge optimization for distant times */
+        cycles = (int)(m / (Time64_T) days_in_gregorian_cycle);
+        if( cycles ) {
+            m -= (cycles * (Time64_T) days_in_gregorian_cycle);
+            year += (cycles * years_in_gregorian_cycle);
+        }
+
+        /* Years */
+        leap = IS_LEAP (year);
+        while (m >= (Time64_T) length_of_year[leap]) {
+            m -= (Time64_T) length_of_year[leap];
+            year++;
+            leap = IS_LEAP (year);
+        }
+
+        /* Months */
+        v_tm_mon = 0;
+        while (m >= (Time64_T) days_in_month[leap][v_tm_mon]) {
+            m -= (Time64_T) days_in_month[leap][v_tm_mon];
+            v_tm_mon++;
+        }
+    } else {
+        year--;
+
+        /* Gregorian cycles */
+        cycles = (int)((m / (Time64_T) days_in_gregorian_cycle) + 1);
+        if( cycles ) {
+            m -= (cycles * (Time64_T) days_in_gregorian_cycle);
+            year += (cycles * years_in_gregorian_cycle);
+        }
+
+        /* Years */
+        leap = IS_LEAP (year);
+        while (m < (Time64_T) -length_of_year[leap]) {
+            m += (Time64_T) length_of_year[leap];
+            year--;
+            leap = IS_LEAP (year);
+        }
+
+        /* Months */
+        v_tm_mon = 11;
+        while (m < (Time64_T) -days_in_month[leap][v_tm_mon]) {
+            m += (Time64_T) days_in_month[leap][v_tm_mon];
+            v_tm_mon--;
+        }
+        m += (Time64_T) days_in_month[leap][v_tm_mon];
+    }
+
+    p->tm_year = year;
+    if( p->tm_year != year ) {
+#ifdef EOVERFLOW
+        errno = EOVERFLOW;
+#endif
+        return NULL;
+    }
+
+    /* At this point m is less than a year so casting to an int is safe */
+    p->tm_mday = (int) m + 1;
+    p->tm_yday = julian_days_by_month[leap][v_tm_mon] + (int)m;
+    p->tm_sec  = v_tm_sec;
+    p->tm_min  = v_tm_min;
+    p->tm_hour = v_tm_hour;
+    p->tm_mon  = v_tm_mon;
+    p->tm_wday = v_tm_wday;
+    
+    assert(check_tm(p));
+
+    return p;
+}
+
+
+struct TM *localtime64_r (const Time64_T *time, struct TM *local_tm)
+{
+    time_t safe_time;
+    struct tm safe_date;
+    struct TM gm_tm;
+    Year orig_year;
+    int month_diff;
+
+    assert(local_tm != NULL);
+
+    /* Use the system localtime() if time_t is small enough */
+    if( SHOULD_USE_SYSTEM_LOCALTIME(*time) ) {
+        safe_time = (time_t)*time;
+
+        TIME64_TRACE1("Using system localtime for %lld\n", *time);
+
+        LOCALTIME_R(&safe_time, &safe_date);
+
+        copy_tm_to_TM64(&safe_date, local_tm);
+        assert(check_tm(local_tm));
+
+        return local_tm;
+    }
+
+    if( gmtime64_r(time, &gm_tm) == NULL ) {
+        TIME64_TRACE1("gmtime64_r returned null for %lld\n", *time);
+        return NULL;
+    }
+
+    orig_year = gm_tm.tm_year;
+
+    if (gm_tm.tm_year > (2037 - 1900) ||
+        gm_tm.tm_year < (1970 - 1900)
+       )
+    {
+        TIME64_TRACE1("Mapping tm_year %lld to safe_year\n", (Year)gm_tm.tm_year);
+        gm_tm.tm_year = safe_year((Year)(gm_tm.tm_year + 1900)) - 1900;
+    }
+
+    safe_time = (time_t)timegm64(&gm_tm);
+    if( LOCALTIME_R(&safe_time, &safe_date) == NULL ) {
+        TIME64_TRACE1("localtime_r(%d) returned NULL\n", (int)safe_time);
+        return NULL;
+    }
+
+    copy_tm_to_TM64(&safe_date, local_tm);
+
+    local_tm->tm_year = orig_year;
+    if( local_tm->tm_year != orig_year ) {
+        TIME64_TRACE2("tm_year overflow: tm_year %lld, orig_year %lld\n",
+              (Year)local_tm->tm_year, (Year)orig_year);
+
+#ifdef EOVERFLOW
+        errno = EOVERFLOW;
+#endif
+        return NULL;
+    }
+
+
+    month_diff = local_tm->tm_mon - gm_tm.tm_mon;
+
+    /*  When localtime is Dec 31st previous year and
+        gmtime is Jan 1st next year.
+    */
+    if( month_diff == 11 ) {
+        local_tm->tm_year--;
+    }
+
+    /*  When localtime is Jan 1st, next year and
+        gmtime is Dec 31st, previous year.
+    */
+    if( month_diff == -11 ) {
+        local_tm->tm_year++;
+    }
+
+    /* GMT is Jan 1st, xx01 year, but localtime is still Dec 31st 
+       in a non-leap xx00.  There is one point in the cycle
+       we can't account for which the safe xx00 year is a leap
+       year.  So we need to correct for Dec 31st comming out as
+       the 366th day of the year.
+    */
+    if( !IS_LEAP(local_tm->tm_year) && local_tm->tm_yday == 365 )
+        local_tm->tm_yday--;
+
+    assert(check_tm(local_tm));
+    
+    return local_tm;
+}
+
+
+int valid_tm_wday( const struct TM* date ) {
+    if( 0 <= date->tm_wday && date->tm_wday <= 6 )
+        return 1;
+    else
+        return 0;
+}
+
+int valid_tm_mon( const struct TM* date ) {
+    if( 0 <= date->tm_mon && date->tm_mon <= 11 )
+        return 1;
+    else
+        return 0;
+}
+
+
+char *asctime64_r( const struct TM* date, char *result ) {
+    /* I figure everything else can be displayed, even hour 25, but if
+       these are out of range we walk off the name arrays */
+    if( !valid_tm_wday(date) || !valid_tm_mon(date) )
+        return NULL;
+
+    sprintf(result, TM64_ASCTIME_FORMAT,
+        wday_name[date->tm_wday],
+        mon_name[date->tm_mon],
+        date->tm_mday, date->tm_hour,
+        date->tm_min, date->tm_sec,
+        1900 + date->tm_year);
+
+    return result;
+}
+
+
+char *ctime64_r( const Time64_T* time, char* result ) {
+    struct TM date;
+
+    localtime64_r( time, &date );
+    return asctime64_r( &date, result );
+}
+
+
+/* Non-thread safe versions of the above */
+struct TM *localtime64(const Time64_T *time) {
+    tzset();
+    return localtime64_r(time, &Static_Return_Date);
+}
+
+struct TM *gmtime64(const Time64_T *time) {
+    return gmtime64_r(time, &Static_Return_Date);
+}
+
+char *asctime64( const struct TM* date ) {
+    return asctime64_r( date, Static_Return_String );
+}
+
+char *ctime64( const Time64_T* time ) {
+    tzset();
+    return asctime64(localtime64(time));
+}
Index: src/libbu/y2038/time64.h
===================================================================
--- src/libbu/y2038/time64.h	(revision 0)
+++ src/libbu/y2038/time64.h	(working copy)
@@ -0,0 +1,81 @@
+#ifndef TIME64_H
+#    define TIME64_H
+
+#include <time.h>
+#include "time64_config.h"
+
+/* Set our custom types */
+typedef INT_64_T        Int64;
+typedef Int64           Time64_T;
+typedef Int64           Year;
+
+
+/* A copy of the tm struct but with a 64 bit year */
+struct TM64 {
+        int     tm_sec;
+        int     tm_min;
+        int     tm_hour;
+        int     tm_mday;
+        int     tm_mon;
+        Year    tm_year;
+        int     tm_wday;
+        int     tm_yday;
+        int     tm_isdst;
+
+#ifdef HAS_TM_TM_GMTOFF
+        long    tm_gmtoff;
+#endif
+
+#ifdef HAS_TM_TM_ZONE
+        char    *tm_zone;
+#endif
+};
+
+
+/* Decide which tm struct to use */
+#ifdef USE_TM64
+#define TM      TM64
+#else
+#define TM      tm
+#endif   
+
+
+/* Declare public functions */
+struct TM *gmtime64_r    (const Time64_T *, struct TM *);
+struct TM *localtime64_r (const Time64_T *, struct TM *);
+struct TM *gmtime64      (const Time64_T *);
+struct TM *localtime64   (const Time64_T *);
+
+char *asctime64          (const struct TM *);
+char *asctime64_r        (const struct TM *, char *);
+
+char *ctime64            (const Time64_T*);
+char *ctime64_r          (const Time64_T*, char*);
+
+Time64_T   timegm64      (const struct TM *);
+Time64_T   mktime64      (struct TM *);
+Time64_T   timelocal64   (struct TM *);
+
+
+/* Not everyone has gm/localtime_r(), provide a replacement */
+#ifdef HAS_LOCALTIME_R
+#    define LOCALTIME_R(clock, result) localtime_r(clock, result)
+#else
+#    define LOCALTIME_R(clock, result) fake_localtime_r(clock, result)
+#endif
+#ifdef HAS_GMTIME_R
+#    define GMTIME_R(clock, result)    gmtime_r(clock, result)
+#else
+#    define GMTIME_R(clock, result)    fake_gmtime_r(clock, result)
+#endif
+
+
+/* Use a different asctime format depending on how big the year is */
+#ifdef USE_TM64
+    #define TM64_ASCTIME_FORMAT "%.3s %.3s%3d %.2d:%.2d:%.2d %lld\n"
+#else
+    #define TM64_ASCTIME_FORMAT "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n"
+#endif
+
+
+#endif
Index: src/libbu/y2038/time64_config.h
===================================================================
--- src/libbu/y2038/time64_config.h	(revision 0)
+++ src/libbu/y2038/time64_config.h	(working copy)
@@ -0,0 +1,84 @@
+/* Configuration
+   -------------
+   Define as appropriate for your system.
+   Sensible defaults provided.
+*/
+
+
+#ifndef TIME64_CONFIG_H
+#    define TIME64_CONFIG_H
+
+/* Debugging
+   TIME_64_DEBUG
+   Define if you want debugging messages
+*/
+/* #define TIME_64_DEBUG */
+
+
+/* INT_64_T
+   A 64 bit integer type to use to store time and others.
+   Must be defined.
+
+   MODIFIED: Use BRL-CAD's int64_t
+*/
+#define INT_64_T                int64_t
+
+
+/* USE_TM64
+   Should we use a 64 bit safe replacement for tm?  This will
+   let you go past year 2 billion but the struct will be incompatible
+   with tm.  Conversion functions will be provided.
+*/
+/* #define USE_TM64 */
+
+
+/* Availability of system functions.
+
+   HAS_GMTIME_R
+   Define if your system has gmtime_r()
+
+   HAS_LOCALTIME_R
+   Define if your system has localtime_r()
+
+   HAS_TIMEGM
+   Define if your system has timegm(), a GNU extension.
+
+   MODIFIED: Reduce necessary number of CMake checks by assuming that
+   nothing nonstandard exists.
+*/
+/* #define HAS_GMTIME_R */
+/* #define HAS_LOCALTIME_R */
+/* #define HAS_TIMEGM */
+
+
+/* Details of non-standard tm struct elements.
+
+   HAS_TM_TM_GMTOFF
+   True if your tm struct has a "tm_gmtoff" element.
+   A BSD extension.
+
+   HAS_TM_TM_ZONE
+   True if your tm struct has a "tm_zone" element.
+   A BSD extension.
+*/
+/* #define HAS_TM_TM_GMTOFF */
+/* #define HAS_TM_TM_ZONE */
+
+
+/* USE_SYSTEM_LOCALTIME
+   USE_SYSTEM_GMTIME
+   USE_SYSTEM_MKTIME
+   USE_SYSTEM_TIMEGM
+   Should we use the system functions if the time is inside their range?
+   Your system localtime() is probably more accurate, but our gmtime() is
+   fast and safe.
+
+   MODIFIED: Never use any system functions so that we don't have to
+   worry about the size of the host time_t.
+*/
+/* #define USE_SYSTEM_LOCALTIME */
+/* #define USE_SYSTEM_GMTIME */
+/* #define USE_SYSTEM_MKTIME */
+/* #define USE_SYSTEM_TIMEGM */
+
+#endif /* TIME64_CONFIG_H */
Index: src/libbu/y2038/time64_limits.h
===================================================================
--- src/libbu/y2038/time64_limits.h	(revision 0)
+++ src/libbu/y2038/time64_limits.h	(working copy)
@@ -0,0 +1,27 @@
+/* 
+   Maximum and minimum inputs your system's respective time functions
+   can correctly handle.  time64.h will use your system functions if
+   the input falls inside these ranges and corresponding USE_SYSTEM_*
+   constant is defined.
+*/
+
+#ifndef TIME64_LIMITS_H
+#define TIME64_LIMITS_H
+
+/* Max/min for localtime() */
+#define SYSTEM_LOCALTIME_MAX     2147483647
+#define SYSTEM_LOCALTIME_MIN    -2147483647-1
+
+/* Max/min for gmtime() */
+#define SYSTEM_GMTIME_MAX        2147483647
+#define SYSTEM_GMTIME_MIN       -2147483647-1
+
+/* MODIFIED: Moved max/min for mktime() to time64.c to avoid having to
+   initialize it with system-dependent field ordering.
+*/
+
+/* MODIFIED: Removed SYSTEM_TIMEGM_MAX/MIN since they are not used
+   anywhere
+*/
+
+#endif /* TIME64_LIMITS_H */
