1 program da_advance_time
3 ! modified from da_advance_cymdh,
4 ! - has accuracy down to second,
5 ! - can use day/hour/minute/second (with/without +/- sign) to advance time,
6 ! - can digest various input date format if it still has the right order (ie. cc yy mm dd hh nn ss)
7 ! - can digest flexible time increment
8 ! - can output in wrf date format (ccyy-mm-dd_hh:nn:ss)
9 ! - can specify output date format
10 ! - can output Julian day
11 ! - can output Gregorian days and seconds (since year 1601)
13 ! eg.: da_advance_time 20070730 12 # advance 12 h
14 ! da_advance_time 2007073012 -1d2h30m30s # back 1 day 2 hours 30 minutes and 30 seconds
15 ! da_advance_time 2007073012 1s-3h30m # back 3 hours 30 minutes less 1 second
16 ! da_advance_time 200707301200 2d1s -w # advance 2 days and 1 second, output in wrf date format
17 ! da_advance_time 2007-07-30_12:00:00 2d1s -w # same as previous example
18 ! da_advance_time 200707301200 2d1s -f ccyy-mm-dd_hh:nn:ss # same as previous example
19 ! da_advance_time 2007073006 120 -j # advance 120 h, and print year and Julian day
20 ! da_advance_time 2007073006 120 -J # advance 120 h, print year, Julian day, hour, minute and second
21 ! da_advance_time 2007073006 0 -g # print Gregorian day and second (since year 1601)
25 #define iargc ipxfargc
31 ! integer function iargc()
34 integer, external :: iargc
36 integer :: ccyy
, mm
, dd
, hh
, nn
, ss
, dday
, dh
, dn
, ds
, gday
, gsec
38 integer :: nargum
, i
, n
40 character(len
=80), dimension(10) :: argum
42 character(len
=14) :: ccyymmddhhnnss
44 character(len
=80) :: out_date_format
, dtime
48 integer, parameter :: stdout
=6
52 if ( nargum
< 2 ) then
53 write(unit
=stdout
, fmt
='(a)') &
54 'Usage: da_advance_time ccyymmddhh[nnss] [+|-]dt[d|h|m|s] [-w|-W|-wrf|-WRF] [-f|-F date_format] [-j|-J] [-g|-G]'
55 write(unit
=stdout
, fmt
='(a)') &
56 'Option: -w|-W|-wrf|-WRF output in wrf date format as ccyy-mm-dd_hh:nn:ss'
57 write(unit
=stdout
, fmt
='(a)') &
58 ' -f|-F specify output date format, such as ccyy-mm-dd_hh:nn:ss, or ''ccyy/mm/dd hh:nn:ss'''
59 write(unit
=stdout
, fmt
='(a)') &
60 ' -j|-J print Julian day'
61 write(unit
=stdout
, fmt
='(a)') &
62 ' -g|-G print Gregorian days and seconds (since year 1601)'
63 write(unit
=stdout
, fmt
='(a)') &
64 'Example: da_advance_time 20070730 12 # advance 12 h'
65 write(unit
=stdout
, fmt
='(a)') &
66 ' da_advance_time 2007073012 -1d2h30m30s # back 1 day 2 hours 30 min and 30 sec'
67 write(unit
=stdout
, fmt
='(a)') &
68 ' da_advance_time 2007073012 1s-3h30m # back 3 hours 30 minutes less 1 second'
69 write(unit
=stdout
, fmt
='(a)') &
70 ' da_advance_time 200707301200 1d1s -w # advance 1 day 1 sec, output in wrf date format'
71 write(unit
=stdout
, fmt
='(a)') &
72 ' da_advance_time 2007-07-30_12:00:00 2d1s -w # same as previous example'
73 write(unit
=stdout
, fmt
='(a)') &
74 ' da_advance_time 200707301200 2d1s -f ccyy-mm-dd_hh:nn:ss # same as previous'
75 write(unit
=stdout
, fmt
='(a)') &
76 ' da_advance_time 2007073006 120 -j # advance 120 h, and print year and Julian day'
77 write(unit
=stdout
, fmt
='(a)') &
78 ' da_advance_time 2007073006 120 -J # advance 120 h, print year, Julian day, hour, minute and second'
79 write(unit
=stdout
, fmt
='(a)') &
80 ' da_advance_time 2007073006 0 -g # print Gregorian day and second (since year 1601)'
81 write(unit
=stdout
, fmt
='(a)') ''
89 call getarg(i
,argum(i
))
92 ccyymmddhhnnss
= parsedate(argum(1))
93 datelen
= len_trim(ccyymmddhhnnss
)
95 if (datelen
== 8) then
96 read(ccyymmddhhnnss(1:10), fmt
='(i4, 2i2)') ccyy
, mm
, dd
100 else if (datelen
== 10) then
101 read(ccyymmddhhnnss(1:10), fmt
='(i4, 3i2)') ccyy
, mm
, dd
, hh
104 else if (datelen
== 12) then
105 read(ccyymmddhhnnss(1:12), fmt
='(i4, 4i2)') ccyy
, mm
, dd
, hh
, nn
107 else if (datelen
== 14) then
108 read(ccyymmddhhnnss(1:14), fmt
='(i4, 5i2)') ccyy
, mm
, dd
, hh
, nn
, ss
110 stop 'wrong input date'
113 if (.not
. validdate(ccyy
,mm
,dd
,hh
,nn
,ss
)) then
114 stop 'Start date is not valid, or has wrong format'
119 dtime
= trim(argum(2))
120 call parsedt(dtime
,dday
,dh
,dn
,ds
)
126 ! advance minute according to second
136 ! advance hour according to minute
146 ! advance day according to hour
157 ! advance day if dday /= 0
158 if (dday
/= 0) call change_date ( ccyy
, mm
, dd
, dday
)
160 write(ccyymmddhhnnss(1:14), fmt
='(i4, 5i2.2)') ccyy
, mm
, dd
, hh
, nn
, ss
161 if ( nargum
== 2 ) then
163 if(nn
/= 0) datelen
=12
164 if(ss
/= 0) datelen
=14
166 write(unit
=stdout
, fmt
='(a)') ccyymmddhhnnss(1:datelen
)
167 else if ( nargum
> 2 ) then
169 do while (i
<= nargum
)
170 select
case ( trim(argum(i
)) )
171 case ('-w', '-W', '-wrf','-WRF')
172 out_date_format
= 'ccyy-mm-dd_hh:nn:ss'
173 write(unit
=stdout
, fmt
='(a)') trim(formatdate(ccyymmddhhnnss
, out_date_format
))
176 out_date_format
= trim(argum(i
+1))
177 write(unit
=stdout
, fmt
='(a)') trim(formatdate(ccyymmddhhnnss
, out_date_format
))
180 write(unit
=stdout
, fmt
='(I4,I4)') ccyy
, julian_day(ccyy
,mm
,dd
)
183 write(unit
=stdout
, fmt
='(I4,I4,I3,I3,I3)') ccyy
, julian_day(ccyy
,mm
,dd
),hh
,nn
,ss
186 call gregorian_day_sec(ccyy
,mm
,dd
,hh
,nn
,ss
,gday
,gsec
)
187 write(unit
=stdout
, fmt
='(I8,I8)') gday
, gsec
197 subroutine change_date( ccyy
, mm
, dd
, delta
)
201 integer, intent(inout
) :: ccyy
, mm
, dd
202 integer, intent(in
) :: delta
204 integer :: dday
, direction
207 direction
= sign(1,delta
)
221 dd
= getmmday(ccyy
,mm
)
223 elseif ( dd
> getmmday(ccyy
,mm
)) then
236 end subroutine change_date
240 subroutine getarg(i
, harg
)
242 character(len
=*) :: harg
243 integer :: ierr
, ilen
, i
245 call pxfgetarg(i
, harg
, ilen
, ierr
)
247 end subroutine getarg
250 function parsedate(datein
)
251 character(len
=80), intent(in
) :: datein
253 character(len
=14) :: parsedate
254 character(len
=1 ) :: ch
256 parsedate
= '00000000000000'
258 do n
= 1, len_trim(datein
)
260 if (ch
>= '0' .and
. ch
<= '9') then
265 if (parsedate(11:14) == '0000') then
266 parsedate(11:14) = ''
267 else if(parsedate(13:14) == '00') then
268 parsedate(13:14) = ''
271 end function parsedate
273 subroutine parsedt(dt
,dday
,dh
,dn
,ds
)
274 character(len
=80), intent(in
) :: dt
275 integer, intent(inout
) :: dday
, dh
, dn
, ds
277 character(len
=1 ) :: ch
278 integer :: n
,i
,d
,s
,nounit
279 ! initialize time and sign
287 do n
= 1, len_trim(dt
)
291 read(ch
,fmt
='(i1)') i
314 stop "Decimal values not allowed"
318 if (nounit
==1) dh
=d
*s
319 end subroutine parsedt
321 function formatdate(datein
,dateform
)
322 character(len
=14), intent(in
) :: datein
323 character(len
=80), intent(in
) :: dateform
324 character(len
=80) :: formatdate
325 integer :: ic
,iy
,im
,id
,ih
,in
,is
326 ic
=index(dateform
,'cc')
327 iy
=index(dateform
,'yy')
328 im
=index(dateform
,'mm')
329 id
=index(dateform
,'dd')
330 ih
=index(dateform
,'hh')
331 in
=index(dateform
,'nn')
332 is
=index(dateform
,'ss')
333 formatdate
=trim(dateform
)
334 if (ic
/= 0) formatdate(ic
:ic
+1) = datein(1:2)
335 if (iy
/= 0) formatdate(iy
:iy
+1) = datein(3:4)
336 if (im
/= 0) formatdate(im
:im
+1) = datein(5:6)
337 if (id
/= 0) formatdate(id
:id
+1) = datein(7:8)
338 if (ih
/= 0) formatdate(ih
:ih
+1) = datein(9:10)
339 if (in
/= 0) formatdate(in
:in
+1) = datein(11:12)
340 if (is
/= 0) formatdate(is
:is
+1) = datein(13:14)
342 end function formatdate
344 function julian_day(ccyy
,mm
,dd
)
345 integer, intent(in
) :: ccyy
,mm
,dd
346 integer :: julian_day
347 integer, parameter, dimension( 13) :: &
348 bgn_day
= (/ 0, 31, 59, 90, 120, 151, &
349 181, 212, 243, 273, 304, 334, 365 /), &
350 bgn_day_ly
= (/ 0, 31, 60, 91, 121, 152, &
351 182, 213, 244, 274, 305, 335, 366 /)
352 if (isleapyear(ccyy
)) then
353 julian_day
= bgn_day_ly(mm
)+dd
355 julian_day
= bgn_day(mm
)+dd
357 end function julian_day
359 function isleapyear(year
)
360 ! check if year is leapyear
361 integer,intent(in
) :: year
362 logical :: isleapyear
363 if( mod(year
,4) .ne
. 0 ) then
367 if ( mod(year
,100) == 0 .and
. mod(year
,400) .ne
. 0 ) isleapyear
=.FALSE
.
369 end function isleapyear
371 subroutine gregorian_day_sec(year
,month
,day
,hours
,minutes
,seconds
,gday
,gsec
)
372 integer, intent(in
) :: day
, month
, year
, hours
, minutes
, seconds
373 integer, intent(out
) :: gday
, gsec
375 integer :: ndays
, m
, nleapyr
376 integer :: base_year
= 1601
377 integer :: days_per_month(12) = (/31,28,31,30,31,30,31,31,30,31,30,31/)
379 if( year
< base_year
) stop "Year can not be before 1601!"
381 ! compute number of leap years fully past since base_year
382 nleapyr
= (year
- base_year
) / 4 - (year
- base_year
) / 100 + (year
- base_year
) / 400
383 ! Count up days in this year
386 ndays
= ndays
+ days_per_month(m
)
387 if(isleapyear(year
) .and
. m
== 2) ndays
= ndays
+ 1
389 gsec
= seconds
+ 60*(minutes
+ 60*hours
)
390 gday
= day
- 1 + ndays
+ 365*(year
- base_year
- nleapyr
) + 366*(nleapyr
)
392 end subroutine gregorian_day_sec
394 function validdate(ccyy
,mm
,dd
,hh
,nn
,ss
)
395 integer, intent(in
) :: ccyy
,mm
,dd
,hh
,nn
,ss
401 if(ss
> 59 .or
. ss
< 0 .or
. &
402 nn
> 59 .or
. nn
< 0 .or
. &
403 hh
> 23 .or
. hh
< 0 .or
. &
405 mm
> 12 .or
. mm
< 1 ) validdate
= .false
.
407 if (mm
== 2 .and
. ( dd
> 29 .or
. &
408 ((.not
. isleapyear(ccyy
)) .and
. dd
> 28))) &
410 end function validdate
412 function getmmday(ccyy
,mm
)
413 integer, intent(in
) :: ccyy
,mm
416 integer, dimension(12) :: mmday
, mmday_ly
418 mmday
= (/31,28,31,30,31,30,31,31,30,31,30,31/)
419 mmday_ly
= (/31,29,31,30,31,30,31,31,30,31,30,31/)
421 if(isleapyear(ccyy
)) then
422 getmmday
=mmday_ly(mm
)
427 end function getmmday
429 end program da_advance_time