-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcalendarmod.f90
189 lines (127 loc) · 3.78 KB
/
calendarmod.f90
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
module calendarmod
use iso_fortran_env, only : int16,int32,real32,real64
use ieee_arithmetic
implicit none
integer, parameter :: i2 = int16
integer, parameter :: i4 = int32
integer, parameter :: sp = real32
integer, parameter :: dp = real64
public :: timestruct
public :: ymdt2jd
public :: jd2ymdt
private :: checkyear
type timestruct
integer(i4) :: y = -1
integer(i4) :: m = -1
integer(i4) :: d = -1
integer(i4) :: hr = -1
integer(i4) :: min = -1
real(sp) :: sec = -1._sp
real(dp) :: jd = 0._dp ! Julian date
end type timestruct
contains
! -----------------------------------------------------------------
integer(i4) function checkyear(y) result(y2)
implicit none
integer(i4), intent(in) :: y
! ---
if ( y < 0 ) then
y2 = y + 1
else if ( y == 0 ) then
write(0,*)'error, calendar does not have a year 0'
stop
else
y2 = y
end if
end function checkyear
! -----------------------------------------------------------------
subroutine ymdt2jd(dt)
! Retrieve a Julian date based on a (proleptic) Gregorian year-month-day-[time] combination
! Based on routines in calpak.f90 by John Burkardt
implicit none
! argument
type(timestruct), intent(inout) :: dt
! local variables
integer(i4) :: d_prime
integer(i4) :: g
! integer(i4) :: ierror
integer(i4) :: j1
integer(i4) :: j2
integer(i4) :: m_prime
integer(i4) :: y2
integer(i4) :: y_prime
real(dp) :: timefrac
! -----------------
! Check if the time is provided, if not fall back to default of 00:00:00.0
timefrac = 0.
if (dt%hr >= 0) then
if (dt%min >= 0 .and. dt%sec >= 0.) then
timefrac = (real(dt%hr) + real(dt%min) / 60. + dt%sec / 3600.) / 24.
else
write(0,*)'must include full time string or nothing at all, falling back to midnight default'
end if
end if
! ---
! Convert the calendar date to a computational date
y2 = checkyear(dt%y)
y_prime = y2 + 4716 - (14 - dt%m) / 12
m_prime = mod (dt%m + 9,12)
d_prime = dt%d - 1
! Convert the computational date to a Julian date
j1 = (1461 * y_prime) / 4
j2 = (153 * m_prime + 2) / 5
g = (3 * ((y_prime + 184) / 100) / 4) - 38
dt%jd = real(j1 + j2 + d_prime - 1401 - g,kind=dp) - 0.5_dp + timefrac
end subroutine ymdt2jd
! -----------------------------------------------------------------
subroutine jd2ymdt(dt)
! Retrieve a (proleptic) Gregorian year-month-day-[time] combination based on a Julian date
! Based on routines in calpak.f90 by John Burkardt
implicit none
! argument
type(timestruct), intent(inout) :: dt
! local variables
integer(i4) :: g
integer(i4) :: j
integer(i4) :: j_prime
integer(i4) :: y_prime
integer(i4) :: m_prime
integer(i4) :: d_prime
integer(i4) :: t_prime
! real(sp) :: f
real(dp) :: fpart
real(dp) :: hr
real(dp) :: mr
! Determine the computational date (Y'/M'/D').
j = int(dt%jd + 0.5_dp)
! f = (dt%jd + 0.5_dp) - real(j,kind=dp)
g = (3 * ((4 * j + 274277) / 146097) / 4) - 38
j_prime = j + 1401 + g
y_prime = (4 * j_prime + 3) / 1461
t_prime = mod(4 * j_prime + 3, 1461) / 4
m_prime = (5 * t_prime + 2) / 153
d_prime = mod(5 * t_prime + 2, 153) / 5
! Convert the computational date to a calendar date.
dt%d = d_prime + 1
dt%m = mod(m_prime + 2,12) + 1
dt%y = y_prime - 4716 + (14 - dt%m) / 12
! If there is a remainder calculate the time -
! NB this algorithm resolves to noon on the selected computational date
fpart = dt%jd - floor(dt%jd)
if (fpart > 0.) then
hr = 24. * fpart
mr = 60. * (hr - floor(hr))
dt%sec = 60. * real(mr - floor(mr),kind=sp)
dt%hr = int(hr)
dt%min = int(mr)
else
dt%hr = 0
dt%min = 0
dt%sec = 0.
end if
! Any year before 1 AD must be moved one year further back, since
! this calendar does not include a year 0.
if (dt%y <= 0) dt%y = dt%y - 1
end subroutine jd2ymdt
! -----------------------------------------------------------------
end module calendarmod