Have you heard about Y2K problem? If yes, then you must know about what actually that problem was. It was the most solemn problem was arrived in the year 2000 which had concerned with computer and non digital documentation.
A Data storage situation which resulted from the practice of abbreviating a four digit year to two digits, which had made year 2000 indistinguishable from 1900 by truncating first two digits resulted into 00 and 00. But as we know “Nothing is permanent in the world, not even problems”. Scientist really had proved that proverb true by solving it. This is why today we are continuing with same computers with correct date.
Okay, now come back to the point.
January 19, 2038 3:14:08? What this date and timing meant to say!! You may have some idea as I have discussed such a scenario just before. This time also the problem has concerned to the
DATE. You may consider it as major than last or minor than last. The problem is with the storage capacity of Linux, which going to exceed on the date and time January 19, 2038 3:14:08!! After one more second you will see Fri Dec 13 20:45:52 1901. It is quite wondering, but corrects also.
When it will happen?
It is going to happen after 24 years from today and we all are going to face it which is known as a
Unix Millennium Bug. All the software and systems will be affected on that date which has stored system time in 32-bit integer and interpret the numbers as the numbers of seconds since
00:00:00 UTC on January 1, 1970.
What will happen when the time will go beyond the January 19, 2038 3:14:08?
If the time will go beyond to the January 19, 2038 3:14:08, then it will start counting negative. And the output, which you will see would be the subtraction resulted in (2147483647, it will wrap to -2147483648) December 13, 1901 rather than in 2038. This will happen due to the storage capacity of UNIX is assigned to 32 bit integer which have started on January 1, 1970 00:00:00 GMT will have exceeded maximum value of storage.
How to prevent 2038 bug again?
- You should use 64 bit integer rather than 32 bit integer storage.
- You should use TIMESTAMP rather than DATETIME.
- These are two ways that can help you to prevent such an occurrence for further possible solutions.
What can I do as a developer?
As a developer here I have mentioned some steps that you should take to prevent 2038 bug as well to prevent such a glitch to occur again.
1) Using 64 bits large data types for storing dates in databases.
2) Check whether the time is encoded or not. If the time is encoded and stored into 4 byte database, then from now you should store it in 5 byte database because afterwards the encoded time is decoded into a signed integer.
3) When you are using time as a text string, use GMT time because you can easily convert GMT time using GMT time convert function to clean 2038 bug. You can find the GMT function code as below. Due to GMT function your code will work correctly with GMT time through the year 2038. And if you find an error after words, then it could be due to cosmetic problem that can be solved easily.
How you can remove 2038 bug naturally on other system while running your code?
While using
time_t in your code, make sure that it remains consistent throughout the code. Stick to the OS standard functions to convert times and use them exactly as directed in the documentation. Avoid casting time values for any type except the types available in the system libraries.
Other solutions for 32-bit systems.
On a 32-bit system,
time_t has a signed 32-bit integer C/C++ type. A common way to get the time is:
1. time_t now;
2. time (&now);
In 32-bit system we are using signed 32-bit integer which has the constraint of negative to positive value. As the time will cross the value of the 32-integer will be negative. You can use unsigned 32-bit integer to solve this problem. If you are using the unsigned 32-bit integer, then the value will remain positive though crossing 2038 year and you will see the correct time even after you cross 2106.
Another solution for solving the 2038 bug is to use 64-bit integer to store the value. Replace the entire 32-bit integer with 64-bit integer. This is quite crafty because if you will try to recompile, system ought to use 64-bit integer and the time will be in the 64-bit time type.
Above solutions are particularly for 32 and 64 bit integer. While the below solution is applied to both 32-bit as well 64-bit systems. By using this solution you no need to consider any past date.
Define the
double float type to store the date. Double float will be the clear integer value up to the 143 million years from now unless the size of the mantissa is exceeded. The function will look like as follows:
typedef double timef_t;
timef_t timef (void)
{
time_t now;
time (&now);
if (sizeof (now) == 4)
{
unsigned int v;
v = (unsigned int) now;
return (double) v;
}
else
{
return (double) now;
}
}
If you want to use UTC time instead of GTM time, then you need to use the following function which works on both OS. Unix as well as Windows.
typedef double timef_t;
timef_t millitimef (void)
{
struct timeb t;
ftime (&t);
if (sizeof (t.time) == 4)
{
unsigned int v;
v = (unsigned int) t.time;
return (double) v * 1000.0 + (double) t.millitm;
}
else
{
return (double) t.time * 1000.0 + (double) t.millitm;
}
}
Above both functions are used to display the correct time in seconds. But if you want to display time in text then, you need to apply following solution:
To get broken down time format, you can use the following functions:
gmtime(),
gmtime_r(),
localtime()
localtime_r()
Above four functions are used to convert integer number of seconds since 1970 into a “struct tm” which contains the year, month, day, hour, minute, second and week day.
strftime() converts broken time to a string.
gmtime() and gmtime_r() convert broken time to Greenwich time.
localtime() and localtime_r() convert to time offset by the number of hours left or right of Greenwich with the addition of any daylight savings offsets.
_r variations in above functions are re entrant versions of the same functions.
asctime() and ctime() can be created using the above functions if you are using it.
What problem occurs when using localtime() function?
The implementation of localtime() function is quite tough because the every country has different day light saving policies. It means function demands maintenance and a large number of daylight saving parameters for each country on earth.
Most simple functions are gmtime_r() if you are only interested in dates after 1970 and not interested in leap second adjustment.
strftime() function is 2038 bug clean as it doesn’t work with Unix epoch.
#define LEAP_CHECK(n) ((!(((n) + 1900) % 400) || (!(((n) + 1900) % 4) && (((n) + 1900) % 100))) != 0)
#define WRAP(a,b,m) ((a) = ((a) < 0 ) ? ((b)--, (a) + (m)) : (a))
struct tm *simple_gmtime_r (time_t *_t, struct tm *p)
{
static const int days[4][13] = {
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
{0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366},
};
int v_tm_sec, v_tm_min, v_tm_hour, v_tm_mon, v_tm_wday, v_tm_tday;
int leap;
long long t;
long m;
if (sizeof (time_t) == 4) {
unsigned int __t;
__t = (unsigned int) *_t;
t = (long long) __t;
} else {
t = *_t;
}
v_tm_sec = (int) ((long long) t % (long long) 60);
t /= 60;
v_tm_min = (int) ((long long) t % (long long) 60);
t /= 60;
v_tm_hour = (int) ((long long) t % (long long) 24);
t /= 24;
v_tm_tday = (int) t;
WRAP (v_tm_sec, v_tm_min, 60);
WRAP (v_tm_min, v_tm_hour, 60);
WRAP (v_tm_hour, v_tm_tday, 24);
if ((v_tm_wday = (v_tm_tday + 4) % 7) < 0)
v_tm_wday += 7;
m = (long) v_tm_tday;
p->tm_year = 70;
leap = LEAP_CHECK (p->tm_year);
while (m >= (long) days[leap + 2][12]) {
m -= (long) days[leap + 2][12];
p->tm_year++;
leap = LEAP_CHECK (p->tm_year);
}
v_tm_mon = 0;
while (m >= (long) days[leap][v_tm_mon]) {
m -= (long) days[leap][v_tm_mon];
v_tm_mon++;
}
p->tm_mday = (int) m + 1;
p->tm_yday = days[leap + 2][v_tm_mon] + 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;
return p;
}
static struct tm *simple_gmtime (time_t *_t)
{
static struct tm p;
return u32_s64_gmtime (_t, &p);
}
Above functions are used to get the localtime() with the day light saving. And if you are happy with Greenwich time, then you should use simple_gmtime_r() and strftime() functions to get the correct dates after 2038.