PROGRAM NHOUR
C$$$  MAIN PROGRAM DOCUMENTATION BLOCK
C
C MAIN PROGRAM: NHOUR        COMPUTE FORECAST HOUR
C   PRGMMR: IREDELL          ORG: NP23        DATE: 1998-08-18
C
C ABSTRACT: PROGRAM TO COMPUTE FORECAST HOUR
C   GIVEN THE VERIFYING DATE AND THE INITIAL DATE.
C
C PROGRAM HISTORY LOG:
C   95-02-28  IREDELL
C   97-09-22  IREDELL  4-DIGIT YEAR ALLOWED; 2-DIGIT YEAR STANDARDIZED
C   98-03-25  IREDELL  4-DIGIT YEAR FOR ALL DATES.  A 2-DIGIT YEAR WILL
C                      BE INTERPRETED AS A YEAR IN THE FIRST CENTURY
C                      WHICH SHOULD BE ALL RIGHT BEFORE THE YEAR 2000.
C                      STANDARD ERROR WARNINGS WILL BE GIVEN FOR SUCH
C                      DATES UNTIL 1 SEPT 1998 AFTER WHICH NHOUR ABORTS.
C                      THE NEW Y2K-COMPLIANT W3LIB PACKAGE IS USED.
C 1998-08-17  IREDELL  DROP-DEAD DATE RESET TO 1 SEPT 1999
C 1999-04-22  Gilbert  Changed subroutine EXIT(N) to ERREXIT(N) so that
C                      error return values are passed back to the shell
C                      properly.
C 1999-09-02  IREDELL  STANDARDIZED 4-DIGIT YEAR AS IN NDATE
C
C USAGE:      nhour vdate [idate]
C   INPUT ARGUMENT LIST:
C     VDATE    - VERIFYING DATE IN YYYYMMDDHH FORMAT.
C     IDATE    - INITIAL DATE IN YYYYMMDDHH FORMAT.
C                IDATE DEFAULTS TO THE UTC DATE AND HOUR.
C   OUTPUT ARGUMENT LIST:
C     NHOUR    - FORECAST HOUR
C                LEADING ZEROES ADDED TO MAKE IT AT LEAST TWO DIGITS.
C                LEADING MINUS SIGN ADDED IF IDATE COMES AFTER VDATE.
C   EXIT STATES:
C     0      - SUCCESS
C     1      - FAILURE; INVALID ARGUMENT
C     2      - FAILURE; INCORRECT NUMBER OF ARGUMENTS
C
C SUBPROGRAMS CALLED:
C   IARGC           GET NUMBER OF ARGUMENTS
C   GETARG          GET ARGUMENT
C   W3DIFDAT        RETURN A TIME INTERVAL BETWEEN TWO DATES
C   W3PRADAT        FORMAT A DATE AND TIME INTO CHARACTERS
C   W3UTCDAT        RETURN THE UTC DATE AND TIME
C   ERRMSG          WRITE A MESSAGE TO STDERR
C   ERREXIT         EXIT PROGRAM
C
C ATTRIBUTES:
C   LANGUAGE: FORTRAN 90
C
C$$$
      CHARACTER*256 CARG,CFMT
      INTEGER IDAT(8),JDAT(8)
      REAL RINC(5)
      LOGICAL W3VALDAT
C - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
C  CHECK NUMBER OF ARGUMENTS
      NARG=IARGC()
      IF(NARG.LT.1.OR.NARG.GT.2) THEN
        CALL ERRMSG('nhour: Incorrect number of arguments')
        CALL EUSAGE
        CALL ERREXIT(2)
      ENDIF
C - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
C  GET AND CHECK FIRST ARGUMENT (VERIFYING DATE)
      CALL GETARG(1,CARG)
      NCARG=LEN_TRIM(CARG)
      WRITE(CFMT,'("(I",I2,",3I2)")') NCARG-6
      JDAT=0
      READ(CARG,CFMT,IOSTAT=IRET) JDAT(1),JDAT(2),JDAT(3),JDAT(5)
      IF(IRET.NE.0.OR..NOT.W3VALDAT(JDAT)) THEN
        CALL ERRMSG('nhour: Invalid date '//CARG(1:NCARG))
        CALL EUSAGE
        CALL ERREXIT(1)
      ENDIF
C - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
C  GET AND CHECK SECOND ARGUMENT (INITIAL DATE)
      IF(NARG.GE.2) THEN
        CALL GETARG(2,CARG)
        NCARG=LEN_TRIM(CARG)
        WRITE(CFMT,'("(I",I2,",3I2)")') NCARG-6
        IDAT=0
        READ(CARG,CFMT,IOSTAT=IRET) IDAT(1),IDAT(2),IDAT(3),IDAT(5)
        IF(IRET.NE.0.OR..NOT.W3VALDAT(IDAT)) THEN
          CALL ERRMSG('nhour: Invalid date '//CARG(1:NCARG))
          CALL EUSAGE
          CALL ERREXIT(1)
        ENDIF
      ELSE
        CALL W3UTCDAT(IDAT)
      ENDIF
C - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
C  COMPUTE AND PRINT HOUR DIFFERENCE
      CALL W3DIFDAT(JDAT,IDAT,2,RINC)
      IHOUR=NINT(RINC(2))
      NDIG=LOG10(ABS(IHOUR)+0.5)+1
      NDIG=MAX(NDIG,2)
      IF(IHOUR.LT.0) NDIG=NDIG+1
      WRITE(CFMT,'("(I",I2,".2)")') NDIG
      PRINT CFMT,IHOUR
C - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      CONTAINS
C - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
C  WRITE USAGE
      SUBROUTINE EUSAGE
      CALL ERRMSG('Usage: nhour vdate [idate]')
      ENDSUBROUTINE
C - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      ENDPROGRAM
!-----------------------------------------------------------------------
      subroutine w3difdat(jdat,idat,it,rinc)
!$$$   SUBPROGRAM  DOCUMENTATION  BLOCK
!
! SUBPROGRAM: W3DIFDAT       RETURN A TIME INTERVAL BETWEEN TWO DATES
!   AUTHOR: MARK IREDELL     ORG: WP23       DATE: 98-01-05
!
! ABSTRACT: THIS SUBPROGRAM RETURNS THE ELAPSED TIME INTERVAL FROM
!   AN NCEP ABSOLUTE DATE AND TIME GIVEN IN THE SECOND ARGUMENT UNTIL
!   AN NCEP ABSOLUTE DATE AND TIME GIVEN IN THE FIRST ARGUMENT.
!   THE OUTPUT TIME INTERVAL IS IN ONE OF SEVEN CANONICAL FORMS
!   OF THE NCEP RELATIVE TIME INTERVAL DATA STRUCTURE.
!
! PROGRAM HISTORY LOG:
!   98-01-05  MARK IREDELL
!
! USAGE:  CALL W3DIFDAT(JDAT,IDAT,IT,RINC)
!
!   INPUT VARIABLES:
!     JDAT       INTEGER (8) NCEP ABSOLUTE DATE AND TIME
!                (YEAR, MONTH, DAY, TIME ZONE,
!                 HOUR, MINUTE, SECOND, MILLISECOND)
!     IDAT       INTEGER (8) NCEP ABSOLUTE DATE AND TIME
!                (YEAR, MONTH, DAY, TIME ZONE,
!                 HOUR, MINUTE, SECOND, MILLISECOND)
!     IT         INTEGER RELATIVE TIME INTERVAL FORMAT TYPE
!                (-1 FOR FIRST REDUCED TYPE (HOURS ALWAYS POSITIVE),
!                 0 FOR SECOND REDUCED TYPE (HOURS CAN BE NEGATIVE),
!                 1 FOR DAYS ONLY, 2 FOR HOURS ONLY, 3 FOR MINUTES ONLY,
!                 4 FOR SECONDS ONLY, 5 FOR MILLISECONDS ONLY)
!
!   OUTPUT VARIABLES:
!     RINC       REAL (5) NCEP RELATIVE TIME INTERVAL
!                (DAYS, HOURS, MINUTES, SECONDS, MILLISECONDS)
!                (TIME INTERVAL IS POSITIVE IF JDAT IS LATER THAN IDAT.)
!
! SUBPROGRAMS CALLED:
!     IW3JDN         COMPUTE JULIAN DAY NUMBER     
!     W3REDDAT       REDUCE A TIME INTERVAL TO A CANONICAL FORM
!
! ATTRIBUTES:
!   LANGUAGE: FORTRAN 90
!
!$$$
      integer jdat(8),idat(8)
      real rinc(5)
      real rinc1(5)
! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
!  difference the days and time and put into canonical form
      rinc1(1)=iw3jdn(jdat(1),jdat(2),jdat(3))-
     &         iw3jdn(idat(1),idat(2),idat(3))
      rinc1(2:5)=jdat(5:8)-idat(5:8)
      call w3reddat(it,rinc1,rinc)
! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      end
       FUNCTION IW3JDN(IYEAR,MONTH,IDAY)
C$$$   SUBPROGRAM  DOCUMENTATION  BLOCK
C
C SUBPROGRAM: IW3JDN         COMPUTE JULIAN DAY NUMBER
C   AUTHOR: JONES,R.E.       ORG: W342       DATE: 87-03-29
C
C ABSTRACT: COMPUTES JULIAN DAY NUMBER FROM YEAR (4 DIGITS), MONTH,
C   AND DAY. IW3JDN IS VALID FOR YEARS 1583 A.D. TO 3300 A.D.
C   JULIAN DAY NUMBER CAN BE USED TO COMPUTE DAY OF WEEK, DAY OF
C   YEAR, RECORD NUMBERS IN AN ARCHIVE, REPLACE DAY OF CENTURY,
C   FIND THE NUMBER OF DAYS BETWEEN TWO DATES.
C
C PROGRAM HISTORY LOG:
C   87-03-29  R.E.JONES
C   89-10-25  R.E.JONES   CONVERT TO CRAY CFT77 FORTRAN
C
C USAGE:   II = IW3JDN(IYEAR,MONTH,IDAY)
C
C   INPUT VARIABLES:
C     NAMES  INTERFACE DESCRIPTION OF VARIABLES AND TYPES
C     ------ --------- -----------------------------------------------
C     IYEAR  ARG LIST  INTEGER   YEAR           ( 4 DIGITS)
C     MONTH  ARG LIST  INTEGER   MONTH OF YEAR   (1 - 12)
C     IDAY   ARG LIST  INTEGER   DAY OF MONTH    (1 - 31)
C
C   OUTPUT VARIABLES:
C     NAMES  INTERFACE DESCRIPTION OF VARIABLES AND TYPES
C     ------ --------- -----------------------------------------------
C     IW3JDN FUNTION   INTEGER   JULIAN DAY NUMBER
C                      JAN. 1,1960 IS JULIAN DAY NUMBER 2436935
C                      JAN. 1,1987 IS JULIAN DAY NUMBER 2446797
C
C   REMARKS: JULIAN PERIOD WAS DEVISED BY JOSEPH SCALIGER IN 1582.
C     JULIAN DAY NUMBER #1 STARTED ON JAN. 1,4713 B.C. THREE MAJOR
C     CHRONOLOGICAL CYCLES BEGIN ON THE SAME DAY. A 28-YEAR SOLAR
C     CYCLE, A 19-YEAR LUNER CYCLE, A 15-YEAR INDICTION CYCLE, USED
C     IN ANCIENT ROME TO REGULATE TAXES. IT WILL TAKE 7980 YEARS
C     TO COMPLETE THE PERIOD, THE PRODUCT OF 28, 19, AND 15.
C     SCALIGER NAMED THE PERIOD, DATE, AND NUMBER AFTER HIS FATHER
C     JULIUS (NOT AFTER THE JULIAN CALENDAR). THIS SEEMS TO HAVE
C     CAUSED A LOT OF CONFUSION IN TEXT BOOKS. SCALIGER NAME IS
C     SPELLED THREE DIFFERENT WAYS. JULIAN DATE AND JULIAN DAY
C     NUMBER ARE INTERCHANGED. A JULIAN DATE IS USED BY ASTRONOMERS
C     TO COMPUTE ACCURATE TIME, IT HAS A FRACTION. WHEN TRUNCATED TO
C     AN INTEGER IT IS CALLED AN JULIAN DAY NUMBER. THIS FUNCTION
C     WAS IN A LETTER TO THE EDITOR OF THE COMMUNICATIONS OF THE ACM
C     VOLUME 11 / NUMBER 10 / OCTOBER 1968. THE JULIAN DAY NUMBER
C     CAN BE CONVERTED TO A YEAR, MONTH, DAY, DAY OF WEEK, DAY OF
C     YEAR BY CALLING SUBROUTINE W3FS26.
C
C ATTRIBUTES:
C   LANGUAGE: CRAY CFT77 FORTRAN
C   MACHINE:  CRAY Y-MP8/864, CRAY Y-MP EL2/256
C
C$$$
C
       IW3JDN  =    IDAY - 32075
     &            + 1461 * (IYEAR + 4800 + (MONTH - 14) / 12) / 4
     &            + 367 * (MONTH - 2 - (MONTH -14) / 12 * 12) / 12
     &            - 3 * ((IYEAR + 4900 + (MONTH - 14) / 12) / 100) / 4
       RETURN
       END
      subroutine w3reddat(it,rinc,dinc)
!$$$   SUBPROGRAM  DOCUMENTATION  BLOCK
!
! SUBPROGRAM: W3REDDAT       REDUCE A TIME INTERVAL TO A CANONICAL FORM
!   AUTHOR: MARK IREDELL     ORG: WP23       DATE: 98-01-05
!
! ABSTRACT: THIS SUBPROGRAM REDUCES AN NCEP RELATIVE TIME INTERVAL
!   INTO ONE OF SEVEN CANONICAL FORMS, DEPENDING ON THE INPUT IT VALUE.
!
!   First reduced format type (IT=-1):
!        RINC(1) is an arbitrary integer.
!        RINC(2) is an integer between 00 and 23, inclusive.
!        RINC(3) is an integer between 00 and 59, inclusive.
!        RINC(4) is an integer between 00 and 59, inclusive.
!        RINC(5) is an integer between 000 and 999, inclusive.
!      If RINC(1) is negative, then the time interval is negative.
!    
!   Second reduced format type (IT=0):
!      If the time interval is not negative, then the format is:
!        RINC(1) is zero or a positive integer. 
!        RINC(2) is an integer between 00 and 23, inclusive.
!        RINC(3) is an integer between 00 and 59, inclusive.
!        RINC(4) is an integer between 00 and 59, inclusive.
!        RINC(5) is an integer between 000 and 999, inclusive.
!      Otherwise if the time interval is negative, then the format is:
!        RINC(1) is zero or a negative integer. 
!        RINC(2) is an integer between 00 and -23, inclusive.
!        RINC(3) is an integer between 00 and -59, inclusive.
!        RINC(4) is an integer between 00 and -59, inclusive.
!        RINC(5) is an integer between 000 and -999, inclusive.
!    
!   Days format type (IT=1):
!        RINC(1) is arbitrary.
!        RINC(2) is zero.
!        RINC(3) is zero.
!        RINC(4) is zero.
!        RINC(5) is zero.
!    
!   Hours format type (IT=2):
!        RINC(1) is zero.
!        RINC(2) is arbitrary.
!        RINC(3) is zero.
!        RINC(4) is zero.
!        RINC(5) is zero.
!      (This format should not express time intervals longer than 300 years.)
!    
!   Minutes format type (IT=3):
!        RINC(1) is zero.
!        RINC(2) is zero.
!        RINC(3) is arbitrary.
!        RINC(4) is zero.
!        RINC(5) is zero.
!      (This format should not express time intervals longer than five years.)
!    
!   Seconds format type (IT=4):
!        RINC(1) is zero.
!        RINC(2) is zero.
!        RINC(3) is zero.
!        RINC(4) is arbitrary.
!        RINC(5) is zero.
!      (This format should not express time intervals longer than one month.)
!    
!   Milliseconds format type (IT=5):
!        RINC(1) is zero.
!        RINC(2) is zero.
!        RINC(3) is zero.
!        RINC(4) is zero.
!        RINC(5) is arbitrary.
!     (This format should not express time intervals longer than one hour.)
!
! PROGRAM HISTORY LOG:
!   98-01-05  MARK IREDELL
!
! USAGE:  CALL W3REDDAT(IT,RINC,DINC)
!
!   INPUT VARIABLES:
!     IT         INTEGER RELATIVE TIME INTERVAL FORMAT TYPE
!                (-1 FOR FIRST REDUCED TYPE (HOURS ALWAYS POSITIVE),
!                 0 FOR SECOND REDUCED TYPE (HOURS CAN BE NEGATIVE),
!                 1 FOR DAYS ONLY, 2 FOR HOURS ONLY, 3 FOR MINUTES ONLY,
!                 4 FOR SECONDS ONLY, 5 FOR MILLISECONDS ONLY)
!     RINC       REAL (5) NCEP RELATIVE TIME INTERVAL
!                (DAYS, HOURS, MINUTES, SECONDS, MILLISECONDS)
!
!   OUTPUT VARIABLES:
!     DINC       REAL (5) NCEP RELATIVE TIME INTERVAL
!                (DAYS, HOURS, MINUTES, SECONDS, MILLISECONDS)
!
! SUBPROGRAMS CALLED:
!
! ATTRIBUTES:
!   LANGUAGE: FORTRAN 90
!
!$$$
      real rinc(5),dinc(5)
!  parameters for number of units in a day
!  and number of milliseconds in a unit
!  and number of next smaller units in a unit, respectively
      integer,dimension(5),parameter:: itd=(/1,24,1440,86400,86400000/),
     &                                 itm=itd(5)/itd
c      integer,dimension(4),parameter:: itn=itd(2:5)/itd(1:4)
      integer itn(4)
      integer,parameter:: np=16
      integer iinc(4),jinc(5),kinc(5)

      itn(1) = itd(2)/itd(1)
      itn(2) = itd(3)/itd(2)
      itn(3) = itd(4)/itd(3)
      itn(4) = itd(5)/itd(4)

c      print *,'itn(1)= ',itn(1)
c      print *,'itn(2)= ',itn(2)
c      print *,'itn(3)= ',itn(3)
c      print *,'itn(4)= ',itn(4)

! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
!  first reduce to the first reduced form
      iinc=floor(rinc(1:4))
!  convert all positive fractional parts to milliseconds
!  and determine canonical milliseconds
      jinc(5)=nint(dot_product(rinc(1:4)-iinc,real(itm(1:4)))+rinc(5))
      kinc(5)=modulo(jinc(5),itn(4))
!  convert remainder to seconds and determine canonical seconds
      jinc(4)=iinc(4)+(jinc(5)-kinc(5))/itn(4)
      kinc(4)=modulo(jinc(4),itn(3))
!  convert remainder to minutes and determine canonical minutes
      jinc(3)=iinc(3)+(jinc(4)-kinc(4))/itn(3)
      kinc(3)=modulo(jinc(3),itn(2))
!  convert remainder to hours and determine canonical hours
      jinc(2)=iinc(2)+(jinc(3)-kinc(3))/itn(2)
      kinc(2)=modulo(jinc(2),itn(1))
!  convert remainder to days and compute milliseconds of the day
      kinc(1)=iinc(1)+(jinc(2)-kinc(2))/itn(1)
      ms=dot_product(kinc(2:5),itm(2:5))
! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
!  next reduce to either single value canonical form
!  or to one of the two reduced forms
      if(it.ge.1.and.it.le.5) then
!  ensure that exact multiples of 1./np are expressed exactly
!  (other fractions may have precision errors)
        rp=(np*ms)/itm(it)+mod(np*ms,itm(it))/real(itm(it))
        dinc=0
        dinc(it)=real(kinc(1))*itd(it)+rp/np
      else
!  the reduced form is done except the second reduced form is modified
!  for negative time intervals with fractional days
        dinc=kinc
        if(it.eq.0.and.kinc(1).lt.0.and.ms.gt.0) then
          dinc(1)=dinc(1)+1
          dinc(2:5)=mod(ms-itm(1),itm(1:4))/itm(2:5)
        endif
      endif
! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      end
!-----------------------------------------------------------------------
      logical function w3valdat(idat)
!$$$   SUBPROGRAM  DOCUMENTATION  BLOCK
!
! SUBPROGRAM: W3VALDAT       DETERMINE THE VALIDITY OF A DATE AND TIME
!   AUTHOR: MARK IREDELL     ORG: WP23       DATE: 98-01-05
!
! ABSTRACT: THIS LOGICAL FUNCTION RETURNS TRUE IF THE INPUT IS A VALID
!   NCEP ABSOLUTE DATE AND TIME.
!
! PROGRAM HISTORY LOG:
!   98-01-05  MARK IREDELL
!
! USAGE:  ...=W3VALDAT(IDAT)
!
!   INPUT VARIABLES:
!     IDAT       INTEGER (8) NCEP ABSOLUTE DATE AND TIME
!                (YEAR, MONTH, DAY, TIME ZONE,
!                 HOUR, MINUTE, SECOND, MILLISECOND)
!
!   OUTPUT VARIABLES:
!     W3VALDAT   LOGICAL TRUE IF IDAT IS A VALID NCEP DATE AND TIME
!
! SUBPROGRAMS CALLED:
!     IW3JDN         COMPUTE JULIAN DAY NUMBER     
!     W3FS26         YEAR, MONTH, DAY FROM JULIAN DAY NUMBER
!     W3REDDAT       REDUCE A TIME INTERVAL TO A CANONICAL FORM
!
! ATTRIBUTES:
!   LANGUAGE: FORTRAN 90
!
!$$$
      integer idat(8)
      real rinc1(5),rinc2(5)
      integer jdat(8)
! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
!  essentially move the date and time by a zero time interval
!  and see if the same date and time is returned
      rinc1(1)=0
      rinc1(2:5)=idat(5:8)
      call w3reddat(-1,rinc1,rinc2)
      jldayn=iw3jdn(idat(1),idat(2),idat(3))+nint(rinc2(1))
      call w3fs26(jldayn,jdat(1),jdat(2),jdat(3),jdow,jdoy)
!  the time zone is valid if it is in signed hhmm format
!  with hh between -23 and 23 and mm equal to 00 or 30
      jdat(4)=mod(idat(4)/100,24)*100+mod(mod(idat(4),100),60)/30*30
      jdat(5:8)=nint(rinc2(2:5))
      w3valdat=all(idat.eq.jdat)
! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      end
!-----------------------------------------------------------------------
      subroutine w3utcdat(idat)
!$$$   SUBPROGRAM  DOCUMENTATION  BLOCK
!
! SUBPROGRAM: W3UTCDAT       RETURN THE UTC DATE AND TIME
!   AUTHOR: MARK IREDELL     ORG: WP23       DATE: 98-01-05
!
! ABSTRACT: THIS SUBPROGRAM RETURNS THE UTC (GREENWICH) DATE AND TIME
!   IN THE NCEP ABSOLUTE DATE AND TIME DATA STRUCTURE.
!
! PROGRAM HISTORY LOG:
!   98-01-05  MARK IREDELL
! 1999-04-28  Gilbert         - added a patch to check for the proper
!                               UTC offset.  Needed until the IBM bug
!                               in date_and_time is fixed.  The patch
!                               can then be removed.  See comments in
!                               the section blocked with "&&&&&&&&&&&".
! 1999-08-12  Gilbert         - Changed so that czone variable is saved
!                               and the system call is only done for
!                               first invocation of this routine.
!
! USAGE:  CALL W3UTCDAT(IDAT)
!
!   OUTPUT VARIABLES:
!     IDAT       INTEGER (8) NCEP ABSOLUTE DATE AND TIME
!                (YEAR, MONTH, DAY, TIME ZONE,
!                 HOUR, MINUTE, SECOND, MILLISECOND)
!
! SUBPROGRAMS CALLED:
!     DATE_AND_TIME  FORTRAN 90 SYSTEM DATE INTRINSIC
!     IW3JDN         COMPUTE JULIAN DAY NUMBER     
!     W3FS26         YEAR, MONTH, DAY FROM JULIAN DAY NUMBER
!
! ATTRIBUTES:
!   LANGUAGE: FORTRAN 90
!
!$$$
      integer idat(8)
      character cdate*8,ctime*10,czone*5
! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
!  get local date and time but use the character time zone
      call date_and_time(cdate,ctime,czone,idat)
      read(czone,'(i5)') idat(4)
! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
!  convert to hours and minutes to UTC time
!  and possibly adjust the date as well
      idat(6)=idat(6)-mod(idat(4),100)
      idat(5)=idat(5)-idat(4)/100
      idat(4)=0
      if(idat(6).lt.00) then
        idat(6)=idat(6)+60
        idat(5)=idat(5)-1
      elseif(idat(6).ge.60) then
        idat(6)=idat(6)-60
        idat(5)=idat(5)+1
      endif
      if(idat(5).lt.00) then
        idat(5)=idat(5)+24
        jldayn=iw3jdn(idat(1),idat(2),idat(3))-1
        call w3fs26(jldayn,idat(1),idat(2),idat(3),idaywk,idayyr)
      elseif(idat(5).ge.24) then
        idat(5)=idat(5)-24
        jldayn=iw3jdn(idat(1),idat(2),idat(3))+1
        call w3fs26(jldayn,idat(1),idat(2),idat(3),idaywk,idayyr)
      endif
! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      end
       SUBROUTINE W3FS26(JLDAYN,IYEAR,MONTH,IDAY,IDAYWK,IDAYYR)
C$$$   SUBPROGRAM  DOCUMENTATION  BLOCK
C
C SUBPROGRAM: W3FS26         YEAR, MONTH, DAY FROM JULIAN DAY NUMBER
C   AUTHOR: JONES,R.E.       ORG: W342       DATE: 87-03-29
C
C ABSTRACT: COMPUTES YEAR (4 DIGITS), MONTH, DAY, DAY OF WEEK, DAY
C   OF YEAR FROM JULIAN DAY NUMBER. THIS SUBROUTINE WILL WORK
C   FROM 1583 A.D. TO 3300 A.D.
C
C PROGRAM HISTORY LOG:
C   87-03-29  R.E.JONES
C   89-10-25  R.E.JONES   CONVERT TO CRAY CFT77 FORTRAN
C
C USAGE:  CALL W3FS26(JLDAYN,IYEAR,MONTH,IDAY,IDAYWK,IDAYYR)
C
C   INPUT VARIABLES:
C     NAMES  INTERFACE DESCRIPTION OF VARIABLES AND TYPES
C     ------ --------- -----------------------------------------------
C     JLDAYN ARG LIST  INTEGER   JULIAN DAY NUMBER
C
C   OUTPUT VARIABLES:
C     NAMES  INTERFACE DESCRIPTION OF VARIABLES AND TYPES
C     ------ --------- -----------------------------------------------
C     IYEAR  ARG LIST  INTEGER   YEAR  (4 DIGITS)
C     MONTH  ARG LIST  INTEGER   MONTH
C     IDAY   ARG LIST  INTEGER   DAY
C     IDAYWK ARG LIST  INTEGER   DAY OF WEEK (1 IS SUNDAY, 7 IS SAT)
C     IDAYYR ARG LIST  INTEGER   DAY OF YEAR (1 TO 366)
C
C   REMARKS: A JULIAN DAY NUMBER CAN BE COMPUTED BY USING ONE OF THE
C     FOLLOWING STATEMENT FUNCTIONS. A DAY OF WEEK CAN BE COMPUTED
C     FROM THE JULIAN DAY NUMBER. A DAY OF YEAR CAN BE COMPUTED FROM
C     A JULIAN DAY NUMBER AND YEAR.
C
C      IYEAR (4 DIGITS)
C
C      JDN(IYEAR,MONTH,IDAY) = IDAY - 32075
C    &            + 1461 * (IYEAR + 4800 + (MONTH - 14) / 12) / 4
C    &            + 367 * (MONTH - 2 - (MONTH -14) / 12 * 12) / 12
C    &            - 3 * ((IYEAR + 4900 + (MONTH - 14) / 12) / 100) / 4
C
C      IYR (4 DIGITS) , IDYR(1-366) DAY OF YEAR
C
C      JULIAN(IYR,IDYR) = -31739 + 1461 * (IYR + 4799) / 4
C    &                    -3 * ((IYR + 4899) / 100) / 4 + IDYR
C
C      DAY OF WEEK FROM JULIAN DAY NUMBER, 1 IS SUNDAY, 7 IS SATURDAY.
C
C      JDAYWK(JLDAYN) = MOD((JLDAYN + 1),7) + 1
C
C      DAY OF YEAR FROM JULIAN DAY NUMBER AND 4 DIGIT YEAR.
C
C      JDAYYR(JLDAYN,IYEAR) = JLDAYN -
C     &  (-31739+1461*(IYEAR+4799)/4-3*((IYEAR+4899)/100)/4)
C
C      THE FIRST FUNCTION WAS IN A LETTER TO THE EDITOR COMMUNICATIONS
C      OF THE ACM  VOLUME 11 / NUMBER 10 / OCTOBER, 1968. THE 2ND
C      FUNCTION WAS DERIVED FROM THE FIRST. THIS SUBROUTINE WAS ALSO
C      INCLUDED IN THE SAME LETTER. JULIAN DAY NUMBER 1 IS
C      JAN 1,4713 B.C. A JULIAN DAY NUMBER CAN BE USED TO REPLACE A
C      DAY OF CENTURY, THIS WILL TAKE CARE OF THE DATE PROBLEM IN
C      THE YEAR 2000, OR REDUCE PROGRAM CHANGES TO ONE LINE CHANGE
C      OF 1900 TO 2000. JULIAN DAY NUMBERS CAN BE USED FOR FINDING
C      RECORD NUMBERS IN AN ARCHIVE OR DAY OF WEEK, OR DAY OF YEAR.
C
C ATTRIBUTES:
C   LANGUAGE: CRAY CFT77 FORTRAN
C   MACHINE:  CRAY Y-MP8/864
C
C$$$
C
       L      = JLDAYN + 68569
       N      = 4 * L / 146097
       L      = L - (146097 * N + 3) / 4
       I      = 4000 * (L + 1) / 1461001
       L      = L - 1461 * I / 4 + 31
       J      = 80 * L / 2447
       IDAY   = L - 2447 * J / 80
       L      = J / 11
       MONTH  = J + 2 - 12 * L
       IYEAR  = 100 * (N - 49) + I + L
       IDAYWK = MOD((JLDAYN + 1),7) + 1
       IDAYYR = JLDAYN -
     &  (-31739 +1461 * (IYEAR+4799) / 4 - 3 * ((IYEAR+4899)/100)/4)
       RETURN
       END
C-----------------------------------------------------------------------
      SUBROUTINE ERRMSG(CMSG)
C$$$  SUBPROGRAM DOCUMENTATION BLOCK
C
C SUBPROGRAM: ERRMSG         WRITE A MESSAGE TO STDERR
C   PRGMMR: IREDELL          ORG: W/NMC23     DATE: 95-10-31
C
C ABSTRACT: WRITE A MESSAGE TO STDERR.
C
C PROGRAM HISTORY LOG:
C   95-10-31  IREDELL
C
C USAGE:    CALL ERRMSG(CMSG)
C   INPUT ARGUMENTS:
C     CMSG         CHARACTER*(*) MESSAGE TO WRITE
C
C REMARKS: THIS IS A MACHINE-DEPENDENT SUBPROGRAM.
C
C ATTRIBUTES:
C   LANGUAGE: FORTRAN
C   MACHINE:  CRAY
C
C$$$
      CHARACTER*(*) CMSG
C - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      WRITE(0,'(A)') CMSG
C - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      RETURN
      END
      SUBROUTINE ERREXIT(IRET)
C$$$  SUBPROGRAM DOCUMENTATION  BLOCK
C                .      .    .                                       .
C SUBPROGRAM:  ERREXIT       EXIT WITH A RETURN CODE
C   PRGMMR: IREDELL          ORG: NP23        DATE:1998-06-04
C
C ABSTRACT: EXIT WITH A RETURN CODE
C
C PROGRAM HISTORY LOG:
C   1998-06-04  IREDELL
C   1999-01-26  Gilbert     - changed to use XLF utility routine exit_(n)
C                             instead of exit(n).  exit_(n) will return
C                             the proper value ( n must be 4 byte int )
C                             to the sh/ksh shell status variable $?
C                             ( $status for csh ) on the IBM SP.
C
C USAGE:    CALL ERREXIT(IRET)
C   INPUT ARGUMENT LIST:
C     IRET     - INTEGER RETURN CODE
C
C SUBPROGRAMS CALLED:
C   EXIT_      - EXITS FROM A FORTRAN PROGRAM
C
C ATTRIBUTES:
C   LANGUAGE: XLF FORTRAN 90
C   MACHINE: IBM SP
C
C$$$
      INTEGER IRET
      INTEGER(4) JRET
      JRET=IRET
      stop 95
      END