Line data Source code
1 : !> \file mo_julian.f90
2 : !> \brief \copybrief mo_julian
3 : !> \details \copydetails mo_julian
4 :
5 : !> \brief Julian date conversion routines
6 : !> \details Julian date to and from day, month, year, and also from day, month, year, hour, minute, and second.
7 : !! Also convience routines for Julian dates of IMSL are provided.
8 : !! \note Julian day definition starts at noon of the 1st January 4713 BC.\n
9 : !! Here, the astronomical definition is used,
10 : !! i.e. the year 1 BC (historic) is counted as 0 (astronomic), 2 BC is -1, etc.\n
11 : !! This means that Julian day definition starts as 01.01.-4712 in astronomical units.\n
12 : !! \n
13 : !! julday and caldat start at midnight of the 1st January 4713 BC.
14 : !! So date2dec and julday as well as dec2date and caldat are shifted by half a day.\n
15 : !! Use date2dec with dec2date together for fractional Julian dates
16 : !! and use julday with caldat together for integer Julian days.
17 : !> \changelog
18 : !! - Matthias Cuntz, Dec 2011
19 : !! - Matthias Cuntz, Jan 2013
20 : !! - added date2dec and dec2date
21 : !! - Matthias Cuntz, May 2014
22 : !! - changed to new algorithm with astronomical units removed numerical recipes
23 : !! - David Schaefer, Oct 2015
24 : !! - addded 360 day calendar procedures
25 : !! - David Schaefer, Jan 2016
26 : !! - addded 365 day calendar procedures
27 : !! - David Schaefer, Feb 2016
28 : !! - implemented wrapper function and the module calendar state
29 : !> \author Matthias Cuntz
30 : !> \date Dec 2011
31 : !> \copyright Copyright 2005-\today, the CHS Developers, Sabine Attinger: All rights reserved.
32 : !! FORCES is released under the LGPLv3+ license \license_note
33 : MODULE mo_julian
34 :
35 : USE mo_kind, ONLY : i4, i8, dp
36 :
37 : IMPLICIT NONE
38 :
39 : PRIVATE
40 :
41 : PUBLIC :: caldat
42 : PUBLIC :: date2dec ! Fractional Julian day from day, month, year, hour, minute, and second
43 : PUBLIC :: dec2date ! Day, month, year, hour, minute, and second from fractional Julian day
44 : PUBLIC :: julday ! Julian day from day, month and year
45 : PUBLIC :: ndays ! IMSL Julian day from day, month and year
46 : PUBLIC :: ndyin ! Day, month and year from IMSL Julian day
47 : public :: setCalendar
48 : public :: caldatJulian
49 :
50 : integer(i4), save, private :: calendar = 1
51 :
52 : ! ------------------------------------------------------------------
53 :
54 : !> \brief Set module private variable calendar
55 :
56 : !> \details
57 : !! Set which type of calendar to use.
58 : !!
59 : !! \b Example
60 : !!
61 : !! Set to Julian calendar.
62 : !! \code{.f90}
63 : !! call caldat("julian")
64 : !! call caldat(1)
65 : !! \endcode
66 :
67 : !> \param[in] "character(len=*)/integer(i4) :: selector" {"julian"/1|"365day"/2|"360day"/3}
68 :
69 : !> \author David Schaefer
70 : !> \date Jan 2015
71 :
72 : interface setCalendar
73 : module procedure setCalendarInteger, setCalendarString
74 : end interface setCalendar
75 :
76 : CONTAINS
77 :
78 2 : subroutine setCalendarString(selector)
79 : character(*), intent(in) :: selector
80 :
81 0 : select case(selector)
82 : case("julian")
83 0 : call setCalendarInteger(1)
84 : case("365day")
85 1 : call setCalendarInteger(2)
86 : case("360day")
87 1 : call setCalendarInteger(3)
88 : case default
89 0 : print*, "Unknown selector! Select on of 'julian', '365day', '360day'."
90 0 : stop 1
91 : end select
92 2 : end subroutine setCalendarString
93 :
94 :
95 2 : subroutine setCalendarInteger(selector)
96 : integer(i4), intent(in) :: selector
97 :
98 2 : if ((selector .lt. 1) .or. (selector .gt. 3)) then
99 0 : print*, "Unknown selector! Select on of 1, 2, 3."
100 0 : stop 1
101 : end if
102 2 : calendar = selector
103 :
104 2 : end subroutine setCalendarInteger
105 :
106 : ! ------------------------------------------------------------------
107 :
108 : !> \brief Select a calendar
109 :
110 : !> \details Returns a valid calendar index, based on the given optional argument
111 : !! and/or the module global private variable calendar. If an invalid selector is passed,
112 : !! its value is ignored and the global calendar value retuned instead.
113 : !!
114 : !! \b Example
115 : !!
116 : !! Returns a valid index which is 3
117 : !! \code{.f90}
118 : !! idx = selectCalendar(3)
119 : !! \endcode
120 :
121 : !> \param[in] "integer(i4), optional :: selector" Calendar selector {1|2|3}
122 :
123 : !> \author David Schaefer
124 : !> \date Jan 2015
125 130924072 : pure function selectCalendar(selector)
126 : integer(i4), intent(in), optional :: selector
127 : integer(i4) :: selectCalendar
128 :
129 130924072 : selectCalendar = calendar
130 130924072 : if (present(selector)) then
131 4 : if ((selector .gt. 0) .and. (selector .lt. 4)) then
132 4 : selectCalendar = selector
133 : end if
134 : end if
135 :
136 2 : end function selectCalendar
137 :
138 : ! ------------------------------------------------------------------
139 :
140 : !> \brief Day, month and year from Julian day in the current or given calendar
141 :
142 : !> \details
143 : !! Wrapper around the calendar specific caldat procedures.
144 : !! Inverse of the function julday. Here julian is input as a Julian Day Number,
145 : !! and the routine outputs d0d, mm, and yy as the day, month, and year on which the specified
146 : !! Julian Day started at noon.\n
147 : !!
148 : !! The zeroth Julian Day depends on the called procedure. See their documentation for details.
149 : !!
150 : !! \b Example
151 : !!
152 : !! Converts julday to dd, mm, yy format
153 : !! \code{.f90}
154 : !! call caldat(julday, dd, mm, yy)
155 : !! \endcode
156 :
157 : !> \param[in] "integer(i4) :: julday" Julian day
158 : !> \param[out] "integer(i4) :: dd" Day in month of Julian day
159 : !> \param[out] "integer(i4) :: mm" Month in year of Julian day
160 : !> \param[out] "integer(i4) :: yy" Year of Julian day
161 : !> \param[in] "integer(i4) :: calendar" The calendar to use, the global calendar
162 : !! will be used by default
163 :
164 : !> \author David Schaefer
165 : !> \date Jan 2015
166 7636067 : elemental subroutine caldat(julian, dd, mm, yy, calendar)
167 :
168 : implicit none
169 :
170 : integer(i4), intent(in) :: julian
171 : integer(i4), intent(out) :: dd, mm, yy
172 : integer(i4), intent(in), optional :: calendar
173 :
174 5158863 : select case(selectCalendar(calendar))
175 : case(1)
176 5158863 : call caldatJulian(julian, dd, mm, yy)
177 : case(2)
178 1606002 : call caldat365(julian, dd, mm, yy)
179 : case(3)
180 871202 : call caldat360(julian, dd, mm, yy)
181 : end select
182 :
183 130924072 : end subroutine caldat
184 :
185 : ! ------------------------------------------------------------------
186 :
187 : !> \brief Day, month, year, hour, minute, and second from fractional Julian day in the current or given calendar
188 :
189 : !> \details Wrapper around the calendar specific dec2date procedures.
190 : !! Inverse of the function date2dec. Here dec2date is input as a fractional Julian Day.
191 : !! The routine outputs dd, mm, yy, hh, nn, ss as the day, month, year, hour, minute, and second
192 : !! on which the specified Julian Day started at noon.
193 : !!
194 : !! The zeroth Julian Day depends on the called procedure. See their documentation for details.
195 : !!
196 : !! \b Example
197 : !!
198 : !! Takes in fraction julian date fJulian to covert to dd, mm, yy, hh, nn, ss
199 : !! \code{.f90}
200 : !! call dec2date(fJulian, dd, mm, yy, hh, nn, ss)
201 : !! \endcode
202 :
203 : !> \param[in] "real(dp) :: fJulian" fractional Julian day
204 : !> \param[in] "integer(i4), optional :: calendar" The calendar to use, the global calendar
205 : !! will be used by default
206 : !> \param[out] "integer(i4), optional :: dd" Day in month of Julian day
207 : !> \param[out] "integer(i4), optional :: mm" Month in year of Julian day
208 : !> \param[out] "integer(i4), optional :: yy" Year of Julian day
209 : !> \param[out] "integer(i4), optional :: hh" Hour of Julian day
210 : !> \param[out] "integer(i4), optional :: nn" Minute in hour of Julian day
211 : !> \param[out] "integer(i4), optional :: ss" Second in minute of hour of Julian day
212 :
213 :
214 : ! HISTORY
215 : !> \author David Schaefer
216 : !> \date Jan 2015
217 77101599 : elemental subroutine dec2date(julian, dd, mm, yy, hh, nn, ss, calendar)
218 :
219 : implicit none
220 :
221 : real(dp), intent(in) :: julian
222 : integer(i4), intent(out), optional :: dd, mm, yy, hh, nn, ss
223 : integer(i4), intent(in), optional :: calendar
224 :
225 35345303 : select case(selectCalendar(calendar))
226 : case(1)
227 35345303 : call dec2dateJulian(julian, dd, mm, yy, hh, nn, ss)
228 : case(2)
229 11242011 : call dec2date365(julian, dd, mm, yy, hh, nn, ss)
230 : case(3)
231 30514285 : call dec2date360(julian, dd, mm, yy, hh, nn, ss)
232 : end select
233 :
234 7636067 : end subroutine dec2date
235 :
236 : ! ------------------------------------------------------------------
237 :
238 : !> \brief Fractional Julian day from day, month, year, hour, minute, second in the current calendar
239 :
240 : !> \details Wrapper around the calendar specific date2dec procedures.
241 : !! In this routine date2dec returns the fractional Julian Day that begins at noon
242 : !! of the calendar date specified by month mm, day dd, and year yy, all integer variables.
243 : !!
244 : !! The zeroth Julian Day depends on the called procedure. See their documentation for details.
245 : !!
246 : !! \b Example
247 : !!
248 : !! Take dd, mm, yy, hh, nn, ss to fractional Julian date
249 : !! \code{.f90}
250 : !! date2dec = date2dec(dd, mm, yy, hh, nn, ss)
251 : !! \endcode
252 :
253 : !> \param[in] "integer(i4), optional :: dd" Day in month of Julian day (default: 1)
254 : !> \param[in] "integer(i4), optional :: mm" Month in year of Julian day (default: 1)
255 : !> \param[in] "integer(i4), optional :: yy" Year of Julian day (default: 1)
256 : !> \param[in] "integer(i4), optional :: hh" Hours of Julian day (default: 0)
257 : !> \param[in] "integer(i4), optional :: nn" Minutes of hour of Julian day (default: 0)
258 : !> \param[in] "integer(i4), optional :: ss" Secondes of minute of hour of Julian day (default: 0)
259 : !> \param[in] "integer(i4), optional :: calendar" The calendar to use, the global calendar
260 : !! will be used by default
261 : !> \retval "real(dp) :: date2dec" Fractional Julian day
262 :
263 : !> \author David Schaefer
264 : !> \date Jan 2015
265 41957130 : elemental function date2dec(dd, mm, yy, hh, nn, ss, calendar)
266 :
267 : implicit none
268 :
269 : integer(i4), intent(in), optional :: dd, mm, yy
270 : integer(i4), intent(in), optional :: hh, nn, ss
271 : integer(i4), intent(in), optional :: calendar
272 : real(dp) :: date2dec
273 :
274 20196767 : select case(selectCalendar(calendar))
275 : case(1)
276 20196767 : date2dec = date2decJulian(dd, mm, yy, hh, nn, ss)
277 : case(2)
278 6424013 : date2dec = date2dec365(dd, mm, yy, hh, nn, ss)
279 : case(3)
280 15336350 : date2dec = date2dec360(dd, mm, yy, hh, nn, ss)
281 : end select
282 :
283 77101599 : end function date2dec
284 :
285 : ! ------------------------------------------------------------------
286 :
287 : !> \brief Julian day from day, month and year in the current or given calendar
288 :
289 : !> \details Wrapper around the calendar specific julday procedures.
290 : !! In this routine julday returns the Julian Day Number that begins at noon of the calendar
291 : !! date specified by month mm, day dd, and year yy, all integer variables.
292 : !!
293 : !! The zeroth Julian Day depends on the called procedure. See their documentation for details.
294 : !!
295 : !! \b Example
296 : !!
297 : !! Take dd, mm, yy, hh, nn, ss to Julian date
298 : !! \code{.f90}
299 : !! date2dec = date2dec(dd, mm, yy)
300 : !! \endcode
301 :
302 : !> \param[in] "integer(i4) :: dd" Day in month of Julian day
303 : !> \param[in] "integer(i4) :: mm" Month in year of Julian day
304 : !> \param[in] "integer(i4) :: yy" Year of Julian day
305 : !> \param[in] "integer(i4), optional :: calendar" The calendar to use, the global calendar
306 : !! will be used by default
307 :
308 : !> \retval "integer(i4) :: julian" Julian day
309 :
310 : !> \author David Schaefer
311 : !> \date Jan 2015
312 4229276 : elemental function julday(dd, mm, yy, calendar)
313 :
314 : implicit none
315 :
316 : integer(i4), intent(in) :: dd, mm, yy
317 : integer(i4), intent(in), optional :: calendar
318 : integer(i4) :: julday
319 :
320 2634272 : select case(selectCalendar(calendar))
321 : case(1)
322 2634272 : julday = juldayJulian(dd, mm, yy)
323 : case(2)
324 803002 : julday = julday365(dd, mm, yy)
325 : case(3)
326 792002 : julday = julday360(dd, mm, yy)
327 : end select
328 :
329 41957130 : end function julday
330 :
331 : ! ------------------------------------------------------------------
332 :
333 : !> \brief Day, month and year from Julian day
334 :
335 : !> \details Inverse of the function juldayJulian. Here julian is input as a Julian Day Number,
336 : !! and the routine outputs id, mm, and yy as the day, month, and year on which the specified
337 : !! Julian Day started at noon.
338 : !!
339 : !! The zeroth Julian Day is 01.01.-4712, i.e. the 1st January 4713 BC.
340 : !!
341 : !! Julian day definition starts at 1st January 4713 BC.\n
342 : !! Here, the astronomical definition is used,
343 : !! i.e. the year 1 BC (historic) is counted as 0 (astronomic), 2 BC is -1, etc.\n
344 : !! This means that Julian day definition starts as 01.01.-4712 in astronomical units.\n
345 : !!
346 : !! \b Example
347 : !!
348 : !! \code{.f90}
349 : !! 2415021 is 01.01.1900
350 : !! call caldatJulian(2415021, dd, mm, yy)
351 : !! \endcode
352 : !!
353 : !! \b Literature
354 : !! 1. http://de.wikipedia.org/wiki/Julianisches_Datum
355 : !! which is different to the english Wiki\n
356 : !! 2. http://en.wikipedia.org/wiki/Julian_day
357 : !! It is essentially the same as Numerical Recipes but uses astronomical instead of historical units.
358 :
359 :
360 : !> \param[in] "integer(i4) :: Julday" Julian day
361 : !> \param[out] "integer(i4) :: dd" Day in month of Julian day
362 : !> \param[out] "integer(i4) :: mm" Month in year of Julian day
363 : !> \param[out] "integer(i4) :: yy" Year of Julian day
364 :
365 : !> \note Julian day definition starts at noon of the 1st January 4713 BC.\n
366 : !! Here, the astronomical definition is used,
367 : !! i.e. the year 1 BC (historic) is counted as 0 (astronomic), 2 BC is -1, etc.\n
368 : !! This means that Julian day definition starts as 01.01.-4712 in astronomical units.\n
369 : !!
370 : !! julday and caldat start at midnight of the 1st January 4713 BC.
371 : !! So date2decJulian and juldayJulian as well as dec2dateJulian and caldatJulian are shifted by half a day.\n
372 : !! Use date2decJulian with dec2dateJulian together for fractional Julian dates
373 : !! and use juldayJulian with caldatJulian together for integer Julian days.
374 :
375 : !> \author Matthias Cuntz
376 : !> \date Dec 2011
377 : !! - modified julday from Numerical Recipes
378 : !> \date May 2014
379 : !! - changed to new algorithm with astronomical units
380 : !! - removed numerical recipes
381 :
382 : !> \author David Schaefer
383 : !> \date Jan 2016
384 : !! - renamed procodure
385 5158863 : ELEMENTAL SUBROUTINE caldatJulian(julian, dd, mm, yy)
386 :
387 : IMPLICIT NONE
388 :
389 : INTEGER(i4), INTENT(IN) :: julian
390 : INTEGER(i4), INTENT(OUT) :: dd, mm, yy
391 :
392 : INTEGER(i8) :: A, B, C, D, E, g
393 : INTEGER(i4), PARAMETER :: IGREG = 2299161_i4
394 :
395 5158863 : if (julian < IGREG) then
396 4598411 : A = int(julian, i8) ! julian
397 : else
398 560452 : g = int((real(julian, dp) - 1867216.25_dp) / 36524.25_dp, i8) ! gregorian
399 560452 : A = julian + 1_i8 + g - g / 4_i8
400 : end if
401 :
402 5158863 : B = A + 1524_i8
403 5158863 : C = int((real(B, dp) - 122.1_dp) / 365.25_dp, i8)
404 5158863 : D = int(365.25_dp * real(C, dp), i8)
405 5158863 : E = int(real(B - D, dp) / 30.6001_dp, i8)
406 :
407 5158863 : dd = int(B - D - int(30.6001_dp * real(E, dp), i8), i4)
408 :
409 5158863 : if (E<14_i8) then
410 4322010 : mm = int(E - 1_i8, i4)
411 : else
412 836853 : mm = int(E - 13_i8, i4)
413 : end if
414 :
415 5158863 : if (mm > 2) then
416 4322010 : yy = int(C - 4716_i8, i4)
417 : else
418 836853 : yy = int(C - 4715_i8, i4)
419 : end if
420 :
421 4229276 : END SUBROUTINE caldatJulian
422 :
423 :
424 : ! ------------------------------------------------------------------
425 :
426 : !> \brief Fractional Julian day from day, month, year, hour, minute, second
427 :
428 : !> \details In this routine date2decJulian returns the fractional Julian Day that begins at noon
429 : !! of the calendar date specified by month mm, day dd, and year yy, all integer variables.
430 : !!
431 : !! The zeroth Julian Day is 01.01.-4712 at noon, i.e. the 1st January 4713 BC 12:00:00 h.
432 : !!
433 : !! Julian day definition starts at noon of the 1st January 4713 BC.\n
434 : !! Here, the astronomical definition is used,
435 : !! i.e. the year 1 BC (historic) is counted as 0 (astronomic), 2 BC is -1, etc.\n
436 : !! This means that Julian day definition starts as 01.01.-4712 in astronomical units.\n
437 : !!
438 : !! \b Example
439 : !!
440 : !! 2415020.5 is 01.01.1900 00:00
441 : !! \code{.f90}
442 : !! julian = date2decJulian(01,01,1990)
443 : !! \endcode
444 : !! \code{.f90}
445 : !! 2415021.0 is 01.01.1900 12:00
446 : !! \endcode
447 : !! julian = date2decJulian(01,01,1990,12,00)
448 : !!
449 : !! \b Literature
450 : !!
451 : !! 1. http://de.wikipedia.org/wiki/Julianisches_Datum
452 : !! which is different to the english Wiki \n
453 : !! 2. http://en.wikipedia.org/wiki/Julian_day
454 : !! It is essentially the same as Numerical Recipes but uses astronomical instead of historical units.
455 : !! Numerical regulation of fractions is after \n
456 : !! 3. IDL routine julday.pro. Copyright (c) 1988-2011, ITT Visual Information Solutions.
457 :
458 : !> \param[in] "integer(i4), optional :: dd" Day in month of Julian day (default: 1)
459 : !> \param[in] "integer(i4), optional :: mm" Month in year of Julian day (default: 1)
460 : !> \param[in] "integer(i4), optional :: yy" Year of Julian day (default: 1)
461 : !> \param[in] "integer(i4), optional :: hh" Hours of Julian day (default: 0)
462 : !> \param[in] "integer(i4), optional :: nn" Minutes of hour of Julian day (default: 0)
463 : !> \param[in] "integer(i4), optional :: ss" Secondes of minute of hour of Julian day (default: 0)
464 : !> \retval "real(dp) :: date2dec" Fractional Julian day
465 :
466 : !> \note Julian day definition starts at noon of the 1st January 4713 BC.\n
467 : !! Here, the astronomical definition is used,
468 : !! i.e. the year 1 BC (historic) is counted as 0 (astronomic), 2 BC is -1, etc.\n
469 : !! This means that Julian day definition starts as 01.01.-4712 in astronomical units.\n
470 : !!
471 : !! juldayJulian and caldatJulian start at midnight of the 1st January 4713 BC.
472 : !! So date2decJulian and juldayJulian as well as dec2dateJulian and caldatJulian are shifted by half a day.\n
473 : !! Use date2decJulian with dec2dateJulian together for fractional Julian dates
474 : !! and use juldayJulian with caldatJulian together for integer Julian days.
475 :
476 : !> \author Matthias Cuntz
477 : !> \date Jan 2013
478 : !> \date May 2014
479 : !! - changed to new algorithm with astronomical units
480 : !> - removed numerical recipes
481 :
482 : !> \author David Schaefer
483 : !> \date Jan 2016
484 : !! - renamed procodure
485 :
486 20196767 : ELEMENTAL FUNCTION date2decJulian(dd, mm, yy, hh, nn, ss)
487 :
488 : IMPLICIT NONE
489 :
490 : INTEGER(i4), INTENT(IN), OPTIONAL :: dd, mm, yy
491 : INTEGER(i4), INTENT(IN), OPTIONAL :: hh, nn, ss
492 : REAL(dp) :: date2decJulian
493 :
494 : INTEGER(i4), PARAMETER :: IGREG2 = 15 + 31 * (10 + 12 * 1582)
495 : INTEGER(i4), PARAMETER :: IGREG1 = 4 + 31 * (10 + 12 * 1582)
496 : INTEGER(i4) :: idd, imm, iyy
497 20196767 : REAL(dp) :: ihh, inn, iss
498 : INTEGER(i8) :: jm, jy
499 20196767 : REAL(dp) :: jd, H, eps
500 : INTEGER(i8) :: A, B
501 :
502 : ! Presets
503 20196767 : idd = 1
504 20196767 : if (present(dd)) idd = dd
505 20196767 : imm = 1
506 20196767 : if (present(mm)) imm = mm
507 20196767 : iyy = 1
508 20196767 : if (present(yy)) iyy = yy
509 20196767 : ihh = 0.0_dp
510 20196767 : if (present(hh)) ihh = real(hh, dp)
511 20196767 : inn = 0.0_dp
512 20196767 : if (present(nn)) inn = real(nn, dp)
513 20196767 : iss = 0.0_dp
514 20196767 : if (present(ss)) iss = real(ss, dp)
515 :
516 20196767 : if (imm > 2) then
517 16920907 : jm = int(imm, i8)
518 16920907 : jy = int(iyy, i8)
519 : else
520 3275860 : jm = int(imm + 12, i8)
521 3275860 : jy = int(iyy - 1, i8)
522 : end if
523 :
524 20196767 : jd = real(idd, dp)
525 :
526 20196767 : H = ihh / 24._dp + inn / 1440._dp + iss / 86400._dp
527 :
528 20196767 : if (dd + 31 * (mm + 12 * yy) >= IGREG2) then ! gregorian
529 1803484 : A = jy / 100_i8
530 1803484 : B = 2_i8 - A + A / 4_i8
531 18393283 : else if (dd + 31 * (mm + 12 * yy) <= IGREG1) then ! julian
532 18393283 : B = 0_i8
533 : ! else
534 : ! stop 'No Gregorian dates between 04.10.1582 and 15.10.1582'
535 : end if
536 :
537 : ! Fractional Julian day starts at noon
538 : date2decJulian = floor(365.25_dp * real(jy + 4716_i8, dp)) + &
539 20196767 : floor(30.6001_dp * real(jm + 1_i8, dp)) + jd + H + real(B, dp) - 1524.5_dp
540 :
541 : ! Add a small offset (proportional to julian date) for correct re-conversion.
542 20196767 : eps = epsilon(1.0_dp)
543 20196767 : eps = max(eps * abs(date2decJulian), eps)
544 20196767 : date2decJulian = date2decJulian + eps
545 :
546 5158863 : END FUNCTION date2decJulian
547 :
548 : ! ------------------------------------------------------------------
549 :
550 : !> \brief Day, month, year, hour, minute, and second from fractional Julian day
551 :
552 : !> \details Inverse of the function date2decJulian. Here dec2dateJulian is input as a fractional Julian Day,
553 : !! which starts at noon of the 1st January 4713 BC, i.e. 01.01.-4712.
554 : !! The routine outputs dd, mm, yy, hh, nn, ss as the day, month, year, hour, minute, and second
555 : !! on which the specified Julian Day started at noon.
556 : !!
557 : !! The zeroth Julian Day is 01.01.-4712 at noon, i.e. the 1st January 4713 BC at noon.
558 : !!
559 : !! Julian day definition starts at 1st January 4713 BC.\n
560 : !! Here, the astronomical definition is used,
561 : !! i.e. the year 1 BC (historic) is counted as 0 (astronomic), 2 BC is -1, etc.\n
562 : !! This means that Julian day definition starts as 01.01.-4712 in astronomical units.\n
563 : !!
564 : !! \b Example
565 : !!
566 : !! 2415020.5 is 01.01.1900 00:00
567 : !! \code{.f90}
568 : !! call caldatJulian(2415020.5, dd, mm, yy, hh, nn)
569 : !! \endcode
570 : !! 2415021.0 is 01.01.1900 12:00
571 : !! \code{.f90}
572 : !! call caldatJulian(2415021., dd, mm, yy, hh, nn, ss)
573 : !! \endcode
574 : !!
575 : !! \b Literature
576 : !!
577 : !! 1. http://de.wikipedia.org/wiki/Julianisches_Datum
578 : !! which is different to the english Wiki\n
579 : !! 2. http://en.wikipedia.org/wiki/Julian_day
580 : !! It is essentially the same as Numerical Recipes but uses astronomical instead of historical units.
581 : !! Here the sometimes 60 sec as output are corrected at the end.
582 :
583 : !> \param[in] "real(dp) :: fJulian" fractional Julian day
584 : !> \param[out] "integer(i4), optional :: dd" Day in month of Julian day
585 : !> \param[out] "integer(i4), optional :: mm" Month in year of Julian day
586 : !> \param[out] "integer(i4), optional :: yy" Year of Julian day
587 : !> \param[out] "integer(i4), optional :: hh" Hour of Julian day
588 : !> \param[out] "integer(i4), optional :: nn" Minute in hour of Julian day
589 : !> \param[out] "integer(i4), optional :: ss" Second in minute of hour of Julian day
590 :
591 : !> \note Julian day definition starts at noon of the 1st January 4713 BC.\n
592 : !! Here, the astronomical definition is used,
593 : !! i.e. the year 1 BC (historic) is counted as 0 (astronomic), 2 BC is -1, etc.\n
594 : !! This means that Julian day definition starts as 01.01.-4712 in astronomical units.\n
595 : !!
596 : !! juldayJulian and caldatJulian start at midnight of the 1st January 4713 BC.
597 : !! So date2decJulian and juldayJulian as well as dec2dateJulian and caldatJulian are shifted by half a day.\n
598 : !! Use date2decJulian with dec2dateJulian together for fractional Julian dates
599 : !! and use juldayJulian with caldatJulian together for integer Julian days.
600 :
601 : !> \author Matthias Cuntz
602 : !> \date Jan 2013
603 : !> \date May 2014
604 : !! - changed to new algorithm with astronomical units
605 : !> - removed numerical recipes
606 :
607 : !> \author David Schaefer
608 : !> \date Jan 2016
609 : !! - renamed procodure
610 35345303 : ELEMENTAL SUBROUTINE dec2dateJulian(julian, dd, mm, yy, hh, nn, ss)
611 :
612 : IMPLICIT NONE
613 :
614 : REAL(dp), INTENT(IN) :: julian
615 : INTEGER(i4), INTENT(OUT), OPTIONAL :: dd, mm, yy
616 : INTEGER(i4), INTENT(OUT), OPTIONAL :: hh, nn, ss
617 :
618 : INTEGER(i4) :: day, month, year, hour, minute, second
619 35345303 : REAL(dp) :: fraction
620 : ! REAL(dp) :: eps
621 :
622 : INTEGER(i8) :: A, B, C, D, E, g, Z
623 : INTEGER(i4), PARAMETER :: IGREG = 2299161_i4
624 :
625 35345303 : Z = int(julian + 0.5, i8)
626 :
627 35345303 : if (Z < IGREG) then
628 : A = Z ! julian
629 : else
630 3157062 : g = int((real(Z, dp) - 1867216.25_dp) / 36524.25_dp, i8) ! gregorian
631 3157062 : A = Z + 1_i8 + g - g / 4_i8
632 : end if
633 :
634 35345303 : B = A + 1524_i8
635 35345303 : C = int((real(B, dp) - 122.1_dp) / 365.25_dp, i8)
636 35345303 : D = int(365.25_dp * real(C, dp), i8)
637 35345303 : E = int(real(B - D, dp) / 30.6001_dp, i8)
638 :
639 35345303 : day = int(B - D - int(30.6001_dp * real(E, dp), i8), i4)
640 :
641 35345303 : if (E<14_i8) then
642 29612121 : month = int(E - 1_i8, i4)
643 : else
644 5733182 : month = int(E - 13_i8, i4)
645 : end if
646 :
647 35345303 : if (month > 2) then
648 29612121 : year = int(C - 4716_i8, i4)
649 : else
650 5733182 : year = int(C - 4715_i8, i4)
651 : end if
652 :
653 : ! ! Fractional part
654 : ! eps = 1e-12_dp ! ~ 5000*epsilon(1.0_dp)
655 : ! eps = max(eps * abs(real(Z,dp)), eps)
656 : ! fraction = julian + 0.5_dp - real(Z,dp)
657 : ! hour = min(max(floor(fraction * 24.0_dp + eps), 0), 23)
658 : ! fraction = fraction - real(hour,dp)/24.0_dp
659 : ! minute = min(max(floor(fraction*1440.0_dp + eps), 0), 59)
660 : ! second = max(nint((fraction - real(minute,dp)/1440.0_dp)*86400.0_dp), 0)
661 :
662 : ! Fractional part
663 35345303 : fraction = julian + 0.5_dp - real(Z, dp)
664 35345303 : hour = min(max(floor(fraction * 24.0_dp), 0), 23)
665 35345303 : fraction = fraction - real(hour, dp) / 24.0_dp
666 35345303 : minute = min(max(floor(fraction * 1440.0_dp), 0), 59)
667 35345303 : second = max(nint((fraction - real(minute, dp) / 1440.0_dp) * 86400.0_dp), 0)
668 :
669 : ! If seconds==60
670 35345303 : if (second==60) then
671 148394 : second = 0
672 148394 : minute = minute + 1
673 148394 : if (minute==60) then
674 2553 : minute = 0
675 2553 : hour = hour + 1
676 2553 : if (hour==24) then
677 98 : hour = 0
678 98 : call caldat(julday(day, month, year) + 1, day, month, year)
679 : end if
680 : end if
681 : end if
682 :
683 35345303 : if (present(dd)) dd = day
684 35345303 : if (present(mm)) mm = month
685 35345303 : if (present(yy)) yy = year
686 35345303 : if (present(hh)) hh = hour
687 35345303 : if (present(nn)) nn = minute
688 35345303 : if (present(ss)) ss = second
689 :
690 20196767 : END SUBROUTINE dec2dateJulian
691 :
692 : ! ------------------------------------------------------------------
693 :
694 : !> \brief Julian day from day, month and year
695 :
696 : !> \details In this routine juldayJulian returns the Julian Day Number that begins at noon of the calendar
697 : !! date specified by month mm, day dd, and year yy, all integer variables.
698 : !!
699 : !! The zeroth Julian Day is 01.01.-4712 at noon, i.e. the 1st January 4713 BC 12:00:00 h.
700 : !!
701 : !! Julian day definition starts at noon of the 1st January 4713 BC.\n
702 : !! Here, the astronomical definition is used,
703 : !! i.e. the year 1 BC (historic) is counted as 0 (astronomic), 2 BC is -1, etc.\n
704 : !! This means that Julian day definition starts as 01.01.-4712 in astronomical units.\n
705 : !!
706 : !! \b Example
707 : !!
708 : !! 2415021 is 01.01.1900 and 2440588 is 01.01.1970
709 : !! \code{.f90}
710 : !! julian = juldayJulian(01,01,1990)
711 : !! \endcode
712 : !! See also example in test directory
713 : !!
714 : !! \b Literature
715 : !! 1. http://de.wikipedia.org/wiki/Julianisches_Datum
716 : !! which is different to the english Wiki \n
717 : !! 2. http://en.wikipedia.org/wiki/Julian_day
718 : !! It is essentially the same as Numerical Recipes but uses astronomical instead of historical units.
719 :
720 : !> \param[in] "integer(i4) :: dd" Day in month of Julian day
721 : !> \param[in] "integer(i4) :: mm" Month in year of Julian day
722 : !> \param[in] "integer(i4) :: yy" Year of Julian day
723 : !> \retval "integer(i4) :: julian" Julian day
724 :
725 : !> \note Julian day definition starts at noon of the 1st January 4713 BC.\n
726 : !! Here, the astronomical definition is used,
727 : !! i.e. the year 1 BC (historic) is counted as 0 (astronomic), 2 BC is -1, etc.\n
728 : !! This means that Julian day definition starts as 01.01.-4712 in astronomical units.\n
729 : !!
730 : !! juldayJulian and caldatJulian start at midnight of the 1st January 4713 BC.
731 : !! So date2decJulian and juldayJulian as well as dec2dateJulian and caldatJulian are shifted by half a day.\n
732 : !! Use date2decJulian with dec2dateJulian together for fractional Julian dates
733 : !! and use juldayJulian with caldatJulian together for integer Julian days.
734 :
735 : !> \author Matthias Cuntz
736 : !> \date Dec 2011
737 : !! - modified julday from Numerical Recipes
738 : !> \date May 2014
739 : !! - changed to new algorithm with astronomical units
740 : !! - removed numerical recipes
741 : !> \author David Schaefer
742 : !> \date Jan 2016
743 : !! - renamed procodure
744 2634272 : ELEMENTAL FUNCTION juldayJulian(dd, mm, yy)
745 :
746 : IMPLICIT NONE
747 :
748 : INTEGER(i4), INTENT(IN) :: dd, mm, yy
749 : INTEGER(i4) :: juldayJulian
750 :
751 : INTEGER(i4), PARAMETER :: IGREG2 = 15 + 31 * (10 + 12 * 1582)
752 : INTEGER(i4), PARAMETER :: IGREG1 = 4 + 31 * (10 + 12 * 1582)
753 : INTEGER(i8) :: jd, jm, jy
754 : INTEGER(i8) :: A, B
755 :
756 2634272 : if (mm > 2) then
757 2206948 : jm = int(mm, i8)
758 2206948 : jy = int(yy, i8)
759 : else
760 427324 : jm = int(mm + 12, i8)
761 427324 : jy = int(yy - 1, i8)
762 : end if
763 :
764 2634272 : jd = int(dd, i8)
765 :
766 2634272 : if (dd + 31 * (mm + 12 * yy) >= IGREG2) then ! gregorian
767 335021 : A = jy / 100_i8
768 335021 : B = 2_i8 - A + A / 4_i8
769 2299251 : else if (dd + 31 * (mm + 12 * yy) <= IGREG1) then ! julian
770 2299251 : B = 0_i8
771 : ! else
772 : ! stop 'No Gregorian dates between 04.10.1582 and 15.10.1582'
773 : end if
774 :
775 : ! add 0.5 to Wiki formula because formula was for fractional day
776 : ! juldayJulian = int(365.25_dp*real(jy+4716_i8,dp) + real(int(30.6001*real(jm+1_i8,dp),i8),dp) + real(jd+B,dp) - 1524.5_dp, i4)
777 : juldayJulian = int(365.25_dp * real(jy + 4716_i8, dp) + real(int(30.6001 * real(jm + 1_i8, dp), i8), dp) &
778 2634272 : + real(jd + B, dp) - 1524.5_dp + 0.5_dp, i4)
779 :
780 35345303 : END FUNCTION juldayJulian
781 :
782 : ! ------------------------------------------------------------------
783 :
784 : !> \brief IMSL Julian day from day, month and year
785 :
786 : !> \details In this routine ndays returns the IMSL Julian Day Number. Julian days begin at noon of the calendar
787 : !! date specified by month mm, day dd, and year yy, all integer variables. IMSL treats 01.01.1900
788 : !! as a reference and assigns a Julian day 0 to it.
789 : !!
790 : !! ndays = julday(dd,mm,yy) - julday(01,01,1900)
791 : !!
792 : !! \b Example
793 : !!
794 : !! 0 is 01.01.1900
795 : !! \code{.f90}
796 : !! julian = ndays(01,01,1990)
797 : !! \endcode
798 : !! See also example in test directory
799 :
800 : !> \param[in] "integer(i4) :: dd" Day in month of IMSL Julian day
801 : !> \param[in] "integer(i4) :: mm" Month in year of IMSL Julian day
802 : !> \param[in] "integer(i4) :: yy" Year of IMSL Julian day
803 : !> \retval "integer(i4) :: julian" IMSL Julian day, i.e. days before or after 01.01.1900
804 :
805 : !> \author Matthias Cuntz
806 : !> \date Dec 2011
807 :
808 109576 : ELEMENTAL FUNCTION ndays(dd, mm, yy)
809 :
810 : IMPLICIT NONE
811 :
812 : INTEGER(i4), INTENT(IN) :: dd, mm, yy
813 : INTEGER(i4) :: ndays
814 :
815 : INTEGER(i4), PARAMETER :: IMSLday = 2415021_i4
816 :
817 109576 : ndays = julday(dd, mm, yy) - IMSLday
818 :
819 2634272 : END FUNCTION ndays
820 :
821 : ! ------------------------------------------------------------------
822 :
823 : !> \brief Day, month and year from IMSL Julian day
824 :
825 : !> \details Inverse of the function ndys. Here ISML Julian is input as a Julian Day Number
826 : !! minus the Julian Day Number of 01.01.1900, and the routine outputs id, mm, and yy
827 : !! as the day, month, and year on which the specified Julian Day started at noon.
828 : !!
829 : !! ndyin is caldat(IMSLJulian + 2415021, dd, mm, yy)
830 : !!
831 : !! \b Example
832 : !!
833 : !! 0 is 01.01.1900
834 : !! \code{.f90}
835 : !! call ndyin(0,dd,mm,yy)
836 : !! \endcode
837 : !! See also example in test directory
838 :
839 : !> \param[in] "integer(i4) :: julian" IMSL Julian day, i.e. days before or after 01.01.1900
840 : !> \param[out] "integer(i4) :: dd" Day in month of IMSL Julian day
841 : !> \param[out] "integer(i4) :: mm" Month in year of IMSL Julian day
842 : !> \param[out] "integer(i4) :: yy" Year of IMSL Julian day
843 :
844 : !> \author Matthias Cuntz
845 : !> \date Dec 2011
846 :
847 109576 : ELEMENTAL SUBROUTINE ndyin(julian, dd, mm, yy)
848 :
849 : IMPLICIT NONE
850 :
851 : INTEGER(i4), INTENT(IN) :: julian
852 : INTEGER(i4), INTENT(OUT) :: dd, mm, yy
853 :
854 : INTEGER(i4), PARAMETER :: IMSLday = 2415021_i4
855 :
856 109576 : call caldat(julian + IMSLday, dd, mm, yy)
857 :
858 219152 : END SUBROUTINE ndyin
859 :
860 : ! ------------------------------------------------------------------
861 :
862 : !> \brief Day, month and year from Julian day in a 360 day calendar
863 :
864 : !> \details Inverse of the function julday360. Here julian is input as a Julian Day Number,
865 : !! and the routine outputs dd, mm, and yy as the day, month, and year on which the specified
866 : !! Julian Day started at noon.
867 : !!
868 : !! The zeroth Julian Day here is 01.01.0000
869 : !!
870 : !! \b Example
871 : !!
872 : !! \code{.f90}
873 : !! call caldat360(julday, dd, mm, yy)
874 : !! \endcode
875 :
876 : !> \param[in] "integer(i4) :: julday" Julian day
877 : !> \param[out] "integer(i4) :: dd" Day in month of Julian day
878 : !> \param[out] "integer(i4) :: mm" Month in year of Julian day
879 : !> \param[out] "integer(i4) :: yy" Year of Julian day
880 :
881 : !> \author David Schaefer
882 : !> \date Oct 2015
883 31385570 : elemental subroutine caldat360(julian, dd, mm, yy)
884 :
885 : implicit none
886 :
887 : integer(i4), intent(in) :: julian
888 : integer(i4), intent(out) :: dd, mm, yy
889 : integer(i4), parameter :: year = 360, month = 30
890 : integer(i4) :: remainder
891 :
892 31385570 : yy = julian / year
893 31385570 : remainder = mod(abs(julian), year)
894 31385570 : mm = remainder / month + 1
895 31385570 : dd = mod(abs(julian), month) + 1
896 :
897 109576 : end subroutine caldat360
898 :
899 : ! ------------------------------------------------------------------
900 :
901 : !> \brief Julian day from day, month and year in a 360_day calendar
902 :
903 : !> \details In this routine julday360 returns the Julian Day Number that begins at noon of the calendar
904 : !! date specified by month mm, day dd, and year yy, all integer variables.
905 : !!
906 : !! The zeroth Julian Day is 01.01.0000
907 : !!
908 : !! \b Example
909 : !!
910 : !! \code{.f90}
911 : !! julian = julday360(dd, mm, yy)
912 : !! \endcode
913 :
914 : !> \param[in] "integer(i4) :: dd" Day in month of Julian day
915 : !> \param[in] "integer(i4) :: mm" Month in year of Julian day
916 : !> \param[in] "integer(i4) :: yy" Year of Julian day
917 : !> \retval "integer(i4) :: julian" Julian day
918 :
919 : !> \author David Schaefer
920 : !> \date Oct 2015
921 16128435 : elemental function julday360(dd, mm, yy)
922 :
923 : implicit none
924 :
925 : integer(i4), intent(in) :: dd, mm, yy
926 : integer(i4) :: julday360
927 : integer(i4), parameter :: year = 360, month = 30
928 :
929 16128435 : julday360 = abs(yy) * year + (mm - 1) * month + (dd - 1)
930 2 : if (yy < 0) julday360 = julday360 * (-1)
931 :
932 47514005 : end function julday360
933 :
934 : ! ------------------------------------------------------------------
935 :
936 : !> \brief Day, month, year, hour, minute, and second from fractional Julian day in a 360_day calendar
937 :
938 : !> \details Inverse of the function date2dec360. Here dec2date360 is input as a fractional Julian Day.
939 : !! The routine outputs dd, mm, yy, hh, nn, ss as the day, month, year, hour, minute, and second
940 : !! on which the specified Julian Day started at noon.
941 : !!
942 : !! The zeroth Julian Day is 01.01.0000 at noon.
943 : !!
944 : !! \b Example
945 : !!
946 : !! \code{.f90}
947 : !! call dec2date360(fJulian, dd, mm, yy, hh, nn, ss)
948 : !! \endcode
949 :
950 : !> \param[in] "real(dp) :: fJulian" fractional Julian day
951 : !> \param[out] "integer(i4), optional :: dd" Day in month of Julian day
952 : !> \param[out] "integer(i4), optional :: mm" Month in year of Julian day
953 : !> \param[out] "integer(i4), optional :: yy" Year of Julian day
954 : !> \param[out] "integer(i4), optional :: hh" Hour of Julian day
955 : !> \param[out] "integer(i4), optional :: nn" Minute in hour of Julian day
956 : !> \param[out] "integer(i4), optional :: ss" Second in minute of hour of Julian day
957 :
958 : !> \author David Schaefer
959 : !> \date Oct 2015
960 30514285 : elemental subroutine dec2date360(julian, dd, mm, yy, hh, nn, ss)
961 :
962 : implicit none
963 :
964 : real(dp), intent(in) :: julian
965 : integer(i4), intent(out), optional :: dd, mm, yy
966 : integer(i4), intent(out), optional :: hh, nn, ss
967 : integer(i4) :: day, month, year
968 30514285 : real(dp) :: fraction, fJulian
969 : integer(i4) :: hour, minute, second
970 :
971 30514285 : fJulian = julian + .5_dp
972 30514285 : call caldat360(int(floor(fJulian), i4), day, month, year)
973 :
974 30514285 : fraction = fJulian - floor(fJulian)
975 30514285 : hour = min(max(floor(fraction * 24.0_dp), 0), 23)
976 30514285 : fraction = fraction - real(hour, dp) / 24.0_dp
977 30514285 : minute = min(max(floor(fraction * 1440.0_dp), 0), 59)
978 30514285 : second = max(nint((fraction - real(minute, dp) / 1440.0_dp) * 86400.0_dp), 0)
979 :
980 : ! If seconds==60
981 30514285 : if (second==60) then
982 127327 : second = 0
983 127327 : minute = minute + 1
984 127327 : if (minute==60) then
985 2078 : minute = 0
986 2078 : hour = hour + 1
987 2078 : if (hour==24) then
988 83 : hour = 0
989 83 : call caldat360(julday360(day, month, year) + 1, day, month, year)
990 : end if
991 : end if
992 : end if
993 :
994 30514285 : if (present(dd)) dd = day
995 30514285 : if (present(mm)) mm = month
996 30514285 : if (present(yy)) yy = year
997 30514285 : if (present(hh)) hh = hour
998 30514285 : if (present(nn)) nn = minute
999 30514285 : if (present(ss)) ss = second
1000 :
1001 16128435 : end subroutine dec2date360
1002 :
1003 : ! ------------------------------------------------------------------
1004 :
1005 : !> \brief Fractional Julian day from day, month, year, hour, minute, second in 360 day calendar
1006 :
1007 : !> \details In this routine date2dec360 returns the fractional Julian Day that begins at noon
1008 : !! of the calendar date specified by month mm, day dd, and year yy, all integer variables.
1009 : !!
1010 : !! The zeroth Julian Day is 01.01.0000 at noon.
1011 : !!
1012 : !! \b Example
1013 : !!
1014 : !! \code{.f90}
1015 : !! date2dec360 = date2dec360(dd, mm, yy, hh, nn, ss)
1016 : !! \endcode
1017 :
1018 : !> \param[in] "integer(i4), optional :: dd" Day in month of Julian day (default: 1)
1019 : !> \param[in] "integer(i4), optional :: mm" Month in year of Julian day (default: 1)
1020 : !> \param[in] "integer(i4), optional :: yy" Year of Julian day (default: 1)
1021 : !> \param[in] "integer(i4), optional :: hh" Hours of Julian day (default: 0)
1022 : !> \param[in] "integer(i4), optional :: nn" Minutes of hour of Julian day (default: 0)
1023 : !> \param[in] "integer(i4), optional :: ss" Secondes of minute of hour of Julian day (default: 0)
1024 : !> \retval "real(dp) :: date2dec360" Fractional Julian day
1025 :
1026 : !> \author David Schaefer
1027 : !> \date Oct 2015
1028 15336350 : elemental function date2dec360(dd, mm, yy, hh, nn, ss)
1029 :
1030 : implicit none
1031 :
1032 : integer(i4), intent(in), optional :: dd, mm, yy
1033 : integer(i4), intent(in), optional :: hh, nn, ss
1034 15336350 : real(dp) :: date2dec360, eps
1035 : integer(i4) :: idd, imm, iyy
1036 15336350 : real(dp) :: ihh, inn, iss
1037 15336350 : real(dp) :: hour
1038 :
1039 : ! Presets
1040 15336350 : idd = 1
1041 15336350 : if (present(dd)) idd = dd
1042 15336350 : imm = 1
1043 15336350 : if (present(mm)) imm = mm
1044 15336350 : iyy = 1
1045 15336350 : if (present(yy)) iyy = yy
1046 15336350 : ihh = 0.0_dp
1047 15336350 : if (present(hh)) ihh = real(hh, dp)
1048 15336350 : inn = 0.0_dp
1049 15336350 : if (present(nn)) inn = real(nn, dp)
1050 15336350 : iss = 0.0_dp
1051 15336350 : if (present(ss)) iss = real(ss, dp)
1052 :
1053 15336350 : hour = ihh / 24._dp + inn / 1440._dp + iss / 86400._dp - .5_dp
1054 :
1055 : ! Fractional Julian day starts at noon
1056 15336350 : date2dec360 = real(julday360(idd, imm, iyy), dp) + hour
1057 :
1058 : ! Add a small offset (proportional to julian date) for correct re-conversion.
1059 15336350 : eps = epsilon(1.0_dp)
1060 15336350 : eps = max(eps * abs(date2dec360), eps)
1061 15336350 : date2dec360 = date2dec360 + eps
1062 :
1063 30514285 : end function date2dec360
1064 :
1065 : ! ------------------------------------------------------------------
1066 :
1067 : !> \brief Day, month and year from Julian day in a 365 day calendar
1068 :
1069 : !> \details Inverse of the function julday365. Here julian is input as a Julian Day Number,
1070 : !! and the routine outputs dd, mm, and yy as the day, month, and year on which the specified
1071 : !! Julian Day started at noon.
1072 : !!
1073 : !! The zeroth Julian Day here is 01.01.0000
1074 : !!
1075 : !! \b Example
1076 : !!
1077 : !! \code{.f90}
1078 : !! call caldat365(julday, dd, mm, yy)
1079 : !! \endcode
1080 :
1081 : !> \param[in] "integer(i4) :: julday" Julian day
1082 : !> \param[out] "integer(i4) :: dd" Day in month of Julian day
1083 : !> \param[out] "integer(i4) :: mm" Month in year of Julian day
1084 : !> \param[out] "integer(i4) :: yy" Year of Julian day
1085 :
1086 : !> \author David Schaefer
1087 : !> \date Dec 2015
1088 12848044 : elemental subroutine caldat365(julian, dd, mm, yy)
1089 :
1090 : implicit none
1091 :
1092 : integer(i4), intent(in) :: julian
1093 : integer(i4), intent(out) :: dd, mm, yy
1094 : integer(i4), parameter :: year = 365
1095 : integer(i4), dimension(12), parameter :: months = (/31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31/)
1096 : integer(i4) :: remainder
1097 :
1098 12848044 : yy = julian / year
1099 12848044 : remainder = mod(abs(julian), year) + 1
1100 :
1101 83847408 : do mm = 1, size(months)
1102 83847408 : if (remainder .le. months(mm)) then
1103 : exit
1104 : end if
1105 83847408 : remainder = remainder - months(mm)
1106 : end do
1107 :
1108 12848044 : dd = remainder
1109 :
1110 15336350 : end subroutine caldat365
1111 :
1112 : ! ------------------------------------------------------------------
1113 :
1114 : !> \brief Julian day from day, month and year in a 365_day calendar
1115 :
1116 : !> \details In this routine julday365 returns the Julian Day Number that begins at noon of the calendar
1117 : !! date specified by month mm, day dd, and year yy, all integer variables.
1118 : !!
1119 : !! The zeroth Julian Day is 01.01.0000
1120 : !!
1121 : !! \b Example
1122 : !!
1123 : !! \code{.f90}
1124 : !! julian = julday365(dd, mm, yy)
1125 : !! \endcode
1126 :
1127 : !> \param[in] "integer(i4) :: dd" Day in month of Julian day
1128 : !> \param[in] "integer(i4) :: mm" Month in year of Julian day
1129 : !> \param[in] "integer(i4) :: yy" Year of Julian day
1130 : !> \retval "integer(i4) :: julian" Julian day
1131 :
1132 : !> \author David Schaefer
1133 : !> \date Dec 2015
1134 7227046 : elemental function julday365(dd, mm, yy)
1135 :
1136 : implicit none
1137 :
1138 : integer(i4), intent(in) :: dd, mm, yy
1139 : integer(i4) :: julday365
1140 : integer(i4), parameter :: year = 365
1141 : integer(i4), dimension(12), parameter :: months = (/31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31/)
1142 :
1143 47164393 : julday365 = abs(yy) * year + sum(months(1 : mm - 1)) + (dd - 1)
1144 :
1145 7227046 : if (yy < 0) julday365 = julday365 * (-1)
1146 :
1147 12848044 : end function julday365
1148 :
1149 : ! ------------------------------------------------------------------
1150 :
1151 : !> \brief Day, month, year, hour, minute, and second from fractional Julian day in a 365_day calendar
1152 :
1153 : !> \details Inverse of the function date2dec. Here dec2date365 is input as a fractional Julian Day.
1154 : !! The routine outputs dd, mm, yy, hh, nn, ss as the day, month, year, hour, minute, and second
1155 : !! on which the specified Julian Day started at noon.
1156 : !!
1157 : !! The zeroth Julian Day is 01.01.0000 at noon.
1158 : !!
1159 : !! \b Example
1160 : !!
1161 : !! \code{.f90}
1162 : !! call dec2date365(fJulian, dd, mm, yy, hh, nn, ss)
1163 : !! \endcode
1164 :
1165 : !> \param[in] "real(dp) :: fJulian" fractional Julian day
1166 : !> \param[out] "integer(i4), optional :: dd" Day in month of Julian day
1167 : !> \param[out] "integer(i4), optional :: mm" Month in year of Julian day
1168 : !> \param[out] "integer(i4), optional :: yy" Year of Julian day
1169 : !> \param[out] "integer(i4), optional :: hh" Hour of Julian day
1170 : !> \param[out] "integer(i4), optional :: nn" Minute in hour of Julian day
1171 : !> \param[out] "integer(i4), optional :: ss" Second in minute of hour of Julian day
1172 :
1173 : !> \author David Schaefer
1174 : !> \date Dec 2015
1175 11242011 : elemental subroutine dec2date365(julian, dd, mm, yy, hh, nn, ss)
1176 :
1177 : implicit none
1178 :
1179 : real(dp), intent(in) :: julian
1180 : integer(i4), intent(out), optional :: dd, mm, yy
1181 : integer(i4), intent(out), optional :: hh, nn, ss
1182 : integer(i4) :: day, month, year
1183 11242011 : real(dp) :: fraction, fJulian
1184 : integer(i4) :: hour, minute, second
1185 :
1186 11242011 : fJulian = julian + .5_dp
1187 11242011 : call caldat365(int(floor(fJulian), i4), day, month, year)
1188 :
1189 11242011 : fraction = fJulian - floor(fJulian)
1190 11242011 : hour = min(max(floor(fraction * 24.0_dp), 0), 23)
1191 11242011 : fraction = fraction - real(hour, dp) / 24.0_dp
1192 11242011 : minute = min(max(floor(fraction * 1440.0_dp), 0), 59)
1193 11242011 : second = max(nint((fraction - real(minute, dp) / 1440.0_dp) * 86400.0_dp), 0)
1194 :
1195 : ! If seconds==60
1196 11242011 : if (second==60) then
1197 46482 : second = 0
1198 46482 : minute = minute + 1
1199 46482 : if (minute==60) then
1200 782 : minute = 0
1201 782 : hour = hour + 1
1202 782 : if (hour==24) then
1203 31 : hour = 0
1204 31 : call caldat365(julday365(day, month, year) + 1, day, month, year)
1205 : end if
1206 : end if
1207 : end if
1208 :
1209 11242011 : if (present(dd)) dd = day
1210 11242011 : if (present(mm)) mm = month
1211 11242011 : if (present(yy)) yy = year
1212 11242011 : if (present(hh)) hh = hour
1213 11242011 : if (present(nn)) nn = minute
1214 11242011 : if (present(ss)) ss = second
1215 :
1216 7227046 : end subroutine dec2date365
1217 :
1218 : ! ------------------------------------------------------------------
1219 :
1220 : !> \brief Fractional Julian day from day, month, year, hour, minute, second in 365 day calendar
1221 :
1222 : !> \details In this routine date2dec365 returns the fractional Julian Day that begins at noon
1223 : !! of the calendar date specified by month mm, day dd, and year yy, all integer variables.
1224 : !!
1225 : !! The zeroth Julian Day is 01.01.0000 at noon.
1226 : !!
1227 : !! \b Example
1228 : !!
1229 : !! \code{.f90}
1230 : !! date2dec365 = date2dec365(dd, mm, yy, hh, nn, ss)
1231 : !! \endcode
1232 :
1233 : !> \param[in] "integer(i4), optional :: dd" Day in month of Julian day (default: 1)
1234 : !> \param[in] "integer(i4), optional :: mm" Month in year of Julian day (default: 1)
1235 : !> \param[in] "integer(i4), optional :: yy" Year of Julian day (default: 1)
1236 : !> \param[in] "integer(i4), optional :: hh" Hours of Julian day (default: 0)
1237 : !> \param[in] "integer(i4), optional :: nn" Minutes of hour of Julian day (default: 0)
1238 : !> \param[in] "integer(i4), optional :: ss" Secondes of minute of hour of Julian day (default: 0)
1239 : !> \retval "real(dp) :: date2dec365" Fractional Julian day
1240 :
1241 : !> \author David Schaefer
1242 : !> \date Dec 2015
1243 6424013 : elemental function date2dec365(dd, mm, yy, hh, nn, ss)
1244 :
1245 : implicit none
1246 :
1247 : integer(i4), intent(in), optional :: dd, mm, yy
1248 : integer(i4), intent(in), optional :: hh, nn, ss
1249 6424013 : real(dp) :: date2dec365, eps
1250 : integer(i4) :: idd, imm, iyy
1251 6424013 : real(dp) :: ihh, inn, iss
1252 6424013 : real(dp) :: hour
1253 :
1254 : ! Presets
1255 6424013 : idd = 1
1256 6424013 : if (present(dd)) idd = dd
1257 6424013 : imm = 1
1258 6424013 : if (present(mm)) imm = mm
1259 6424013 : iyy = 1
1260 6424013 : if (present(yy)) iyy = yy
1261 6424013 : ihh = 0.0_dp
1262 6424013 : if (present(hh)) ihh = real(hh, dp)
1263 6424013 : inn = 0.0_dp
1264 6424013 : if (present(nn)) inn = real(nn, dp)
1265 6424013 : iss = 0.0_dp
1266 6424013 : if (present(ss)) iss = real(ss, dp)
1267 :
1268 6424013 : hour = ihh / 24._dp + inn / 1440._dp + iss / 86400._dp - .5_dp
1269 :
1270 : ! Fractional Julian day starts at noon
1271 6424013 : date2dec365 = real(julday365(idd, imm, iyy), dp) + hour
1272 :
1273 : ! Add a small offset (proportional to julian date) for correct re-conversion.
1274 6424013 : eps = epsilon(1.0_dp)
1275 6424013 : eps = max(eps * abs(date2dec365), eps)
1276 6424013 : date2dec365 = date2dec365 + eps
1277 :
1278 11242011 : end function date2dec365
1279 :
1280 :
1281 : ! ------------------------------------------------------------------
1282 :
1283 : END MODULE mo_julian
|