In date_and_time.c, 'time' is called. If the routine then goes on to call 'gettimeofday', it extracts the milliseconds value from the 'gettimeofday' call, but gets the seconds value from the old call to 'time'. This can result in consecutive times of (say) 2006 11 29 12 4 34 999 2006 11 29 12 4 34 0 being generated. Notice that the seconds in the later call have not 'ticked over', because they refer to the earlier time value accessed by 'time', while the milliseconds refer to the "correct" value accessed by gettimeofday.
Here is our test code: !!!!!!!!!!!!!!!!!!!! Test program for DATE_AND_TIME PROGRAM DATE_AND_TIME_TEST ! .. Implicit None Statement .. IMPLICIT NONE ! .. Parameters .. INTEGER, PARAMETER :: NOUT = 6 INTEGER, PARAMETER :: WP = KIND(0.0D0) ! .. Non-Generic Interface Blocks .. INTERFACE FUNCTION ORDER_TIME(ITIME1,ITIME2) ! .. Function Return Value .. INTEGER :: ORDER_TIME ! .. Array Arguments .. INTEGER, INTENT (IN) :: ITIME1(7), ITIME2(7) END FUNCTION ORDER_TIME END INTERFACE ! .. Local Scalars .. REAL (KIND=WP) :: E, ETOL, EXPE, T INTEGER :: I, N, NFAILS LOGICAL :: PASS ! .. Local Arrays .. INTEGER :: DATE_TIME(8), ITIME1(7), ITIME2(7) ! .. Intrinsic Functions .. INTRINSIC ABS, DATE_AND_TIME, KIND, MIN ! .. Executable Statements .. CONTINUE PASS = .TRUE. EXPE = 2.718281828E0_WP ! Make up to 10000 calls of DATE_AND_TIME and check that they return ! monotonic non-decreasing times, by calling ORDER_TIME. NFAILS = 0 CALL DATE_AND_TIME(VALUES=DATE_TIME) ITIME2(1:3) = DATE_TIME(1:3) ITIME2(4:7) = DATE_TIME(5:8) ! Output start time. WRITE (NOUT,FMT=99991) ITIME2(1:7) I = 2 DO ! Save the old time in ITIME1. ITIME1(1:7) = ITIME2(1:7) ! Delay a bit by computing e. ETOL = 0.001_WP E = 1.0E0_WP T = 1.0E0_WP DO N = 1, 100000 - MIN(I,6) T = T/N E = E + T END DO ! This test is just so that E gets used and the loop ! above isn't optimised away. IF (ABS(E-EXPE)>ETOL) THEN IF (PASS) THEN PASS = .FALSE. WRITE (NOUT,FMT=99999) E, EXPE END IF END IF ! Get the new time in ITIME2. CALL DATE_AND_TIME(VALUES=DATE_TIME) ITIME2(1:3) = DATE_TIME(1:3) ITIME2(4:7) = DATE_TIME(5:8) IF (ORDER_TIME(ITIME1,ITIME2)==1 .AND. NFAILS<5) THEN NFAILS = NFAILS + 1 PASS = .FALSE. WRITE (NOUT,FMT=99998) ITIME1, ITIME2 END IF ! Continue round the loop up to at most 10000 times, unless at ! least two different times have been found and we've done ! the loop at least 1000 times. IF ((ORDER_TIME(ITIME1,ITIME2)/=-1 .AND. I<10000) .OR. I<1000) THEN I = I + 1 ELSE EXIT END IF END DO ! Make one final check to ensure that all the times in the loop ! above were not identical. ! Output end time. WRITE (NOUT,FMT=99990) ITIME2(1:7) IF (ORDER_TIME(ITIME1,ITIME2)/=-1 .AND. NFAILS<5) THEN PASS = .FALSE. WRITE (NOUT,FMT=99997) ITIME1, ITIME2 END IF IF (PASS) THEN WRITE (NOUT,FMT=99996) ELSE WRITE (NOUT,FMT=99995) END IF 99999 FORMAT (1X/1X,'Computed e as ',1P,E13.5,' instead of ',E13.5) 99998 FORMAT (1X/1X,'Two consecutive calls of DATE_AND_TIME returned:', & 2(/1X,7I5)/2X, & '- the first should be not later than the second but is.') 99997 FORMAT (1X/1X,'Two calls of DATE_AND_TIME returned:',2(/1X,7I5)/2X, & '- the first should be earlier than the second but is not.') 99996 FORMAT (1X/1X,'TEST OF DATE_AND_TIME PASSED OK') 99995 FORMAT (1X/1X,'TEST OF DATE_AND_TIME FAILS') 99991 FORMAT (1X,'*** Started at',7I5) 99990 FORMAT (1X/1X,'***** Ended at',7I5) END PROGRAM DATE_AND_TIME_TEST FUNCTION ORDER_TIME(ITIME1,ITIME2) ! .. Implicit None Statement .. IMPLICIT NONE ! .. Function Return Value .. INTEGER :: ORDER_TIME ! .. Parameters .. INTEGER, PARAMETER :: WP = KIND(0.0D0) ! .. Array Arguments .. INTEGER, INTENT (IN) :: ITIME1(7), ITIME2(7) ! .. Local Scalars .. INTEGER :: I ! .. Intrinsic Functions .. INTRINSIC KIND ! .. Executable Statements .. CONTINUE ! Compare the integer array format times. I = 1 DO IF (ITIME1(I)==ITIME2(I) .AND. I<7) THEN I = I + 1 ELSE EXIT END IF END DO IF (ITIME1(I)<ITIME2(I)) THEN ORDER_TIME = -1 ELSE IF (ITIME1(I)==ITIME2(I)) THEN ORDER_TIME = 0 ELSE ORDER_TIME = 1 END IF RETURN END FUNCTION ORDER_TIME !!!!!!!!!!!!!!!!!!!! End of test program for DATE_AND_TIME The fix to date_and_time is pretty obvious. Here's how we made the relevant section of the code work: /* Some unchanged date_and_time.c code above here */ #ifndef HAVE_NO_DATE_TIME time_t lt; struct tm local_time; struct tm UTC_time; lt = time (NULL); if (lt != (time_t) -1) { #if HAVE_GETTIMEOFDAY { struct timeval tp; #if GETTIMEOFDAY_ONE_ARGUMENT if (!gettimeofday (&tp)) #else #if HAVE_STRUCT_TIMEZONE struct timezone tzp; /* Some systems such as HP-UX, do have struct timezone, but gettimeofday takes void* as the 2nd arg. However, the effect of passing anything other than a null pointer is unspecified on HP-UX. Configure checks if gettimeofday actually fails with a non-NULL arg and pretends that struct timezone is missing if it does fail. */ if (!gettimeofday (&tp, &tzp)) #else if (!gettimeofday (&tp, (void *) 0)) #endif /* HAVE_STRUCT_TIMEZONE */ #endif /* GETTIMEOFDAY_ONE_ARGUMENT */ /* All arguments can be derived from tp. */ lt = tp.tv_sec; values[7] = tp.tv_usec / 1000; } #else { /* All arguments can be derived from lt. */ values[7] = 0; } #endif /* HAVE_GETTIMEOFDAY */ local_time = *localtime (<); UTC_time = *gmtime (<); values[0] = 1900 + local_time.tm_year; values[1] = 1 + local_time.tm_mon; values[2] = local_time.tm_mday; values[3] = (local_time.tm_min - UTC_time.tm_min + 60 * (local_time.tm_hour - UTC_time.tm_hour + 24 * (local_time.tm_yday - UTC_time.tm_yday))); values[4] = local_time.tm_hour; values[5] = local_time.tm_min; values[6] = local_time.tm_sec; #if HAVE_SNPRINTF if (__date) /* Some unchanged date_and_time.c code below here */ -- Summary: Intrinsic date_and_time can go back in time Product: gcc Version: 4.2.0 Status: UNCONFIRMED Severity: critical Priority: P3 Component: libfortran AssignedTo: unassigned at gcc dot gnu dot org ReportedBy: mathewc at nag dot co dot uk http://gcc.gnu.org/bugzilla/show_bug.cgi?id=30015