Skip to content

zcobell/fdate

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

39 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

FDate Library

CI License: GPLv3 CMake Fortran

A modern datetime library for Fortran that provides comprehensive date and time manipulation capabilities through a clean Fortran interface built on top of Howard Hinnant's date library, which was used as the basis for std::chrono date operations in C++20.

Background

The FDate library bridges the gap between modern C++ datetime functionality and Fortran applications. While Fortran excels in scientific computing, it has traditionally lacked robust datetime manipulation capabilities. This library provides:

  • Millisecond precision datetime objects
  • TimeDelta objects for duration calculations
  • Comprehensive formatting and parsing with customizable format strings
  • Full operator overloading for natural datetime arithmetic
  • Memory-safe design using value types instead of pointers
  • Cross-platform compatibility

The library uses a design where both t_DateTime and t_TimeDelta objects are represented internally as 64-bit integers (milliseconds since epoch for t_DateTime, total milliseconds for t_TimeDelta), and avoids using any other in-memory structures in the interface between C++ and Fortran.

Features

DateTime Operations

  • Create dates from components (year, month, day, hour, minute, second, millisecond)
  • Parse dates from strings with custom format specifiers
  • Parse dates using arrays of format options (tries each format until one succeeds)
  • Parse with automatic format detection plus custom fallback formats
  • Format dates to strings (including ISO 8601)
  • Get current system time
  • Extract individual components (year, month, day, etc.)
  • Julian Day Number and Julian Day calculations for astronomical applications
  • Full comparison operators (==, <, >, <=, >=, /=)
  • Add/subtract TimeDeltas to/from DateTime objects

TimeDelta Operations

  • Create time deltas from various units (days, hours, minutes, seconds, milliseconds)
  • Extract components and total values
  • Arithmetic operations (addition, subtraction, multiplication, division)
  • Full comparison operators (==, <, >, <=, >=, /=)
  • String representation

Installation

Prerequisites

  • CMake 3.14 or higher
  • Modern C++ compiler supporting at least C++17
  • Fortran 2008 compiler (gfortran, ifort, etc.)

Building

mkdir build && cd build
cmake .. -DCMAKE_INSTALL_PREFIX=/path/to/install
make && make install

This will build a shared library and place it at the specified installation location. From there, you can use it in your Fortran projects by linking against the installed library and including the Fortran module file in the installed include directory.

CMake Options

  • FDATE_ENABLE_TESTING: Enable testing suite (default: OFF)

Fortran Usage Examples

Basic DateTime Creation and Manipulation

program datetime_examples
   use mod_datetime, only: t_datetime, t_timedelta, now
   implicit none
   
   type(t_datetime) :: dt1, dt2, current_time
   type(t_timedelta) :: ts1, ts2, difference
   character(len=64) :: date_string
   
   ! Create a specific datetime: January 15, 2024, 14:30:45.123
   dt1 = t_datetime(2024, 1, 15, 14, 30, 45, 123)
   
   ! Create date-only (time defaults to 00:00:00.000)
   dt2 = t_datetime(2024, 12, 25)
   
   ! Get current system time
   current_time = now()
   
   ! Extract components
   write(*,*) 'Year:', dt1%year()
   write(*,*) 'Month:', dt1%month()
   write(*,*) 'Day:', dt1%day()
   write(*,*) 'Hour:', dt1%hour()
   write(*,*) 'Minute:', dt1%minute()
   write(*,*) 'Second:', dt1%second()
   write(*,*) 'Millisecond:', dt1%millisecond()
   
end program datetime_examples

TimeDelta Operations

program timedelta_examples
   use mod_datetime, only: t_timedelta, t_datetime, operator(+), operator(-), operator(*), operator(/)
   implicit none
   
   type(t_timedelta) :: ts1, ts2, ts_result
   type(t_datetime) :: dt_start, dt_end
   
   ! Create timedeltas from different units
   ts1 = t_timedelta(days=2, hours=3, minutes=45, seconds=30, milliseconds=500)
   ts2 = t_timedelta(hours=5)  ! 5 hours
   
   ! Arithmetic operations
   ts_result = ts1 + ts2      ! Addition
   ts_result = ts1 - ts2      ! Subtraction
   ts_result = ts1 * 2        ! Multiplication
   ts_result = ts1 / 2        ! Division
   
   ! Extract components
   write(*,*) 'Days:', ts1%days()
   write(*,*) 'Hours:', ts1%hours()
   write(*,*) 'Total hours:', ts1%total_hours()
   write(*,*) 'Total milliseconds:', ts1%total_milliseconds()
   
   ! Calculate difference between two dates
   dt_start = t_datetime(2024, 1, 1, 9, 0, 0)
   dt_end = t_datetime(2024, 1, 5, 17, 30, 0)
   ts_result = dt_end - dt_start
   
   write(*,*) 'Time difference:', ts_result%to_string()
   
end program timedelta_examples

Date Arithmetic and Comparisons

program date_arithmetic
   use mod_datetime, only: t_datetime, t_timedelta, operator(+), operator(-), operator(<), operator(>), operator(==)
   implicit none
   
   type(t_datetime) :: meeting_time, deadline, reminder
   type(t_timedelta) :: one_week, two_hours
   
   ! Set meeting time
   meeting_time = t_datetime(2024, 6, 15, 14, 0, 0)  ! June 15, 2PM
   
   ! Create timedelta objects
   one_week = t_timedelta(days=7)
   two_hours = t_timedelta(hours=2)
   
   ! Calculate deadline (one week after meeting)
   deadline = meeting_time + one_week
   
   ! Set reminder (2 hours before meeting)
   reminder = meeting_time - two_hours
   
   ! Comparisons
   if (reminder < meeting_time) then
      write(*,*) 'Reminder is set before the meeting'
   end if
   
   if (deadline > meeting_time) then
      write(*,*) 'Deadline is after the meeting'
   end if
   
   ! Check if dates are equal
   if (meeting_time == meeting_time) then
      write(*,*) 'Dates are identical'
   end if
   
end program date_arithmetic

String Formatting and Parsing

program string_operations
   use mod_datetime, only: t_datetime
   implicit none
   
   type(t_datetime) :: dt, parsed_dt
   character(len=64) :: formatted_string
   
   ! Create a datetime
   dt = t_datetime(2024, 3, 14, 9, 26, 53, 589)
   
   ! Format to different string representations
   formatted_string = dt%strftime('%Y-%m-%d %H:%M:%S')
   write(*,*) 'Standard format: ', trim(formatted_string)
   
   formatted_string = dt%strftime('%B %d, %Y at %I:%M %p')
   write(*,*) 'Verbose format: ', trim(formatted_string)
   
   ! ISO 8601 format
   formatted_string = dt%to_iso_string()
   write(*,*) 'ISO format: ', trim(formatted_string)
   
   ! Some Custom formats
   formatted_string = dt%strftime('%A, %B %d, %Y') ! Full weekday and month names
   write(*,*) 'Custom format: ', trim(formatted_string)
   formatted_string = dt%strftime('%Y-%m-%dT%H:%M:%S',.true.) ! ISO with milliseconds
   write(*,*) 'ISO with milliseconds: ', trim(formatted_string)
   
   ! Parse a date string
   parsed_dt = t_datetime('2024-03-14 09:26:53', 'auto') ! Note, 'auto' as a format string is provided 
                                                         ! as a default if no format is specified 
  
   ! Check if parsing was successful
   if (parsed_dt%valid()) then
       write(*,*) 'Parsed date: ', parsed_dt%strftime('%Y-%m-%d %H:%M:%S')
   else
       write(*,*) 'Failed to parse date string'
   end if
   
end program string_operations

Array-Based Parsing with Multiple Format Options

The library supports parsing with multiple format options, trying each format in order until one succeeds:

program array_parsing_examples
   use mod_datetime, only: t_datetime
   implicit none
   
   type(t_datetime) :: parsed_dt
   character(len=30) :: date_strings(3) = [ &
      '2024/03/14 09:26:53        ', &  ! Slash-separated format
      '14-Mar-2024 09:26:53       ', &  ! Month name format  
      '20240314092653             '  ]  ! Compact format
   character(len=30) :: formats(4) = [ &
      '%Y/%m/%d %H:%M:%S          ', &  ! Slash format
      '%d-%b-%Y %H:%M:%S          ', &  ! Month name format
      '%Y%m%d%H%M%S               ', &  ! Compact format
      '%Y-%m-%d %H:%M:%S          '  ]  ! Standard format
   integer :: i
   
   ! Try parsing with an array of format options
   do i = 1, size(date_strings)
      parsed_dt = t_datetime(trim(date_strings(i)), formats)
      
      if (parsed_dt%valid()) then
         write(*,*) 'Successfully parsed: ', trim(date_strings(i))
         write(*,*) 'Result: ', parsed_dt%strftime('%Y-%m-%d %H:%M:%S')
      else
         write(*,*) 'Failed to parse: ', trim(date_strings(i))
      end if
   end do

   ! Parse with auto-detection and custom fallbacks
   parsed_dt = t_datetime('2024.03.14T09:26:53', ['%Y.%m.%dT%H:%M:%S     ', &
                                                   '%Y.%m.%d %H:%M:%S     '])
   
end program array_parsing_examples

Julian Day Calculations

The library provides Julian Day Number (JDN) and Julian Day (JD) calculations for astronomical applications:

program julian_day_examples
   use mod_datetime, only: t_datetime
   implicit none
   
   type(t_datetime) :: dt
   integer(kind=8) :: jdn
   real(kind=8) :: jd
   
   ! Create datetime objects for well-known astronomical dates
   
   ! January 1, 2000 at noon UTC (J2000.0 epoch)
   dt = t_datetime(2000, 1, 1, 12, 0, 0)
   jdn = dt%julian_day_number()    ! Returns 2451545
   jd = dt%julian_day()            ! Returns 2451545.0
   
   write(*,*) 'Y2K noon JDN:', jdn
   write(*,*) 'Y2K noon JD:', jd
   
   ! January 1, 2000 at midnight UTC
   dt = t_datetime(2000, 1, 1, 0, 0, 0)
   jd = dt%julian_day()            ! Returns 2451544.5
   write(*,*) 'Y2K midnight JD:', jd
   
   ! Current time with fractional day
   dt = t_datetime(2025, 7, 24, 15, 30, 45, 500)
   jdn = dt%julian_day_number()    ! Integer days since JD epoch
   jd = dt%julian_day()            ! Fractional days including time
   
   write(*,*) 'Current JDN:', jdn
   write(*,*) 'Current JD with time:', jd
   
   ! Julian Day calculations are useful for:
   ! - Astronomical computations
   ! - Converting between different calendar systems  
   ! - High-precision time interval calculations
   ! - Satellite orbital mechanics
   
end program julian_day_examples

Julian Day Notes:

  • Julian Day Number (JDN) is the integer count of days since January 1, 4713 BCE (proleptic Julian calendar)
  • Julian Day (JD) includes fractional time, where JD 0.0 begins at noon UTC
  • Commonly used in astronomy for precise time calculations
  • The library uses overflow-safe 64-bit integer arithmetic for reliability

Format Specifiers

The library supports typical format specifiers for date formatting:

Specifier Description Example
%Y 4-digit year 2024
%m Month (01-12) 03
%d Day of month (01-31) 14
%H Hour (00-23) 09
%M Minute (00-59) 26
%S Second (00-59) 53
%B Full month name March
%A Full weekday name Thursday

Error Handling

The library handles errors gracefully:

  • Invalid date parameters return special error timestamps
  • Parsing failures are indicated by returned error values

Thread Safety

The library is thread-safe for read operations. For write operations in multi-threaded environments, appropriate synchronization should be implemented by the calling application.

Contributing

Contributions are welcome! Please ensure that:

  • All new features include appropriate test cases
  • Code follows the existing style conventions
  • Documentation is updated for new functionality

License

This project is licensed under the GNU General Public License v3.0. See the LICENSE file for details.

Support

For questions, bug reports, or feature requests, please create an issue in the project repository.

About

Fortran Date/Time Library

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published