libTheSky
Routines to compute sky positions of Sun, Moon, planets and more
All Namespaces Files Functions Variables Pages
date_time.f90
Go to the documentation of this file.
1!> \file date_time.f90 Contains date and time procedures for libTheSky
2
3
4! Copyright (c) 2002-2025 Marc van der Sluys - Nikhef/Utrecht University - marc.vandersluys.nl
5!
6! This file is part of the libTheSky package,
7! see: https://libthesky.sf.net/
8!
9! This is free software: you can redistribute it and/or modify it under the terms of the
10! European Union Public Licence 1.2 (EUPL 1.2).
11!
12! This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
13! without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14! See the EU Public Licence for more details.
15!
16! You should have received a copy of the European Union Public Licence along with this code.
17! If not, see <https://www.eupl.eu/1.2/en/>.
18
19
20! set_date_and_time: Set global date/time variables (year, month, ..., minute, second) to specified values
21! get_date_and_time: Retrieve the current global date/time variables (year, ..., second, stored in TheSky_local)
22! set_date_and_time_to_system_clock: Set global date/time variables (year, month, ..., minute, second) to system clock
23! set_date_and_time_to_jd2000: Set global date/time variables (year, month, ..., minute, second) to JD2000.0
24
25! calctime: Calculates ut, jd and jde
26! calc_gmst: Calculate Greenwich Mean Sidereal Time in RAD!
27! calc_deltat: Calculates deltat from jd: SLOW!
28! calc_deltat_ymd: Calculates deltat from y,m,d, faster
29! find_deltat_in_range: Find a precise value for DeltaT through linear interpolation in tabulated values
30
31! jd2dtm: Converts JD to y,m,d,h,m,s, input in UT, output in LT
32! jd2dthm: Converts JD to y,m,d,h,m, input in UT, output in LT (no seconds)
33! jd2ltime: Converts JD to time; input JD in UT, output time in hours LT
34
35! printdate: Converts JD to y,m,d,h,m,s and prints it out
36! printdate1: prints date/time of JD (UT) without hard return
37! print_date_time_and_location Display a 'program header' with date, time and location
38
39! dls: Beginning and end of daylight savings for given year
40! gettz: Get the time zone (1 or 2) for JD
41! dow: Calculates day of week
42! woy: Calculates the week-of-year number
43
44
45
46!***********************************************************************************************************************************
47!> \brief Date and time procedures
48
50 implicit none
51 save
52
53contains
54
55 !*********************************************************************************************************************************
56 !> \brief Set global date/time variables (year, month, ..., minute, second in TheSky_local) to specified values
57 !!
58 !! \param year Year
59 !! \param month Month
60 !! \param day Day
61 !!
62 !! \param hour Hour
63 !! \param minute Minute
64 !! \param second Second
65
66 subroutine set_date_and_time(year,month,day, hour,minute, second)
67 use sufr_kinds, only: double
68 use thesky_local, only: lyear=>year,lmonth=>month,lday=>day, lhour=>hour,lminute=>minute,lsecond=>second
69
70 implicit none
71 integer, intent(in) :: year,month,day, hour,minute
72 real(double), intent(in) :: second
73
74 lyear = year
75 lmonth = month
76 lday = dble(day)
77 lhour = hour
78 lminute = minute
79 lsecond = second
80
81 end subroutine set_date_and_time
82 !*********************************************************************************************************************************
83
84
85
86 !*********************************************************************************************************************************
87 !> \brief Retrieve the current global date/time variables (year, month, ..., minute, second, stored in TheSky_local)
88 !!
89 !! \param year Year (output)
90 !! \param month Month (output)
91 !! \param day Day (output)
92 !!
93 !! \param hour Hour (output)
94 !! \param minute Minute (output)
95 !! \param second Second (output)
96
97 subroutine get_date_and_time(year,month,day, hour,minute, second)
98 use sufr_kinds, only: double
99 use thesky_local, only: lyear=>year,lmonth=>month,lday=>day, lhour=>hour,lminute=>minute,lsecond=>second
100
101 implicit none
102 integer, intent(out) :: year,month,day, hour,minute
103 real(double), intent(out) :: second
104
105 year = lyear
106 month = lmonth
107 day = nint(lday)
108
109 hour = lhour
110 minute = lminute
111 second = lsecond
112
113 end subroutine get_date_and_time
114 !*********************************************************************************************************************************
115
116
117
118 !*********************************************************************************************************************************
119 !> \brief Set global date/time variables (year, month, ..., minute, second in TheSky_local) to system clock
120 !!
121 !! \param ut Return UT instead of local system time (optional).
122
124 use sufr_date_and_time, only: system_clock_2_ymdhms, consistent_date_time
126
127 implicit none
128 logical, optional, intent(in) :: ut
129 integer :: dy
130
131 call system_clock_2_ymdhms(year,month,dy, hour,minute,second, tz=tz)
132
133 if(present(ut)) then
134 if(ut) then
135 hour = hour - nint(tz) ! CHECK: integer tz only!
136 tz = 0.d0
137 call consistent_date_time(year,month,dy, hour,minute,second)
138 end if
139 end if
140
141 day = dble(dy)
142
144 !*********************************************************************************************************************************
145
146 !*********************************************************************************************************************************
147 !> \brief Convert date/time in ymdhms from UT to local time.
148 !!
149 !! \param year Year CE: input: UT, output: LT (I/O)
150 !! \param month Month of year: input: UT, output: LT (I/O)
151 !! \param dy Day of month: input: UT, output: LT (I/O)
152 !!
153 !! \param hour Hour of day: input: UT, output: LT (I/O)
154 !! \param minute Minute of time: input: UT, output: LT (I/O)
155 !! \param second Second of time: input: UT, output: LT (I/O)
156 !!
157 !! \param tz Timezone of date (output)
158 !!
159 !! \param tz0 Default timezone for date/time
160 !! \param dsttp Timezone type for date/time
161
162 subroutine ut2lt_ymdhms(year,month,dy, hour,minute,second, tz, tz0,dsttp)
163 use sufr_kinds, only: double
164 use sufr_date_and_time, only: ymdhms2jd, consistent_date_time
165
166 implicit none
167 integer, intent(inout) :: year,month,dy, hour,minute
168 real(double), intent(inout) :: second
169 real(double), intent(out) :: tz
170 integer, intent(in) :: dsttp
171 real(double), intent(in) :: tz0
172
173 real(double) :: jd
174
175 jd = ymdhms2jd(year,month,dy, hour,minute,second)
176 tz = gettz(jd, tz0,dsttp)
177 hour = hour + nint(tz)
178
179 call consistent_date_time(year,month,dy, hour,minute,second) ! Ensure date/time is consistent (e.g. not hour=25).
180
181 end subroutine ut2lt_ymdhms
182 !*********************************************************************************************************************************
183
184
185 !*********************************************************************************************************************************
186 !> \brief Set global date/time variables (year, month, ..., minute, second in TheSky_local) to JD2000.0
187
189 implicit none
190
191 call set_date_and_time(2000,1,1, 0,0,0.d0)
192
193 end subroutine set_date_and_time_to_jd2000
194 !*********************************************************************************************************************************
195
196
197
198 !*********************************************************************************************************************************
199 !> \brief Compute UT, JD, JDE, DeltaT and TZ, using the date and (local) time and TZ stored in the module TheSky_local
200 !!
201 !! \param ut Universal time (output)
202 !! \param jd Julian date (output)
203 !! \param jde Julian date (output)
204 !!
205 !! \note
206 !! - Date and time are obtained from year, month, day, hour, minute, second, through the module TheSky_local, assuming LOCAL time
207 !! - DeltaT and TZ are returned through the module TheSky_local
208 !! - Computed JD is in UNIVERSAL time
209 !!
210 !! \todo Remove recomputation of 'UT JD'!!!
211
212 subroutine calctime(ut,jd,jde)
213 use sufr_kinds, only: double
214 use sufr_date_and_time, only: cal2jd
215 use sufr_numerics, only: dne
217
218 implicit none
219 real(double), intent(out) :: ut,jd,jde
220 real(double) :: lt, oldtz
221
222 ! Compute local time, UT and JD:
223 lt = dble(hour) + (dble(minute) + second/60.d0)/60.d0
224 ut = lt - tz
225 jd = cal2jd(year,month,day+ut/24.d0) ! This is the 'UT JD'
226
227 ! Determine the correct timezone from the JD, and recompute UT and JD:
228 oldtz = tz
229 tz = gettz(jd)
230 if(dne(tz, oldtz)) then ! CHECK - this may be a very stupid idea - provide UT hms and tz=0, and this
231 ut = lt - tz ! will currently return a non-UT ut, jd if dsttp = 1,2 or tz0 != 0
232 jd = cal2jd(year,month,day+ut/24.d0) ! This is the 'UT JD'
233 end if
234
236 jde = jd + deltat/86400.d0
237
238 end subroutine calctime
239 !*********************************************************************************************************************************
240
241
242 !*********************************************************************************************************************************
243 !> \brief Calculate Greenwich Mean Sidereal Time for any instant, in radians
244 !!
245 !! \param jd Julian day of computation
246 !! \param DeltaT ΔT (s) (optional - default: use DelTaT from TheSky_local)
247 !!
248 !! \retval calc_gmst Greenwich Mean Sidereal Time (radians)
249 !!
250 !! \see Explanatory Supplement to the Astronomical Almanac, 3rd edition, Eq. 6.66 (2012)
251
252 function calc_gmst(jd, DeltaT)
253 use sufr_kinds, only: double
254 use sufr_constants, only: jd2000
255 use sufr_angles, only: rev
256 use thesky_local, only: deltatmod=>deltat
257
258 implicit none
259 real(double), intent(in) :: jd
260 real(double), intent(in), optional :: deltat
261 real(double) :: calc_gmst, djd,djd2,djd4, gmst, deltatl
262
263 deltatl = deltatmod ! Use Delta T from module by default
264 if(present(deltat)) deltatl = deltat ! Use Delta T from optional variable if present
265
266 djd = jd-jd2000 ! Julian Days after 2000.0 UT
267 djd2 = djd**2
268 djd4 = djd2**2
269
270 gmst = 4.89496121042905d0 + 6.30038809894828323d0*djd + 5.05711849d-15*djd2 - 4.378d-28*djd2*djd - 8.1601415d-29*djd4 &
271 - 2.7445d-36*djd4*djd + 7.0855723730d-12*deltatl ! Eq. 6.66
272
273 calc_gmst = rev(gmst) ! If corrected for equation of the equinoxes: agst = rev(gmst + dpsi*cos(eps))
274
275 end function calc_gmst
276 !*********************************************************************************************************************************
277
278
279 !*********************************************************************************************************************************
280 !> \brief Calculate Greenwich Mean Sidereal Time for any instant, in radians, using Meeus
281 !!
282 !! \param jd Julian day of computation
283 !! \retval gmst_meeus Greenwich Mean Sidereal Time in radians
284 !!
285 !! \see Meeus, Sect. 12: Siderial time at Greenwich; Eq.12.4
286
287 function gmst_meeus(jd)
288 use sufr_kinds, only: double
289 use sufr_constants, only: jd2000
290 use sufr_angles, only: rev
291
292 implicit none
293 real(double), intent(in) :: jd
294 real(double) :: gmst_meeus, djd,tjc,tjc2, gmst
295
296 djd = jd-jd2000 ! Julian Days after 2000.0 UT
297 tjc = djd/36525.d0 ! Julian Centuries after 2000.0 UT
298 tjc2 = tjc**2
299
300 gmst = 4.89496121273579229d0 + 6.3003880989849575d0*djd + 6.77070812713916d-6*tjc2 - 4.5087296615715d-10*tjc2*tjc ! Eq. 12.4
301
302 gmst_meeus = rev(gmst) ! If corrected for equation of the equinoxes: agst = rev(gmst + dpsi*cos(eps))
303
304 end function gmst_meeus
305 !*********************************************************************************************************************************
306
307
308 !*********************************************************************************************************************************
309 !> \brief Computes JD, DeltaT and TZ, from date/time variables in module TheSky_local
310 !!
311 !! \param JD Julian date (output)
312 !!
313 !! \note
314 !! - Date and time are obtained from year, month, day, hour, minute, second, through the module TheSky_local
315 !! - DeltaT and TZ are returned through the module TheSky_local
316
317 subroutine localtime2jd(jd)
318 use sufr_kinds, only: double
319 use sufr_date_and_time, only: cal2jd
321
322 implicit none
323 real(double), intent(out) :: jd
324 real(double) :: lt,ut
325
326 lt = hour + (minute + second/60.d0)/60.d0 ! LT
327 ut = lt - tz ! UT
328 jd = cal2jd(year, month, day+ut/24.d0) ! UT
329
330 tz = gettz(jd) ! Compute actual timezone
331 ut = lt - tz ! UT
332 jd = cal2jd(year, month, day+ut/24.d0) ! UT
333
335 ! jde = jd + deltat/86400.d0
336
337 end subroutine localtime2jd
338 !*********************************************************************************************************************************
339
340
341 !*********************************************************************************************************************************
342 !> \brief Compute DeltaT for a given JD
343 !!
344 !! \param jd Julian day
345 !! \param force_recompute Force recomputation of DeltaT, even if the year is the same as in the last call (done in calc_deltat_ymd())
346 !!
347 !! \retval calc_deltat Delta T (s)
348 !!
349 !! \note VERY SLOW, use calc_deltat_ymd() if y,m,d are known
350
351 function calc_deltat(jd, force_recompute)
352 use sufr_kinds, only: double
353 use sufr_date_and_time, only: jd2cal
354 use sufr_numerics, only: deq0
356 use thesky_local, only: year,month,day
357
358 implicit none
359 real(double), intent(in) :: jd
360 logical, intent(in), optional :: force_recompute
361
362 real(double) :: calc_deltat,d
363 integer :: y,m
364 logical :: force_recompute_l
365
366
367 ! If deltat_forced > -9999, use that value:
368 if(deltat_forced .gt. -9999.d0) then
370 return
371 end if
372
373
374 ! Optional variable:
375 force_recompute_l = .false.
376 if(present(force_recompute)) force_recompute_l = force_recompute
377
378 if(deq0(jd)) then ! Use variables from module local
379 y = year
380 m = month
381 d = day
382 else
383 call jd2cal(jd, y,m,d) ! SLOW!
384 end if
385
386 calc_deltat = calc_deltat_ymd(y,m,d, force_recompute_l)
387
388 end function calc_deltat
389 !*********************************************************************************************************************************
390
391
392
393 !*********************************************************************************************************************************
394 !> \brief Compute DeltaT for given y,m,d
395 !!
396 !! \param y Year
397 !! \param m Month
398 !! \param d Day
399 !!
400 !! \param force_recompute Force recomputation of DeltaT, even if the year is the same as in the last call
401 !!
402 !! \retval calc_deltat_ymd Delta T (s)
403 !!
404 !! \note
405 !! - Faster than calc_deltat. Use this routine rather than calc_deltat() if y,m,d are known
406
407 function calc_deltat_ymd(y,m,d, force_recompute)
408 use sufr_kinds, only: double
409 use sufr_numerics, only: deq
411
412 implicit none
413 integer, intent(in) :: y,m
414 real(double), intent(in) :: d
415 logical, intent(in), optional :: force_recompute
416
417 integer :: yr1,yr2
418 real(double) :: calc_deltat_ymd, y0,dy, ddt
419 real(double), save :: calc_deltat_old, y0_old
420 logical :: force_recompute_l
421
422 ! If deltat_forced > -9999, use that value:
423 if(deltat_forced .gt. -9999.d0) then
425 return
426 end if
427
428
429 ! Optional variable:
430 force_recompute_l = .false.
431 if(present(force_recompute)) force_recompute_l = force_recompute
432
433 calc_deltat_ymd = 60.d0
434
435 y0 = (dble(m-1)+((d-1)/31.d0))/12.d0 + y ! ~decimal year
436
437 ! Return old value if same instance:
438 if(deq(y0,y0_old) .and. .not. force_recompute_l) then
439 calc_deltat_ymd = calc_deltat_old
440 return
441 end if
442
443
444 yr1 = deltat_minyr
445 yr2 = deltat_maxyr
446
447 if(y.ge.yr1.and.y.le.yr2) then ! Historical value is known; look it up and interpolate
448
450
451 else ! Historical value is not known, extrapolate
452
453 if(y.lt.yr1) then ! Earlier than historical record
454 dy = y0 - dble(deltat_minyr)
455 ddt = (deltat_values(2)-deltat_values(1)) / (deltat_years(2)-deltat_years(1)) ! 1 and 2 are spaced by 100 yr
456 calc_deltat_ymd = deltat_values(1) + ddt * dy + deltat_accel * dy**2
457 end if
458
459 if(y.gt.yr2) then ! Future extrapolation
460 dy = y0 - dble(deltat_maxyr)
462 end if
463
464 end if ! if(y.gt.yr0.and.y.lt.yr1)
465
466
467 calc_deltat_old = calc_deltat_ymd
468 y0_old = y0
469
470 ! write(0,'(A)')' WARNING: fixed DeltaT!!!'
471 ! calc_deltat_ymd = 66.4d0
472
473 end function calc_deltat_ymd
474 !*********************************************************************************************************************************
475
476
477 !*********************************************************************************************************************************
478 !> \brief Find a precise value for DeltaT through linear interpolation of the two adjacent tabulated values
479 !!
480 !! \param y Current year
481 !! \param y0 Current date, as fractional year
482 !!
483 !! \retval find_deltat_in_range Delta T (s)
484
485 function find_deltat_in_range(y,y0)
486 use sufr_kinds, only: double
488
489 implicit none
490 real(double) :: find_deltat_in_range
491 integer, intent(in) :: y
492 real(double),intent(in) :: y0
493 integer :: i
494 real(double) :: dt0,dt,yr0,yr,a
495
496 do i=deltat_n-1,1,-1 ! i is usually near deltat_n, so start from the back
497 if(deltat_years(i).le.y) exit
498 end do
499 i = max(i,1) ! i can be 0 if whole do loop is run without match
500
501 yr0 = deltat_years(i)
502 dt0 = deltat_values(i)
503 yr = deltat_years(i+1)
504 dt = deltat_values(i+1)
505
506 a = (dt-dt0)/dble(yr-yr0)
507 find_deltat_in_range = dt0 + a*(y0-yr0)
508
509 end function find_deltat_in_range
510 !*********************************************************************************************************************************
511
512
513 !*********************************************************************************************************************************
514 !> \brief Compute DeltaT for given JD, using a simple parabolic approximation
515 !!
516 !! \param jd Julian day for computation
517 !!
518 !! \retval deltat The value for DeltaT (= TDT-UT) in seconds.
519
521 use sufr_kinds, only: double
522 use thesky_constants, only: jd1820
523
524 implicit none
525 real(double), intent(in) :: jd
526 real(double) :: calc_deltat_approx
527
528 ! 12 + 0.5 * 1.8e-3/86400/(36525*86400) * ((jd-jd1820)*86400)**2 ! Comprehensible notation
529 calc_deltat_approx = 12.d0 + 0.5d0 * 1.8d-3 / 36525.d0 * (jd-jd1820)**2 ! Simplified notation
530
531 end function calc_deltat_approx
532 !*********************************************************************************************************************************
533
534
535 !*********************************************************************************************************************************
536 !> \brief Convert a Julian day (UT) to LOCAL date and time (h,m,s)
537 !!
538 !! \param jd Julian day (UT)
539 !!
540 !! \param yy Year (CE, LT) (output)
541 !! \param mm Month (LT) (output)
542 !! \param d Day (LT) (output)
543 !! \param h Hour (LT) (output)
544 !! \param m Minute (LT) (output)
545 !! \param s Second (+ fraction, LT) (output)
546
547 subroutine jd2dtm(jd, yy,mm,d, h,m,s)
548 use sufr_kinds, only: double, dbl
549 use sufr_system, only: quit_program_error
550 use sufr_constants, only: mlen
551 use sufr_date_and_time, only: jd2cal, leapyr
552 use thesky_local, only: tz
553
554 implicit none
555 real(double), intent(in) :: jd
556 integer, intent(out) :: yy,mm,d,h,m
557 real(double), intent(out) :: s
558 real(double) :: dd,tm
559
560 call jd2cal(jd + tz/24.d0, yy,mm,dd) ! in LT
561 if(mm.lt.1 .or. mm.gt.12) call quit_program_error('libTheSky: jd2dtm(): something went wrong in the jd2cal() call: month must be between 1 and 12.', 1) ! Needed because of mlen()
562
563 ! jd2cal returns zeroes if JD not defined (i.e., JD=-huge), and mlen(mm) is not defined - catch this:
564 if(yy.eq.0.and.mm.eq.0) then
565 d = 0
566 h = 0
567 m = 0
568 s = 0.0_dbl
569 return
570 end if
571
572
573 mlen(2) = 28 + leapyr(yy)
574
575 d = int(dd)
576 tm = (dd - dble(d))*24.d0
577 h = int(tm)
578 m = int((tm-h)*60.d0)
579 s = (tm-h-m/60.d0)*3600.d0
580
581 if(s.gt.59.999) then
582 s = 0.d0
583 m = m + 1
584 end if
585 if(m.eq.60) then
586 m = 0
587 h = h+1
588 end if
589 if(h.eq.24) then
590 h = 0
591 d = d+1
592 end if
593 if(d.gt.mlen(mm)) then
594 d = d - mlen(mm)
595 mm = mm + 1
596 end if
597 if(mm.gt.12) then
598 mm = mm - 12
599 yy = yy + 1
600 end if
601
602 end subroutine jd2dtm
603 !*********************************************************************************************************************************
604
605
606 !*********************************************************************************************************************************
607 !> \brief Convert a Julian day (UT) to LOCAL date and time (h,m - no seconds)
608 !!
609 !! \param jd Julian day (UT)
610 !! \param yy Year (CE, LT) (output)
611 !! \param mm Month (LT) (output)
612 !! \param d Day (LT) (output)
613 !! \param h Hour (LT) (output)
614 !! \param m Minute (LT) (output)
615
616 subroutine jd2dthm(jd, yy,mm, d,h,m)
617 use sufr_kinds, only: double
618 use sufr_system, only: quit_program_error
619 use sufr_constants, only: mlen
620 use sufr_date_and_time, only: jd2cal, leapyr
621 use thesky_local, only: tz
622
623 implicit none
624 real(double), intent(in) :: jd
625 integer, intent(out) :: yy,mm,d,h,m
626 real(double) :: dd,tm
627
628 call jd2cal(jd+tz/24.d0, yy,mm,dd) ! in LT
629 if(mm.lt.1 .or. mm.gt.12) call quit_program_error('libTheSky: jd2dthm(): something went wrong in the jd2cal() call: month must be between 1 and 12.', 1) ! Needed because of mlen()
630
631 ! jd2cal returns zeroes if JD not defined (i.e., JD=-huge), and mlen(mm) is not defined - catch this:
632 if(yy.eq.0 .and. m.eq.0) then
633 d = 0
634 h = 0
635 m = 0
636 return
637 end if
638
639 mlen(2) = 28 + leapyr(yy)
640
641 d = int(dd)
642 tm = (dd - dble(d))*24
643 h = int(tm)
644 m = nint((tm-h)*60)
645
646 if(m.ge.60) then
647 m = m-60
648 h = h+1
649 end if
650 if(h.ge.24) then
651 h = h-24
652 d = d+1
653 end if
654 if(d.gt.mlen(mm)) then
655 d = d - mlen(mm)
656 mm = mm + 1
657 end if
658 if(mm.gt.12) then
659 mm = mm - 12
660 yy = yy + 1
661 end if
662
663 end subroutine jd2dthm
664 !*********************************************************************************************************************************
665
666
667
668 !*********************************************************************************************************************************
669 !> \brief Convert a Julian day (UT) to a local time (LT, h)
670 !!
671 !! \param jd0 Julian day (UT)
672 !! \retval jd2ltime Local time (h)
673
674 function jd2ltime(jd0)
675 use sufr_kinds, only: double
676 use sufr_date_and_time, only: jd2cal
677 use thesky_local, only: tz
678
679 implicit none
680 real(double), intent(in) :: jd0
681 real(double) :: jd1,dd,jd2ltime
682 integer :: d,mm,yy
683
684 jd1 = jd0 + tz/24.d0 ! UT -> LT
685 call jd2cal(jd1,yy,mm,dd)
686 d = int(dd)
687 jd2ltime = (dd - dble(d))*24.d0
688
689 end function jd2ltime
690 !*********************************************************************************************************************************
691
692
693
694
695
696
697 !*********************************************************************************************************************************
698 !> \brief Prints date/time of a given Julian day (UT) to standard output
699 !!
700 !! \param jd Julian day (UT)
701 !! \param jde Julian day ephemeris time (dynamical time)
702 !!
703 !! \note
704 !! - calls printdate1()
705
706 subroutine printdate(jd, jde)
707 use sufr_kinds, only: double
708 implicit none
709 real(double), intent(in) :: jd
710 real(double), intent(in), optional :: jde
711
712 if(present(jde)) then
713 call printdate1(jd, jde)
714 else
715 call printdate1(jd)
716 end if
717 write(*,*)''
718
719 end subroutine printdate
720 !*********************************************************************************************************************************
721
722 !*********************************************************************************************************************************
723 !> \brief Prints date/time of a given Julian day (UT) to standard output, but without a newline
724 !!
725 !! \param jd Julian day (UT)
726 !! \param jde Julian day ephemeris time (dynamical time)
727
728 subroutine printdate1(jd, jde)
729 use sufr_kinds, only: double
730 use sufr_date_and_time, only: jd2cal
731
732 implicit none
733 real(double), intent(in) :: jd
734 real(double), intent(in), optional :: jde
735 real(double) :: dd,tm,s
736 integer :: d,mm,yy,h,m
737
738 call jd2cal(jd+1.d-10, yy,mm,dd)
739
740 d = int(dd)
741
742 tm = (dd - dble(d))*24.d0
743 h = int(tm)
744 m = int((tm-h)*60.d0)
745 s = (tm-h-m/60.d0)*3600.d0
746
747 if(s.gt.59.999d0) then
748 s = s - 60.d0
749 m = m + 1
750 end if
751 if(m.ge.60) then
752 m = m - 60
753 h = h + 1
754 end if
755 if(h.ge.24) then
756 h = h - 24
757 d = d + 1
758 end if
759
760 write(*,'(A,F0.6)', advance='no') ' JD: ', jd
761 if(present(jde)) write(*,'(A,F0.6)', advance='no') ' JDE: ', jde
762 write(*,'(A,I0,2I3.2, A,2I3.2,F7.3,A)', advance='no') ' date: ',yy,mm,d, ' time: ',h,m,s,' UT'
763
764 end subroutine printdate1
765 !*********************************************************************************************************************************
766
767
768
769 !*********************************************************************************************************************************
770 !> \brief Find the two Julian days of the beginning and the end of daylight-savings time in the EU for a given year
771 !!
772 !! \param yr Year (CE)
773 !!
774 !! \param jdb Julian day of beginning of DST (output)
775 !! \param jde Julian day of end of DST (output)
776
777 subroutine dls(yr, jdb,jde)
778 use sufr_kinds, only: double
779 use sufr_date_and_time, only: cal2jd
780
781 implicit none
782 integer, intent(in) :: yr
783 real(double), intent(out) :: jdb,jde
784 real(double) :: d1,d2
785 integer :: lyr,m1,m2
786
787 lyr = yr
788
789 d1 = 31.d0
790 m1 = 3
791 d2 = 31.d0
792 m2 = 10
793
794 d1 = d1 - dble(dow(cal2jd(lyr,m1,d1)))
795 jdb = cal2jd(lyr,m1,d1)
796 d2 = d2 - dble(dow(cal2jd(lyr,m2,d2)))
797 jde = cal2jd(lyr,m2,d2)
798
799 end subroutine dls
800 !*********************************************************************************************************************************
801
802
803
804
805
806 !*********************************************************************************************************************************
807 !> \brief Returns time zone: tz0 (standard time) or tz0+1 (daylight-savings time)
808 !!
809 !! \param jd Julian day (UT?)
810 !! \param tz0 Default time zone for the current location ('winter time')
811 !! \param dsttp Daylight-savings time rules to use: 0: no DST (e.g. UT), 1: EU, 2: USA/Canada
812 !!
813 !! \retval gettz The time zone (h; positive east of Greenwich)
814 !!
815 !! \note
816 !! - currently only implemented for no DST (dsttp=0), EU (dsttp=1) and USA/Canada >2007 (dsttp=2)
817 !! - tz0 and dsttp can be provided through the module TheSky_local, or using the optional arguments. Note
818 !! that using the latter will update the former!
819
820 function gettz(jd, tz0,dsttp)
821 use sufr_kinds, only: double
822 use sufr_system, only: warn
823 use sufr_date_and_time, only: jd2cal, cal2jd
824
825 use thesky_local, only: ldsttp=>dsttp, ltz0=>tz0, tz
826
827 implicit none
828 real(double), intent(in) :: jd
829 real(double), intent(in), optional :: tz0
830 integer, intent(in), optional :: dsttp
831
832 real(double) :: gettz,dd,jd0,d0
833 integer :: m,m1,m2,y
834
835
836 ! Handle optional variables:
837 if(present(tz0)) ltz0 = tz0
838 if(present(dsttp)) ldsttp = dsttp
839
840 gettz = 0.d0
841 call jd2cal(jd,y,m,dd)
842
843 if(ldsttp.lt.0.or.ldsttp.gt.2) then
844 ldsttp = 1
845 call warn('gettz(): (l)ldsttp must be 0-2; resetting to 1 (Europe)')
846 end if
847
848
849 if(ldsttp.eq.1) then ! Europe (Netherlands)
850 if(y.lt.1977) then
851 gettz = 0.d0
852 else
853 m1 = 3
854 m2 = 10
855 if(y.lt.1996) m2 = 9
856
857 if(m.gt.m1.and.m.le.m2) gettz = 1.d0
858
859 if(m.eq.m1.or.m.eq.m2) then
860 jd0 = cal2jd(y,m,31.d0) + (m2 - 10)
861 d0 = 31.d0 - dble(dow(jd0)) + (m2 - 10)
862 jd0 = cal2jd(y,m,d0+1.d0/24.d0) ! 0 UT = 1 MET = 2 MEZT
863 if(m.eq.m1.and.jd.gt.jd0) gettz = 1.d0
864 if(m.eq.m2.and.jd.gt.jd0) gettz = 0.d0
865 end if ! if(m.eq.m1.or.m.eq.m2)
866 end if ! if(y.lt.1977)
867 end if ! if(ldsttp.eq.1)
868
869
870 if(ldsttp.eq.2) then ! USA/Canada
871
872 !if(y.lt.2007) then
873 ! m1 = 4 ! April
874 ! maxdom1 = 7 ! Maximum day of month: first Sunday
875 ! m2 = 10 ! October
876 ! maxdom2 = 31 ! Maximum day of month: last Sunday
877 ! m = m-1
878 !end if
879
880 call warn('DST rules not implemented prior to 2007!')
881
882 if(y.ge.2007) then
883 m1 = 3 ! March
884 m2 = 11 ! November
885
886 if(m.gt.m1.and.m.lt.m2) gettz = 1.d0
887
888 if(m.eq.m1) then
889 jd0 = cal2jd(y,m,1.999999d0) ! 1st of the month, end of the day UT
890 d0 = dble(14 - dow(jd0-1)) ! jd0-1: switch from 7 to 1 iso 6 to 0; last possible day of month: 14
891 jd0 = cal2jd(y,m,d0+(2.d0-tz)/24.d0) ! 2h LT
892 if(jd.gt.jd0) gettz = 1.d0
893 end if
894
895 if(m.eq.m2) then
896 jd0 = cal2jd(y,m,1.999999d0) ! 1st of the month, end of the day UT
897 d0 = dble(7 - dow(jd0-1)) ! jd0-1: switch from 7 to 1 iso 6 to 0; last possible day of month: 7
898 jd0 = cal2jd(y,m,d0+(2.d0-tz)/24.d0) ! 2h LT
899 if(jd.lt.jd0) gettz = 1.d0
900 end if
901
902 ! call printdate(jd0)
903 end if
904 end if
905
906 gettz = ltz0 + gettz
907 ! gettz = ltz0 + 1.d0 ! Force DST
908
909 end function gettz
910 !*********************************************************************************************************************************
911
912
913
914 !*********************************************************************************************************************************
915 !> \brief Display a banner with date, time and location of calculation. Computes and returns UT, JD and JDE.
916 !!
917 !! \param op Output unit (optional, default 6)
918 !! \param nlbef Number of newlines before output (optional, default 0)
919 !! \param nlaf Number of newlines after output (optional, default 0)
920 !!
921 !! \param ut UT: Universal Time (optional)
922 !! \param jd JD: Julian day (optional)
923 !! \param jde JDE: Apparent Julian day (optional)
924 !!
925 !! \param locname Location name (optional)
926 !! \param tzname Time-zone name (optional)
927 !!
928 !! \note
929 !! - uses the module local to obtain the variables year, month, etc.
930 !! - calls calctime(), gettz()
931
932 subroutine print_date_time_and_location(op,nlbef,nlaf, ut,jd,jde, locname,tzname)
933 use sufr_kinds, only: double
934 use sufr_constants, only: r2d,r2h, endays,enmonths
935 use sufr_time2string, only: hms_sss
936 use sufr_text, only: d2s
937
939
940
941 implicit none
942 integer, intent(in), optional :: op, nlbef,nlaf
943 real(double), intent(out), optional :: ut,jd,jde
944 character, intent(in), optional :: locname*(*),tzname*(*)
945 integer :: il, opl, nlbefl,nlafl
946 real(double) :: gmst, lt, utl,jdl,jdel
947 character :: locnamel*(999),tznamel*(99)
948
949 ! Optional input parameters:
950 opl = 6 ! Output unit - local variable
951 if(present(op)) opl = op
952 nlbefl = 0 ! Number of newlines before output - local variable
953 if(present(nlbef)) nlbefl = nlbef
954 nlafl = 0 ! Number of newlines after output - local variable
955 if(present(nlaf)) nlafl = nlaf
956 locnamel = ' ' ! Name of the location - local variable
957 if(present(locname)) locnamel = trim(locname)
958 tznamel = ' ' ! Name of the time zone - local variable
959 if(present(tzname)) tznamel = trim(tzname)//','
960
961
962 call calctime(utl,jdl,jdel)
963 tz = gettz(jdl)
964
965 call calctime(utl,jdl,jdel)
966 lt = hour + (minute + second/60.d0)/60.d0 + 1.d-50 ! so that 0 doesn't give --:--
967 utl = utl + 1.d-50 ! so that 0 doesn't give --:--
968
969 gmst = calc_gmst(jdl, deltat)*r2h ! Greenwich mean sidereal time in hours
970
971 do il=1,nlbefl
972 write(opl,'(A)') '' ! Newline
973 end do
974
975 write(opl,'(A20,3A,I3,A1,I5, A9,A13,2x,A, A13,2(A4,A), A4,A,A1)') &
976 'LOCAL: Date: ',trim(endays(dow(jdl))),' ', trim(enmonths(month)),nint(day),',',year, &
977 'Time:',hms_sss(lt),'tz: '//trim(tznamel)//' '//d2s(tz,1), &
978 'Location:','l: ',d2s(lon0*r2d,4),'b: ',d2s(lat0*r2d,4), 'h: ',d2s(height,1),'m'
979
980 write(opl,'(A17,A13, A5,F15.6, A10,F0.2,A1, A6,F15.6, A7,F8.4, 5x,A)') &
981 'UNIVERSAL: UT:',hms_sss(utl), 'JD:',jdl, 'DeltaT: ',deltat,'s', &
982 'JDE:',jdel, 'GMST:',gmst, trim(locnamel)
983
984 do il=1,nlafl
985 write(opl,'(A)') '' ! Newline
986 end do
987
988 ! Optional output parameters:
989 if(present(ut)) ut = utl
990 if(present(jd)) jd = jdl
991 if(present(jde)) jde = jdel
992
993 end subroutine print_date_time_and_location
994 !*********************************************************************************************************************************
995
996
997 !*********************************************************************************************************************************
998 !> \brief Calculates day of week (0 - Sunday, ..., 6=Saturday)
999 !!
1000 !! \param jd0 Julian day (UT)
1001 !! \retval dow The day of week (0=Sunday, ..., 6=Saturday)
1002 !!
1003 !! \note Input in UT, output in local time
1004 !! \todo Switch using dow(jd) to dow_ut(jd+tz/24.d0) to make it general
1005
1006 function dow(jd0)
1007 use sufr_kinds, only: double
1008 use thesky_local, only: tz
1009
1010 implicit none
1011 real(double), intent(in) :: jd0
1012 integer :: dow
1013 real(double) :: jd,jw
1014
1015 jd = dble(nint(jd0+tz/24.d0))-0.5d0 ! ~ JD
1016 jw = (jd + 1.5d0)/7.d0 ! ~ 'Julian week'
1017 dow = nint(jd + 1.5d0 - floor(jw)*7) ! Day of week (0-6)
1018
1019 end function dow
1020 !*********************************************************************************************************************************
1021
1022
1023 !*********************************************************************************************************************************
1024 !> \brief Calculate the week-of-year number
1025 !!
1026 !! \param jd Julian day
1027 !! \retval woy Week number in the year
1028 !!
1029 !! \todo
1030 !! - CHECK: int or floor?
1031 !! - Depends on dow(), which depends on module local -> switch to dow_ut()
1032
1033 function woy(jd)
1034 use sufr_kinds, only: double
1035 use sufr_date_and_time, only: doy
1036
1037 implicit none
1038 real(double), intent(in) :: jd
1039 integer :: woy
1040
1041 woy = int(dble(doy(jd) + 7 - dow(jd))/7.d0) ! Use int or floor ?
1042
1043 end function woy
1044 !*********************************************************************************************************************************
1045
1046
1047
1048 !*********************************************************************************************************************************
1049 !> \brief Calculate the date of Easter using Gauss' method.
1050 !!
1051 !! \param year Julian/Gregorian year to compute date of Easter for.
1052 !!
1053 !! \param month Julian/Gregorian month of year of Easter Sunday.
1054 !! \param day Julian/Gregorian day of month of Easter Sunday.
1055 !!
1056 !! \note
1057 !! - Based on Gauß, C.F., 1816. Berichtigung zu dem Aufsatze: Berechnung des Osterfestes. Zeitschrift für
1058 !! Astronomie und verwandte Wissenschaften, Bd, 1, p.158.
1059 !! - Taken from https://en.wikipedia.org/wiki/Date_of_Easter
1060 !!
1061
1062 subroutine easter_gauss(year, month, day)
1063 implicit none
1064 integer, intent(in) :: year
1065 integer, intent(out) :: month, day
1066 integer :: a,b,c, k,p,q, M,N, d,e
1067
1068 a = modulo(year, 19)
1069 b = modulo(year, 4)
1070 c = modulo(year, 7)
1071
1072 if(year>1582) then ! Gregorian calendar
1073 k = floor(year / 100.d0)
1074 p = floor((13 + 8*k) / 25.d0)
1075 q = floor(k / 4.d0)
1076 m = modulo(15 + k - p - q, 30)
1077 n = modulo( 4 + k - q, 7)
1078 else ! Julian calendar
1079 m = 15
1080 n = 6
1081 end if
1082
1083 d = modulo(19*a + m, 30)
1084 e = modulo(2*b + 4*c + 6*d + n, 7)
1085
1086 month = 4
1087 day = 22 + d + e
1088
1089
1090 ! Sort out the details:
1091 if((d.eq.29) .and. (e.eq.6)) then ! Replace 26 with 19 April in some cases
1092 day = 19
1093 else if( (d.eq.28) .and. (e.eq.6) .and. (a.gt.10) ) then ! Replace 25 with 18 April in some cases
1094 day = 18
1095 else if(day.gt.31) then ! If day > 31, we're in April
1096 day = day - 31
1097 else ! If not, we're in March
1098 month = 3
1099 end if
1100
1101 end subroutine easter_gauss
1102 !*********************************************************************************************************************************
1103
1104
1105
1106 !*********************************************************************************************************************************
1107 !> \brief Calculate the date of Passover or Pesach (Jewish Easter) using Gauss' method.
1108 !!
1109 !! \param year Julian/Gregorian year to compute date of Passover for.
1110 !!
1111 !! \param month Julian/Gregorian month of year of the DAY of Passover.
1112 !! \param day Julian/Gregorian day of month of the DAY of Passover.
1113 !!
1114 !! \note
1115 !! - Based on Gauss, C.F., 1802. Berechnung des Judischen Osterfestes. Werke, pp.80-81, as described in Meeus, Ch.9.
1116 !! - Passover is always on 15 Nisan. Note that this is the DAY of Passover, the day AFTER the evening of the (first) Seder.
1117 !! - Valid for the Julian and Gregorian calendar.
1118 !! - The Jewish year is the Julian/Gregorian year + 3760.
1119 !! - For the years -4000 - +4000, this corresponds EXACTLY to Har’El, Z., 2005. Gauss Formula for the Julian Date
1120 !! of Passover. A∆(A), 1, p.0. Har’El also computes the day of week, which for the same years corresponds exactly
1121 !! to dow(cal2jd(year,month,day)) with cal2jd from libSUFR/date_and_time, dow() from libTheSky/datetimeSky, and
1122 !! year,month,day as in the current subroutine. Note that Passover day can only fall on Sun, Tue, Thu or Sat.
1123
1124 subroutine passover_gauss(year, month, day)
1125 use sufr_kinds, only: double
1126
1127 implicit none
1128 integer, intent(in) :: year
1129 integer, intent(out) :: month, day
1130 integer :: C,S, a,b, j
1131 real(double) :: Q, r
1132
1133 if(year.lt.1583) then ! Use the Julian calendar
1134 s = 0
1135 else ! Gregorian calendar
1136 c = floor(year / 100.d0)
1137 s = floor((3*c - 5)/4.d0) ! difference between Julian and Gregorian calendars, in days
1138 end if
1139
1140 a = modulo(12*year + 12, 19)
1141 b = modulo(year, 4)
1142
1143 q = -1.904412361576d0 + 1.554241796621d0*a + 0.25d0*b - 0.003177794022d0*year + s
1144 j = modulo(floor(q) + 3*year + 5*b + 2 - s, 7)
1145 r = q - floor(q) ! Decimal fraction of Q
1146
1147
1148 if(j.eq.2 .or. j.eq.4 .or. j.eq.6) then
1149 day = floor(q) + 23
1150 else if(j.eq.1 .and. a.gt.6 .and. r.gt.0.632870370d0) then
1151 day = floor(q) + 24
1152 else if(j.eq.0 .and. a.gt.11 .and. r.gt.0.897723765d0) then
1153 day = floor(q) + 23
1154 else
1155 day = floor(q) + 22
1156 end if
1157
1158 if(day.gt.31) then ! We're in April
1159 month = 4
1160 day = day - 31
1161 else ! We're in March
1162 month = 3
1163 end if
1164
1165 end subroutine passover_gauss
1166 !*********************************************************************************************************************************
1167
1168
1169end module thesky_datetime
1170!***********************************************************************************************************************************
1171
Constants used in libTheSky.
Definition modules.f90:23
real(double) deltat_forced
Forced value for DeltaT, overriding computation.
Definition modules.f90:35
real(double) deltat_accel
Acceleration for DeltaT parabola.
Definition modules.f90:32
integer deltat_minyr
Start year of DeltaT measurements.
Definition modules.f90:39
real(double) deltat_change
Change for DeltaT parabola.
Definition modules.f90:33
real(double) jd1820
JD of 1820.0, for DeltaT.
Definition modules.f90:36
real(double), dimension(deltat_nmax) deltat_values
Values of DeltaT.
Definition modules.f90:30
integer deltat_n
Actual number of DeltaT measurements.
Definition modules.f90:38
real(double) deltat_0
Zero point for DeltaT parabola.
Definition modules.f90:34
real(double), dimension(deltat_nmax) deltat_years
Years for DeltaT values.
Definition modules.f90:31
integer deltat_maxyr
End year of DeltaT measurements.
Definition modules.f90:40
Date and time procedures.
Definition date_time.f90:49
subroutine set_date_and_time(year, month, day, hour, minute, second)
Set global date/time variables (year, month, ..., minute, second in TheSky_local) to specified values...
Definition date_time.f90:67
real(double) function calc_gmst(jd, deltat)
Calculate Greenwich Mean Sidereal Time for any instant, in radians.
subroutine passover_gauss(year, month, day)
Calculate the date of Passover or Pesach (Jewish Easter) using Gauss' method.
subroutine ut2lt_ymdhms(year, month, dy, hour, minute, second, tz, tz0, dsttp)
Convert date/time in ymdhms from UT to local time.
real(double) function find_deltat_in_range(y, y0)
Find a precise value for DeltaT through linear interpolation of the two adjacent tabulated values.
subroutine calctime(ut, jd, jde)
Compute UT, JD, JDE, DeltaT and TZ, using the date and (local) time and TZ stored in the module TheSk...
real(double) function gettz(jd, tz0, dsttp)
Returns time zone: tz0 (standard time) or tz0+1 (daylight-savings time)
subroutine get_date_and_time(year, month, day, hour, minute, second)
Retrieve the current global date/time variables (year, month, ..., minute, second,...
Definition date_time.f90:98
subroutine easter_gauss(year, month, day)
Calculate the date of Easter using Gauss' method.
subroutine set_date_and_time_to_jd2000()
Set global date/time variables (year, month, ..., minute, second in TheSky_local) to JD2000....
integer function dow(jd0)
Calculates day of week (0 - Sunday, ..., 6=Saturday)
real(double) function calc_deltat_ymd(y, m, d, force_recompute)
Compute DeltaT for given y,m,d.
subroutine dls(yr, jdb, jde)
Find the two Julian days of the beginning and the end of daylight-savings time in the EU for a given ...
subroutine set_date_and_time_to_system_clock(ut)
Set global date/time variables (year, month, ..., minute, second in TheSky_local) to system clock.
real(double) function calc_deltat(jd, force_recompute)
Compute DeltaT for a given JD.
subroutine jd2dthm(jd, yy, mm, d, h, m)
Convert a Julian day (UT) to LOCAL date and time (h,m - no seconds)
real(double) function jd2ltime(jd0)
Convert a Julian day (UT) to a local time (LT, h)
real(double) function calc_deltat_approx(jd)
Compute DeltaT for given JD, using a simple parabolic approximation.
subroutine print_date_time_and_location(op, nlbef, nlaf, ut, jd, jde, locname, tzname)
Display a banner with date, time and location of calculation. Computes and returns UT,...
subroutine printdate1(jd, jde)
Prints date/time of a given Julian day (UT) to standard output, but without a newline.
real(double) function gmst_meeus(jd)
Calculate Greenwich Mean Sidereal Time for any instant, in radians, using Meeus.
subroutine localtime2jd(jd)
Computes JD, DeltaT and TZ, from date/time variables in module TheSky_local.
subroutine jd2dtm(jd, yy, mm, d, h, m, s)
Convert a Julian day (UT) to LOCAL date and time (h,m,s)
integer function woy(jd)
Calculate the week-of-year number.
subroutine printdate(jd, jde)
Prints date/time of a given Julian day (UT) to standard output.
Local parameters for libTheSky: location, date, time.
Definition modules.f90:56
real(double) second
Seconds of time of current instant.
Definition modules.f90:69
integer month
Month of year of current instant.
Definition modules.f90:73
integer minute
Minute of time of current instant.
Definition modules.f90:75
real(double) tz
Current value of time zone, taking into account DST (hours; >0 is east of Greenwich)
Definition modules.f90:67
real(double) height
Altitude of the observer above sea level (m)
Definition modules.f90:65
real(double) tz0
Standard value of time zone, without DST ("winter time"; hours; >0 is east of Greenwich)
Definition modules.f90:68
real(double) lon0
Longitude of the observer (rad)
Definition modules.f90:64
real(double) deltat
Current value of DeltaT (s)
Definition modules.f90:66
real(double) lat0
Latitude of the observer (rad)
Definition modules.f90:63
integer hour
Hour of time of current instant.
Definition modules.f90:74
real(double) day
Day of month of current instant, with decimals if desired.
Definition modules.f90:70
integer dsttp
DST type for current location (0: none, 1: EU, 2: USA+Canada (after 2007)
Definition modules.f90:76
integer year
Year CE of current instant.
Definition modules.f90:72