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日正午)からの通日を使うプログラマーもいるようです。
今回はこの辺で終ります。
つづく
