QNX4 works beyond 2038: is it a bug?

I stumbled upon an unexpected behaviour of QNX v4.25G (Proc32 v4.25Q) - it
seemingly continues to handle the time properly at least for some more
days past 19/01/2038.
Here is an example:

#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <tzfile.h>

#define NEED_MJD 1

#ifdef NEED_MJD

define JULIAN_OFFSET (1721059 - 2400000) /* use MJD */

#else

define JULIAN_OFFSET 1721059 /* full Julian Days */

#endif

typedef unsigned long jday;

jday
Tm2Julian( struct tm Tm )
{
jday JulDay;
int Year; /
full four-digit year /
int Year1; /
Year, less 1 */

Year = Tm->tm_year + TM_YEAR_BASE;
Year1 = Year - 1; /* year, less 1 /
JulDay = Year * 365; /
approx days /
JulDay += (Year+3) / 4; /
leap years /
JulDay -= Year1 / 100; /
fix for 100 /
JulDay += Year1 / 400; /
fix for 400 /
JulDay += Tm->tm_yday; /
ordinal day /
JulDay += JULIAN_OFFSET; /
fix */

return JulDay;
}

time_t
mjd2unixtime( unsigned long jday )
{
time_t unixtime;

// if( 40587 <= jday & jday <= 90297 ) /* apparently works /
if( 40587 <= jday & jday <= 65442 ) /
garateed to work /
{
unixtime = ( jday - 40587 ) * 60
6024;
return( unixtime);
}
return -1; /
there is no time_t representation for dates /
} /
before 01/01/1970 and after 19/01/2038 */

void
main( void )
{
jday jd;
time_t t_o_d;
char buf[26];
struct tm tmbuf;

jd = 40586; /* 31/12/1969 /
t_o_d = mjd2unixtime( jd );
_gmtime( &t_o_d, &tmbuf );
printf( “MJD(%d)==%.24s GMT, time_t==0x%lx\n”, jd, _asctime( &tmbuf, buf
), t_o_d );
jd = 40587; /
01/01/1970 /
t_o_d = mjd2unixtime( jd );
_gmtime( &t_o_d, &tmbuf );
printf( “MJD(%d)==%.24s GMT, time_t==0x%lx\n”, jd, _asctime( &tmbuf, buf
), t_o_d );
jd = 45000; /
31/01/1982 /
t_o_d = mjd2unixtime( jd );
_gmtime( &t_o_d, &tmbuf );
printf( “MJD(%d)==%.24s GMT, time_t==0x%lx\n”, jd, _asctime( &tmbuf, buf
), t_o_d );
jd = 53005; /
01/01/2004 /
t_o_d = mjd2unixtime( jd );
_gmtime( &t_o_d, &tmbuf );
printf( “MJD(%d)==%.24s GMT, time_t==0x%lx\n”, jd, _asctime( &tmbuf, buf
), t_o_d );
jd = 53963; /
16/08/2006 /
t_o_d = mjd2unixtime( jd );
_gmtime( &t_o_d, &tmbuf );
printf( “MJD(%d)==%.24s GMT, time_t==0x%lx)\n”, jd, _asctime( &tmbuf, buf
), t_o_d );
jd = 65442; /
19/01/2038 /
t_o_d = mjd2unixtime( jd );
_gmtime( &t_o_d, &tmbuf );
printf( “MJD(%d)==%.24s GMT, time_t==0x%lx\n”, jd, _asctime( &tmbuf, buf
), t_o_d );
jd = 65443; /
20/01/2038 /
t_o_d = mjd2unixtime( jd );
_gmtime( &t_o_d, &tmbuf );
printf( “MJD(%d)==%.24s GMT, time_t==0x%lx\n”, jd, _asctime( &tmbuf, buf
), t_o_d );
jd = 90296; /
06/02/2106 /
t_o_d = mjd2unixtime( jd );
_gmtime( &t_o_d, &tmbuf );
printf( “MJD(%d)==%.24s GMT, time_t==0x%lx\n”, jd, _asctime( &tmbuf, buf
), t_o_d );
jd = 90297; /
07/02/2106 */
t_o_d = mjd2unixtime( jd );
_gmtime( &t_o_d, &tmbuf );
printf( “MJD(%d)==%.24s GMT, time_t==0x%lx\n”, jd, _asctime( &tmbuf, buf
), t_o_d );
}

Somewhere closer to 2106 the date starts to be off by one day - instead of
07/02/2106 it shows 06/02/2106.

Please comment!

Tony.

On Wed, 16 Aug 2006 04:57:17 +0400, Tony <mts.spb.suxx@mail.ru> wrote:

Somewhere closer to 2106 the date starts to be off by one day - instead
of 07/02/2106 it shows 06/02/2106.
I poked around and it seems that both MJD 88585 and MJD 88586 in current

“gmtime()” maps to the same “01JUN2101”. From that day on “gmtime()” is
off by a day.
Untill that date everything seems to work correctly.

Please comment.

Tony.

PS
Is it posible to obtain the sources for “gmtime()” to fix and replace in
Watcom’s libraryes?
(Watcom C v10.6B+Security patch)

I wonder how is this possible?

The <time.h> file declares the “time_t” as the “signed long”…

Tony.

On Wed, 16 Aug 2006 16:57:43 +0400, Tony <mts.spb.suxx@mail.ru> wrote:

I poked around and it seems that both MJD 88585 and MJD 88586 in current
“gmtime()” maps to the same “01JUN2101”. From that day on “gmtime()” is
off by a day.
Interestingly, the week-day increases correctly:

MJD(88585)==Wed Jun 01 00:00:00 2101 GMT, time_t==0xf72e9d00
MJD(88586)==Thu Jun 01 00:00:00 2101 GMT, time_t==0xf72fee80

Week-day calculation seems correct right to the time_t=0xffffffff (MJD
90297, Sun Feb 07 2106), it is “day of month” that gets botched…

Tony.

Tony wrote:

I wonder how is this possible?

By using an unsigned type internally?

The <time.h> file declares the “time_t” as the “signed long”…

Since POSIX specifically says that negative time_t values are undefined,
we’re free to map them to dates after 2038. It’s not a bug, it’s a
feature. :wink:

http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_14

On Wed, 16 Aug 2006 20:08:55 +0400, Wojtek Lerch <Wojtek_L@yahoo.ca> wrote:

It’s not a bug, it’s a feature.
I do remember the warning in the Documentation - "If you need to use QNX4

after 2038 - ask for individual support plan" - or something like that.

I did not try setting the system time that far yet. I do not know if I’ll
be able to recover an installation if something goes “Oops!”… May be not
only the “/bin/date” faults but something in the “/bin/Fsys” or “Proc32”
too. Scary!

I’ll try this in VMwared installation first.

Tony.

On Wed, 16 Aug 2006 19:22:30 +0400, Tony <mts.spb.suxx@mail.ru> wrote:

I poked around and it seems that both MJD 88585 and MJD 88586 in
current “gmtime()” maps to the same “01JUN2101”. From that day on
“gmtime()” is off by a day.
Interestingly, the week-day increases correctly:
MJD(88585)==Wed Jun 01 00:00:00 2101 GMT, time_t==0xf72e9d00
MJD(88586)==Thu Jun 01 00:00:00 2101 GMT, time_t==0xf72fee80

Week-day calculation seems correct right to the time_t=0xffffffff (MJD
90297, Sun Feb 07 2106), it is “day of month” that gets botched…
This “_gmtime()” does not have this defect and I believe it works

correctly all over the range of “*timer” values - from 0x0 up to
0xffffffff.

#include <time.h>

#define YEAR0 1900
#define EPOCH_YR 1970
#define SECS_DAY (24L * 60L * 60L)
#define LEAPYEAR(year) (!((year) % 4) && (((year) % 100) ||
!((year) % 400)))
#define YEARSIZE(year) (LEAPYEAR(year) ? 366 : 365)

const int _ytab[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}
};

struct tm
*_gmtime(const time_t *timer, struct tm *tmbuf)
{
time_t time = *timer;
unsigned long dayclock, dayno;
int year = EPOCH_YR;

dayclock = (unsigned long) time % SECS_DAY;
dayno = (unsigned long) time / SECS_DAY;

tmbuf->tm_sec = dayclock % 60;
tmbuf->tm_min = (dayclock % 3600) / 60;
tmbuf->tm_hour = dayclock / 3600;
tmbuf->tm_wday = (dayno + 4) % 7; // Day 0 was a thursday
while (dayno >= (unsigned long) YEARSIZE(year))
{
dayno -= YEARSIZE(year);
year++;
}
tmbuf->tm_year = year - YEAR0;
tmbuf->tm_yday = dayno;
tmbuf->tm_mon = 0;
while (dayno >= (unsigned long) _ytab[LEAPYEAR(year)][tmbuf->tm_mon])
{
dayno -= _ytab[LEAPYEAR(year)][tmbuf->tm_mon];
tmbuf->tm_mon++;
}
tmbuf->tm_mday = dayno + 1;
tmbuf->tm_isdst = 0;

return tmbuf;
}

Linking with it gives correct printouts.
Seems it is worth replacing the original object in the “clib3r.lib” with
this one.

Tony