PP#11~Fortran Tips#6 Class Date

PP#11~Fortran Tips#6 Class Date

取りとめのないテーマが続きますが、今回はDate Classの作成です。C/C++ではポピュラーなクラスですが、Fortranでは何故か作成例が少ない。自作したのでご紹介しておきます。

module class_Date
!********************************************************************************
!*  Class Date (Simpler Version)                                                *
!********************************************************************************
!================================================================================ 
!   How to Use:
!       use class_Date
!       Type(Date)::    Today, Tomorrow...           
!================================================================================ 
    implicit none
    public::	    Date,       operator(+),    operator(-),                    &
                    readDate,   printDate
    type Date
        private
        integer::	day,    month,  year
    end type Date
    private
    !leap year                     1  2  3  4  5  6  7  8  9 10 11 12
    integer::	days(12,0:1) = (/ 31,28,31,30,31,30,31,31,30,31,30,31,          &
                				  31,29,31,30,31,30,31,31,30,31,30,31 /)
    character::	mName(13)*3  = (/ "Jan","Feb","Mar","Apr","May","Jun","Jul",    &
                                  "Aug","Sep","Oct","Nov","Dec","non" /)
!================================================================================
    interface operator(+) 
        module procedure  AddDate
    end interface 
    interface operator(-) 
        module procedure  SubDate
    end interface 
!================================================================================
    contains
        function Date_(d, m, y) result (ObjDate)  !public constructor
            integer, intent(in)::	d, m, y
            type (Date):: 			ObjDate
            integer::				mon, day
            mon = m ; day = d
            if (mon < 1 .or. mon > 12) mon = 13
            if (day < 1 .or. day > 31) day =  0
            ObjDate = Date (day, mon, y) 
        end function Date_
        function LeapYear(x) result(y)
            integer, intent(in)::	x
            integer::				y
            if ((mod(x,4) == 0 .and. mod(x,100) /= 0) .or. (mod(x,400) == 0)) then
                y = 1
            else
                y = 0
            endif
        end function
        function AddDate(ObjDate,y) result(SujDate)
            type(Date), intent(in)::    ObjDate
            integer, intent(in)::	    y    !days to be added
            type(Date)::			    SujDate
            !if (y < 0) return   !SubDate(ObjDate, -y)
            SujDate%day   = ObjDate%day + y
            SujDate%month = ObjDate%month
            SujDate%year  = ObjDate%year
            do while (SujDate%day > days(SujDate%month,LeapYear(SujDate%year))) 
                SujDate%day   = SujDate%day - days(SujDate%month,LeapYear(SujDate%year))
                SujDate%month = SujDate%month + 1
                if (SujDate%month > 12) then
                    SujDate%year  = SujDate%year + 1
                    SujDate%month = 1
                endif
            enddo          
        end function
        function SubDate(ObjDate,y) result(SujDate)
            type(Date), intent(in)::	ObjDate
            integer, intent(in)::		y    !days to be subtructed
            type(Date)::				SujDate
            !if (y < 0) return   !AddDate(ObjDate, -y)
            SujDate%day   = ObjDate%day - y
            SujDate%month = ObjDate%month
            SujDate%year  = ObjDate%year
            do while (SujDate%day < 1)
                SujDate%month = SujDate%month - 1
                if (SujDate%month < 1) then
                    SujDate%year  = SujDate%year - 1
                    SujDate%month = 12
                endif
                SujDate%day = SujDate%day + days(SujDate%month,LeapYear(SujDate%year))
            enddo          
        end function
        subroutine readDate(Input,ObjDate)
            type(Date), intent(out)::	ObjDate
            integer,intent(in)::        Input
            read (Input,*) ObjDate%day, ObjDate%month, ObjDate%year
        end subroutine readDate
        subroutine printDate(ObjDate)
            type(Date), intent(in)::	ObjDate
            if (ObjDate%month < 1 .or. ObjDate%month > 12) then
                print *, "Invalid month" 
            else if (ObjDate%day   < 1 .or. ObjDate%day   > 31) then
                print *, "Invalid day  "
            else
                print '(i2," - ",a3," - ",i4)', ObjDate%day,trim(mName(ObjDate%month)), ObjDate%year
            endif
        end subroutine printDate
!================================================================================
end module class_Date

出力はこんな感じ。

call_DateClass

参考までに、呼び込み用のルーチンをのせておきます。

program call_DateClass
!********************************************************************************
!*  call_DateClass                                                           *
!********************************************************************************
    use class_Date
    implicit none    
    integer,parameter:: fin = 5,    fout = 6
    integer::           dTime           
    Type(Date)::        startTime,  elapseTime                 
!================================================================================
        print '(/"Input Date to originate (like 1 7 2000):          "\)'
        call readDate(fin,startTime)
    do
        print '(/"Input Days to add or deduct (0 to exit loop)):    "\)'
        read(fin,*) dTime;  if (dTime == 0) exit
        if (dTime > 0) then
            elapseTime = startTime + abs(dTime)
        else
            elapseTime = startTime - abs(dTime)
        endif
        print '(/"Elapsed time to output:                           "\)'
        call printDate(elapseTime)  
    enddo
    print '(/"Reached end of program")'
!================================================================================
    stop
!================================================================================
end program

余談ですが、暦についてどの程度ご存知ですか。

現在世界的に使われている暦はグレゴリオ暦ですが、それ以前はユリウス・カエサルが制定したユリウス暦が使われていました。

この違いは閏年に関係があります。

ユリウス暦では1年を原則として365日、4年毎に置く閏年を366日として平均年を365.25日としていたが、実際の平均太陽年は365.2422…日と長く、約128年で1日分早まってしまいます。そこで、天文学者らは1年を365.2425日と決めたので、400年間に97回の閏年を置くことで400年間における平均年を3365日 + 97/400 = 365.2425日としたのがグレゴリオ暦です。

上述した閏年の計算コードでは、「西暦の年数が100で割り切れ、且つ400では割り切れない年を平年とし、これ以外の年で西暦年数が4で割り切れる年は閏年」としています。

一見、科学技術計算とは無縁と思われるかもしれませんが、こんなところにも気を使う必要があります。

また、ユリウス暦の基準日時(紀元前4713年1月1日正午)からの通日を使うプログラマーもいるようです。

今回はこの辺で終ります。

つづく

Comments are closed.