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
出力はこんな感じ。
参考までに、呼び込み用のルーチンをのせておきます。
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日正午)からの通日を使うプログラマーもいるようです。
今回はこの辺で終ります。
つづく