IODA Bundle
utils_mod.f90
Go to the documentation of this file.
1 module utils_mod
2 
3 ! adapated from WRFDA/var/da/da_tools/da_advance_time.inc
4 
5 implicit none
6 private
7 public :: da_advance_time
8 
9 contains
10 
11 subroutine da_advance_time (date_in, dtime, date_out)
12 
13  ! HISTORY: 11/17/2008 modified and simplified from da_util/da_advance_time.f90
14  !
15  ! modified from da_advance_cymdh,
16  ! - has accuracy down to second,
17  ! - can use day/hour/minute/second (with/without +/- sign) to advance time,
18  ! - can digest various input date format if it still has the right order (ie. cc yy mm dd hh nn ss)
19  ! - can digest flexible time increment
20  !
21  ! eg.: da_advance_time 20070730 12 # advance 12 h
22  ! da_advance_time 2007073012 -1d2h30m30s # back 1 day 2 hours 30 minutes and 30 seconds
23  ! da_advance_time 2007073012 1s-3h30m # back 3 hours 30 minutes less 1 second
24  !
25 
26  implicit none
27 
28  character(len=*), intent(in) :: date_in, dtime
29  character(len=14), intent(out) :: date_out
30 
31  integer :: ccyy, mm, dd, hh, nn, ss, dday, dh, dn, ds, gday, gsec
32  integer :: i, n
33  character(len=14) :: ccyymmddhhnnss
34  integer :: datelen
35 
36  ccyymmddhhnnss = parsedate(date_in)
37  datelen = len_trim(ccyymmddhhnnss)
38 
39  if (datelen == 8) then
40  read(ccyymmddhhnnss(1:10), fmt='(i4, 2i2)') ccyy, mm, dd
41  hh = 0
42  nn = 0
43  ss = 0
44  else if (datelen == 10) then
45  read(ccyymmddhhnnss(1:10), fmt='(i4, 3i2)') ccyy, mm, dd, hh
46  nn = 0
47  ss = 0
48  else if (datelen == 12) then
49  read(ccyymmddhhnnss(1:12), fmt='(i4, 4i2)') ccyy, mm, dd, hh, nn
50  ss = 0
51  else if (datelen == 14) then
52  read(ccyymmddhhnnss(1:14), fmt='(i4, 5i2)') ccyy, mm, dd, hh, nn, ss
53  else
54  stop 'wrong input date'
55  endif
56 
57  if (.not. validdate(ccyy,mm,dd,hh,nn,ss)) then
58  write(0,*) trim(ccyymmddhhnnss)
59  stop 'Start date is not valid, or has wrong format'
60  endif
61 
62  call parsedt(dtime,dday,dh,dn,ds)
63 
64  hh = hh + dh
65  nn = nn + dn
66  ss = ss + ds
67 
68  ! advance minute according to second
69  do while (ss < 0)
70  ss = ss + 60
71  nn = nn - 1
72  end do
73  do while (ss > 59)
74  ss = ss - 60
75  nn = nn + 1
76  end do
77 
78  ! advance hour according to minute
79  do while (nn < 0)
80  nn = nn + 60
81  hh = hh - 1
82  end do
83  do while (nn > 59)
84  nn = nn - 60
85  hh = hh + 1
86  end do
87 
88  ! advance day according to hour
89  do while (hh < 0)
90  hh = hh + 24
91  dday = dday - 1
92  end do
93 
94  do while (hh > 23)
95  hh = hh - 24
96  dday = dday + 1
97  end do
98 
99  ! advance day if dday /= 0
100  if (dday /= 0) call change_date ( ccyy, mm, dd, dday)
101 
102  write(ccyymmddhhnnss(1:14), fmt='(i4, 5i2.2)') ccyy, mm, dd, hh, nn, ss
103  !if (datelen<14) then
104  ! if(nn /= 0) datelen=12
105  ! if(ss /= 0) datelen=14
106  !endif
107  date_out = ccyymmddhhnnss
108 
109 contains
110 
111 subroutine change_date( ccyy, mm, dd, delta )
112 
113  implicit none
114 
115  integer, intent(inout) :: ccyy, mm, dd
116  integer, intent(in) :: delta
117 
118  integer, dimension(12) :: mmday
119  integer :: dday, direction
120 
121  mmday = (/31,28,31,30,31,30,31,31,30,31,30,31/)
122 
123  mmday(2) = 28
124 
125  if (mod(ccyy,4) == 0) then
126  mmday(2) = 29
127 
128  if (mod(ccyy,100) == 0) then
129  mmday(2) = 28
130  end if
131 
132  if (mod(ccyy,400) == 0) then
133  mmday(2) = 29
134  end if
135  end if
136 
137  dday = abs(delta)
138  direction = sign(1,delta)
139 
140  do while (dday > 0)
141 
142  dd = dd + direction
143 
144  if (dd == 0) then
145  mm = mm - 1
146 
147  if (mm == 0) then
148  mm = 12
149  ccyy = ccyy - 1
150  end if
151 
152  dd = mmday(mm)
153  elseif ( dd > mmday(mm)) then
154  dd = 1
155  mm = mm + 1
156  if(mm > 12 ) then
157  mm = 1
158  ccyy = ccyy + 1
159  end if
160  end if
161 
162  dday = dday - 1
163 
164  end do
165  return
166 end subroutine change_date
167 
168 function parsedate(datein)
169  character(len=*), intent(in) :: datein
170 
171  character(len=14) :: parsedate
172  character(len=1 ) :: ch
173  integer :: n, i
174  parsedate = '00000000000000'
175  i=0
176  do n = 1, len_trim(datein)
177  ch = datein(n:n)
178  if (ch >= '0' .and. ch <= '9') then
179  i=i+1
180  parsedate(i:i)=ch
181  end if
182  end do
183  if (parsedate(11:14) == '0000') then
184  parsedate(11:14) = ''
185  else if(parsedate(13:14) == '00') then
186  parsedate(13:14) = ''
187  end if
188  return
189 end function parsedate
190 
191 subroutine parsedt(dt,dday,dh,dn,ds)
192  character(len=*), intent(in) :: dt
193  integer, intent(inout) :: dday, dh, dn, ds
194 
195  character(len=1 ) :: ch
196  integer :: n,i,d,s,nounit
197  ! initialize time and sign
198  nounit=1
199  dday=0
200  dh=0
201  dn=0
202  ds=0
203  d=0
204  s=1
205  do n = 1, len_trim(dt)
206  ch = dt(n:n)
207  select case (ch)
208  case ('0':'9')
209  read(ch,fmt='(i1)') i
210  d=d*10+i
211  case ('-')
212  s=-1
213  case ('+')
214  s=1
215  case ('d')
216  nounit=0
217  dday=dday+d*s
218  d=0
219  case ('h')
220  nounit=0
221  dh=dh+d*s
222  d=0
223  case ('n','m')
224  nounit=0
225  dn=dn+d*s
226  d=0
227  case ('s')
228  nounit=0
229  ds=ds+d*s
230  d=0
231  case default
232  end select
233  end do
234  if (nounit==1) dh=d*s
235 end subroutine parsedt
236 
237 function isleapyear(year)
238  ! check if year is leapyear
239  integer,intent(in) :: year
240  logical :: isleapyear
241  if( mod(year,4) .ne. 0 ) then
242  isleapyear=.false.
243  else
244  isleapyear=.true.
245  if ( mod(year,100) == 0 .and. mod(year,400) .ne. 0 ) isleapyear=.false.
246  endif
247 end function isleapyear
248 
249 function validdate(ccyy,mm,dd,hh,nn,ss)
250  integer, intent(in) :: ccyy,mm,dd,hh,nn,ss
251 
252  logical :: validdate
253 
254  validdate = .true.
255 
256  if(ss > 59 .or. ss < 0 .or. &
257  nn > 59 .or. nn < 0 .or. &
258  hh > 23 .or. hh < 0 .or. &
259  dd < 1 .or. &
260  mm > 12 .or. mm < 1 ) validdate = .false.
261 
262  if (mm == 2 .and. ( dd > 29 .or. &
263  ((.not. isleapyear(ccyy)) .and. dd > 28))) &
264  validdate = .false.
265 end function validdate
266 
267 end subroutine da_advance_time
268 
269 end module utils_mod
subroutine, public da_advance_time(date_in, dtime, date_out)
Definition: utils_mod.f90:12
logical function isleapyear(year)
Definition: utils_mod.f90:238
character(len=14) function parsedate(datein)
Definition: utils_mod.f90:169
subroutine parsedt(dt, dday, dh, dn, ds)
Definition: utils_mod.f90:192
subroutine change_date(ccyy, mm, dd, delta)
Definition: utils_mod.f90:112
logical function validdate(ccyy, mm, dd, hh, nn, ss)
Definition: utils_mod.f90:250