serv-terr / pbl_met

"pbl_met" is a set of Fortran modules aimed at building meteorological processors and other software pertaining Planetary Boundary Layer (PBL) applications.
GNU Lesser General Public License v3.0
2 stars 1 forks source link

In pbl_time, would not it be a good thing to have two new functions converting between REAL(8) time stamps and date/time with sub-second resolution? #7

Closed mafavaron closed 6 years ago

mafavaron commented 6 years ago

To date, subroutines like PackTime and UnpackTime exist which perform the same task between INTEGER(4) timestamps and date/time parts.

Something similar should happen with REAL(8) time stamps, to allow users a more direct use.

mafavaron commented 6 years ago

Here some of my considerations (after a bit of thought).

Supporting REAL(8) time stamps is surely possible, and more than advisable.

But, REAL(8) values encompass a range incomparably larger than INTEGER(4), so the problem arises of deciding whether to extend the current PackTime/Unpack time or not.

To date, if you try converting the largest possible INTEGER(4) value to date/time you get this final date:

2038-01-19 03:14:07

The minimum is, of course, the Epoch,

1970-01-01 00:00:00

Is January 18, 2038, far away enough? A bit less than 20 years from now: if we're lucky, many of us who are reading this may aspect to be still alive by then - maybe, still maintaining the code base? Not that much, after all...

But on the other side, PackTime/UnpackTime are used in tons of legacy code (not only at Servizi Territorio - that would not be a huge problem - but also at ARPA Lombardy, ARPA Lazio, ARPA Puglia, ENICHEM, ENIPOWER, A2A, ...). I'm quite afraid it spread like a virus.

It would be a very good idea to migrate progressively, inside _pblmet and outside of it, from INTEGER(4) to INTEGER(8) time stamps.

I'm so sorry Fortran does not contain a data type like UNSIGNED INTEGER, because in this case the maximum allowed date/time would have been much larger.

INTEGER(8) has a problem, however: IEEE REAL(8) can represent only a subset of it! Larger than the span of INTEGER(4) however, and this should be more than enough to cover quite a part of the auspicable evolutionary time span of human species.

So, we might agree to use INTEGER(8) time stamps, but restricting their validity range between 0 and an "arbitrarily" set upper limit which 1) is fully representable without precision loss by a REAL(8), and 2) is reasonably large to accommodate the time span of a typical human civilisation - in the meanwhile, someone will figure out something better.

(The typical programmer hubris? Surely the _pblmet will not endure that much. At least, I hope it will replaced by something better within ten years).

mafavaron commented 6 years ago

So, which arbitrary limit? The largest possible? How to locate it?

In IEEE floating point, a REAL(8) is represented as

The bit configuration corresponding to the largest integer is then made of 52 ones (plus an implied one in most significant position).

If (if!) I have not misunderstood something, the following code should write values, from which the largest "integer" can be deduced:

`program testFloat

implicit none

real(8) :: f, g, h
integer :: i

f = B'0011111111111111111111111111111111111111111111111111111111111111'
print *, f

do i = 1, 54
    f = f * 2
    g = f - 1.
    h = g + 1.
    print *, f, g, h, f == h
end do

end program testFloat `

The output of the preceding program is:

` 1.9999999999999998
3.9999999999999996 2.9999999999999996 3.9999999999999996 T 7.9999999999999991 6.9999999999999991 7.9999999999999991 T 15.999999999999998 14.999999999999998 15.999999999999998 T 31.999999999999996 30.999999999999996 31.999999999999996 T 63.999999999999993 62.999999999999993 63.999999999999993 T 127.99999999999999 126.99999999999999 127.99999999999999 T 255.99999999999997 254.99999999999997 255.99999999999997 T 511.99999999999994 510.99999999999994 511.99999999999994 T 1023.9999999999999 1022.9999999999999 1023.9999999999999 T 2047.9999999999998 2046.9999999999998 2047.9999999999998 T 4095.9999999999995 4094.9999999999995 4095.9999999999995 T 8191.9999999999991 8190.9999999999991 8191.9999999999991 T 16383.999999999998 16382.999999999998 16383.999999999998 T 32767.999999999996 32766.999999999996 32767.999999999996 T 65535.999999999993 65534.999999999993 65535.999999999993 T 131071.99999999999 131070.99999999999 131071.99999999999 T 262143.99999999997 262142.99999999997 262143.99999999997 T 524287.99999999994 524286.99999999994 524287.99999999994 T 1048575.9999999999 1048574.9999999999 1048575.9999999999 T 2097151.9999999998 2097150.9999999998 2097151.9999999998 T 4194303.9999999995 4194302.9999999995 4194303.9999999995 T 8388607.9999999991 8388606.9999999991 8388607.9999999991 T 16777215.999999998 16777214.999999998 16777215.999999998 T 33554431.999999996 33554430.999999996 33554431.999999996 T 67108863.999999993 67108862.999999993 67108863.999999993 T 134217727.99999999 134217726.99999999 134217727.99999999 T 268435455.99999997 268435454.99999997 268435455.99999997 T 536870911.99999994 536870910.99999994 536870911.99999994 T 1073741823.9999999 1073741822.9999999 1073741823.9999999 T 2147483647.9999998 2147483646.9999998 2147483647.9999998 T 4294967295.9999995 4294967294.9999995 4294967295.9999995 T 8589934591.9999990 8589934590.9999990 8589934591.9999990 T 17179869183.999998 17179869182.999998 17179869183.999998 T 34359738367.999996 34359738366.999996 34359738367.999996 T 68719476735.999992 68719476734.999992 68719476735.999992 T 137438953471.99998 137438953470.99998 137438953471.99998 T 274877906943.99997 274877906942.99997 274877906943.99997 T 549755813887.99994 549755813886.99994 549755813887.99994 T 1099511627775.9999 1099511627774.9999 1099511627775.9999 T 2199023255551.9998 2199023255550.9998 2199023255551.9998 T 4398046511103.9995 4398046511102.9995 4398046511103.9995 T 8796093022207.9990 8796093022206.9990 8796093022207.9990 T 17592186044415.998 17592186044414.998 17592186044415.998 T 35184372088831.996 35184372088830.996 35184372088831.996 T 70368744177663.992 70368744177662.992 70368744177663.992 T 140737488355327.98 140737488355326.98 140737488355327.98 T 281474976710655.97 281474976710654.97 281474976710655.97 T 562949953421311.94 562949953421310.94 562949953421311.94 T 1125899906842623.9 1125899906842622.9 1125899906842623.9 T 2251799813685247.8 2251799813685246.8 2251799813685247.8 T 4503599627370495.5 4503599627370494.5 4503599627370495.5 T 9007199254740991.0 9007199254740990.0 9007199254740991.0 T

      1. F
      1. T `

So, it seems the largest "integer" is

$T_M = 9007199254740991$

This value is much larger than the maximum INTEGER(4) value, 2147483647.

And, much smaller than the maximum INTEGER(8) value, 9223372036854775807.

We may then use $T_M$ as upper time limit, if REAL(8) time stamps are desired.

mafavaron commented 6 years ago

The possibility a REAL(8) time stamp is invalid demands some method is introduced to flag conversions of "wrong" dates and times.

Currently, PackTime and UnpackTime address this problem (with INTEGER(4) time stamps) quite indirectly and, let me say, confusing. In a very large part, this is due to the procedure interface, of SUBROUTINE type.

The new procedures should then be of function type, with an INTEGER(4) return code whose value is 0 on successful return, as usual, and 1 in case of an off-range value.

As for names, instead of a neutral-looking "PackTime", would it maybe be better using a clearer name like "ToEpoch"? "FromEpoch"?

mafavaron commented 6 years ago

Another question is, whether taking time zone into account, or not.

In PackTime/UnpackTime a decision was taken to not take time zones into account. All routines sensitive to time zone (e.g. the estimator for extraterrestrial shortwave solar radiation) contain a time zone parameter, so its explicit enforcing in date and times was not considered necessary.

The same remains true today, in my opinion.

So, would it for me only, time zone should not be part of a date-time value.

My opinion is, also, that using date-time values without time zone, once you know the rule, is easier to remember and more predictable, than on the contrary - I'm thinking to the not always clear (to me) treatment of date-times of POSIXct type in R.

mafavaron commented 6 years ago

And, as a further addition, why the ...@#*¶... should one declare SIX values (iYear, iMonth, iDay, ...) just to call from-to Epoch conversions, like when using PackTime / UnpackTime.

Might it be better to have a data type which is simpler (shorter-to-write) to declare? Like so:

rEpoch = ToEpoch(tDateTime)

where, say, tDateTime is an instance of the data type

type DateTime
    integer(2)  :: iYear, iMonth, iDay, iHour, iMinute
    real(8)        :: rSecond
end type DateTime

???

Or, more extremely even (object-oriented syntax),

rEpoch = tDateTime % toEpoch()   ???

I personally feel the OOP version more concise and immediate to grasp.

mafavaron commented 6 years ago

Leap seconds in new functions?!

No, no, please no!

(No real need in current context)

mafavaron commented 6 years ago

O, my goodness. I've tried with the largest integer allowed within a REAL(8) as the epoch value.

The year is, let me say, "quite large": 285428751. That would allow to count a number of years like from Triassic to present. My hope is, by then, our species will be still here, maybe a bit changed, but I imagine it would have something better in their hands than the pbl_met...

Maybe, a largest date like 10000-01-01 00:00:00, exclusive, would be just enough, isn't it? It would still be ridiculously far away, but at least the maximum legal year would be 9999, that is, printable in 4 characters.

The Epoch corresponding to 10000-01-01 00:00:00 is 253402300800.00000. A value around 1012, not 1016. That might be good enough?

Deal made, you convinced me Mauri! :-)

mafavaron commented 6 years ago

New DateTime class implemented as said, without removing extant PackTime / UnpackTime.

From now on it is possible to work using REAL(8) time stamps.

Issue may then be closed.

mafavaron commented 6 years ago

Comment from Daniel Fraternali (I write it, because he can't access the net in this moment):

  1. Why not adding a warning message when close to end year?
  2. Ok with double precision.