2023年2月22日水曜日

UNIX時間(2)

UNIX時間(1)からの続き

macOS上のgfortranFortran 95)で,UNIX時間を表示させようとしてつまづいた。

他の言語の場合,対応する組み込み関数が用意されているので簡単に実現できるのだけれど,Fortranの場合,date_and_timeという日時取得関数やsystem_clockというシステム時間取得関数などがあるが,いずれもUNIX時間には対応していない。

ChatGPTやBingChatにきいても,Fortranの場合はシステム依存性があるので処理系によってまちまちだというばかり,macOSではどうかと水を向けても思わしい答えが返ってこない。

仕方がないので,ユーザ関数として定義することにするが,適当な例題がみつからない。C言語の例ならすぐに見つかった。これをChatGPTにFortranに変換してもらったが,うまくいなかい。そもそも,例題のアルゴリズムが,西暦0年3月を起点にしてとか,UNIX日から月の算出にmonth = (unixday * 5 + 2) / 153 を使うとか技巧的すぎるのだ。

そこで,シンプルに,与えられたUNIX時間(秒)を24*60*60=86400秒で割ったUNIX日(日)から毎年の日数を引き算することにした。グレゴリオ暦のある年がうるう年かどうかの判定なら簡単である。これが負になるところが対応する年であり,足し戻したUNIX日から月単位で引き算し,これが負になるところが対応する月である。これを足し戻せば対応する日付にたどりつく。

このプログラムでは,整数を4バイト=32ビットの符号付き整数として扱っているため,2038年問題に引っかかる。ut = (2**15-1)*(2**16+1) +2**15 -9*3600 =2**31-1 -9*3600 に1加えると異常値が出ることを確かめられる。


!------- time.f95 ------- 2023.02.22 ------- K.Koshigiri -------!
!-------+-------+-------+-------+-------+-------+-------+-------!
program time_code
!-------+-------+-------+-------+-------+-------+-------+-------!
  implicit none
  integer :: dtm(8),dtm0(8),ut,ut0,jst; 
  integer(8) :: ut8
  character*10 a,b,c,fmt
!
  interface
    function dtm2ut(dtm)
      integer :: dtm(8),dtm2ut
    end function dtm2ut
    function ut2dtm(ut)
      integer :: ut2dtm(8),ut
    end function ut2dtm
  end interface
!
!現在時刻の取得
  call date_and_time(a,b,c, dtm)
  call system_clock(count=ut)
! call gtime(count=ut)
  print *, ut
  print *, ' '//a(1:4)//'-'//a(5:6)//'-'//a(7:8)
  print *, ' '//b(1:2)//':'//b(3:4)//':'//b(5:10)
  jst = dtm(4) ; print *, c, jst ; write(*, *)
!UNIX時間へ変換
  ut = dtm2ut(dtm)
  write(*,'("unixtime = ",i10)') ut
!年月日時分秒へ変換
  write(*,'(i6,"-",i2,"-",i2,i6,i3,":",i2,":",i2,".",i3)') ut2dtm(ut)
!UNIX基準時(JST)
  dtm0 = [1970,1,1,jst,9,0,0,0]
  write(*,'("unixtime = ",i11)') dtm2ut(dtm0)
  ut0 = 0
  write(*,'(i6,"-",i2,"-",i2,i6,i3,":",i2,":",i2,".",i3)') ut2dtm(ut0)
!2038年問題(JST)
  dtm = [2038,1,19,jst,12,14,7,0]
  write(*,'("unixtime = ",i11)') dtm2ut(dtm)
  ut = (2**15-1)*(2**16+1) +2**15 -9*3600 
  write(*,'(i6,"-",i2,"-",i2,i6,i3,":",i2,":",i2,".",i3)') ut2dtm(ut) 
!
end program time_code
!-------+-------+-------+-------+-------+-------+-------+-------!
function dtm2ut(dtm) result(ut) 
!-------+-------+-------+-------+-------+-------+-------+-------!
! dtm=[y,m,d,dev,hour,min,sec,msec]@JST ->
! unixtime@GMT (formal seconds from 1970-1-1 00:00:00)
  implicit none
  integer :: ut, dtm(8), ud, y, m, d, hour, min, sec
  integer, parameter :: ed = 719468  ! 1970_1_1 <= 1900_1_1
  integer, parameter :: md(13) = &
  [0,31,61,92,122,153,184,214,245,275,306,337,365]
!
  y = dtm(1) ; m = dtm(2) ; d = dtm(3)
  hour = dtm(5) - 9 ; min = dtm(6) ; sec = dtm(7)
  if (m<3) then
    m = m + 12; y = y - 1
  endif
  m = m - 3 ! 0:March
  ud = y*365 + y/4 - y/100 + y/400 + md(m+1) + d - 1 - ed
  ut = (ud * 24 + hour) * 60 + min
  ut = (ut * 60 + sec)  ! return unixtime(s)
end function dtm2ut
!-------+-------+-------+-------+-------+-------+-------+-------!
function ut2dtm(ut) result(dtm)
!-------+-------+-------+-------+-------+-------+-------+-------!
! ut=unixtime to dtm[y,m,d,dev,hour,min,sec,msec] for JST
  implicit none
  integer :: dtm(8), ut, jst, ud, wd, y, m, d, hour, min, sec
  integer, parameter :: md(12) =  [31,28,31,30,31,30,31,31,30,31,30,31]
!
  interface
    function lp(y, m)
      integer :: lp, y, m
    end function lp
  end interface
!
  jst = 9*60
  ut = ut + jst*60
  sec = mod(ut, 60)
  min = mod(ut / 60, 60)
  hour = mod(ut / 3600, 24)
  ud = ut / (24*60*60)
!  wd = mod(ud + 3, 7)
!
  y = 1969
  do while (ud >= 0)
    y = y + 1 ; ud = ud - 365 - lp(y,2)
  end do
  ud = ud + 365 + lp(y,2)
!
  m = 0
  do while (ud >= 0)
    m = m + 1 ; ud = ud - md(m) - lp(y,m)
  end do
  d = ud + md(m) + lp(y,m) + 1
!
  dtm = [y,m,d,jst,hour,min,sec,0]
end function ut2dtm
!-------+-------+-------+-------+-------+-------+-------+-------!
function lp(y,m) result(leap)
!-------+-------+-------+-------+-------+-------+-------+-------!
  implicit none
  integer y, m, leap
!
  leap = 0
  if(m == 2) then
    if( mod(y,4)==0 .and. mod(y,100)/=0 .or. mod(y,400)==0 ) then
      leap = 1
    endif
  endif
end function lp
!-------+-------+-------+-------+-------+-------+-------+-------!


0 件のコメント: