0

I am looking for feedback on a couple of functions/subroutines for converting between Julian and Gregorian calendar in Fortran. It is for an internal software for extreme value analysis, so it needs to be robust.

I am currently using something I found online (credit: http://aa.usno.navy.mil/faq/docs/JD_Formula.php) with a slight modification to suit my needs of also having a fractional time added to the Julian date.

To convert a date vector (Gregorian format) to Julian and fractional time:

INTEGER FUNCTION JULIAN(YEAR,MONTH,DAY)

    IMPLICIT NONE
    INTEGER, INTENT(IN) :: YEAR,MONTH,DAY

    JULIAN = DAY-32075+1461*(YEAR+4800+(MONTH-14)/12)/4+367*(MONTH-2-(MONTH-14)/12*12)/12-3*((YEAR+4900+(MONTH-14)/12)/100)/4

END FUNCTION

REAL(DOUBLE) FUNCTION FRACTIONTIME(HOUR,MINUTE,SECOND)

    IMPLICIT NONE
    INTEGER, INTENT(IN) :: HOUR,MINUTE,SECOND

    FRACTIONTIME = (HOUR + (MINUTE + SECOND/60.D0)/60.D0)/24.D0

END FUNCTION FRACTIONTIME

The Julian date I store is then JD = Julian(...) + Fractiontime(...)

And for converting back to Gregorian format:

SUBROUTINE GREGORIAN(JD,YEAR,MONTH,DAY,HOUR,MINUTE,SECOND)

    IMPLICIT NONE
    REAL(DOUBLE), INTENT(IN) :: JD
    INTEGER, INTENT(OUT) :: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND
    REAL(DOUBLE) :: JT
    INTEGER :: I,J,K,L,N

    L = INT(JD)+68569
    N = 4*L/146097
    L = L-(146097*N+3)/4
    I = 4000*(L+1)/1461001
    L = L-1461*I/4+31
    J = 80*L/2447
    K = L-2447*J/80
    L = J/11
    J = J+2-12*L
    I = 100*(N-49)+I+L

    YEAR = I
    MONTH = J
    DAY = K

    JT = DMOD(JD,1.D0)*24.D0
    HOUR = INT(JT)
    JT = DMOD(JT,1.D0)*60.D0
    MINUTE = INT(JT)
    JT = DMOD(JT,1.D0)*60.D0
    SECOND = NINT(JT)

    IF (SECOND == 60) THEN
        SECOND = SECOND-60
        MINUTE = MINUTE+1
    END IF

END SUBROUTINE GREGORIAN

If this is perfect as-is, then see this as a way to share the work, but if you see any problems or have suggestions for other ways to tackle this, I am all ears!

Short description of what I am actually doing:

  1. Time series of e.g. wind speeds are stored in a standard format with a date vector (year, month, day, hour, minute, second) as the first 6 columns and then the wind speed in another.
  2. Peaks in the wind speeds are identified, which need to be statistically independent, i.e. more than 72 hours apart
  3. In order to ensure this, I store the Julian and then test for any neighbouring peaks that are JD+3 and JD-3 apart
  4. For output reasons and because I don't want to drag my original date vector around, I convert the Julian back to Gregorian

And to keep in line with the FAQ:

What is the best way to convert between Julian and Gregorian in Fortran 95/2003?

karga
  • 41
  • 3
  • 1
    If you want someone to review your code and suggest improvements you might want to try [code review](http://codereview.stackexchange.com/). – d_1999 Sep 27 '16 at 07:52
  • You have a test for second == 60. Can minute ever == 60? – Holmz Sep 27 '16 at 18:57
  • 1
    Also why not make a module out of it and then it become more easily used? – Holmz Sep 27 '16 at 18:59
  • And you probably want to use ISO_ and BIND(C)Somewhere. This is an ideal place to have Interface so one can call it with Int, byte, unsigned long and get the right stuff with any calling end combo from fortran or c. The c would just need 1 .h specifier and could call the explicit function. – Holmz Sep 27 '16 at 19:28
  • There is nothing in your comments or code that shows an awareness that, as an example, 2457659.0 means noon Sep 27, 2016 Universal Time. There is no statement about the time zone of the time that is to be input to your code. There is no statement about the time zone of the output Gregorian date and time. Even if you have considered all this, no one will trust your code unless you document these issues in excruciating detail. – Gerard Ashton Sep 27 '16 at 21:46
  • @GerardAshton it still seems like a great candidate for Karsten to do the whole enchilada properly. (The devil is the details) – Holmz Sep 28 '16 at 12:08
  • @d_1999 I wasn't aware of that section and reading the FAQ before posting, it was my impression that I followed it – karga Sep 29 '16 at 07:56
  • @Holmz The test on seconds is because of the NINT. I am not sure why the NINT is needed for seconds and not the the others, but it might be because I need the remainders for the others. When passing values on the hour, it sometimes went to 60 seconds, so I added the test. I don't know what the ISO_ means, but I only use it for the data types I have given here. – karga Sep 29 '16 at 07:58
  • @GerardAshton Agree that it sould be properly commented, but we are in developing stages where all the documentation is written in the pseudocode at the moment. Why do you think that a .0 date value is ever at noon? To my understanding that is at midnight. – karga Sep 29 '16 at 08:01
  • The definition of Julian date is widely agreed to. One source is http://asa.usno.navy.mil/SecM/Glossary.html#_J . The definition is "Julian date (JD): the interval of time in days and fractions of a day, since 4713 B.C. January 1, Greenwich noon, Julian proleptic calendar." – Gerard Ashton Sep 29 '16 at 13:40
  • Disregard previous comment. The definition of Julian date is widely agreed to. One source is http://asa.usno.navy.mil/SecM/Glossary.html#_J . The definition is "Julian date (JD): the interval of time in days and fractions of a day, since 4713 B.C. January 1, Greenwich noon, Julian proleptic calendar." You might find more useful the modified Julian date, which counts from Greenwich midnight November 17, 1858. I am not aware of any widely recognized date count that everyone agrees places .0 at midnight local time. – Gerard Ashton Sep 29 '16 at 13:47
  • Also the "Introduction" paragraph in the source Karsten Garborg used to find the formulas also says the count is from Greenwich noon. – Gerard Ashton Sep 29 '16 at 13:50
  • One can call SQRT against a float, real, double, long, long long, DOUBLE, INTEGER, BYTE, REAL etc. If you want people to be able to call your function passing in different kinds of arguments, then you would specifically need to allow that. If you want it callable from c, then you need to google away on that too, https://gcc.gnu.org/onlinedocs/gfortran/Interoperable-Subroutines-and-Functions.html – Holmz Oct 01 '16 at 03:40
  • I have a problem with this code. I would like to convert this julian date: 2448257.50000000 I expects to get 1991/01/01 00:00. However I get 1990/12/31 12:00. Could you please help me? – diedro Mar 04 '20 at 21:05
  • The calculation of JULIAN is quite infamous in that it relies on how FORTRAN truncates when doing integer division. So it will not translate to another language and may not be compatible to FORTRAN 95. Originally it was designed to fit on a single line (1 punch card). – TazAstroSpacial Sep 30 '21 at 06:27
  • To diedro, The definition of Julian date is that it starts at 12:00 (midday) i.e GMAT which starts at Greenwich Noon (Gerhard). The Modified Julian date subtracts a big round number and half a day. MJD = JD - 2400000.5. There are other forms see wkipedia – TazAstroSpacial Sep 30 '21 at 06:41
  • One more thing: Beware you will need int32 rather than int to calculate the variable JULIAN. – TazAstroSpacial Sep 30 '21 at 06:44

0 Answers0