NAME
M_time(3fm) - [M_time] Fortran module for manipulating and presenting time and date values
DESCRIPTION

The M_time(3f) Fortran module and associated utility programs provide date and time-related procedures. Both a procedural and OOP (Object Oriented Programming) interface are provided. Each routine is accompanied by a man(1) page which includes a sample program for that procedure. This manual, the source and example utility programs are included in the download.

Utility programs that use the M_time(3f) module:

The M_time(3f) module

  • provides for formatting dates.
  • facilitates simple computations using time and date values in the recent era.
  • allow for macro-level timing of code.

The M_TIME(3f) module complements the DATE_AND_TIME(3f) procedure, which is the standard intrinsic subroutine that returns the current date and time in the Gregorian calendar. That is, the primary way this module represents dates is as an integer array with the same meaning for elements as defined by the DATE_AND_TIME(3f) routine. In addition it can calculate or read many other date representations such as ...

  • Julian Dates
  • Unix Epoch Dates
  • High-level date formatting
  • Ordinal days of the year
  • days of the week
  • ISO-8601 week numbers
  • month and weekday names

Julian and Unix Epoch Dates are particularly useful for manipulating dates in simple numeric expressions.

The extensive formatting options include showing SYSTEM_CLOCK(3f) and CPU_USAGE(3f) information along with Gregorian date information, allowing for the easy incorporation of timing information into program messages. In addition to conventional Civilian Calendar dates, the module supports the ISO-8601 standard methods of displaying dates.

A Fortran-callable sleep(3c)/usleep(3c) procedure is also provided.

SYNOPSIS
UNIX EPOCH
date_to_unix(dat,UNIXTIME,IERR) %epoch() Convert date array to Unix Time
unix_to_date(unixtime,DAT,IERR)   Convert Unix Time to date array
d2u(dat) result (UNIXTIME)   Convert date array to Unix Time
u2d(unixtime) result (DAT)   Convert Unix Time to date array
JULIAN
julian_to_date(julian,DAT,IERR)   Convert Julian Date to date array
date_to_julian(dat,JULIAN,IERR) %julian() Converts date array to Julian Date
d2j(dat) result (JULIAN)   Convert date array to Julian Date
j2d(julian) result (DAT)   Convert Julian Date to date array
DAY OF WEEK
dow(dat,[WEEKDAY],[DAY],IERR) %weekday() Convert date array to day of the week as number(Mon=1) and name
WEEK OF YEAR
d2w(dat,ISO_YEAR,ISO_WEEK,ISO_WEEKDAY,ISO_NAME)   calculate iso-8601 Week-numbering year date yyyy-Www-d
w2d(iso_year,iso_week,iso_weekday,DAT)   calculate date given iso-8601 Week date yyyy-Www-d
ORDINAL DAY
d2o(dat) result(ORDINAL) %ordinal() given date array return ordinal day of year, Jan 1st=1
o2d(ordinal,[year]) result(DAT) given ordinal day of year return date array, Jan 1st=1
ordinal_to_date(ordinal,year,DAT) given ordinal day of year return date array, Jan 1st=1
ordinal_seconds() return seconds since beginning of year
PRINTING DATES
fmtdate(dat,format) result (TIMESTR) %format([STRING]) Convert date array to string using format
fmtdate_usage(indent)   display macros recognized by fmtdate(3f)
now(format) result (NOW)   return string representing current time given format
box_month(dat,CALEN)   print specified month into character array
MONTH NAME
mo2v(month_name) result (MONTH_NUMBER)   given month name return month number
v2mo(month_number) result (MONTH_NAME)   given month number return month name
mo2d(month_name) result (DAT)   return date array for first day of given month name in specified year
ASTROLOGICAL
easter(year,dat)   calculate month and day Easter falls on for given year
moon_fullness(DAT) result(FULLNESS)  percentage of moon phase from new to full
phase_of_moon(DAT) result(PHASE)  return name for phase of moon for given date
DURATION
sec2days(seconds) result(dhms)   converts seconds to string D-HH:MM:SS
days2sec(string) result(seconds)   converts string D-HH:MM:SS to seconds
READING DATES
guessdate(anot,dat)   Converts a date string to a date array, in various formats
C INTERFACE
system_sleep(wait_seconds)   Call sleep(3c) or usleep(3c)
FORMATTING OPTIONS IN FMTDATE

You can easily use Julian Ephemeris Dates and Unix Epoch Times to add and subtract times from dates or to calculate the interval between dates. But JEDs and UETs and even the Gregorian Calendar arrays in the DAT arrays are not the way we typically describe a date on the Civilian Calendar. So the fmtdate(3f) routine lets us print a DAT array in a variety of familiar styles.

The fmtdate(3f) and now(3f) procedures let you display a Gregorian date using either keywords for standard formats or using macros in a user-specified formatting string. A formatting string may contain the following macros:


Description                                        Example

 Base time array:
  (1) %Y -- year, yyyy                                2016
  (2) %M -- month of year, 01 to 12                   07
  (3) %D -- day of month, 01 to 31                    27
      %d -- day of month, with suffix (1st, 2nd,...)  27th
  (4) %Z -- minutes from UTC                          -0240
      %z -- -+hh:mm from UTC                          -04:00
      %T -- -+hhmm  from UTC                          -0400
  (5) %h -- hours, 00 to 23                           21
      %H -- hour (1 to 12, or twelve-hour clock)      09
      %N -- midnight< AM <=noon; noon<= PM <midnight  PM
  (6) %m -- minutes, 00 to 59                         24
  (7) %s -- sec, 00 to 59                             22
  (8) %x -- milliseconds 000 to 999                   512
 Conversions:
      %E -- Unix Epoch time                           1469669062.5129952
      %e -- integer value of Unix Epoch time          1469669063
      %J -- Julian  date                              2457597.559
      %j -- integer value of Julian Date(Julian Day)  2457597
      %O -- Ordinal day (day of year)                 209
      %o -- whole days since Unix Epoch date          17009
      %U -- day of week, 1..7 Sunday=1                4
      %u -- day of week, 1..7 Monday=1                3
      %i -- ISO week of year 1..53                    30
      %I -- iso-8601 week-numbering date(yyyy-Www-d)  2016-W30-3
  Names:
      %l -- abbreviated month name                    Jul
      %L -- full month name                           July
      %w -- first three characters of weekday         Wed
      %W -- weekday name                              Wednesday
      %p -- phase of moon                             New
      %P -- percent of way from new to full moon      -1%
  Literals:
      %% -- a literal %                               %
      %t -- tab character
      %b -- blank character
      %B -- exclamation(bang) character
      %n -- new line (system dependent)
      %q -- single quote (apostrophe)
      %Q -- double quote
  Program timing:
      %c -- CPU_TIME(3f) output                       .78125000000000000E-001
      %C -- number of times this routine is used      1
      %S -- seconds since last use of this format     .0000000000000000
      %k -- time in seconds from SYSTEM_CLOCK(3f)     588272.750
      %K -- time in clicks from SYSTEM_CLOCK(3f)      588272750

If no percent (%) is found in the format one of several
alternate substitutions occurs.

If the format is composed entirely of one of the following
keywords the following substitution occurs:

  "iso-8601",
  "iso"        ==> %Y-%M-%DT%h:%m:%s%z  ! Example: 2017-08-26T18:56:33,510912700-04:00

  "iso-8601W",
  "isoweek"    ==> %I
  "sql"        ==> "%Y-%M-%D %h:%m:%s.%x"
  "sqlday"     ==> "%Y-%M-%D"
  "sqltime"    ==> "%h:%m:%s.%x"
  "rfc-2822"   ==> %w, %D %l %Y %h:%m:%s %T  !  Example: Mon, 14 Aug 2006 02:34:56 -0600

  "rfc-3339"   ==> %Y-%M-%DT%h:%m:%s%z  !  Example: 2006-08-14 02:34:56-06:00
  "date"       ==> %w %l %D %h:%m:%s UTC%z %Y
  "short"      ==> %w, %l %d, %Y %H:%m:%s %N UTC%z
  "long"," "   ==> %W, %L %d, %Y %H:%m:%s %N UTC%z
  "suffix"     ==> %Y%D%M%h%m%s
  "formal"     ==> The %d of %L %Y
  "lord"       ==> the %d day of %L in the year of our Lord %Y
  "easter"     ==> FOR THE YEAR OF THE CURRENT DATE:
                      Easter day: the %d day of %L in the year of our Lord %Y
  "all"        ==> A SAMPLE OF DATE FORMATS

otherwise the following words are replaced with the most
common macros:

  STRING   MACRO  EXAMPLE
  year     %Y     2016
  month    %M     07
  MONTH    %L     July
  Month    %l     Jul
  day      %D     27
  DAY      %d     7th
  hour     %h     21
  HOUR     %H     10
  GOOD     %N     AM
  minute   %m     24
  second   %s     22
  weekday  %u     3
  Weekday  %w     Thu
  WEEKDAY  %W     Thursday
  timezone %T     -0400
  Timezone %Z     -240
  TIMEZONE %z     -04:00

  epoch    %e     1469669063
  julian   %j     2457597
  ordinal  %O     209

f none of these keywords are found then every letter that
s a macro is assumed to have an implied percent in front
f it. For example:

  YMDhms ==> %Y%M%D%h%m%s ==> 20160727212422
OOPS INTERFACE

If you prefer an Object-oriented interface the M_time_oop module (included with the M_time module source) provides an OOP interface to the M_time module.

EXAMPLES

The following example program demonstrates the extensive options available for formatting a date as well as how to use the module to calculate dates such as "Yesterday" and "Tomorrow", as well as how to use the Object Oriented interface to the conventional procedures found in the M_time(3fm) module.

>
program demo_M_time
use M_time, only:  j2d, d2j, u2d, d2u, fmtdate, realtime
integer                      :: dat(8)
real(kind=realtime)          :: julian, unixtime
character(len=*),parameter   :: iso_fmt='%Y-%M-%DT%h:%m:%s.%x%z'
character(len=:),allocatable :: friendly

   friendly='%W, %L %d, %Y %H:%m:%s %N' ! a nice friendly format

   call date_and_time(values=dat)  ! current time is placed in array

   write(*,*)'Today'
   write(*,*)'ISO       ',fmtdate(dat,iso_fmt)
   write(*,*)'Friendly  ',fmtdate(dat,friendly)
   write(*,*)'ISO week  ',fmtdate(dat,'%I')

   julian=d2j(dat)
   unixtime=d2u(dat)

   write(*,*)'Yesterday' ! subtract a day from scalar time and print
   write(*,*)'          ',fmtdate(u2d(unixtime-86400),iso_fmt)
   write(*,*)'          ',fmtdate(j2d(julian-1.0),friendly)
   write(*,*)'          ',fmtdate(j2d(julian-1.0),'%I')

   write(*,*)'Tomorrow'  ! add a day to scalar time and print
   write(*,*)'          ',fmtdate(u2d(unixtime+86400),iso_fmt)
   write(*,*)'          ',fmtdate(j2d(julian+1.0),friendly)
   write(*,*)'          ',fmtdate(j2d(julian+1.0),'%I')

   write(*,*)'Next Week'  ! add a week to scalar time and print
   write(*,*)'          ',fmtdate(u2d(unixtime+7*86400),iso_fmt)
   write(*,*)'          ',fmtdate(j2d(julian+7.0),friendly)
   write(*,*)'          ',fmtdate(j2d(julian+7.0),'%I')

end program demo_M_time

Sample output of example program ...


 Today
 ISO       2015-12-22T08:07:34.025-0300
 Friendly  Tuesday, December 22nd, 2015 08:07:34 AM
 ISO week  2015-W52-2
 Yesterday
           2015-12-21T08:07:34.025-0300
           Monday, December 21st, 2015 08:07:34 AM
           2015-W52-1
 Tomorrow
           2015-12-23T08:07:34.025-0300
           Wednesday, December 23rd, 2015 08:07:34 AM
           2015-W52-3
 Next Week
           2015-12-29T08:07:34.025-0300
           Tuesday, December 29th, 2015 08:07:34 AM
           2015-W53-2
DEFINITIONS

A "date_and_time" array "DAT" has the same format as the array of values generated by the Fortran intrinsic DATE_AND_TIME(3f). That is, it is an 8-element integer array containing year, month, day, Time zone difference from UTC in minutes, hour, minutes, seconds, and milliseconds of the second. This array represents a date on the Proleptic Gregorian Calendar.

The Proleptic Gregorian Calendar assumes the Gregorian Calendar existed back to the beginning of the Julian Day calendar (4713 BC). This means historic dates will often be confused, as the Julian Calendar was used in the USA until 1752-09-03, for example. The Gregorian Calendar was formally decreed on 1582-10-15 but was not adapted in many countries. The Julian Calendar was first used around 45 BC. Note that the Proleptic Gregorian Calendar includes a year zero (0). It is frequently used in computer software to simplify the handling of older dates. For example, it is the calendar used by MySQL, SQLite, PHP, CIM, Delphi, Python and COBOL. The Proleptic Gregorian Calendar is explicitly required for all dates before 1582 by ISO 8601:2004 (clause 4.3.2.1 The Gregorian calendar) if the partners to information exchange agree.

Unix Epoch Time (UET) is defined as the number of seconds since 00:00:00 on January 1st. 1970, UTC.

A JED is defined as a Julian Ephemeris Date. JED days start at noon (not at midnight). 4713-01-01 BC at noon is defined as JED 0.0.

If you are not familiar with them, in this context Julian Dates and Unix Epoch Times are scalar numbers that allow for easy computations using dates (to go back one day just subtract one from a Julian Date, for example). Since these values are generally not considered intelligible, routines are included to convert between these scalar values and the date array so human-readable results can be obtained.

Coordinated Universal Time (French: Temps universel coordonn'e), abbreviated as UTC, is the primary time standard by which the world regulates clocks and time. It is within about 1 second of mean solar time at 0o longitude;[1] it does not observe daylight saving time. It is one of several closely related successors to Greenwich Mean Time (GMT). For most purposes, UTC is considered interchangeable with GMT, but GMT is no longer precisely defined by the scientific community.

LIMITATIONS

Like most collections of date and time procedures M_time is not a high-precision library that accounts internally for leap seconds and relativistic effects.

M_time(3f) is intended for use in the recent era and is not appropriate for use with historical dates that used some other calendar scheme such as the Julian Calendar. That is, you have to remember to account for conversions to other calendar systems when using historical dates.

When Daylight Savings is in effect calculations will generally be correct, as the date model includes a timezone value; but you are responsible for ensuring dates you create use the correct timezone value or otherwise account for Daylight Savings Time as needed.

Currently, dates are manipulated using the current system timezone, which can typically be set using the environment variable TZ. So if you desire to set the default timezone you generally set the environment variable before executing your program. This is compatible with current observed behavior for the intrinsic procedure DATE_AND_TIME(3f) with compilers I have tested with, but does not seem to be a specified behavior as far as the standard is concerned. That is, DATE_AND_TIME(3f) returns a vector that contains a current time zone, but does not specify how a current time zone can be explicitly set. Since this library is intentionally designed to complement DATE_AND_TIME(3f) it adopts the same behavior. A routine to let you set a default time zone could be added in the future.

Note the environment variable can be set using put_environment_variable(3f) from the libGPF library:


     use M_system, only : put_environment_variable
     call put_environment_variable('TZ','America/New_York',ierr)

There is no warranty on this code, and it is certain to change.

SEE ALSO

The ISO-8601 standard is often used for business-related transactions.

There are (of course) the C/C++ intrinsics which provide much of the same functionality that should be bindable to Fortran via the ISO_C_BINDING module.

If you are looking for a high-precision Fortran library that is well tested for manipulating dates I would suggest looking at the NASA SPICElib library. If you care about Leap Seconds, Orbital Mechanics, GPS/Satellite communications, and Astronomy it is worth a look.

The Fortran Wiki fortranwiki.org contains information on other libraries and modules that provide date-time procedures.