ROMS
Loading...
Searching...
No Matches
dateclock.F
Go to the documentation of this file.
1#include "cppdefs.h"
3!
4!git $Id$
5!================================================== Hernan G. Arango ===
6! Copyright (c) 2002-2025 The ROMS Group !
7! Licensed under a MIT/X style license !
8! See License_ROMS.md !
9!=======================================================================
10! !
11! This module contains several routines to manage ROMS date, clocks, !
12! and calendars: !
13! !
14! caldate Converts current model time (days) to calendar date. !
15! All the returned variables require keyword syntax !
16! since they are optional. !
17! !
18! datenum Converts requested date (year, month, day, ...) into !
19! a serial number according to the supported calendar!
20! options. !
21! !
22! datestr Converts date number to date string of the form: !
23! YYYY-MM-DD hh:mm:ss.ss !
24! !
25! datevec Converts a given date number to a date vector. It is !
26! inverse routine to "datenum". !
27! !
28! day_code Given (month, day, year) it returns a numerical code !
29! (0 to 6) for the day of the week. !
30! !
31! get_date Retuns today date string of the form: !
32! DayOfWeak - Month day, year - hh:mm:ss ?M !
33! !
34! ref_clock Sets application time clock/reference and loads its !
35! to structure Rclock of TYPE T_CLOCK. !
36! !
37! ROMS_clock Given (year, month, day, hour, minutes, seconds), !
38! this routine returns ROMS clock time since !
39! initialization from the reference date. It is !
40! used when importing fields from coupled models. !
41! !
42! time_iso8601 Encodes current model time to am ISO 8601 string !
43! !
44! time_string Encodes current model time to a string. !
45! !
46! time_units Decodes time attributes units. !
47! !
48! yearday Given (year,month,day) this integer function returns !
49! the day of the year. !
50! !
51!=======================================================================
52!
53 USE mod_kinds
54!
55 implicit none
56!
57 PUBLIC :: caldate
58 PUBLIC :: datenum
59 PUBLIC :: datestr
60 PUBLIC :: datevec
61 PUBLIC :: day_code
62 PUBLIC :: get_date
63 PUBLIC :: ref_clock
64 PUBLIC :: roms_clock
65 PUBLIC :: time_iso8601
66 PUBLIC :: time_string
67 PUBLIC :: time_units
68 PUBLIC :: yearday
69!
70 CONTAINS
71!
72!***********************************************************************
73 SUBROUTINE caldate (CurrentTime, &
74 & yy_i, yd_i, mm_i, dd_i, h_i, m_i, s_i, &
75 & yd_dp, dd_dp, h_dp, m_dp, s_dp)
76!***********************************************************************
77! !
78! This routine converts current model time (in days) to calendar !
79! date. All the output arguments require keyword syntax since they !
80! are all optional. For Example, to get just the fractional (real) !
81! day-of-year: !
82! !
83! CALL caldate (tdays(ng), yd_dp=yday) !
84! !
85! On Input: !
86! !
87! CurrentTime Model current time (real; days) !
88! !
89! On Output: !
90! !
91! yy_i Year including century (integer; OPTIONAL) !
92! yd_i Day of the year (integer; OPTIONAL) !
93! mm_i Month of the year, 1=Jan, ... (integer; OPTIONAL) !
94! dd_i Day of the month (integer; OPTIONAL) !
95! h_i Hour of the day (integer; OPTIONAL) !
96! m_i Minutes of the hour, 0 - 59 (integer; OPTIONAL) !
97! s_i Seconds of the minute (integer; OPTIONAL) !
98! !
99! yd_dp Day of the year (real, fraction; OPTIONAL) !
100! dd_dp Day of the month (real, fraction; OPTIONAL) !
101! h_dp Hour of the day (real, fraction; OPTION) !
102! m_dp Minutes of the hour (real, fraction; OPTION) !
103! s_dp Seconds of the minute (real, fraction; OPTIONAL) !
104! !
105! Notice that a calendar obtained by extending backward in time from !
106! its invention or implementation is called the Proleptic version of !
107! the calendar. !
108! !
109!***********************************************************************
110!
111 USE mod_param
112 USE mod_scalars
113!
114 USE round_mod, ONLY : round
115!
116! Imported variable declarations.
117!
118 real(dp), intent(in) :: currenttime
119!
120 integer, intent(out), optional :: yy_i
121 integer, intent(out), optional :: yd_i
122 integer, intent(out), optional :: mm_i
123 integer, intent(out), optional :: dd_i
124 integer, intent(out), optional :: h_i
125 integer, intent(out), optional :: m_i
126 integer, intent(out), optional :: s_i
127!
128 real(dp), intent(out), optional :: yd_dp
129 real(dp), intent(out), optional :: dd_dp
130 real(dp), intent(out), optional :: h_dp
131 real(dp), intent(out), optional :: m_dp
132 real(dp), intent(out), optional :: s_dp
133!
134! Local variable declarations.
135!
136 logical :: isdayunits
137
138 integer :: myday, myhour, myminutes, myseconds
139 integer :: mymonth, myyday, myyear
140
141 real(dp) :: datenumber, dayfraction, refdatenumber
142 real(dp) :: hour, minutes, seconds
143!
144!-----------------------------------------------------------------------
145! Get calendar date from model current time (days).
146!-----------------------------------------------------------------------
147!
148 refdatenumber=rclock%DateNumber(1) ! fractional days
149!
150! The model clock is the elapsed time since reference time of the form
151! 'time-units since YYYY-MM-DD hh:mm:ss'. It is called the Gregorian
152! Calendar or Gregorian Proleptic Calendar.
153!
154 calendar : IF (int(time_ref).gt.0) THEN
155 datenumber=refdatenumber+currenttime ! fractional days
156 dayfraction=abs(datenumber-aint(datenumber))
157!
158 isdayunits=.true.
159 CALL datevec (datenumber, isdayunits, myyear, mymonth, myday, &
160 & myhour, myminutes, seconds, minutes, hour)
161 myyday=yearday(myyear, mymonth, myday)
162 myseconds=int(seconds)
163!
164! The model clock is the elapsed time since reference time of the form
165! 'time-units since 0001-01-01 00:00:00'. It is used in analytical
166! test cases. It has a year length of 365.2425 days (adapted on
167! 15 October 1582 by Gregorian Calendar). It is called the Proleptic
168! Gregorian Calendar.
169!
170 ELSE IF (int(time_ref).eq.0) THEN
171 datenumber=refdatenumber+currenttime ! fractional days
172 dayfraction=abs(datenumber-aint(datenumber))
173!
174 isdayunits=.true.
175 CALL datevec (datenumber, isdayunits, myyear, mymonth, myday, &
176 & myhour, myminutes, seconds, minutes, hour)
177 myyday=yearday(myyear, mymonth, myday)
178 myseconds=int(seconds)
179!
180! The model clock is the elapsed time since reference time of the form
181! 'time-units since 0001-01-01 00:00:00'. It can be used for
182! climatological solutions. It has a year length of 360 days and
183! every month has 30 days. It is called the 360_day calendar by
184! numerical modelers.
185!
186 ELSE IF (int(time_ref).eq.-1) THEN
187 datenumber=refdatenumber+currenttime ! fractional days
188 dayfraction=abs(datenumber-aint(datenumber))
189!
190 isdayunits=.true.
191 CALL datevec (datenumber, isdayunits, myyear, mymonth, myday, &
192 & myhour, myminutes, seconds, minutes, hour)
193 myyday=int(datenumber-real(myyear*360,dp)+1)
194 myseconds=int(seconds)
195!
196! The model clock is the elapsed time since reference time of the form
197! 'time-units since 1968-05-23 00:00:00 GMT'. It is a Truncated Julian
198! day introduced by NASA and primarily used by Astronomers. It has
199! a year length of 365.25 days. It is less used nowadays since the length
200! of the year is 648 seconds less (365.2425) resulting in too many leap
201! years. So it was corrected after 15 October 1582 and it is now called
202! the Gregorian Calendar.
203!
204 ELSE IF (int(time_ref).eq.-2) THEN
205 IF (currenttime.ge.refdatenumber) THEN ! fractional day
206 datenumber=currenttime ! from origin
207 ELSE
208 datenumber=refdatenumber+currenttime ! fractional days
209 END IF ! plus truncation
210 dayfraction=abs(datenumber-aint(datenumber))
211!
212 isdayunits=.true.
213 CALL datevec (datenumber, isdayunits, myyear, mymonth, myday, &
214 & myhour, myminutes, seconds, minutes, hour)
215 myyday=yearday(myyear, mymonth, myday)
216 myseconds=int(seconds)
217 END IF calendar
218!
219!-----------------------------------------------------------------------
220! Load requested time clock values.
221!-----------------------------------------------------------------------
222!
223 IF (PRESENT(yd_i)) yd_i=myyday
224 IF (PRESENT(yy_i)) yy_i=myyear
225 IF (PRESENT(mm_i)) mm_i=mymonth
226 IF (PRESENT(dd_i)) dd_i=myday
227 IF (PRESENT(h_i )) h_i =myhour
228 IF (PRESENT(m_i )) m_i =myminutes
229 IF (PRESENT(s_i )) s_i =myseconds
230!
231 IF (PRESENT(yd_dp)) yd_dp=real(myyday,dp)+dayfraction
232 IF (PRESENT(dd_dp)) dd_dp=real(myday,dp)+dayfraction
233 IF (PRESENT(h_dp )) h_dp =hour
234 IF (PRESENT(m_dp )) m_dp =minutes
235 IF (PRESENT(s_dp )) s_dp =seconds
236!
237 RETURN
238 END SUBROUTINE caldate
239!
240!***********************************************************************
241 SUBROUTINE datenum (DateNumber, &
242 & year, month, day, hour, minutes, seconds)
243!***********************************************************************
244! !
245! Converts requested date (year, month, day, ...) into a serial date !
246! number according to the supported calendars options: !
247! !
248! time_ref = -2 Truncated Julian number (Julian/Gregorian) !
249! 'time-units since 1968-05-23 00:00:00' !
250! time_ref = -1 360_day calendar (Proleptic Gregorian) !
251! 'time-units since 0000-12-30 00:00:00' !
252! time_ref = 0 Proleptic Gregorian calendar !
253! 'time-units since 0001-01-01 00:00:00' !
254! time_ref = YYYYMMDD.dd Gregorian or Proleptic Gregorian calendar !
255! 'time-units since YYYY-MM-DD hh:mm:ss' !
256! !
257! For the Proletic Gregogian calendar, the equations are similar to !
258! the Matlab function "datenum": !
259! !
260! Matlab: datenum(0000,00,00)=0 reference date !
261! datenum(0000,01,01)=1 !
262! !
263! but for simplicity, the equations coded here have have a different !
264! origin date (Mar 1, 0000) to facilitate the manipulation of leap !
265! years (adapted from Gary Katch code, Concordia University, Canada) !
266! yielding: !
267! !
268! datenum(0000,03,01)=0 refecence date: Mar 1, 0000 !
269! datenum(0000,01,01)=-59 !
270! !
271! However, to avoid confusion, an offset of 61 days is added to match !
272! Matlab "datenum" function. The difference between 0000-00-00 and !
273! 0000-03-01 is 61 days. !
274! !
275! On 15 October 1582, the Gregorian calendar was introduced with a !
276! year length of 365.2425 days. This is coded as: !
277! !
278! 365 + 1/4 - 1/100 + 1/400 or 365 + 0.25 - 0.01 + 0.0025 !
279! !
280! which is used to account for leap years. The base of Mar 1, 0000 !
281! is taken for simplicity since the length of february is not fixed. !
282! !
283! Notice that a calendar obtained by extending backward in time from !
284! its invention or implementation is called the Proleptic version of !
285! the calendar. For example, the Proleptic Gregorian Calendar extends !
286! backwards the date preceding 15 October 1582 with a year length of !
287! 365.2425 days. !
288! !
289! On Input: !
290! !
291! year Year including the century (integer) !
292! month Month of the year: 1=January, ... (integer) !
293! day Day of the month (integer) !
294! hour Hour of the day, 0, ... 23 (integer, OPTIONAL) !
295! minutes Minutes of the hour (integer, OPTIONAL) !
296! seconds Seconds of the minute (real, OPTIONAL) !
297! !
298! On Output: !
299! !
300! DateNumber Date number (real 1D array), !
301! DateValue(1) => fractional days !
302! DateValue(2) => fractional seconds !
303! !
304!=======================================================================
305!
306 USE mod_scalars, ONLY : time_ref
307!
308! Imported variable declarations.
309!
310 integer, intent(in) :: year, month, day
311
312 integer, intent(in), optional :: hour
313 integer, intent(in), optional :: minutes
314
315 real(dp), intent(in), optional :: seconds
316
317 real(dp), intent(out), dimension(2) :: datenumber
318!
319! Local variable declarations.
320!
321 integer, parameter :: offset = 61
322
323 integer :: myday, myhour, myminutes, mymonth, myyear, y01
324
325 real(dp) :: myseconds
326!
327!-----------------------------------------------------------------------
328! Initialize optional arguments.
329!-----------------------------------------------------------------------
330!
331 IF (PRESENT(hour)) THEN
332 myhour=hour
333 ELSE
334 myhour=0
335 END IF
336!
337 IF (PRESENT(minutes)) THEN
338 myminutes=minutes
339 ELSE
340 myminutes=0
341 END IF
342!
343 IF (PRESENT(seconds)) THEN
344 myseconds=seconds
345 ELSE
346 myseconds=0.0_dp
347 END IF
348!
349!-----------------------------------------------------------------------
350! Date number for the Julian plus Gregorian correction calendar.
351!-----------------------------------------------------------------------
352!
353! The origin of the Proleptic Julian Calendar is January 1, 4713 BC
354! (November 24, 4713 BC, in the Proleptic Gregorian Calendar).
355! Although the formal definition of Julian day numbers starts and
356! ends at noon, here Julian day starts and ends at midnight. So it
357! is 12 hours faster (substract 12 hours to agree with formal
358! definition).
359!
360! datenum(-4713,11,24) = 0 ! Origin: Nov 24, 4713 BC
361! datenum( 1968,05,23) = 2440000 ! Truncated reference (NASA)
362! datenum( 0000,01,01) = 1721060
363!
364 calendar : IF (int(time_ref).eq.-2) THEN
365 IF (month.gt.2) THEN
366 myyear=year
367 mymonth=month-3
368 ELSE
369 myyear=year-1
370 mymonth=month+9
371 END IF
372 y01=myyear/100
373 myyear=myyear-y01*100
374 myday=(146097*y01/4) + (1461*myyear/4) + ((153*mymonth+2)/5) + &
375 & day + 1721119
376!
377!-----------------------------------------------------------------------
378! Date mumber for the 360_day Calendar: the year has a length of 360
379! days and every month has 30 days.
380!-----------------------------------------------------------------------
381!
382! datenum(0000,01,01) = 0
383! datenum(0001,01,01) = 360
384!
385 ELSE IF (int(time_ref).eq.-1) THEN
386 myday=year*360+(month-1)*30+(day-1)
387!
388!-----------------------------------------------------------------------
389! Date number for the Gregorian and Gregorian Proleptic Calendar. It
390! has a year length of 365.2425 days (correspoding to the Gregorian
391! Calendar introduced in 15 October 1582).
392!-----------------------------------------------------------------------
393!
394! datenum(0000,01,01) = 1
395! datenum(0001,01,01) = 367
396!
397 ELSE
398 mymonth=mod(month+9, 12) ! Mar=0, ..., Feb=11
399 myyear=year-int(0.1_dp*real(mymonth,dp)) ! if Jan or Feb,
400! substract 1
401 myday=int(365.0_dp*real(myyear,dp))+ &
402 & int(0.25_dp*real(myyear,dp))- &
403 & int(0.01_dp*real(myyear,dp))+ &
404 & int(0.0025_dp*real(myyear,dp))+ &
405 & int(0.1_dp*(real(mymonth,dp)*306.0_dp + 5.0_dp))+ &
406 & (day - 1)
407!
408! Adjust for Matlab origin 0000-00-00 00:00:00, so we get the same
409! value as their function "datenum". The offset is 61 days.
410!
411! datenum(0000,00,00) = 0
412!
413 IF ((year.eq.0).and.(month.eq.0).and.(day.eq.0)) THEN
414 myday=0;
415 ELSE
416 IF (myday.lt.0) THEN
417 myday=myday+offset-1
418 ELSE
419 myday=myday+offset
420 END IF
421 END IF
422 END IF calendar
423!
424!-----------------------------------------------------------------------
425! Add fractional day to serial day number (day and seconds).
426!-----------------------------------------------------------------------
427!
428! Fractional date number (units=day).
429!
430 datenumber(1)=real(myday,dp)+ &
431 & real(myhour,dp)/24.0_dp+ &
432 & real(myminutes,dp)/1440.0_dp+ &
433 & myseconds/86400.0_dp
434!
435! Fractional date number (units=second).
436!
437 datenumber(2)=real(myday,dp)*86400.0_dp+ &
438 & real(myhour,dp)*3600.0_dp+ &
439 & real(myminutes,dp)*60.0_dp+ &
440 & myseconds
441
442 RETURN
443 END SUBROUTINE datenum
444!
445!***********************************************************************
446 SUBROUTINE datestr (DateNumber, IsDayUnits, DateString)
447!***********************************************************************
448! !
449! Converts a given date number as computed by "datenum" to a date !
450! string. Matlab has similar function. !
451! !
452! On Input: !
453! !
454! DateNumber Date number (real; scalar) as computed by !
455! by "datenum": !
456! IsDayUnits Date number units (logical): !
457! IsDayUnits = .TRUE. fractional days !
458! IsDayUnits = .FALSE. frational seconds !
459! !
460! On Output: !
461! !
462! DateSring Date string (YYYY-MM-DD hh:mm:ss.ss) !
463! !
464!***********************************************************************
465!
466! Imported variable declarations.
467!
468 logical, intent(in) :: isdayunits
469
470 real(dp), intent(in) :: datenumber
471
472 character (len=*), intent(out) :: datestring
473!
474! Local variable declarations.
475!
476 integer :: i, year, month, day, hour, minutes
477
478 real(dp):: f_hour, f_minutes, seconds
479
480 character (len= 5) :: sec_string
481 character (len=22) :: string
482!
483!-----------------------------------------------------------------------
484! Compute date vector from serial date number.
485!-----------------------------------------------------------------------
486!
487 CALL datevec (datenumber, isdayunits, year, month, day, hour, &
488 & minutes, seconds, f_minutes, f_hour)
489!
490!-----------------------------------------------------------------------
491! Set date string.
492!-----------------------------------------------------------------------
493!
494! Encode fractional seconds to a string. Round to one digit.
495!
496 WRITE (sec_string, '(f5.2)') seconds
497 DO i=1,len(sec_string) ! replace leading
498 IF (sec_string(i:i).eq.char(32)) THEN ! space(s) with
499 sec_string(i:i)='0' ! zeros(s)
500 END IF
501 END DO
502!
503! Encode date string.
504!
505 WRITE (string,10) year, month, day, hour, minutes, sec_string
506 10 FORMAT (i4.4,'-',i2.2,'-',i2.2,1x,i2.2,':',i2.2,':',a)
507!
508 datestring=trim(string)
509!
510 RETURN
511 END SUBROUTINE datestr
512!
513!***********************************************************************
514 SUBROUTINE datevec (DateNumber, IsDayUnits, &
515 & year, month, day, hour, minutes, seconds, &
516 & F_minutes, F_hour)
517!***********************************************************************
518! !
519! Converts a given date number as computed by "datenum" to a date !
520! vector (year, month, day, hour, minutes, seconds). It is the !
521! inverse routine for "datenum" above. !
522! !
523! On Input: !
524! !
525! DateNumber Date number (real; scalar) as computed by !
526! by "datenum": !
527! IsDayUnits Date number units (logical): !
528! IsDayUnits = .TRUE. fractional days !
529! IsDayUnits = .FALSE. frational seconds !
530! !
531! On Output: !
532! !
533! year Year including the century (integer; YYYY) !
534! month Month of the year: 1=January, ... (integer) !
535! day Day of the month (integer) !
536! hour Hour of the day, 0, ... 23 (integer) !
537! minutes Minutes of the hour (integer) !
538! seconds Seconds of the minute (real) !
539! !
540! F_minutes Fractional minutes (real) !
541! F_hour Fractional hours (real) !
542! !
543!***********************************************************************
544!
545 USE mod_scalars, ONLY : rclock, time_ref
546 USE round_mod, ONLY : round
547!
548! Imported variable declarations.
549!
550 logical, intent(in) :: isdayunits
551
552 real(dp), intent(in) :: datenumber
553
554 integer, intent(out) :: year, month, day, hour, minutes
555
556 real(dp), intent(out) :: f_hour, f_minutes, seconds
557!
558! Local variable declarations.
559!
560 logical :: prolepticjulian = .false.
561
562 integer :: myday, mymonth, myyear, yday
563 integer :: ja, jalpha, jb, jc, jd, jday, je
564
565 integer, parameter :: gregorian = 2299161 ! 15 Oct, 1582 A.D.
566
567 real(dp), parameter :: offset = 61.0_dp
568
569 real(dp) :: ct, dayfraction, mydatenumber
570 real(dp) :: dd, jr, js, mo, yy
571!
572!-----------------------------------------------------------------------
573! Compute date vector from date number for the Julian with Gregorian
574! Calendar correction.
575!-----------------------------------------------------------------------
576!
577! If truncated, add reference date number (2440000 days). The origin
578! of the Proleptic Julian Calendar is Jan 1, 4713 BC (that is,
579! Nov 24, 4713 BC in the Proleptic Gregorian Calendar).
580!
581! Although the formal definition holds that Julian day starts and ends
582! at noon, here Julian day starts and ends at midnight.
583!
584! It is assumed that if input DateNumber is greater or equal to the
585! Gregorian Calendar start, its value is full and not Reduced,
586! Modified, or Truncated.
587!
588 calendar : IF (int(time_ref).eq.-2) THEN
589 IF (isdayunits) THEN
590 IF (datenumber.ge.real(gregorian,dp)) THEN
591 mydatenumber=datenumber
592 ELSE
593 mydatenumber=datenumber+rclock%DateNumber(1)
594 END IF
595 ELSE
596 IF (datenumber.ge.(real(gregorian,dp)*86400.0_dp)) THEN
597 mydatenumber=datenumber/86400.0_dp
598 ELSE
599 mydatenumber=(datenumber+rclock%DateNumber(2))/86400.0_dp
600 END IF
601 END IF
602 dayfraction=abs(mydatenumber-aint(mydatenumber))
603!
604 IF (prolepticjulian) THEN ! Proleptic Julian Calendar
605 jday=int(mydatenumber) ! origin: Jan 1, 4713 BC
606 IF (jday.ge.gregorian) THEN
607 jalpha=int(((jday-1867216)-0.25_dp)/36524.25_dp)! Gregorian
608 ja=jday+1+jalpha-int(0.25_dp*real(jalpha,dp)) ! correction
609 ELSE
610 ja=jday
611 END IF
612 jb=ja+1524
613 jc=int(6680.0_dp+(real(jb-2439870,dp)-122.1_dp)/365.25_dp)
614 jd=365*jc+int(0.25_dp*real(jc,dp))
615 je=int(real(jb-jd,dp)/30.6001_dp)
616 day=jb-jd-int(30.6001_dp*real(je,dp))
617 month=je-1
618 IF (month.gt.12) month=month-12
619 year=jc-4715
620 IF (month.gt.2) year=year-1
621 IF (year .le.0) year=year-1
622 ELSE ! Proleptic Gregorian
623 jr=floor(mydatenumber)-1721119.0_dp ! Calendar, origin:
624 js=4.0_dp*jr-1.0_dp ! Nov 24, 4713 BC
625 yy=floor(js/146097.0_dp)
626 jr=js-146097.0_dp*yy
627 js=floor(jr*0.25_dp)
628 js=4.0_dp*js+3.0_dp
629 jr=floor(js/1461.0_dp)
630 dd=floor(((js-1461.0_dp*jr)+4.0_dp)*0.25_dp)
631 js=5.0_dp*dd-3.0_dp
632 mo=floor(js/153.0_dp)
633 yy=yy*100.0_dp+jr
634!
635 IF (mo.lt.10.0_dp) THEN
636 year =int(yy)
637 month=int(mo+3)
638 ELSE
639 year =int(yy+1)
640 month=int(mo-9)
641 END IF
642 day=int(((js-153.0_dp*mo)+5.0_dp)*0.2_dp)
643 END IF
644!
645 seconds=dayfraction*86400.0_dp
646 ct=3.0_dp*epsilon(seconds) ! comparison tolerance
647 seconds=round(seconds, ct) ! tolerant round function
648 f_hour=seconds/3600.0_dp
649 hour=int(f_hour)
650 seconds=abs(seconds-real(hour*3600,dp))
651 f_minutes=seconds/60.0_dp
652 minutes=int(f_minutes)
653 seconds=abs(seconds-real(minutes*60,dp))
654!
655!-----------------------------------------------------------------------
656! Compute date vector from date mumber for the 360_day calendar: the
657! year has a length of 360 days and every month has 30 day.
658!-----------------------------------------------------------------------
659!
660 ELSE IF (int(time_ref).eq.-1) THEN
661 dayfraction=abs(datenumber-aint(datenumber))
662!
663 IF (isdayunits) THEN
664 year=int(datenumber/360.0_dp)
665 yday=int(datenumber-real(year*360,dp)+1)
666 ELSE
667 year=int(datenumber/31104000.0_dp) ! 360*86400
668 yday=int((datenumber-real(year*31104000,dp)+1)/86400.0_dp)
669 END IF
670 month=((yday-1)/30)+1
671 day=mod(yday-1,30)+1
672!
673 seconds=dayfraction*86400.0_dp
674 ct=3.0_dp*epsilon(seconds) ! comparison tolerance
675 seconds=round(seconds, ct) ! tolerant round function
676 f_hour=seconds/3600.0_dp
677 hour=int(f_hour)
678 seconds=abs(seconds-real(hour*3600,dp))
679 f_minutes=seconds/60.0_dp
680 minutes=int(f_minutes)
681 seconds=abs(seconds-real(f_minutes*60,dp))
682!
683!-----------------------------------------------------------------------
684! Compute date vector from date number for the Gregorian and Gregorian
685! Proleptic Calendar.
686!-----------------------------------------------------------------------
687!
688 ELSE
689 IF (isdayunits) THEN ! fractional days
690 mydatenumber=datenumber
691 ELSE ! fractional seconds
692 mydatenumber=datenumber/86400.0_dp
693 END IF
694 dayfraction=abs(mydatenumber-aint(mydatenumber))
695!
696 IF (mydatenumber.lt.offset) THEN ! adjust for Matlab
697 mydatenumber=mydatenumber-offset+1.0_dp ! zero origin,
698 ELSE ! datenum(0,0,0)=0,
699 mydatenumber=mydatenumber-offset ! 61 days offset
700 ENDIF
701!
702 myyear=int((10000.0_dp*aint(mydatenumber)+14780.0_dp)/ &
703 & 3652425.0_dp)
704 myday=int(mydatenumber)- &
705 & (int(365.0_dp*real(myyear,dp))+ &
706 & int(0.25_dp*real(myyear,dp))- &
707 & int(0.01_dp*real(myyear,dp))+ &
708 & int(0.0025_dp*real(myyear,dp)))
709 IF (myday.lt.0) THEN ! if less than Mar 1
710 myyear=myyear-1 ! easy on leap-years
711 myday=int(mydatenumber)- &
712 & (int(365.0_dp*real(myyear,dp))+ &
713 & int(0.25_dp*real(myyear,dp))- &
714 & int(0.01_dp*real(myyear,dp))+ &
715 & int(0.0025_dp*real(myyear,dp)))
716 END IF
717 mymonth=int((100.0_dp*real(myday,dp)+ 52.0_dp)/3060.0_dp)
718 month=mod(mymonth+2, 12) + 1
719 year=myyear+ &
720 & int((real(mymonth,dp)+2.0_dp)/12.0_dp)
721 day=myday- &
722 & int(0.1_dp*(real(mymonth,dp)*306.0_dp + 5.0_dp)) + 1
723!
724! Fix to match Matlab "datestr" function values with the origin at
725! 0000-00-00 00:00:00
726!
727 IF (datenumber.eq.0.0_dp) THEN
728 year=0
729 month=1
730 day=0
731 END IF
732!
733! Convert fraction of a day.
734!
735 seconds=dayfraction*86400.0_dp
736 ct=3.0_dp*epsilon(seconds) ! comparison tolerance
737 seconds=round(seconds, ct) ! tolerant round function
738!
739 f_hour=seconds/3600.0_dp
740 hour=int(f_hour)
741 seconds=abs(seconds-real(hour*3600,dp))
742 f_minutes=seconds/60.0_dp
743 minutes=int(f_minutes)
744 seconds=abs(seconds-real(minutes*60,dp))
745 END IF calendar
746!
747 RETURN
748 END SUBROUTINE datevec
749!
750!***********************************************************************
751 SUBROUTINE day_code (month, day, year, code)
752!***********************************************************************
753! !
754! This subroutine computes a code for the day of the week, given !
755! the date. This code is good for dates after: !
756! !
757! January 1, 1752 AD !
758! !
759! the year the Gregorian calander was adopted in Britian and the !
760! American colonies. !
761! !
762! On Input: !
763! !
764! month The month, 1=January, 2=February, ... (integer). !
765! day The day of the month (integer). !
766! year The year, including the century (integer). !
767! !
768! On Output: !
769! !
770! code A code for the corresponding day of the week !
771! (integer): !
772! code = 0 => Sunday !
773! code = 1 => Monday !
774! code = 2 => Tuesday !
775! code = 3 => Wednesday !
776! code = 4 => Thursday !
777! code = 5 => Friday !
778! code = 6 => Saturday !
779! !
780!***********************************************************************
781!
782! Imported variable declarations.
783!
784 integer, intent(in) :: month, day, year
785
786 integer, intent(out) :: code
787!
788! Local variable declarations.
789!
790 logical :: leap_flag
791
792 integer, parameter :: base_cen = 1700
793 integer, parameter :: base_qcen = 1600
794 integer, parameter :: base_qyear = 1748
795 integer, parameter :: base_year = 1752
796 integer, parameter :: bym1_dec31 = 5
797 integer, parameter :: feb_end = 59
798
799 integer :: i, leap, no_day, no_yr, nqy, nyc, nyqc
800
801 integer, dimension(12) :: month_day = &
802 & (/ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 /)
803!
804!-----------------------------------------------------------------------
805! Compute the number of years since the base year, the number of
806! years since the beginning of the base century and the number of
807! years since the beginning of the base 400 year.
808!-----------------------------------------------------------------------
809!
810 no_yr=year-base_year
811 nqy=year-base_qyear
812 nyc=year-base_cen
813 nyqc=year-base_qcen
814!
815!-----------------------------------------------------------------------
816! Compute the number of leapdays in that time. Determine if this
817! is a leap year.
818!-----------------------------------------------------------------------
819!
820 leap=nqy/4-nyc/100+nyqc/400
821 leap_flag=((mod(nqy,4).eq.0).and.(mod(nyc,100).ne.0)).or. &
822 & (mod(nyqc,400).eq.0)
823!
824!-----------------------------------------------------------------------
825! Compute the number of days this year. The leap year corrections
826! are:
827! Jan. 1 - Feb. 28 Have not had the leap day counted above.
828! Feb.29 Counting leap day twice.
829!-----------------------------------------------------------------------
830!
831 no_day=day
832 DO i=1,month-1
833 no_day=no_day+month_day(i)
834 END DO
835 IF (leap_flag.and.(no_day.le.feb_end)) no_day=no_day-1
836 IF (leap_flag.and.(month.eq.2).and.(day.eq.29)) no_day=no_day-1
837!
838!-----------------------------------------------------------------------
839! Compute the total number of days since Jan. 1 of the base year,
840! exclusive of the 364 day per year which represent an even 52
841! weeks. Actually, only need to do the addition mod 7.
842!-----------------------------------------------------------------------
843!
844 no_day=mod(no_day,7)+mod(leap,7)+mod(no_yr,7)+bym1_dec31
845!
846!-----------------------------------------------------------------------
847! Get the day of the week code.
848!-----------------------------------------------------------------------
849!
850 code=mod(no_day,7)
851 RETURN
852 END SUBROUTINE day_code
853!
854!***********************************************************************
855 SUBROUTINE get_date (date_str)
856!***********************************************************************
857! !
858! This routine gets today's date string. It uses intrinsic fortran !
859! function "date_and_time" and a 12 hour clock. The string is of !
860! the form: !
861! !
862! DayOfWeak - Month day, year - hh:mm:ss ?M !
863! !
864! On Output: !
865! !
866! date_str Today date string, for example: !
867! !
868! Friday - February 3, 2017 - 3:40:25 PM !
869! !
870!***********************************************************************
871!
872! Imported variable declarations.
873!
874 character (len=*), intent(out) :: date_str
875!
876! Local variable declarations.
877!
878 integer :: iyear, imonth, iday, ihour, iminute, isecond
879 integer :: dindex, i, half, len1, len2, len3
880
881 integer, dimension(8) :: values
882
883 integer, dimension(31) :: lday = &
884 & (/ (1,i=1,9), (2,i=1,22) /)
885
886 integer, dimension(12) :: lmonth = &
887 & (/ 7, 8, 5, 5, 3, 4, 4, 6, 9, 7, 8, 8 /)
888
889 character (len= 5) :: czone
890 character (len= 8) :: cdate
891 character (len=10) :: ctime
892 character (len=11) :: tstring
893 character (len=18) :: today
894 character (len=20) :: fmt
895 character (len=44) :: dstring
896
897 character (len=3), dimension(0:1) :: ampm = &
898 & (/' AM',' PM'/)
899
900 character (len=9), dimension(0:6) :: day = &
901 & (/ 'Sunday ','Monday ','Tuesday ', &
902 & 'Wednesday','Thursday ','Friday ', &
903 & 'Saturday ' /)
904
905 character (len=9), dimension(12) :: month = &
906 & (/ 'January ','February ','March ', &
907 & 'April ','May ','June ', &
908 & 'July ','August ','September', &
909 & 'October ','November ','December ' /)
910!
911!-----------------------------------------------------------------------
912! Get weekday, date and time in short format, then extract its
913! information.
914!-----------------------------------------------------------------------
915!
916 CALL date_and_time (cdate, ctime, czone, values)
917!
918 iyear=values(1) ! 4-digit year
919 imonth=values(2) ! month of the year
920 iday=values(3) ! day of the month
921 ihour=values(5) ! hour of the day, local time
922 iminute=values(6) ! minutes of the hour, local time
923 isecond=values(7) ! seconds of the minute, local time
924!
925!-----------------------------------------------------------------------
926! Convert from 24 hour clock to 12 hour AM/PM clock.
927!-----------------------------------------------------------------------
928!
929 half=ihour/12
930 ihour=ihour-half*12
931 IF (ihour.eq.0) ihour=12
932 IF (half.eq.2) half=0
933!
934!-----------------------------------------------------------------------
935! Get index for the day of the week.
936!-----------------------------------------------------------------------
937!
938 CALL day_code (imonth, iday, iyear, dindex)
939!
940!-----------------------------------------------------------------------
941! Construct date, time and day of the week output string.
942!-----------------------------------------------------------------------
943!
944 WRITE (fmt,10) lmonth(imonth), lday(iday)
945 10 FORMAT ('(a',i1,',1x,i',i1,',1h,,1x,i4)')
946 WRITE (today,fmt) month(imonth), iday, iyear
947 dstring=day(dindex)
948 WRITE (tstring,20) ihour, iminute, isecond, ampm(half)
949 20 FORMAT (i2,':',i2.2,':',i2.2,a3)
950!
951! Concatenate date string.
952!
953 len1=len_trim(dstring)
954 len2=len_trim(today)
955 len3=len_trim(tstring)
956 date_str=trim(adjustl(dstring(1:len1)))
957 IF (len2.gt.0) THEN
958 len1=len_trim(date_str)
959 WRITE (date_str,'(a," - ",a)') trim(date_str(1:len1)), &
960 & trim(today(1:len2))
961 END IF
962 IF (len3.gt.0) THEN
963 len1=len_trim(date_str)
964 WRITE (date_str,'(a," - ",a)') trim(date_str(1:len1)), &
965 & trim(tstring(1:len3))
966 END IF
967 RETURN
968 END SUBROUTINE get_date
969!
970!***********************************************************************
971 SUBROUTINE ref_clock (r_time)
972!***********************************************************************
973! !
974! This routine encodes the relative time attribute that gives the !
975! elapsed interval since a specified reference time. The "units" !
976! attribute takes the form "time-unit since reference-time". !
977! !
978! On Input: !
979! !
980! r_time Time-reference (real; YYYYMMDD.dd; for example, !
981! 20020115.5 for 15 Jan 2002, 12:0:0). !
982! !
983! On Output: !
984! !
985! Rclock The time clock base/reference is loaded into module !
986! (mod_scalars.F) structure: !
987! !
988! Rclock%yday => day of the year !
989! Rclock%year => year including century (YYYY) !
990! Rclock%month => month of the year !
991! Rclock%day => day of the month !
992! Rclock%hour => hour of the day (0,...,23) !
993! Rclock%minutes => minutes of the hour !
994! Rclock%seconds => seconds of the minute !
995! Rclock%base => reference date (YYYYMMDD.dd) !
996! Rclock%DateNumber => date number, 1: days 2: seconds!
997! Rclock%string => attribute (YYYY-MM-DD hh:ss:mm)!
998! Rclock%calendar => date calendar !
999! !
1000!***********************************************************************
1001!
1002 USE mod_param
1003 USE mod_scalars
1004!
1005! Imported variable declarations.
1006!
1007 real(dp), intent(in) :: r_time
1008!
1009! Local variable declarations.
1010!
1011 integer :: ifac
1012 integer :: iday, ihour, isec, iyear, leap, minute, month, yday
1013
1014 real(dp) :: day, sec
1015
1016 real(dp), dimension(2) :: datenumber
1017
1018 character (len=19) :: string
1019 character (len=20) :: calendar
1020!
1021!-----------------------------------------------------------------------
1022! Decode reference time.
1023!-----------------------------------------------------------------------
1024!
1025! The model clock is the elapsed time since reference time of the form
1026! 'time-units since YYYY-MM-DD hh:mm:ss'.
1027!
1028 IF (int(r_time).gt.0) THEN ! day 0: Mar 1, 0000
1029 calendar='proleptic_gregorian'
1030 iyear=max(1,int(r_time*0.0001_dp))
1031 month=min(12,max(1,int((r_time-real(iyear*10000,dp))*0.01_dp)))
1032 day=r_time-aint(r_time*0.01_dp)*100.0_dp
1033 iday=max(1,int(day))
1034 sec=(day-aint(day))*86400.0_dp
1035 ihour=int(sec/3600.0_dp)
1036 minute=int(mod(sec,3600.0_dp)/60.0_dp)
1037 isec=int(mod(sec,60.0_dp))
1038 yday=yearday(iyear, month, iday)
1039 CALL datenum (datenumber, iyear, month, iday, ihour, minute, &
1040 & real(isec,dp))
1041!
1042! The model clock is the elapsed time since reference time of the form
1043! 'time-units since 0001-01-01 00:00:00'. It has a year length of
1044! 365.2425 days
1045!
1046 ELSE IF (int(r_time).eq.0) THEN ! day 0: Mar 1, 0000
1047 calendar='proleptic_gregorian'
1048 iyear=1
1049 month=1
1050 iday=1
1051 ihour=0
1052 minute=0
1053 isec=0
1054 yday=1
1055 yday=yearday(iyear, month, iday)
1056 CALL datenum (datenumber, iyear, month, iday, ihour, minute, &
1057 & real(isec,dp))
1058!
1059! The model clock is the elapsed time since reference time of the form
1060! 'time-units since 0001-01-01 00:00:00'. It has a year length of
1061! 360 days.
1062!
1063! In this calendar, the time in days is simply:
1064!
1065! Time = year * 360 + (month - 1) * 30 + (day - 1)
1066!
1067! And its inverse
1068!
1069! year = INT(Time / 360)
1070! yday = INT((Time - year * 360) + 1)
1071! month = INT(((yday - 1) / 30) + 1)
1072! day = MOD(yday - 1, 30) + 1
1073!
1074! It assumes that the origin (DayNumber=0) corresponds to 01-Jan-0000.
1075! However, historically ROMS assumed that DayNumber=1 corresponded to
1076! 01-Jan-0000 instead. So, there is one day shift. The equations
1077! can be manipulated to give either origin, but it is confusing. The
1078! above equations are cleaner and now effective (HGA: 30-Jan-2018). The
1079! origin (DayNumber=0) occurs on 01-Jan-0000.
1080!
1081! To guarantee compatibility with previous ROMS solutions with this
1082! climatological calendar, the reference date is changed to
1083!
1084! 'time-units since 0000-12-30 00:00:00'
1085!
1086! to fix the one date shift because DayNumber=0 on 01-Jan-0000. Anyway,
1087! it is a highly idealized calendar used in analytical test cases or
1088! climatological solutions.
1089!
1090 ELSE IF (int(r_time).eq.-1) THEN ! day 0: Jan 1, 0000
1091 calendar='360_day'
1092 iyear=0
1093 month=12
1094 iday=30
1095 ihour=0
1096 minute=0
1097 isec=0
1098 yday=360
1099 datenumber(1)=359.0_dp
1100 datenumber(2)=datenumber(1)*86400.0_dp
1101!
1102! The model clock is the elapsed time since reference time of the form
1103! 'time-units since 1968-05-23 00:00:00 GMT'. It is a Truncated Julian
1104! day. It has a year length of 365.25 days.
1105!
1106! The one here is known as the Gregorian Calendar. Although, it is a
1107! minor correction to the Julian Calendar after 15 Oct 1582 with a
1108! year length of 365.2425.
1109!
1110 ELSE IF (int(r_time).eq.-2) THEN ! day 0: Nov 24, 4713 BC
1111 calendar='proleptic_julian'
1112 iyear=1968
1113 month=5
1114 iday=23
1115 ihour=0
1116 minute=0
1117 isec=0
1118 yday=yearday(iyear, month, iday)
1119 datenumber(1)=2440000.0_dp ! Truncated offset
1120 datenumber(2)=datenumber(1)*86400.0_dp
1121 END IF
1122!
1123!-----------------------------------------------------------------------
1124! Set reference-time string, YYYY-MM-DD hh:mm:ss
1125!-----------------------------------------------------------------------
1126!
1127 WRITE (string,10) iyear, month, iday, ihour, minute, isec
1128 10 FORMAT (i4.4,'-',i2.2,'-',i2.2,1x,i2.2,':',i2.2,':',i2.2)
1129!
1130!-----------------------------------------------------------------------
1131! Load time reference clock information into structure.
1132!-----------------------------------------------------------------------
1133!
1134 rclock%yday =yday
1135 rclock%year =iyear
1136 rclock%month =month
1137 rclock%day =iday
1138 rclock%hour =ihour
1139 rclock%minutes =minute
1140 rclock%seconds =isec
1141 rclock%base =r_time
1142 rclock%DateNumber(1)=datenumber(1)
1143 rclock%DateNumber(2)=datenumber(2)
1144 rclock%string =string
1145 rclock%calendar =trim(calendar)
1146!
1147 RETURN
1148 END SUBROUTINE ref_clock
1149!
1150!**********************************************************************
1151 SUBROUTINE roms_clock (year, month, day, hour, minutes, seconds, &
1152 & ClockTime)
1153!***********************************************************************
1154! !
1155! Given any date (year, month, day, hour, minute, second), this !
1156! this routine returns ROMS clock time since initialization in !
1157! seconds from reference date. !
1158! !
1159! This clock time is used when importing fields from coupled models. !
1160! It is assumed that coupling applications use Gregorian calendar, !
1161! INT(time_ref) .ge. 0. !
1162! !
1163! On Input: !
1164! !
1165! year The year, including the century (integer) !
1166! month The month, 1=January, 2=February, ... (integer) !
1167! day The day of the month (integer) !
1168! hour The hour of the day (integer) !
1169! minute The minute of the hour (integer) !
1170! seconds The seconds of the minute (real) !
1171! !
1172! On Output: !
1173! !
1174! ClockTime ROMS clock time since initialization in seconds !
1175! from reference time (real) !
1176! !
1177!***********************************************************************
1178!
1179 USE mod_param
1180 USE mod_scalars
1181!
1182! Imported variable declarations.
1183!
1184 integer, intent(in) :: year, month, day, hour, minutes
1185
1186 real(dp), intent(in) :: seconds
1187 real(dp), intent(out) :: clocktime
1188!
1189! Local variable declarations.
1190!
1191 real(dp), dimension(2) :: datenumber
1192!
1193!-----------------------------------------------------------------------
1194! Compute ROMS clock elapsed time since intialization in seconds from
1195! reference time.
1196!-----------------------------------------------------------------------
1197!
1198! Convert requested date into date number.
1199!
1200 CALL datenum (datenumber, year, month, day, &
1201 & hour, minutes, seconds)
1202!
1203! Compute ROMS clock elapsed time in seconds.
1204!
1205 clocktime=datenumber(2)-rclock%DateNumber(2)
1206!
1207 RETURN
1208 END SUBROUTINE roms_clock
1209!
1210!***********************************************************************
1211 SUBROUTINE time_iso8601 (MyTime, date_string)
1212!***********************************************************************
1213! !
1214! This routine encodes current model time in seconds to a date !
1215! string of the form: !
1216! !
1217! YYYY-MM-DDThh:mm:ssZ !
1218! !
1219! On Input: !
1220! !
1221! MyTime Current model time (seconds) !
1222! !
1223! On Output: !
1224! !
1225! date_string Current model time date string (20 charactes). !
1226! !
1227!***********************************************************************
1228!
1229! Imported variable declarations.
1230!
1231 real(dp), intent(in) :: mytime
1232
1233 character (len=20), intent(out) :: date_string
1234!
1235! Local variable declarations.
1236!
1237 integer :: day, hour, minutes, month, year, seconds
1238 integer :: i
1239!
1240 real(dp) :: currenttime
1241!
1242 character (len=20) :: string
1243!
1244!-----------------------------------------------------------------------
1245! Encode current model time.
1246!-----------------------------------------------------------------------
1247!
1248! Convert current model time to calendar date.
1249!
1250 currenttime=mytime/86400.0_dp ! seconds to days
1251!
1252 CALL caldate (currenttime, &
1253 & yy_i=year, &
1254 & mm_i=month, &
1255 & dd_i=day, &
1256 & h_i=hour, &
1257 & m_i=minutes, &
1258 & s_i=seconds)
1259!
1260! Encode calendar date into a string.
1261!
1262 WRITE (string,10) year, month, day, hour, minutes, seconds
1263 10 FORMAT (i4.4,'-',i2.2,'-',i2.2,'T',i2.2,':',i2.2,':',i2.2,'Z')
1264!
1265 date_string=trim(string)
1266!
1267 RETURN
1268 END SUBROUTINE time_iso8601
1269!
1270!***********************************************************************
1271 SUBROUTINE time_string (MyTime, date_string)
1272!***********************************************************************
1273! !
1274! This routine encodes current model time in seconds to a date !
1275! string of the form: !
1276! !
1277! YYYY-MM-DD hh:mm:ss.ss !
1278! !
1279! The decimal seconds (ss.s) are rounded to the next digit. This !
1280! encoding allows an easy to read reporting time. !
1281! !
1282! On Input: !
1283! !
1284! MyTime Current model time (seconds) !
1285! !
1286! On Output: !
1287! !
1288! date_string Current model time date string (22 charactes). !
1289! !
1290!***********************************************************************
1291!
1292! Imported variable declarations.
1293!
1294 real(dp), intent(in) :: mytime
1295
1296 character (len=22), intent(out) :: date_string
1297!
1298! Local variable declarations.
1299!
1300 integer :: day, hour, minutes, month, year
1301 integer :: i
1302
1303 real(dp) :: currenttime, seconds
1304
1305 character (len= 5) :: sec_string
1306 character (len=22) :: string
1307!
1308!-----------------------------------------------------------------------
1309! Encode current model time.
1310!-----------------------------------------------------------------------
1311!
1312! Convert current model time to calendar date.
1313!
1314 currenttime=mytime/86400.0_dp ! seconds to days
1315!
1316 CALL caldate (currenttime, &
1317 & yy_i=year, &
1318 & mm_i=month, &
1319 & dd_i=day, &
1320 & h_i=hour, &
1321 & m_i=minutes, &
1322 & s_dp=seconds)
1323
1324!
1325! Encode fractional seconds to a string. Round to one digit.
1326!
1327 WRITE (sec_string, '(f5.2)') seconds
1328 DO i=1,len(sec_string) ! replace leading
1329 IF (sec_string(i:i).eq.char(32)) THEN ! space(s) with
1330 sec_string(i:i)='0' ! zeros(s)
1331 END IF
1332 END DO
1333!
1334! Encode calendar date into a string.
1335!
1336 WRITE (string,10) year, month, day, hour, minutes, sec_string
1337 10 FORMAT (i4.4,'-',i2.2,'-',i2.2,1x,i2.2,':',i2.2,':',a)
1338!
1339 date_string=trim(string)
1340!
1341 RETURN
1342 END SUBROUTINE time_string
1343!
1344!***********************************************************************
1345 SUBROUTINE time_units (Ustring, year, month, day, &
1346 & hour, minutes, seconds)
1347!***********************************************************************
1348! !
1349! This routine decodes the time units attribute of the form: !
1350! !
1351! 'time-units since YYYY-MM-DD hh:mm:ss' !
1352! 'time-units since YYYY-MM-DD hh:mm:ss.ss' !
1353! !
1354! and various CF compliant variants. !
1355! !
1356! On Input: !
1357! !
1358! U Time attribute (string) !
1359! !
1360! On Output: !
1361! !
1362! year Year including century (integer) !
1363! month Month of the year, 1=Jan, ..., 12=Dec (integer) !
1364! day Day of the month (integer) !
1365! hour Hour of the day (integer) !
1366! minutes Minutes of the hour, 0 - 59 (integer) !
1367! seconds Seconds of the minute (real) !
1368! !
1369! Examples of valid unit attributes: !
1370! !
1371! 'days since 1900-01-01 00:00:00' !
1372! 'seconds since 1992-10-8 15:15:42.5 -6' !
1373! 'hours since 1990-1-1 0:0:0' !
1374! 'days since 1582-10-15 1:30:15' !
1375! 'days since 1-1-1 0:0:0' !
1376! 'hour since 1997-4-30 1:5:30.5' !
1377! 'second since 1961-1-1' !
1378! 'years since -2000-02-29 00:00:0.000Z' !
1379! 'days since 1-07-15 0:0:0' !
1380! 'days since 0000-01-01 0:0:0' !
1381! !
1382!***********************************************************************
1383!
1384! Imported variable declarations.
1385!
1386 integer, intent(out) :: year, month, day, hour, minutes
1387!
1388 real(dp), intent(out) :: seconds
1389!
1390 character (len=*), intent(in) :: ustring
1391!
1392! Exported variable declarations.
1393!
1394 logical :: decode
1395 integer :: i, iblank, ie, is, iscale, lstr, lvar, nval
1396 integer :: schar
1397
1398 real(dp) :: rval(10)
1399
1400 character (len=20) :: vstring
1401 character (LEN(Ustring)) :: tstring
1402!
1403!-----------------------------------------------------------------------
1404! Decode time string attribute.
1405!-----------------------------------------------------------------------
1406!
1407! Initialize.
1408!
1409 year=0
1410 month=0
1411 day=0
1412 hour=0
1413 minutes=0
1414 seconds=0.0_dp
1415!
1416 DO i=1,len(tstring)
1417 tstring(i:i)=char(32) ! blank space
1418 END DO
1419!
1420! Replace non-numeric characters with blanks.
1421!
1422 tstring=adjustl(trim(ustring))
1423 lstr=len_trim(tstring)
1424!
1425! Only the following ASCII charactes are unchanged:
1426!
1427! Char Dec Control Action
1428! ------------------------------
1429! SP 32 Space
1430! + 43 Plus
1431! - 45 Hyphen, dash, minus
1432! . 46 Period
1433! 0 48 Zero
1434! 1 49 One
1435! 2 50 Two
1436! 3 51 Three
1437! 4 52 Four
1438! 5 53 Five
1439! 6 54 Six
1440! 7 55 Seven
1441! 8 56 Eight
1442! 9 57 Nine
1443!
1444 DO i=1,lstr
1445 schar=ichar(tstring(i:i))
1446 IF (.not.(((48.le.schar).and.(schar.le.57)).or. &
1447 & (schar.eq.32).or.(schar.eq.46))) THEN
1448 tstring(i:i)=char(32) ! blank space
1449 END IF
1450 END DO
1451 tstring=adjustl(trim(tstring))
1452 lstr=len_trim(tstring)
1453!
1454! Check for negative year indicating CE, BC or BCE (Common Era, Before
1455! Christ or Before Common Era).
1456!
1457 IF (index(ustring, 'since -').gt.0) THEN
1458 iscale=-1
1459 ELSE
1460 iscale=1
1461 END IF
1462!
1463! Process numrical values. Since CHAR(45) is retained, take the
1464! absolute value except for the first number representing the year.
1465! The year is the only numerical value that can be negative (BC or
1466! BCE.
1467!
1468 is=1
1469 ie=lstr
1470 iblank=0
1471 nval=0
1472 decode=.false.
1473 DO i=1,lstr
1474 IF (tstring(i:i).eq.char(32)) THEN
1475 IF (tstring(i+1:i+1).ne.char(32)) decode=.true.
1476 iblank=i
1477 ELSE
1478 ie=i
1479 END IF
1480 IF (decode.or.(i.eq.lstr)) THEN
1481 nval=nval+1
1482 vstring=tstring(is:ie)
1483 lvar=len_trim(vstring)
1484 READ (vstring(1:lvar),*) rval(nval)
1485 is=iblank+1
1486 ie=lvar
1487 decode=.false.
1488 END IF
1489 END DO
1490!
1491! Load values.
1492!
1493 DO i=1,nval
1494 SELECT CASE (i)
1495 CASE (1)
1496 year=int(rval(i))*iscale
1497 CASE (2)
1498 month=int(rval(i))
1499 CASE (3)
1500 day=int(rval(i))
1501 CASE (4)
1502 hour=int(rval(i))
1503 CASE (5)
1504 minutes=int(rval(i))
1505 CASE (6)
1506 seconds=rval(i)
1507 END SELECT
1508 END DO
1509 RETURN
1510 END SUBROUTINE time_units
1511!
1512!***********************************************************************
1513 INTEGER FUNCTION yearday (year, month, day) RESULT (yday)
1514!***********************************************************************
1515! !
1516! Given any date year, month, and day, this function returns the !
1517! day of the year. !
1518! !
1519! On Input: !
1520! !
1521! year Year including the century (integer; YYYY) !
1522! month Month of the year: 1=January, ... (integer) !
1523! day Day of the month (integer) !
1524! !
1525! On Output: !
1526! !
1527! yday Day of the year (integer) !
1528! !
1529!***********************************************************************
1530!
1531! Imported variable declarations.
1532!
1533 integer, intent(in) :: year, month, day
1534!
1535! Local variable declarations.
1536!
1537 integer :: fac
1538!
1539!-----------------------------------------------------------------------
1540! Compute day of the year.
1541!-----------------------------------------------------------------------
1542!
1543 IF (((mod(year,4).eq.0).and.(mod(year,100).ne.0)).or. &
1544 & (mod(year,400).eq.0)) THEN
1545 fac=1 ! leap year
1546 ELSE
1547 fac=2
1548 END IF
1549 yday=int((275.0*month)/9) - fac*int((month+9)/12) + day - 30
1550
1551 RETURN
1552 END FUNCTION yearday
1553!
1554 END MODULE dateclock_mod
subroutine, public datestr(datenumber, isdayunits, datestring)
Definition dateclock.F:447
subroutine, public time_string(mytime, date_string)
Definition dateclock.F:1272
subroutine, public roms_clock(year, month, day, hour, minutes, seconds, clocktime)
Definition dateclock.F:1153
subroutine, public time_units(ustring, year, month, day, hour, minutes, seconds)
Definition dateclock.F:1347
subroutine, public time_iso8601(mytime, date_string)
Definition dateclock.F:1212
subroutine, public get_date(date_str)
Definition dateclock.F:856
subroutine, public ref_clock(r_time)
Definition dateclock.F:972
subroutine, public caldate(currenttime, yy_i, yd_i, mm_i, dd_i, h_i, m_i, s_i, yd_dp, dd_dp, h_dp, m_dp, s_dp)
Definition dateclock.F:76
integer function, public yearday(year, month, day)
Definition dateclock.F:1514
subroutine, public datevec(datenumber, isdayunits, year, month, day, hour, minutes, seconds, f_minutes, f_hour)
Definition dateclock.F:517
subroutine, public datenum(datenumber, year, month, day, hour, minutes, seconds)
Definition dateclock.F:243
subroutine, public day_code(month, day, year, code)
Definition dateclock.F:752
integer, parameter dp
Definition mod_kinds.F:25
real(dp) time_ref
type(t_clock) rclock
real(dp) function, public round(x, ct)
Definition round.F:65