I have a date in "YYYY-MM-DD HH:mm:ss" format with the 19 characters in an integer array. Is there an easy way to add one day to this?
What do you mean by "add 1 day"? Exactly 24 hours or the next day with the same hour because the DST changes will be a different problem .
there is no "easy way". There are many different time date functions and third party code. One way is:
1) parse the date/time string into integer value for day, year etc
2) use packtimeqq (ifport) to get seconds since start of 1970
3) add 24*60*60 seconds
3) use unpacktimeqq to get new day, month etc
4) re make a date a date string in your chosen format
I use all my own routines but you need to be aware of leaps years etc
Thanks for the reply.
Darn. unpacktimeqq would be a big help, but for this application it's still not worth that much effort. I looked at m_time, too, but it would still be too big of a job. I was hoping for a module that implements a Date data type similar to visual basic.
It's the pleasure of Fortran. You have to write your own set of utility functions. Then you can use and reuse it in all of your projects and it will growth with time and experience. And the big advantage is that you have not to wait for an hypothetical fix of bugs, fix them yourself.
There are many different solutions for this kind of date/time manipulation. An elegant solution is based on the module at https://wavebitscientific.github.io/datetime-fortran/ . The following code demonstrates how to add 1 day to today's date and print the result in the same string format as you specified.
program xdatetime implicit none character(19) :: dtstr dtstr = '03:31:2020 07:32:15' call addAday(dtstr) print '(1x,A)',dtstr end program subroutine addAday(dtstr) ! Add 1 day to date-time represented in string dtstr use datetime_module implicit none character(19), intent(in out) :: dtstr type(datetime) :: start, nextday type(timedelta) :: dt integer iy,imo,id,ih,im,is read(dtstr,'(i2,1x,i2,1x,i4,1x,i2,1x,i2,1x,i2)')imo,id,iy,ih,im,is ! parse fields start = datetime(); ! default initialization start = datetime(iy,imo,id,ih,im,is) ! put new values into structure dt = timedelta() ! Initialize to zero deltas dt = timedelta(1,0,0,0,0) ! 1 day, 0 hours and 0 minutes nextday = datetime() ! Initialize to default values nextday = start + dt ! new date-time = old date-time + 1 day difference dtstr = nextday%strftime('%m:%d:%Y %H:%M:%S') return end subroutine
The output from the program:
Thanks, mecej4. Someone(s) must have spent a lot of effort developing that code.
I tried to build your sample program using visual studio and got the following linker error. I looked in datetime.f90 at the strptime function, and I don't see what the linker is complaining about. Did you run it with Intel Fortran or some other fortran?
Error LNK2019: unresolved external symbol _strptime referenced in function _MOD_DATETIME_mp_STRPTIME mod_datetime.obj
You need to include in your build the C++ source file strptime.cpp, which is in a different directory in the same distribution. See the very last file in the directory listing at https://github.com/wavebitscientific/datetime-fortran .
Also, if your date string is coming in from an external source, you will have to adapt your input conversion to handle cases where there is not leading zero. Worst case might be ...3:1:2020 7:32:15 (m:d:yyyy h:mm:ss)
Even when told "the input format is this way..." add exception code to your program. It is much easier to deal with potential problems now, rather than later should your program be in use by (many) other people.
Also, data may be submitted from different geographical areas where they submit dd:mm instead of mm:dd
Hmmm. cpp and f90 files in the same project? This is uncharted water for me. I created strptime.cpp containing that code, and added it to the fortran project in visual studio. Not surprisingly, I still get the same linker error because the c++ file isn't getting compiled. What am I missing? Do i need a custom build step to compile the cpp file? My Intel license includes clang++.exe and clang-cl.exe in the Intel bin folder.
Jim - in this application the input string will always be 19 characters. So I believe the leading zero, like 03 for march, will always be there. But I should double check that to make sure.
Compile the C++ source with the command
cl /O2 /MD /c /EHsc strptime.cpp
Then add the resulting file strptime.obj to your link line. There are many other ways, as well, but this one is quite simple.
Thanks. I got the sample program working with /libs:dll but get several linker errors with /libs:static. What am I missing? Again, I'm building with Visual Studio. Do I need to specify an Additional Library Directory? I tried adding C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\lib\x86\ where msvcrt.lib is located, but that didn't help.
Error error LNK2019: unresolved external symbol __except_handler4_common referenced in function __except_handler4 MSVCRT.lib(chandler4gs.obj) Error fatal error LNK1120: 2 unresolved externals Release\DateTime.exe Error error LNK2019: unresolved external symbol __imp__setlocale referenced in function _strptime strptime.obj
Here is a simpler solution, using only Fortran. Please modify to suit the format used for dates and times in the data file.
The derived type datetype in this library does not provide for seconds and milliseconds.
program xaddday character(19) :: dtstr dtstr = '03:31:2020 11:56:45' ! US convention: mm:dd:yyyy hh:MM:ss call addAday(dtstr) print '(1x,A)',dtstr end program subroutine addAday(dtstr) ! Add 1 day to date-time represented in string dtstr use libdate ! http://flibs.sourceforge.net/libdate.html implicit none character(19), intent(in out) :: dtstr type(datetype) :: adate, delta, bdate integer iy,imo,id,ih,im read(dtstr,'(i2,1x,i2,1x,i4,1x,i2,1x,i2)')imo,id,iy,ih,im ! parse fields adate = datetype(iy,imo,id,ih,im) delta = datetype(0,0,1,0,0) ! 1 day bdate = adate + delta call format_date(bdate,'mm:dd:yyyy HH:MM',dtstr(:16)) return end subroutine
Windows filetime structure is an INTEGER(8), stored as two INTEGER(4).
It is a count of 100 ns from 01/01/1601 00:00:00. Using this, time manipulation becomes quite easy.
module Tfunk use kernel32 , only: T_SYSTEMTIME implicit none integer, parameter:: T_time=8 integer, parameter:: T_dataindex=8 integer(T_time),parameter:: Second=10000000_8 contains integer(8) function I8TimeFromSystemTime(systemtime) use kernel32, only: T_filetime, T_systemtime, SystemTimeToFiletime implicit none type(T_systemtime),intent(in):: systemtime type(T_filetime)::filetime integer(T_time):: I8time integer::iret pointer(locI8time,I8time) iret=SystemTimeToFiletime(systemtime,filetime) locI8time=loc(filetime) I8TimeFromSystemTime=I8time end function I8TimeFromSystemTime function SystemtimeFromI8Time(I8time) use kernel32, only: T_filetime,T_systemtime,FileTimeToSystemTime implicit none type(T_systemtime)::SystemtimeFromI8Time integer(8),intent(in):: I8Time type(T_filetime)::filetime type(T_systemtime):: systemtime pointer(locft,filetime) integer:: iret locft=loc(I8Time) iret=FileTimeToSystemTime(filetime,systemtime) SystemtimeFromI8Time=systemtime end function SystemtimeFromI8Time integer(8) function GetSystemtimeAsI8time() use kernel32, only: T_filetime,T_systemtime,GetSystemtimeAsFiletime integer(8):: i8time pointer(LocI8time,i8time) type(T_filetime):: ftime call GetSystemTimeAsFileTime(ftime) LocI8time=loc(ftime) GetSystemtimeAsI8time=i8time end function GetSystemtimeAsI8time integer(8) function GetLocaltimeAsI8time() use kernel32, only: T_filetime,T_systemtime,GetSystemtimeAsFiletime,FileTimeToLocalFileTime integer:: iret integer(8):: i8time pointer(LocI8time,i8time) type(T_filetime):: ftime,ltime call GetSystemTimeAsFileTime(ftime) iret=FileTimeToLocalFileTime( ftime,ltime) LocI8time=loc(ltime) GetLocaltimeAsI8time=i8time end function GetLocaltimeAsI8time end module tfunk program main use Tfunk implicit none ! T_systemtime is defined in ifwinty.f90 (kernel32): ! TYPE T_SYSTEMTIME ! SEQUENCE ! integer(WORD) wYear ! knowns WORD ! integer(WORD) wMonth ! knowns WORD ! integer(WORD) wDayOfWeek ! knowns WORD ! integer(WORD) wDay ! knowns WORD ! integer(WORD) wHour ! knowns WORD ! integer(WORD) wMinute ! knowns WORD ! integer(WORD) wSecond ! knowns WORD ! integer(WORD) wMilliseconds ! knowns WORD ! END TYPE Integer(T_Time):: i8time write(*,*) "T_time=0 at ", SystemtimeFromI8Time(0_8) write(*,*) write(*,*) "Local time is:" i8time=GetLocaltimeAsI8time() write(*,*) SystemtimeFromI8Time(I8time) write(*,*) Write(*,*) "Add 24h" I8time=I8time + 24_8*3600_8*second write(*,*) SystemtimeFromI8Time(I8time) Write(*,*) 'Press enter to close window' read(*,*) end
Fantastic! I like this libdate module a lot. I do not need to handle seconds or milliseconds. Before I started this thread I searched with Google for fortran date handling routines, and all I found was m_time. I wish I had found libdate.
Thanks to all for these contributions.
A permanent entry on my wish list for Fortran is a new data type, Dattime (or whatever). Like some other languages (especially Excel, to use the term loosely) it would handle not only arithmetic, but some functions (e.g. Julian date conversion) and a family of edit descriptors for I/o. Would save lots of people lots of time.
Far too many options for an intrinsic data type. It's straightforward to define a derived type and supply a library for something like this. Formatting, in particular, is especially complex and varied.