PACKTIMEQQ & GMTIME will not work past 2038.

PACKTIMEQQ & GMTIME will not work past 2038.

The number of seconds from 1970 to 2039 exceed the INTERGER*4 maximum value. I am using these called subroutines to determine what day of the week a given date (in the future) is. Is there another way to determine the day of week for a given date?

example code:

PROGRAM MAIN

use IFPORT

INTEGER*4 tarray(9)

INTEGER*4 timedate

INTEGER *2 iYr,imon,iday,ihr,imin,isec

INTEGER JanFirst

DO iyr = 2038, 2039

imon = 1

iday = 1

ihr = 1

imin = 1

isec = 8

CALL PACKTIMEQQ (timedate,iyr,imon,iday,ihr,imin,isec)

! The above call generates the packed time in number of seconds

! since 00:00:00 Greenwich mean time, January 1, 1970.

timedate=timedate-6*60*60

! The above modifies GM time to Central time (6 Hrs difference)

CALL GMTIME (timedate, tarray)

! The above generates an array of information for the packed time in seconds

! The array is Seconds, Minutes, Hours, Day of the Month, Year,

! Day of the Week, Day of the Year, and Daylight Savings Indicator.

JanFirst = tarray(7)

PRINT *, timedate,IYR,JANFIRST

END DO

END

16 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

See this Wikipedia article for a number of methods to do this that can easily be adapted in Fortran.

Steve - Intel Developer Support

You can find several utility functions and subroutines related to date and time by searching the Web. Here, for example, is one, based on one of the routines provided at http://www.mpp.mpg.de/~huber/util/time.html :
[fxfortran]! Uses dow.F from http://www.mpp.mpg.de/~huber/util/dow.FPP
! which uses Zeller's Congruence
!
program tdow
character(len=3) day(7)
integer dow
data day/'Sun','Mon','Tue','Wed','Thu','Fri','Sat'/
write(*,*)'Today is ',day(dow(2012,6,8)+1)
write(*,*)'Xmas 2040 on ',day(dow(2040,12,25)+1)
end
[/fxfortran]which prints out

 Today is     Fri

 Xmas 2040 on Tue

By 2040 if I am still programming in Fortran then it will be just shy of 90 years old.

If I am my daughter will be 35.

JMN

You may not be programming then but your code may still be running, or it may be important to use such future dates as input. This is the lesson we should have learned from the Y2K issue. I think it's great that jlgilber is thinking about it now.

Steve - Intel Developer Support

Quoting Steve Lionel (Intel)You may not be programming then but your code may still be running, or it may be important to use such future dates as input. This is the lesson we should have learned from the Y2K issue. I think it's great that jlgilber is thinking about it now.

In February 2012 I'vemade a blog-post'Is a "Y2K38 disaster" looming? Issues with a 'time_t' type'on Intel Software Network.

Unfortunately, it is stillin 'Pending' state (!!!).

Since that subject is very interesting, and some users of Fortran Forumalready expressed some conserns, I decided to
create an independent thread.

I hope, that my blog-post will be approved before 2038...

Please take a look at a new thread:

http://software.intel.com/en-us/forums/showthread.php?t=105868&o=a&s=lr

Sergey, I will ask the admins about your blog post. It doesn't seem that anyone regularly reviews the pending items, based on what I can see.

Steve - Intel Developer Support

Quoting Steve Lionel (Intel)Sergey, I will ask the admins about your blog post. It doesn't seem that anyone regularly reviews the pending items, based on what I can see.
Thank you, Steve!

Best regards,
Sergey

While you are at it, you should all fix your "leap year" tests. We dodged a bullet in 2000 because
of the "divisible by 400" rule, but 2100 won't be a leap year. If you've been testing for leap years using
a simple test for divisibility by 4, it will break in 2100.

Here are some date functions, posted here previously.

INTEGER FUNCTION dow (yyyy,mm,dd)

	IMPLICIT NONE

	!          Day_Of_Week: (0=Sunday,1=Monday...6=Saturday)

	!          cf J.D.Robertson, CACM 15(10):918

	INTEGER,INTENT(IN)           :: yyyy,mm,dd

	dow = MOD((13*(mm+10-(mm+10)/13*12)-1)/5+dd+77           &

		  +5*(yyyy+(mm-14)/12-(yyyy+(mm-14)/12)/100*100)/4   &

		  +(yyyy+(mm-14)/12)/400-(yyyy+(mm-14)/12)/100*2,7)

END FUNCTION dow
INTEGER FUNCTION ndiy (yyyy,mm,dd)

	IMPLICIT NONE

	!          day count in year

	!          cf J.D.Robertson, CACM 15(10):918

	INTEGER,INTENT(IN)           :: yyyy,mm,dd

	ndiy = 3055*(mm+2)/100-(mm+10)/13*2-91               &

		   +(1-(MOD(yyyy,4)+3)/4+(MOD(yyyy,100)+99)/100  &

		   -(MOD(yyyy,400)+399)/400)*(mm+10)/13+dd

END FUNCTION ndiy
LOGICAL FUNCTION leapyear (year)

	IMPLICIT NONE

	INTEGER,INTENT(IN)		:: year

	IF (ndiy(year, 12, 31) > 365) THEN

		leapyear = .TRUE.

	ELSE

		leapyear = .FALSE.

	END IF

END FUNCTION leapyear

Hello,

both shown functions look very intresting.

Since a long time i was looking for a function reconstructing a date, hour, minute from the following given information:

Units of 'time' is 'minutes since 1955-01-01 00:00' (time convention in netcdf-formated files in the climate community, it is looks like those operations are common in unix-systems)

- sometimes hours,ore secounds since is used.
- the corresponding date is changing.
- the useal time range covers dates until 2100

PACKTIMEQQ and UNPACKTIMEQQ look interessting, but as far as i can understand both are fixed to the date 1,1,1970.

Does anyone knows a package for the above operations?

Thanks in advance
Frank

A common solution to this problem is to use julian date/time and convert that back and forth
to gregorian date/time. Julian date/times are usually stored as double precision reals and start
somewhere in the era before 4000 BC (unless you use modified julian date/time).

There are plenty of implementations around that allow you to do the computations in almost any
programming language. In my Flibs project (http://flibs.sf.net) you will find the libdate module by
Arjan van Dijk, which implements quite a few such computations. (Craig Dedo has recently posted
a very extensive collections of date/time manipulation routines, but unfortunately that has a somewhat
restrictive license due to the source of the algorithms)

Regards,

Arjen

I use a timestamp UDT consisting of an I*4 Julian date and a REAL*4 fraction for the HMS components. Julian dates are arbitrary integers converting the Gregorian calendar into a one-to-one integer sequence, and hence can directly be used for interval arithmetic. This combination is much more useful than any of the MS timestamp data types, universal in application, will never expire or blow up, independent of machine or processor, etc.

Here are the Julian conversion functions (previously posted here)

FUNCTION julian_date (yyyy, mm, dd) RESULT (julian)

	IMPLICIT NONE

	!          converts calendar date to Julian date

	!          cf Fliegel & Van Flandern, CACM 11(10):657, 1968

	!          example: julian_date(1970,1,1)=2440588

	INTEGER,INTENT(IN)           :: yyyy,mm,dd

	INTEGER                      :: julian

	julian = dd - 32075 + 1461*(yyyy + 4800 + (mm - 14)/12)/4 +  &

			 367*(mm - 2 - ((mm - 14)/12)*12)/12 -               &

			 3*((yyyy + 4900 + (mm - 14)/12)/100)/4

END FUNCTION julian_date
SUBROUTINE get_ymd (jd, yyyy, mm, dd)

	IMPLICIT NONE

	!          expands a Julian date into a calendar date

	!          cf Fliegel & Van Flandern, CACM 11(10):657, 1968

	INTEGER,INTENT(IN)           :: jd

	INTEGER,INTENT(OUT)          :: yyyy,mm,dd

	INTEGER                      :: l,n

	l		= jd + 68569

	n		= 4*l/146097

	l		= l - (146097*n + 3)/4

	yyyy	= 4000*(l + 1)/1461001

	l		= l - 1461*yyyy/4 + 31

	mm		= 80*l/2447

	dd		= l - 2447*mm/80

	l		= mm/11

	mm		= mm + 2 - 12*l

	yyyy	= 100*(n - 49) + yyyy + l

END SUBROUTINE get_ymd

Yes, the date part is the number of days since some epoch day long ago. Hours, minutes and seconds
then form the fraction within the day. So, indeed if you only need the date, you can work with integers,
even default-size ones, as 2000 million are a lot of years.

Regards,

Arjen

The JGDATE subroutine shown below can be used to determine the number of days between any two dates. With a little more work to do the time arithmetic, it wouldis easy to calculate the difference in time. This will work with 4-byte integers for elapsed times less than about 3800 years. I also offer a quick way to determine Leap year, as well as the numer of days in any year and month.

Example:
-----------

      JFLAG = 1

      JYY = 1955

      JMM = 1

      JDD = 1

      JHOUR = 0

      JMIN  = 0

      JSM = JHOUR*60 + JMIN

      Call JGDATE ( JFLAG, JDATE_Start, JYY, JMM, JDD )

      JYY = 2012

      JMM = 6

      JDD = 12

      JHOUR = 8

      JMIN = 53

      Call JGDATE ( JFLAG, JDATE_Now, JYY, JMM, JDD )

c

c Elapsed time (minutes)

      JETM = JHOUR*60 + JMIN - JSM

      If ( JETM .lt. 0 ) Then

         JETM = JETM + ( 24 * 60 )

         JDATE_Now = JDATE_Now - 1

      Endif

      JETM = JETM + ( JDATE_Now - JDATE_Start ) * ( 24 * 40 )

c (don't forget to declare all the variables)

c$Message: ' File:JGDate.For'

      Subroutine JGDATE ( JFLAG, JDATE, JYY, JMM, JDD )

C

C     COMPLIMENTS OF BLAST/WIFE 3.0

C     File Updated     03/24/1987   11:34:48

C

      Integer JFLAG, JYY, JMM, JDD, I1, I2, I3

      Integer JDATE, L, JDL, N

c

      Save I1, I2, I3, JDL

c

      Data I1, I2, I3, JDL / -999,-9,-9,0 /

c

      If ( JFLAG .eq. 1 ) Then

c

C     COMPUTE JULIAN DATE FROM GREGORIAN DATE

C

         If ( JYY .eq. I1  .and. JMM .eq. I2 ) Then

C     Relative to Current Month ( BB Mod )

            JDATE=JDD-I3+JDL

C

         Else

C

            L=(JMM-14)/12

            JDATE=JDD-32075+(1461*(JYY+4800+L))/4

     X         +(367*(JMM-2-L*12))/12-(3*((JYY+4900+L)/100))/4

C

            JDL=JDATE

            I1=JYY

            I2=JMM

            I3=JDD

         Endif

c

      Else If ( JFLAG .eq. 2 ) Then

C

C     COMPUTE GREGORIAN DATE FROM JULIAN DATE

         L=JDATE+68569

         N=4*L/146097

         L=L-(146097*N+3)/4

         JYY=4000*(L+1)/1461001

         L=L-1461*JYY/4+31

         JMM=80*L/2447

         JDD=L-2447*JMM/80

         L=JMM/11

         JMM=JMM+2-12*L

         JYY=100*(N-49)+JYY+L

      Endif

      Return

      END

c$Message: ' File:NDom.For'

      Integer Function NDOM ( IYEAR, MON )

C

C     Returns the # of Days in Month MON of Year IYEAR

C     File Updated     03/03/1988   10:42:06

C

      Integer IYEAR, MON

      Integer NDM(12), LEAP

      Integer J1,J2,J3

C

      Data NDM / 31,28,31,30,31,30,31,31,30,31,30,31 /

C

C

      If ( MON .EQ. 2 ) Then

C     February : Test for Leap Year

         J1=Mod(IYEAR,4)

         J2=Mod(IYEAR,100)

         J3=Mod(IYEAR,400)

         LEAP=0

         If ( J1.EQ.0 ) Then

            If ( J2.NE.0 ) Then

               LEAP=1

            Else If ( J3.EQ.0 ) Then

               LEAP=1

            Endif

         Endif

         NDOM=28+LEAP

      Else

         NDOM=NDM(MON)

      Endif

      Return

      END

c (For example, (logical) Leap = ( NDOM ( iYear, 2 ) .eq. 29 )

Well, the "JGDATE" spagetti-monster nicely proves the point that using the Julian() function together with the other companion utilities is far and away the best way of dealing with time/date interval arithmetic, and supports clean, efficient, readable and maintainable code.

Leave a Comment

Please sign in to add a comment. Not a member? Join today