Skip to content

Commit c49fcfc

Browse files
Processing of secondsSinceEpoch and its reverse processing routine. (#80)
* Added reverse processing of `secondsSinceEpoch` - Fixed `secondsSinceEpoch` so that the result of `secondsSinceEpoch` is similar to the output of the C `mktime` function when the timezone is non-zero. - Added procedures to converd the result of `secondsSinceEpoch` back to tm structure and datetime type. * localtime and gmtime rewritten in fortran To improve portability between operating systems, C language's localtime_r and gmtime_r functions were rewritten as fortran functions with the same behavior. * fix typo and improve thread-safety Fixed spelling errors. ”function localtime" is revised to a "pure elemental function localtime" * Added explanation of new code. * Tidy up docs * Remove unused code * Bump minor version * Update contributors list --------- Co-authored-by: milancurcic <[email protected]>
1 parent d2dd967 commit c49fcfc

8 files changed

+310
-11
lines changed

Diff for: .gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,6 @@ src/tests/test-suite.log
2424
src/tests/tests-env.sh.log
2525
src/tests/tests-env.sh.trs
2626
test-driver
27+
28+
docs
29+
clang

Diff for: CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ endif()
66

77
project(datetime-fortran
88
LANGUAGES Fortran
9-
VERSION 1.7.0)
9+
VERSION 1.8.0)
1010
include(CTest)
1111

1212
include(cmake/check_strptime.cmake)

Diff for: CONTRIBUTORS.md

+1
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@
1212
* [Stefano Zaghi](https://github.com/szaghi)
1313
* [Tom Canich](https://github.com/tcanich)
1414
* [Wadud Miah](https://github.com/wadudmiah)
15+
* [Yuichiro Sakamoto](https://github.com/sakamoti)

Diff for: README.md

+155
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,10 @@ See some basic examples [here](examples).
139139
* [*num2date*](#num2date)
140140
* [*strptime*](#strptime)
141141
* [*tm2date*](#tm2date)
142+
* [*machinetimezone*](#machinetimezone)
143+
* [*epochdatetime*](#epochdatetime)
144+
* [*localtime*](#localtime)
145+
* [*gmtime*](#gmtime)
142146

143147
## Derived Types<a id="derived-types"></a>
144148

@@ -1654,6 +1658,7 @@ necessary steps.
16541658
* [*tm2date*](#tm2date)
16551659

16561660
[Back to top](#top)
1661+
<hr>
16571662

16581663
### tm2date
16591664

@@ -1687,5 +1692,155 @@ See example usage for [*strptime*](#strptime).
16871692

16881693
* [*strptime*](#strptime)
16891694

1695+
### machinetimezone
1696+
1697+
```fortran
1698+
real(real64) function machinetimezone()
1699+
```
1700+
1701+
returns the machine's time zone in hours.
1702+
1703+
#### Arguments
1704+
1705+
None
1706+
1707+
#### Return value
1708+
1709+
`real(real64)` A `tz` value.
1710+
1711+
#### Example usage
1712+
1713+
Extracting your local machine's time zone information.
1714+
1715+
```fortran
1716+
type(datetime) :: date
1717+
date = datetime(1970, 1, 1, tz=machinetimezone())
1718+
```
1719+
1720+
#### See also
1721+
1722+
* [*datetime*](#datetime)
1723+
1724+
* [*localtime*](#localtime)
1725+
1726+
* [*gmtime*](#gmtime)
1727+
1728+
[Back to top](#top)
1729+
<hr>
1730+
1731+
### epochdatetime
1732+
1733+
```fortran
1734+
pure elemental type(datetime) function epochdatetime()
1735+
epochdatetime = datetime(1970,1,1,0,0,0,0,tz=0.)
1736+
```
1737+
returns a `datetime` that corresponds to the UNIX epoch.
1738+
1739+
#### Arguments
1740+
1741+
None
1742+
1743+
#### Return value
1744+
1745+
`type(datetime)` A `datetime(1970,1,1,0,0,0,0,tz=0.0)` value.
1746+
1747+
#### Example usage
1748+
1749+
```fortran
1750+
type(datetime) :: epochday
1751+
epochday = epochdatetime()
1752+
```
1753+
1754+
#### See also
1755+
1756+
* [*datetime*](#datetime)
1757+
1758+
* [*localtime*](#localtime)
1759+
1760+
* [*gmtime*](#gmtime)
1761+
1762+
[Back to top](#top)
1763+
<hr>
1764+
1765+
### localtime
1766+
1767+
```fortran
1768+
pure elemental type(datetime) function localtime(epoch, tz)
1769+
! Returns a `datetime` instance from epoch.
1770+
! tz can be obtained from `machinetimezone`
1771+
integer(int64),intent(in) :: epoch
1772+
real(real64),intent(in) :: tz !! local machine time zone information
1773+
```
1774+
Generating a *datetime* type from epoch and timezone.
1775+
1776+
#### Arguments
1777+
1778+
`epoch` A epoch time (second)
1779+
1780+
`tz` A time zone (hour). This value can be generated by using [*machinetimezone*](#machinetimezone)
1781+
1782+
#### Return value
1783+
1784+
`type(datetime)` A `datetime` instance.
1785+
1786+
#### Example usage
1787+
1788+
Convert the epoch seconds to an expression in your machine's time zone.
1789+
1790+
```fortran
1791+
integer(int64), parameter :: epoch = 3600
1792+
real(real64) :: tz
1793+
type(datetime) :: date
1794+
tz = machinetimezone()
1795+
date = localtime(epoch, tz)
1796+
```
1797+
1798+
#### See also
1799+
1800+
* [*datetime*](#datetime)
1801+
1802+
* [*machinetimezone*](#machinetimezone)
1803+
1804+
* [*gmtime*](#gmtime)
1805+
1806+
[Back to top](#top)
1807+
<hr>
1808+
1809+
### gmtime
1810+
1811+
```fortran
1812+
pure elemental type(datetime) function gmtime(epoch)
1813+
! Returns a `datetime` instance from epoch.
1814+
integer(int64),intent(in) :: epoch
1815+
```
1816+
Generating a *datetime* type from epoch.
1817+
1818+
#### Arguments
1819+
1820+
`epoch` A epoch time (second)
1821+
1822+
#### Return value
1823+
1824+
`type(datetime)` A `datetime` instance.
1825+
Result is represented with UTC time.
1826+
1827+
#### Example usage
1828+
1829+
Convert the epoch seconds to an expression in UTC time zone.
1830+
1831+
```fortran
1832+
integer(int64) :: epoch = 3600
1833+
type(datetime) :: day
1834+
day = gmtime(epoch)
1835+
```
1836+
1837+
#### See also
1838+
1839+
* [*datetime*](#datetime)
1840+
1841+
* [*machinetimezone*](#machinetimezone)
1842+
1843+
* [*localtime*](#localtime)
1844+
16901845
[Back to top](#top)
16911846
<hr>

Diff for: VERSION

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.7.1
1+
1.8.0

Diff for: fpm.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "datetime"
2-
version = "1.7.1"
2+
version = "1.8.0"
33
license = "MIT"
44
author = "Milan Curcic"
55
maintainer = "[email protected]"
6-
copyright = "2013-2020 datetime-fortran contributors"
6+
copyright = "2013-2023 datetime-fortran contributors"

Diff for: src/datetime_module.f90

+104-6
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ module datetime_module
22

33
use, intrinsic :: iso_fortran_env, only: int64, real32, real64, &
44
stderr => error_unit
5-
use, intrinsic :: iso_c_binding, only: c_char, c_int, c_null_char, c_associated, C_PTR
5+
use, intrinsic :: iso_c_binding, only: c_char, c_int, c_int64_t, c_null_char, c_associated, C_PTR
66

77
implicit none
88

@@ -15,7 +15,11 @@ module datetime_module
1515
public :: daysInYear
1616
public :: isLeapYear
1717
public :: num2date
18+
public :: machinetimezone
1819
public :: strptime
20+
public :: epochdatetime
21+
public :: localtime
22+
public :: gmtime
1923
public :: tm2date
2024
public :: tm_struct
2125
public :: c_strftime
@@ -178,7 +182,7 @@ module datetime_module
178182

179183
interface
180184

181-
type(C_PTR) function c_strftime(str, slen, format, tm) bind(c, name='strftime')
185+
type(c_ptr) function c_strftime(str, slen, format, tm) bind(c, name='strftime')
182186
! Returns a formatted time string, given input time struct and format.
183187
! See https://www.cplusplus.com/reference/ctime/strftime for reference.
184188
import :: c_char, c_int, tm_struct, C_PTR
@@ -644,13 +648,34 @@ integer(int64) function secondsSinceEpoch(self)
644648
class(datetime), intent(in) :: self
645649
type(timedelta) :: delta
646650
type(datetime) :: this_time, unix_time
651+
integer :: sign, hours, minutes, tzsec
647652

648653
this_time = datetime(self%year, self%month, self%day, &
649654
self%hour, self%minute, self%second)
650655
unix_time = datetime(1970, 1, 1, 0, 0, 0)
651656
delta = this_time - unix_time
652657
secondsSinceEpoch = delta%total_seconds()
653658

659+
if(self % tz == 0_real64) return
660+
661+
! affect timezone information
662+
if(self % tz < 0_real64) then
663+
sign = -1
664+
else
665+
sign = 1
666+
end if
667+
668+
hours = int(abs(self % tz))
669+
minutes = nint((abs(self % tz) - hours) * 60)
670+
671+
if (minutes == 60) then
672+
minutes = 0
673+
hours = hours + 1
674+
end if
675+
676+
tzsec = sign * (hours * h2s + minutes)
677+
secondsSinceEpoch = secondsSinceEpoch - tzsec
678+
654679
end function secondsSinceEpoch
655680

656681

@@ -1098,25 +1123,88 @@ pure elemental type(datetime) function num2date(num)
10981123
end function num2date
10991124

11001125

1101-
type(datetime) function strptime(str,format)
1126+
real(real64) function machinetimezone()
1127+
! Return a real value instance of local machine's timezone.
1128+
character(len=5) :: zone
1129+
integer :: values(8)
1130+
integer :: hour, minute
1131+
1132+
! Obtain local machine time zone information
1133+
call date_and_time(zone=zone, values=values)
1134+
read(zone(1:3), '(i3)') hour
1135+
read(zone(4:5), '(i2)') minute
1136+
1137+
if(hour<0)then
1138+
machinetimezone = real(hour, kind=real64) - real(minute, kind=real64) * m2h
1139+
else
1140+
machinetimezone = real(hour, kind=real64) + real(minute, kind=real64) * m2h
1141+
end if
1142+
end function machinetimezone
1143+
1144+
1145+
type(datetime) function strptime(str,format,tz)
11021146
! A wrapper function around C/C++ strptime function.
11031147
! Returns a `datetime` instance.
11041148
character(*), intent(in) :: str, format
1149+
real(real64), intent(in), optional :: tz
11051150
integer :: rc
11061151
type(tm_struct) :: tm
11071152
rc = c_strptime(trim(str) // c_null_char, trim(format) // c_null_char, tm)
11081153
if (rc == 0) then
11091154
write(stderr, *) "ERROR:datetime:strptime: failed to parse string: ", str
11101155
return
11111156
endif
1112-
strptime = tm2date(tm)
1157+
strptime = tm2date(tm,tz)
11131158
end function strptime
11141159

11151160

1116-
pure elemental type(datetime) function tm2date(ctime)
1161+
pure elemental type(datetime) function epochdatetime()
1162+
epochdatetime = datetime(1970,1,1,0,0,0,0,tz=zero)
1163+
end function epochdatetime
1164+
1165+
1166+
pure elemental type(datetime) function localtime(epoch, tz)
1167+
! Returns a `datetime` instance from epoch.
1168+
! tz can be obtained from `machinetimezone`
1169+
integer(int64),intent(in) :: epoch
1170+
real(real64),intent(in) :: tz !! local machine time zone information
1171+
type(datetime) :: datetime_from_epoch
1172+
type(timedelta) :: td
1173+
integer :: day, sec
1174+
integer(int64) :: localseconds
1175+
1176+
datetime_from_epoch = epochdatetime()
1177+
1178+
localseconds = nint(tz * h2s) + epoch
1179+
!suppress overflow
1180+
day = floor(localseconds/d2s, kind=real32)
1181+
sec = localseconds - day * d2s
1182+
td = timedelta(days=day, seconds=sec)
1183+
datetime_from_epoch % tz = tz
1184+
localtime = datetime_from_epoch + td
1185+
end function localtime
1186+
1187+
1188+
pure elemental type(datetime) function gmtime(epoch)
1189+
! Returns a `datetime` instance from epoch.
1190+
integer(int64),intent(in) :: epoch
1191+
type(datetime) :: datetime_from_epoch
1192+
type(timedelta) :: td
1193+
integer :: day, sec
1194+
datetime_from_epoch = epochdatetime()
1195+
!suppress overflow
1196+
day = floor(epoch/d2s, kind=real32)
1197+
sec = epoch - day * d2s
1198+
td = timedelta(days=day, seconds=sec)
1199+
gmtime = datetime_from_epoch + td
1200+
end function gmtime
1201+
1202+
1203+
pure elemental type(datetime) function tm2date(ctime, tz)
11171204
! Given a `tm_struct` instance, returns a corresponding `datetime`
11181205
! instance.
11191206
type(tm_struct), intent(in) :: ctime
1207+
real(real64), intent(in), optional :: tz ! time zone
11201208

11211209
tm2date % millisecond = 0
11221210
tm2date % second = ctime % tm_sec
@@ -1125,7 +1213,17 @@ pure elemental type(datetime) function tm2date(ctime)
11251213
tm2date % day = ctime % tm_mday
11261214
tm2date % month = ctime % tm_mon+1
11271215
tm2date % year = ctime % tm_year+1900
1128-
tm2date % tz = 0
1216+
1217+
! tm_struct have no information of timze zone.
1218+
! but if you run this library with C language's time.h,
1219+
! localtime function deals system's timezone.
1220+
! So, if you want to similar way, you can set tz value with
1221+
! this library's `machinetimezone` function.
1222+
if(present(tz))then
1223+
tm2date % tz = tz
1224+
else
1225+
tm2date % tz = 0.0_real64
1226+
end if
11291227

11301228
end function tm2date
11311229

0 commit comments

Comments
 (0)