Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
28455 Discussions

PACKTIMEQQ & GMTIME will not work past 2038.

jlgilber1
Beginner
1,259 Views
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

0 Kudos
15 Replies
Steven_L_Intel1
Employee
1,259 Views
See this Wikipedia article for a number of methods to do this that can easily be adapted in Fortran.
0 Kudos
mecej4
Honored Contributor III
1,259 Views
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
[bash] Today is Fri Xmas 2040 on Tue [/bash]
0 Kudos
JohnNichols
Valued Contributor III
1,259 Views
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
0 Kudos
Steven_L_Intel1
Employee
1,259 Views
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.
0 Kudos
SergeyKostrov
Valued Contributor II
1,259 Views
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

0 Kudos
Steven_L_Intel1
Employee
1,259 Views
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.
0 Kudos
SergeyKostrov
Valued Contributor II
1,259 Views
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
0 Kudos
jparsly1
New Contributor I
1,259 Views
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.
0 Kudos
Paul_Curtis
Valued Contributor I
1,259 Views
Here are some date functions, posted here previously.

[bash]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 [/bash]
0 Kudos
tropfen
New Contributor I
1,259 Views
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
0 Kudos
Arjen_Markus
Honored Contributor I
1,259 Views
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
0 Kudos
Paul_Curtis
Valued Contributor I
1,259 Views
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)

[bash]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 [/bash]
0 Kudos
Arjen_Markus
Honored Contributor I
1,259 Views
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
0 Kudos
bbradley
Beginner
1,259 Views
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:
-----------
[bash] 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 )



[/bash]
0 Kudos
Paul_Curtis
Valued Contributor I
1,259 Views
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.
0 Kudos
Reply