├── .github └── workflows │ └── ci.yml ├── .gitignore ├── CMakeLists.txt ├── CONTRIBUTORS.md ├── LICENSE ├── Makefile.am ├── README.md ├── VERSION ├── _config.yml ├── cmake ├── check_strftime.cmake └── check_strptime.cmake ├── configure.ac ├── datetime-fortran.pc.in ├── example ├── CMakeLists.txt ├── README.md └── add_and_subtract.f90 ├── fpm.toml ├── scrub-clean.sh ├── src ├── Makefile.am ├── datetime_module.f90 └── strptime.cpp └── test ├── Makefile.am ├── datetime_tests.f90 └── tests-env.sh /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | push: 5 | paths: 6 | - "**.f90" 7 | - ".github/workflows/ci.yml" 8 | - "CMakeLists.txt" 9 | pull_request: 10 | paths: 11 | - "**.f90" 12 | - ".github/workflows/ci.yml" 13 | - "CMakeLists.txt" 14 | 15 | jobs: 16 | 17 | windows: 18 | runs-on: windows-latest 19 | defaults: 20 | run: 21 | shell: msys2 {0} 22 | steps: 23 | - uses: msys2/setup-msys2@v2 24 | with: 25 | update: true 26 | install: >- 27 | git 28 | mingw-w64-x86_64-cmake 29 | mingw-w64-x86_64-ninja 30 | mingw-w64-x86_64-gcc-fortran 31 | - uses: actions/checkout@v3 32 | 33 | - run: cmake -B build -G Ninja 34 | - run: cmake --build build 35 | - run: ctest --test-dir build -V 36 | 37 | core: 38 | strategy: 39 | matrix: 40 | os: [macos-latest, ubuntu-latest] 41 | runs-on: ${{ matrix.os }} 42 | 43 | steps: 44 | - uses: actions/checkout@v3 45 | 46 | - name: macOS Gfortran setup 47 | if: runner.os == 'macOS' 48 | run: brew reinstall gcc 49 | 50 | - run: cmake -B build 51 | - run: cmake --build build 52 | - run: ctest --test-dir build -V 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build*/ 2 | *.o 3 | *.a 4 | *.mod 5 | TODO 6 | Makefile.in 7 | aclocal.m4 8 | autom4te.cache/ 9 | autoscan.log 10 | configure 11 | configure.scan 12 | depcomp 13 | install-sh 14 | missing 15 | Makefile 16 | config.log 17 | config.status 18 | tests/Makefile 19 | *.tar.gz 20 | datetime-fortran.pc 21 | src/tests/datetime_tests 22 | 23 | src/tests/test-suite.log 24 | src/tests/tests-env.sh.log 25 | src/tests/tests-env.sh.trs 26 | test-driver 27 | 28 | docs 29 | clang 30 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | if(NOT CMAKE_BUILD_TYPE) 4 | set(CMAKE_BUILD_TYPE Release CACHE STRING "default to Release") 5 | endif() 6 | 7 | project(datetime-fortran 8 | LANGUAGES Fortran 9 | VERSION 1.8.0) 10 | include(CTest) 11 | 12 | include(cmake/check_strptime.cmake) 13 | include(cmake/check_strftime.cmake) 14 | 15 | # library to archive (libdatetime.a) 16 | add_library(datetime src/datetime_module.f90) 17 | target_include_directories(datetime INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/include) 18 | set_property(TARGET datetime PROPERTY Fortran_MODULE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include) 19 | 20 | 21 | if(NOT HAVE_C_STRPTIME) 22 | enable_language(CXX) 23 | set(CMAKE_CXX_STANDARD 11) 24 | target_sources(datetime PRIVATE src/strptime.cpp) 25 | endif() 26 | 27 | # MinGW GCC needs this for strftime to work 28 | # https://sourceforge.net/p/mingw-w64/bugs/793/ 29 | target_link_libraries(datetime PRIVATE "$<$:ucrtbase>") 30 | 31 | # tests 32 | if(BUILD_TESTING) 33 | add_executable(datetime_tests test/datetime_tests.f90) 34 | target_link_libraries(datetime_tests datetime) 35 | set_target_properties(datetime_tests PROPERTIES 36 | RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin 37 | LINKER_LANGUAGE Fortran) 38 | add_test(NAME datetime_tests COMMAND $) 39 | 40 | add_subdirectory(example) 41 | endif(BUILD_TESTING) 42 | -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | # Contributors 2 | 3 | * [Alexander Gavrikov](https://github.com/alexavr) 4 | * Bjoern Hendrik Fock 5 | * [Izaak Beekman](https://github.com/zbeekman) 6 | * Marco Galli 7 | * Mark Carter 8 | * [Michael Hirsch](https://github.com/scivision) 9 | * [Milan Curcic](https://github.com/milancurcic) 10 | * [Oleksandr Sasha Huziy](https://github.com/guziy) 11 | * [Paul Leopardi](https://github.com/penguian) 12 | * [Stefano Zaghi](https://github.com/szaghi) 13 | * [Tom Canich](https://github.com/tcanich) 14 | * [Wadud Miah](https://github.com/wadudmiah) 15 | * [Yuichiro Sakamoto](https://github.com/sakamoti) 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2013-2020 datetime-fortran contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = src tests 2 | 3 | # Next line required by automake < 1.15 4 | pkgconfigdir = $(libdir)/pkgconfig 5 | pkgconfig_DATA = datetime-fortran.pc 6 | 7 | scrub-clean : 8 | ./scrub-clean.sh 9 | 10 | CLEANFILES = datetime-fortran.pc 11 | 12 | EXTRA_DIST = CONTRIBUTORS.md LICENSE README.md scrub-clean.sh 13 | 14 | #dist-hook: 15 | # ./mkdist.sh 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # datetime-fortran 2 | 3 | [![Actions Status](https://github.com/wavebitscientific/datetime-fortran/workflows/ci/badge.svg)](https://github.com/wavebitscientific/datetime-fortran/actions) 4 | [![GitHub issues](https://img.shields.io/github/issues/wavebitscientific/datetime-fortran.svg)](https://github.com/wavebitscientific/datetime-fortran/issues) 5 | 6 | Date and time manipulation for modern Fortran. 7 | The fundamental time step is one millisecond. 8 | 9 | ## Getting started 10 | 11 | First, get the code by cloning this repo: 12 | 13 | ```sh 14 | git clone https://github.com/wavebitscientific/datetime-fortran 15 | cd datetime-fortran 16 | ``` 17 | 18 | or by downloading a [release tarball](https://github.com/wavebitscientific/datetime-fortran/releases). 19 | The latter is recommended if you want to build datetime-fortran with autotools and make. 20 | 21 | You can build datetime-fortran with FPM, autotools, CMake, or by simply including 22 | the source file "src/datetime_module.f90" in your project. 23 | NOTE: Windows additionally requires "src/strptime.cpp" in your project. 24 | 25 | ### FPM 26 | 27 | As of version 1.7.0, datetime-fortran can be built using the 28 | [Fortran Package Manager](https://github.com/fortran-lang/fpm). 29 | Follow the directions on that page to install FPM if you haven't already. 30 | 31 | To build, type: 32 | 33 | ```sh 34 | fpm build 35 | ``` 36 | 37 | binary artifacts are created in: 38 | 39 | * Library and module files: `build/gfortran_debug/library` 40 | * test executables: `build/gfortran_debug/tests`. 41 | 42 | To run the tests, type: 43 | 44 | ```sh 45 | fpm test 46 | ``` 47 | 48 | ### Autotools 49 | 50 | ``` 51 | ./configure 52 | make check 53 | make install 54 | ``` 55 | 56 | If you cloned the repo instead of downloading a release tarball, 57 | run `autoreconf -i` first to generate the `configure` script. 58 | 59 | ### CMake 60 | 61 | ```sh 62 | cmake -B build 63 | cmake --build build 64 | ``` 65 | 66 | binary artifacts are created in: 67 | 68 | * Library: `build/lib/libdatetime` 69 | * module: `build/include/datetime.mod` 70 | * test executable: `build/bin/datetime_tests` 71 | 72 | optionally, to install (replace "~/mylibs" with your desired install directory): 73 | 74 | ```sh 75 | cmake -B build -DCMAKE_INSTALL_PREFIX=~/mylibs 76 | cmake --install build 77 | ``` 78 | 79 | optionally, to run self-tests: 80 | 81 | ```sh 82 | cd build 83 | ctest -V 84 | ``` 85 | 86 | ## Use 87 | 88 | Start using datetime-fortran in your code by importing derived types from the module: 89 | 90 | ```fortran 91 | use datetime_module, only: datetime, timedelta, clock 92 | ``` 93 | 94 | See some basic examples [here](examples). 95 | 96 | 97 | 98 | ## API 99 | 100 | * [Derived Types](#derived-types) 101 | * [*datetime*](#datetime) 102 | * [*getYear*](#getyear) 103 | * [*getMonth*](#getmonth) 104 | * [*getDay*](#getday) 105 | * [*getHour*](#gethour) 106 | * [*getMinute*](#getminute) 107 | * [*getSecond*](#getsecond) 108 | * [*getMillisecond*](#getmillisecond) 109 | * [*isocalendar*](#isocalendar) 110 | * [*isoformat*](#isoformat) 111 | * [*isValid*](#isvalid) 112 | * [*now*](#now) 113 | * [*secondsSinceEpoch*](#secondssinceepoch) 114 | * [*strftime*](#strftime) 115 | * [*tm*](#tm) 116 | * [*tzOffset*](#tzoffset) 117 | * [*utc*](#utc) 118 | * [*weekday*](#weekday) 119 | * [*weekdayLong*](#weekdaylong) 120 | * [*weekdayShort*](#weekdayshort) 121 | * [*yearday*](#yearday) 122 | * [*timedelta*](#timedelta) 123 | * [*total_seconds*](#total_seconds) 124 | * [*clock*](#clock) 125 | * [*reset*](#reset) 126 | * [*tick*](#tick) 127 | * [*tm_struct*](#tm_struct) 128 | * [Overloaded operators](#overloaded-operators) 129 | * [Arithmetic operators](#arithmetic-operators) 130 | * [Comparison operators](#comparison-operators) 131 | * [Public procedures](#public-procedures) 132 | * [*c_strftime*](#c_strftime) 133 | * [*c_strptime*](#c_strptime) 134 | * [*date2num*](#date2num) 135 | * [*datetimeRange*](#datetimerange) 136 | * [*daysInMonth*](#daysinmonth) 137 | * [*daysInYear*](#daysinyear) 138 | * [*isLeapYear*](#isleapyear) 139 | * [*num2date*](#num2date) 140 | * [*strptime*](#strptime) 141 | * [*tm2date*](#tm2date) 142 | * [*machinetimezone*](#machinetimezone) 143 | * [*epochdatetime*](#epochdatetime) 144 | * [*localtime*](#localtime) 145 | * [*gmtime*](#gmtime) 146 | 147 | ## Derived Types 148 | 149 | *datetime-fortran* library provides the following derived types: 150 | [*datetime*](#datetime), [*timedelta*](#timedelta), 151 | [*clock*](#clock) and [*tm_struct*](#tm_struct). 152 | 153 | ### datetime 154 | 155 | Main date and time object, defined as: 156 | 157 | ```fortran 158 | type :: datetime 159 | 160 | !! Main datetime class for date and time representation. 161 | 162 | private 163 | 164 | integer :: year = 1 !! year [1-HUGE(year)] 165 | integer :: month = 1 !! month in year [1-12] 166 | integer :: day = 1 !! day in month [1-31] 167 | integer :: hour = 0 !! hour in day [0-23] 168 | integer :: minute = 0 !! minute in hour [0-59] 169 | integer :: second = 0 !! second in minute [0-59] 170 | integer :: millisecond = 0 !! milliseconds in second [0-999] 171 | 172 | real(kind=real64) :: tz = 0 !! timezone offset from UTC [hours] 173 | 174 | contains 175 | 176 | ! getter functions 177 | procedure,pass(self),public :: getYear 178 | procedure,pass(self),public :: getMonth 179 | procedure,pass(self),public :: getDay 180 | procedure,pass(self),public :: getHour 181 | procedure,pass(self),public :: getMinute 182 | procedure,pass(self),public :: getSecond 183 | procedure,pass(self),public :: getMillisecond 184 | procedure,pass(self),public :: getTz 185 | 186 | ! public methods 187 | procedure,pass(self),public :: isocalendar 188 | procedure,pass(self),public :: isoformat 189 | procedure,pass(self),public :: isValid 190 | procedure,nopass, public :: now 191 | procedure,pass(self),public :: secondsSinceEpoch 192 | procedure,pass(self),public :: strftime 193 | procedure,pass(self),public :: tm 194 | procedure,pass(self),public :: tzOffset 195 | procedure,pass(self),public :: utc 196 | procedure,pass(self),public :: weekday 197 | procedure,pass(self),public :: isoweekday 198 | procedure,pass(self),public :: weekdayLong 199 | procedure,pass(self),public :: isoweekdayLong 200 | procedure,pass(self),public :: weekdayShort 201 | procedure,pass(self),public :: isoweekdayShort 202 | procedure,pass(self),public :: yearday 203 | 204 | ! private methods 205 | procedure,pass(self),private :: addMilliseconds 206 | procedure,pass(self),private :: addSeconds 207 | procedure,pass(self),private :: addMinutes 208 | procedure,pass(self),private :: addHours 209 | procedure,pass(self),private :: addDays 210 | 211 | ! operator overloading procedures 212 | procedure,pass(d0),private :: datetime_plus_timedelta 213 | procedure,pass(d0),private :: timedelta_plus_datetime 214 | procedure,pass(d0),private :: datetime_minus_datetime 215 | procedure,pass(d0),private :: datetime_minus_timedelta 216 | procedure,pass(d0),private :: eq 217 | procedure,pass(d0),private :: neq 218 | procedure,pass(d0),private :: gt 219 | procedure,pass(d0),private :: ge 220 | procedure,pass(d0),private :: lt 221 | procedure,pass(d0),private :: le 222 | 223 | generic :: operator(+) => datetime_plus_timedelta,& 224 | timedelta_plus_datetime 225 | generic :: operator(-) => datetime_minus_datetime,& 226 | datetime_minus_timedelta 227 | generic :: operator(==) => eq 228 | generic :: operator(/=) => neq 229 | generic :: operator(>) => gt 230 | generic :: operator(>=) => ge 231 | generic :: operator(<) => lt 232 | generic :: operator(<=) => le 233 | 234 | endtype datetime 235 | ``` 236 | 237 | [*datetime*](#datetime) components are initialized by default, so all arguments are optional. 238 | Arguments may be provided as positional arguments, in the order of their declaration, 239 | or as keyword arguments, in any order. If both positional and keyword arguments are used, 240 | no positional arguments may appear after a keyword argument. 241 | 242 | #### Example usage 243 | 244 | ```fortran 245 | use datetime_module, only:datetime 246 | 247 | type(datetime) :: a 248 | 249 | ! Initialize as default: 250 | a = datetime() ! 0001-01-01 00:00:00 251 | 252 | ! Components can be specified by position: 253 | a = datetime(1984, 12, 10) ! 1984-12-10 00:00:00 254 | 255 | ! Or by keyword: 256 | a = datetime(month=1, day=1, year=1970) ! 1970-01-01 00:00:00 257 | 258 | ! Or combined: 259 | a = datetime(2013, 2, minute=23, day=5) ! 2013-02-05 00:23:00 260 | 261 | ! With timezone offset: 262 | a = datetime(2013, 2, minute=23, day=5, tz=-4) ! 2013-02-05 00:23:00 -0400 263 | 264 | ! Do not use positional after keyword arguments: 265 | a = datetime(year=2013, 2, minute=23, day=5) ! ILLEGAL 266 | ``` 267 | 268 | Note that the current implementation of [*datetime*](#datetime) 269 | does not support daylight saving time (DST) information. 270 | 271 | #### See also 272 | 273 | * [*timedelta*](#timedelta) 274 | 275 | * [*tm_struct*](#tm_struct) 276 | 277 | [Back to top](#top) 278 | 279 |
280 | 281 | ### getYear 282 | 283 | ```fortran 284 | pure elemental integer function getYear(self) 285 | class(datetime),intent(in) :: self 286 | ``` 287 | Returns the year of a `datetime` instance. 288 | 289 | [Back to top](#top) 290 |
291 | 292 | ### getMonth 293 | 294 | ```fortran 295 | pure elemental integer function getMonth(self) 296 | class(datetime),intent(in) :: self 297 | ``` 298 | Returns the month of a `datetime` instance. 299 | 300 | [Back to top](#top) 301 |
302 | 303 | ### getDay 304 | 305 | ```fortran 306 | pure elemental integer function getDay(self) 307 | class(datetime),intent(in) :: self 308 | ``` 309 | Returns the day of a `datetime` instance. 310 | 311 | [Back to top](#top) 312 |
313 | 314 | ### getHour 315 | 316 | ```fortran 317 | pure elemental integer function getHour(self) 318 | class(datetime),intent(in) :: self 319 | ``` 320 | Returns the hour of a `datetime` instance. 321 | 322 | [Back to top](#top) 323 |
324 | 325 | ### getMinute 326 | 327 | ```fortran 328 | pure elemental integer function getMinute(self) 329 | class(datetime),intent(in) :: self 330 | ``` 331 | Returns the minute of a `datetime` instance. 332 | 333 | [Back to top](#top) 334 |
335 | 336 | ### getSecond 337 | 338 | ```fortran 339 | pure elemental integer function getSecond(self) 340 | class(datetime),intent(in) :: self 341 | ``` 342 | Returns the second of a `datetime` instance. 343 | 344 | [Back to top](#top) 345 |
346 | 347 | ### getMillisecond 348 | 349 | ```fortran 350 | pure elemental integer function getMillisecond(self) 351 | class(datetime),intent(in) :: self 352 | ``` 353 | Returns the millisecond of a `datetime` instance. 354 | 355 | [Back to top](#top) 356 |
357 | 358 | ### isocalendar 359 | 360 | ```fortran 361 | function isocalendar(self) 362 | class(datetime),intent(in) :: self 363 | integer,dimension(3) :: isocalendar 364 | ``` 365 | 366 | Returns an array of 3 integers: year, week number, and week day, 367 | as defined by [ISO 8601 week date](http://en.wikipedia.org/wiki/ISO_week_date). 368 | The ISO calendar is a widely used variant of the Gregorian calendar. 369 | The ISO year consists of 52 or 53 full weeks. 370 | A week starts on a Monday (1) and ends on a Sunday (7). 371 | The first week of an ISO year is the first (Gregorian) calendar week 372 | of a year containing a Thursday. 373 | This is called week number 1, and the ISO year of that Thursday 374 | is the same as its Gregorian year. 375 | 376 | [*datetime%isocalendar()*](#isocalendar) is equivalent to Python's 377 | [*datetime.datetime.isocalendar()*](http://docs.python.org/2/library/datetime.html#datetime.datetime.isocalendar). 378 | 379 | #### Example usage 380 | 381 | ```fortran 382 | use datetime_module,only:datetime 383 | 384 | type(datetime) :: a 385 | 386 | a = datetime(2013,1,1) 387 | print *, a % isocalendar() ! Prints: 2013 1 2 388 | ``` 389 | 390 | #### See also 391 | 392 | * [*weekday*](#weekday) 393 | 394 | [Back to top](#top) 395 |
396 | 397 | ### isoformat 398 | 399 | ```fortran 400 | pure elemental character(len=23) function isoformat(self,sep) 401 | class(datetime), intent(in) :: self 402 | character(len=1),intent(in),optional :: sep 403 | ``` 404 | 405 | Returns a character string of length 23 that contains date and time in ISO 8601 406 | format. 407 | 408 | [*datetime%isoformat()*](#isoformat) is equivalent to Python's 409 | [*datetime.datetime.isoformat()*](http://docs.python.org/2/library/datetime.html#datetime.datetime.isoformat), 410 | with the only difference being that [*datetime%isoformat()*](#isoformat) returns the milliseconds 411 | at the end of the string, where as 412 | [*datetime.datetime.isoformat()*](http://docs.python.org/2/library/datetime.html#datetime.datetime.isoformat) 413 | returns microseconds. 414 | 415 | #### Arguments 416 | 417 | `sep` is an optional argument that specifies which character of length 1 will 418 | separate date and time entries. If ommited, defaults to `T`. 419 | 420 | #### Example usage 421 | 422 | ```fortran 423 | use datetime_module,only:datetime 424 | 425 | type(datetime) :: a 426 | 427 | a = datetime(1984,12,10,13,5,0) 428 | 429 | ! Without arguments: 430 | print *, a % isoformat() ! Prints 1984-12-10T13:05:00.000 431 | 432 | ! With a specified separator: 433 | print *, a % isoformat(' ') ! Prints 1984-12-10 13:05:00.000 434 | ``` 435 | 436 | #### See also 437 | 438 | [Back to top](#top) 439 |
440 | 441 | ### isValid 442 | 443 | ```fortran 444 | pure elemental logical function isValid(self) 445 | class(datetime),intent(in) :: self 446 | ``` 447 | 448 | Returns `.true.` if all [*datetime*](#datetime) instance components 449 | have valid values, and .false. otherwise. Components have valid values 450 | if they are within the range indicated in [*datetime*](#datetime) 451 | derived type description. 452 | 453 | Useful for debugging and validating user input. 454 | 455 | #### Example usage 456 | 457 | ```fortran 458 | use datetime_module,only:datetime 459 | 460 | type(datetime) :: a 461 | 462 | a = datetime(1984,12,10,13,5,0) 463 | 464 | print *, a % isValid()! .true. 465 | 466 | a = datetime(1984,13,10,13,5,0) 467 | 468 | print *, a % isValid() ! .false. 469 | ``` 470 | 471 | #### See also 472 | 473 | [Back to top](#top) 474 |
475 | 476 | ### now 477 | 478 | ```fortran 479 | type(datetime) function now(self) 480 | class(datetime),intent(in) :: self 481 | ``` 482 | 483 | Returns the [*datetime*](#datetime) instance representing 484 | the current machine time. Does not support timezones. 485 | 486 | #### Return value 487 | 488 | `self` A `datetime` instance with current machine time. 489 | 490 | #### Example usage 491 | 492 | ```fortran 493 | use datetime_module,only:datetime 494 | 495 | type(datetime) :: a 496 | 497 | a = a % now() ! Assigns current machine time to a 498 | ``` 499 | 500 | [Back to top](#top) 501 |
502 | 503 | ### secondsSinceEpoch 504 | 505 | ```fortran 506 | integer function secondsSinceEpoch(self) 507 | class(datetime),intent(in) :: self 508 | ``` 509 | 510 | Returns an integer number of seconds since the 511 | UNIX Epoch, `1970-01-01 00:00:00 +0000` (UTC). 512 | 513 | #### Return value 514 | 515 | `secondsSinceEpoch` An `integer` scalar containing number of seconds since 516 | UNIX Epoch. 517 | 518 | #### Example usage 519 | 520 | ```fortran 521 | use datetime_module,only:datetime 522 | 523 | type(datetime) :: a 524 | 525 | ! Initialize: 526 | a = datetime(2013,1,1) 527 | 528 | print *, a%secondsSinceEpoch() 529 | ``` 530 | 531 | [Back to top](#top) 532 |
533 | 534 | ### strftime 535 | 536 | ```fortran 537 | character(len=maxstrlen) function strftime(self,format) 538 | class(datetime), intent(in) :: self 539 | character(len=*),intent(in) :: format 540 | ``` 541 | 542 | A *datetime*-bound method that serves as a wrapper around the 543 | C routine *strftime*. 544 | `datetime%strftime` takes only the format string as argument, 545 | and returns the character string representation of the time 546 | information contained in the datetime instance. Thus, this function 547 | takes care of the conversion to `tm_struct` and calling the raw C *strftime*. 548 | Because Fortran does not allow assumed-length character strings as 549 | the type of the function result, a fixed length of `MAXSTRLEN` is used. 550 | `MAXSTRLEN` is currently set to `99`. It is assumed that the desired 551 | time string is shorter than this value. 552 | Any resulting string shorter than `MAXSTRLEN` is padded with spaces, 553 | so it is best to trim the result using the `TRIM` intrinsic function 554 | (see the usage example below). 555 | This *datetime*-bound method is available since version `0.3.0`. 556 | 557 | #### Arguments 558 | 559 | `format` A character string describing the desired format of date and time. 560 | Same as the format for the raw C [*strftime*](#c_strftime). 561 | 562 | #### Return value 563 | 564 | A `character(len=maxstrlen)` representation of *datetime* using `format`. 565 | 566 | #### Example usage 567 | 568 | ```fortran 569 | use datetime_module 570 | 571 | type(datetime) :: a 572 | 573 | a = a % now() 574 | print *, a % isoformat() 575 | 576 | print *, trim(a % strftime("%Y %B %d")) 577 | ``` 578 | 579 | #### See also 580 | 581 | * [*c_strftime*](#c_strftime) 582 | 583 | [Back to top](#top) 584 |
585 | 586 | ### tm 587 | 588 | ```fortran 589 | pure elemental type(tm_struct) function tm(self) 590 | CLASS(datetime),intent(in) :: self 591 | ``` 592 | 593 | Returns a [*tm_struct*](#tm_struct) instance that matches the 594 | time and date information in the caller [*datetime*](#datetime) 595 | instance. 596 | 597 | #### Example usage 598 | 599 | ```fortran 600 | use datetime_module,only:datetime 601 | 602 | type(datetime) :: a 603 | type(tm_struct) :: tm 604 | 605 | ! Initialize: 606 | a = datetime(2013,1,1) 607 | 608 | ! Get tm_struct from datetime: 609 | tm = a % tm() 610 | ``` 611 | 612 | #### See also 613 | 614 | * [*tm_struct*](#tm_struct) 615 | 616 | [Back to top](#top) 617 |
618 | 619 | ### tzOffset 620 | 621 | ```fortran 622 | pure elemental character(len=5) function tzOffset(self) 623 | class(datetime),intent(in) :: self 624 | ``` 625 | 626 | Given a [*datetime*](#datetime) instance, returns a character string with timezone 627 | offset in hours from UTC (Coordinated Universal Time), in format `+hhmm` 628 | or `-hhmm`, depending on the sign, where `hh` are hours and `mm` are minutes. 629 | 630 | #### Arguments 631 | 632 | None. 633 | 634 | #### Return value 635 | 636 | `tzOffset` A `character(len=5)` in the form `+hhmm` 637 | or `-hhmm`, depending on the sign. 638 | 639 | #### Example usage 640 | 641 | ```fortran 642 | use datetime_module,only:datetime 643 | 644 | type(datetime) :: a 645 | type(tm_struct) :: tm 646 | 647 | ! Initialize a datetime instance with timezone offset of -4.75 hours: 648 | a = datetime(2013,1,1,tz=-4.75) 649 | 650 | ! Write tzOffset on screen: 651 | print *, a % tzOffset ! -0445 (offset of 4 hours and 45 minutes) 652 | ``` 653 | 654 | [Back to top](#top) 655 |
656 | 657 | ### utc 658 | 659 | ```fortran 660 | pure elemental type(datetime) function utc(self) 661 | class(datetime),intent(in) :: self 662 | ``` 663 | 664 | Returns the datetime instance at Coordinated Universal Time (UTC). 665 | 666 | #### Return value 667 | 668 | `utc` A `datetime` instance with at UTC (tz = 0). 669 | 670 | #### Example usage 671 | 672 | ```fortran 673 | use datetime_module,only:datetime 674 | 675 | type(datetime) :: a 676 | type(tm_struct) :: tm 677 | 678 | ! Initialize a datetime instance with timezone offset of -4.75 hours: 679 | a = datetime(2013,1,1,tz=-4.75) 680 | 681 | print *, a % isoformat() // a % tzOffset() ! 2013-01-01T00:00:00.000-0445 682 | 683 | ! Convert a to UTC: 684 | a = a % utc() 685 | 686 | print *, a % isoformat() // a % tzOffset() ! 2013-01-01T04:45:00.000+0000 687 | ``` 688 | 689 | #### See also 690 | 691 | * [*tzOffset*](#tzoffset) 692 | 693 | [Back to top](#top) 694 |
695 | 696 | ### weekday 697 | 698 | ```fortran 699 | pure elemental integer function weekday(self) 700 | class(datetime),intent(in) :: self 701 | ``` 702 | 703 | A [*datetime*](#datetime)-bound method to calculate day of the week using 704 | [Zeller's congruence](http://en.wikipedia.org/wiki/Zeller%27s_congruence). 705 | Returns an integer scalar in the range of [0-6], starting from Sunday. 706 | 707 | #### Example usage 708 | 709 | ```fortran 710 | use datetime_module,only:datetime 711 | 712 | type(datetime) :: a 713 | 714 | ! Initialize: 715 | a = datetime(2013,1,1) 716 | 717 | print *, a % weekday() ! 2 718 | ``` 719 | 720 | #### See also 721 | 722 | * [*weekdayLong*](#weekdaylong) 723 | 724 | * [*weekdayShort*](#weekdayshort) 725 | 726 | [Back to top](#top) 727 |
728 | 729 | ### weekdayLong 730 | 731 | ```fortran 732 | pure elemental character(len=9) function weekdayLong(self) 733 | class(datetime),intent(in) :: self 734 | ``` 735 | 736 | Returns the full name of the day of the week. 737 | 738 | #### Example usage 739 | 740 | ```fortran 741 | use datetime_module,only:datetime 742 | 743 | type(datetime) :: a 744 | 745 | ! Initialize: 746 | a = datetime(2013,1,1) 747 | 748 | print *, a % weekdayLong() ! Tuesday 749 | ``` 750 | 751 | #### See also 752 | 753 | * [*weekday*](#weekday) 754 | 755 | * [*weekdayShort*](#weekdayshort) 756 | 757 | [Back to top](#top) 758 |
759 | 760 | ### weekdayShort 761 | 762 | ```fortran 763 | pure elemental character(len=3) function weekdayShort(self) 764 | class(datetime),intent(in) :: self 765 | ``` 766 | 767 | Returns the abbreviated (e.g. Mon) name of the day of the week. 768 | 769 | #### Example usage 770 | 771 | ```fortran 772 | use datetime_module,only:datetime 773 | 774 | type(datetime) :: a 775 | 776 | ! Initialize: 777 | a = datetime(2013,1,1) 778 | 779 | print *, a % weekdayShort() ! Tue 780 | ``` 781 | 782 | #### See also 783 | 784 | * [*weekday*](#weekday) 785 | 786 | * [*weekdayLong*](#weekdaylong) 787 | 788 | [Back to top](#top) 789 |
790 | 791 | ### yearday 792 | 793 | ```fortran 794 | pure elemental integer function yearday(self) 795 | class(datetime),intent(in) :: self 796 | ``` 797 | 798 | [*datetime*](#datetime)-bound procedure. 799 | Returns integer day of the year (ordinal date). 800 | Equals to `1` for any January 1, `365` for a December 31 on a non-leap year, 801 | and `366` for a December 31 on a leap year. 802 | 803 | #### Example usage 804 | 805 | ```fortran 806 | use datetime_module,only:datetime 807 | 808 | type(datetime) :: a 809 | 810 | ! Initialize: 811 | a = datetime(2013,5,1) 812 | 813 | print *, a % yearday() ! 121 814 | ``` 815 | 816 | #### See also 817 | 818 | * [*isocalendar*](#isocalendar) 819 | 820 | [Back to top](#top) 821 |
822 | 823 | ### timedelta 824 | 825 | Represents a duration of time, and a difference between 826 | two [*datetime*](#datetime) objects. It is defined as: 827 | 828 | ```fortran 829 | type :: timedelta 830 | 831 | !! Class of objects that define difference between two datetime 832 | !! instances. 833 | 834 | private 835 | 836 | integer :: days = 0 !! number of days 837 | integer :: hours = 0 !! number of hours 838 | integer :: minutes = 0 !! number of minutes 839 | integer :: seconds = 0 !! number of seconds 840 | integer :: milliseconds = 0 !! number of milliseconds 841 | 842 | contains 843 | 844 | ! getter functions 845 | procedure,pass(self),public :: getDays 846 | procedure,pass(self),public :: getHours 847 | procedure,pass(self),public :: getMinutes 848 | procedure,pass(self),public :: getSeconds 849 | procedure,pass(self),public :: getMilliseconds 850 | 851 | ! public methods 852 | procedure,public :: total_seconds 853 | 854 | ! operator overloading procedures 855 | procedure,private :: timedelta_plus_timedelta 856 | procedure,private :: timedelta_minus_timedelta 857 | procedure,private :: unary_minus_timedelta 858 | procedure,private :: eq 859 | procedure,private :: neq 860 | procedure,private :: gt 861 | procedure,private :: ge 862 | procedure,private :: lt 863 | procedure,private :: le 864 | 865 | generic :: operator(+) => timedelta_plus_timedelta 866 | generic :: operator(-) => timedelta_minus_timedelta,& 867 | unary_minus_timedelta 868 | generic :: operator(==) => eq 869 | generic :: operator(/=) => neq 870 | generic :: operator(>) => gt 871 | generic :: operator(>=) => ge 872 | generic :: operator(<) => lt 873 | generic :: operator(<=) => le 874 | 875 | endtype timedelta 876 | ``` 877 | 878 | All arguments are optional and default to 0. 879 | Similarly to [*datetime*](#datetime) objects, 880 | [*timedelta*](#timedelta) instances can be initialized 881 | using positional and/or keyword arguments. 882 | In addition, a [*timedelta*](#timedelta) object is a result 883 | of subtraction between two [*datetime*](#datetime) objects. 884 | 885 | #### Example usage 886 | 887 | ```fortran 888 | use datetime_module 889 | 890 | type(datetime) :: a,b 891 | type(timedelta) :: c 892 | 893 | ! Initialize as default 894 | c = timedelta() 895 | 896 | ! Positional arguments: 897 | c = timedelta(0,1,15,0,0) ! 1 hour and 15 minutes 898 | 899 | ! Keyword arguments: 900 | c = timedelta(days=1,hours=12) ! 1 day and 12 hours 901 | 902 | ! Difference between datetimes: 903 | a = datetime(2013,5,12,22,0,0) ! 2013-05-12 22:00:00 904 | b = datetime(2012,9,18,14,0,0) ! 2012-09-18 14:00:00 905 | 906 | ! Subtract to get timedelta: 907 | c = a-b 908 | ``` 909 | 910 | [Back to top](#top) 911 |
912 | 913 | ### total_seconds 914 | 915 | ```fortran 916 | pure elemental real(kind=real_dp) function total_seconds(self) 917 | class(timedelta), intent(in) :: self 918 | ``` 919 | 920 | A [*timedelta*](#timedelta)-bound method that returns a number 921 | of seconds contained in the time interval defined by the 922 | [*timedelta*](#timedelta) instance. This method is equivalent 923 | to Python's [*datetime.timedelta.total_seconds*](http://docs.python.org/2/library/datetime.html#timedelta-objects) function. 924 | 925 | #### Return value 926 | 927 | `total_seconds` A total number of seconds (of type `real(kind=real_dp)`) 928 | contained in the [*timedelta*](#timedelta) instance. 929 | 930 | #### Example usage 931 | 932 | ```fortran 933 | use datetime_module,only:timedelta 934 | 935 | type(timedelta) :: td 936 | 937 | td = timedelta(days=5,hours=12,minutes=15,seconds=7,milliseconds=123) 938 | 939 | print *, td%total_seconds() ! 476107.12300000002 940 | ``` 941 | 942 | [Back to top](#top) 943 |
944 | 945 | ### clock 946 | 947 | A generic clock object that contains start and stop times, 948 | tick increment and reset and tick methods. 949 | Most useful when needing to keep track of many [*datetime*](#datetime) instances 950 | that change at different rates, for example, physical models with different 951 | time steps. 952 | 953 | Definition: 954 | 955 | ```fortran 956 | type :: clock 957 | 958 | !! A clock object with a start, stop and current times, tick interval 959 | !! and tick methods. 960 | 961 | type(datetime) :: startTime 962 | type(datetime) :: stopTime 963 | type(datetime) :: currentTime 964 | 965 | type(timedelta) :: tickInterval 966 | 967 | logical :: alarm = .false. 968 | 969 | ! Clock status flags 970 | logical :: started = .false. 971 | logical :: stopped = .false. 972 | 973 | contains 974 | 975 | procedure :: reset 976 | procedure :: tick 977 | 978 | endtype clock 979 | ``` 980 | 981 | [*clock*](#clock) instance must be initialized 982 | with some sane values of `clock%startTime`, `clock%stopTime` and `clock%tickIncrement` 983 | in order to be useful. 984 | 985 | #### Example usage 986 | 987 | ```fortran 988 | use datetime_module 989 | 990 | type(clock) :: myClock 991 | type(datetime) :: myTime 992 | 993 | ! Initialize myTime 994 | myTime = myTime%now() 995 | 996 | ! Initialize myClock 997 | ! Starts from myTime, stops 1 hour later, 1 minute per tick 998 | myClock = clock(startTime = myTime, & 999 | stopTime = myTime+timedelta(hours=1),& 1000 | tickInterval = timedelta(minutes=1)) 1001 | 1002 | do 1003 | 1004 | call myClock % tick() 1005 | 1006 | ! Report current time after each tick 1007 | print *, myClock % currentTime % isoformat(' ') 1008 | 1009 | ! If clock has reached stopTime, exit loop 1010 | if(myClock % stopped)THEN 1011 | exit 1012 | endif 1013 | 1014 | enddo 1015 | ``` 1016 | #### See also 1017 | 1018 | * [*datetime*](#datetime) 1019 | 1020 | * [*timedelta*](#timedelta) 1021 | 1022 | 1023 | [Back to top](#top) 1024 |
1025 | 1026 | ### reset 1027 | 1028 | ```fortran 1029 | pure elemental subroutine reset(self) 1030 | class(clock),intent(inout) :: self 1031 | ``` 1032 | 1033 | Resets the clock to its start time. 1034 | 1035 | #### Example usage 1036 | 1037 | ```fortran 1038 | call myClock%reset() ! Resets myClock%currentTime to myClock%startTime 1039 | ``` 1040 | 1041 | [Back to top](#top) 1042 |
1043 | 1044 | ### tick 1045 | 1046 | ```fortran 1047 | pure elemental subroutine tick(self) 1048 | class(clock),intent(inout) :: self 1049 | ``` 1050 | 1051 | Increments the `currentTime` of the clock instance by one `tickInterval`. 1052 | Sets the `clock%stopped` flag to `.TRUE.` if `clock%currentTime` equals 1053 | or exceeds `clock%stopTime`. 1054 | 1055 | #### Example usage 1056 | 1057 | See [*clock*](#clock) for an example. 1058 | 1059 | #### See also 1060 | 1061 | [Back to top](#top) 1062 |
1063 | 1064 | ### **tm_struct** 1065 | 1066 | Time object compatible with C/C++ *tm* struct. Available mainly 1067 | for the purpose of calling [*c_strftime*](#c_strftime) 1068 | and [*c_strptime*](#c_strptime) procedures. 1069 | 1070 | ```fortran 1071 | type,bind(c) :: tm_struct 1072 | 1073 | !! A derived type provided for compatibility with C/C++ time struct. 1074 | !! Allows for calling strftime and strptime procedures through the 1075 | !! iso_c_binding. 1076 | 1077 | integer(kind=c_int) :: tm_sec !! Seconds [0-60] (1 leap second) 1078 | integer(kind=c_int) :: tm_min !! Minutes [0-59] 1079 | integer(kind=c_int) :: tm_hour !! Hours [0-23] 1080 | integer(kind=c_int) :: tm_mday !! Day [1-31] 1081 | integer(kind=c_int) :: tm_mon !! Month [0-11] 1082 | integer(kind=c_int) :: tm_year !! Year - 1900 1083 | integer(kind=c_int) :: tm_wday !! Day of week [0-6] 1084 | integer(kind=c_int) :: tm_yday !! Days in year [0-365] 1085 | integer(kind=c_int) :: tm_isdst !! DST [-1/0/1] 1086 | 1087 | endtype tm_struct 1088 | ``` 1089 | 1090 | #### See also 1091 | 1092 | * [*datetime*](#datetime) 1093 | 1094 | * [*tm*](#tm) 1095 | 1096 | * [*strftime*](#c_strftime) 1097 | 1098 | * [*strptime*](#c_strptime) 1099 | 1100 | [Back to top](#top) 1101 |
1102 | 1103 | ## Overloaded operators 1104 | 1105 | *datetime-fortran* provides arithmetic and comparison operators 1106 | for [*datetime*](#datetime) and [*timedelta*](#timedelta) objects. 1107 | 1108 | ### Arithmetic operators 1109 | 1110 | Addition (`+`) and subtraction (`-`) operators are available for 1111 | the following combination of derived type pairs: 1112 | 1113 | * `datetime + timedelta`, returns a `datetime` instance; 1114 | 1115 | * `timedelta + datetime`, returns a `datetime` instance; 1116 | 1117 | * `timedelta + timedelta`, returns a `timedelta` instance; 1118 | 1119 | * `timedelta - timedelta`, returns a `timedelta` instance; 1120 | 1121 | * `datetime - datetime`, returns a `timedelta` instance; 1122 | 1123 | * `-timedelta` (unary minus), returns a `timedelta` instance. 1124 | 1125 | Note that `datetime - datetime` operation accounts for timezone (`tz`) 1126 | offsets in each of the `datetime` instances. 1127 | The resulting `timedelta`thus includes the difference between timezones. 1128 | 1129 | ### Comparison operators 1130 | 1131 | *datetime-fortran* supports following binary comparison operators for 1132 | [*datetime*](#datetime) and [*timedelta*](#timedelta) objects: 1133 | `==`, `/=`, `>`, `>=`, `<` and `<=`. 1134 | 1135 | Since version 1.0.5, all comparison operators respect the timezone 1136 | parameter of the datetime instances, so the operands are first 1137 | adjusted to UTC time before making the comparison. 1138 | 1139 | [Back to top](#top) 1140 |
1141 | 1142 | ## Public procedures 1143 | 1144 | ### c_strftime 1145 | 1146 | ```fortran 1147 | function c_strftime(str,slen,format,tm) bind(c,name='strftime') result(rc) 1148 | character(kind=c_char),dimension(*),intent(out) :: str 1149 | integer(kind=c_int),value, intent(in) :: slen 1150 | character(kind=c_char),dimension(*),intent(in) :: format 1151 | type(tm_struct), intent(in) :: tm 1152 | integer(kind=c_int) :: rc 1153 | ``` 1154 | 1155 | An interface to a C/C++ standard library routine. 1156 | Copies into `str` the content of format, expanding its format specifiers 1157 | into the corresponding values that represent the time described in `tm`, 1158 | with a limit of `slen` characters. 1159 | 1160 | Note: This function was renamed from *strftime* to *c_strftime* in version 0.3.0 1161 | to avoid name conflict with *datetime*-bound method [*strftime*](#strftime). 1162 | If working with *datetime* instances, use [*datetime%strftime*](#strftime) instead. 1163 | 1164 | #### Arguments 1165 | 1166 | `str` is the destination character string with the requested date and time. 1167 | 1168 | `slen` is the maximum number of characters to be copied to `str`, 1169 | including the terminating null-character, `char(0)`. 1170 | 1171 | `format` is the character string containing any combination of regular characters and special format specifiers. 1172 | These format specifiers are replaced by the function to the corresponding values to represent the time specified in `tm`. 1173 | For more information on format specifiers see http://www.cplusplus.com/reference/ctime/strftime/. 1174 | 1175 | `tm` is an instance of the type `tm_struct`, containing date and time values to be processed. 1176 | 1177 | #### Return value 1178 | 1179 | If the resulting string fits in less than `slen` characters including the terminating null-character, 1180 | the total number of characters copied to `str` (not including the terminating null-character) is returned. 1181 | Otherwise, zero is returned and the contents of the array are indeterminate. 1182 | 1183 | #### Example usage 1184 | 1185 | ```fortran 1186 | use datetime_module 1187 | 1188 | type(datetime) :: a 1189 | character(len=20) :: res 1190 | integer :: rc 1191 | 1192 | a = a % now() 1193 | 1194 | rc = c_strftime(res,20,"%Y %B %d"//CHAR(0),a%tm()) 1195 | ``` 1196 | 1197 | #### See also 1198 | 1199 | * [*datetime%strftime*](#strftime) 1200 | 1201 | * [*c_strptime*](#c_strptime) 1202 | 1203 | * [*strptime*](#strptime) 1204 | 1205 | * [*tm*](#tm) 1206 | 1207 | * [*tm_struct*](#tm_struct) 1208 | 1209 | 1210 | [Back to top](#top) 1211 |
1212 | 1213 | ### c_strptime 1214 | 1215 | ```fortran 1216 | function c_strptime(str,format,tm) bind(c,name='strptime') result(rc) 1217 | character(kind=c_char),dimension(*),intent(in) :: str 1218 | character(kind=c_char),dimension(*),intent(in) :: format 1219 | type(tm_struct), intent(out) :: tm 1220 | character(kind=c_char,len=1) :: rc 1221 | ``` 1222 | 1223 | An interface to a C/C++ standard library routine. 1224 | Converts the character string `str` to values which are stored in `tm`, using the format specified by `format`. 1225 | 1226 | Note: This function was renamed from *strptime* to *c_strptime* in version 0.3.0 to avoid 1227 | name conflicts with [*strptime*](#strptime) which operates on *datetime* instances. 1228 | If working with *datetime* instances, use [*strptime*](#strptime) instead. 1229 | 1230 | #### Arguments 1231 | 1232 | `str` is the character string containing date and time information. 1233 | 1234 | `format` is the character string containing any combination of regular characters and special format specifiers, 1235 | describing the date and time information in `str`. 1236 | 1237 | `tm` is an instance of the type `tm_struct`, in which the date and time values will be filled upon successful completion 1238 | of the [*c_strptime*](#c_strptime) function. 1239 | 1240 | #### Return value 1241 | 1242 | Upon successful completion, [*c_strptime*](#c_strptime) returns the character 1243 | following the last character parsed. Otherwise, a null character is returned. 1244 | 1245 | #### Example usage 1246 | 1247 | Extracting time difference between two time strings using [*c_strptime*](#c_strptime) 1248 | and [*tm2date*](#tm2date): 1249 | 1250 | ```fortran 1251 | use datetime_module 1252 | 1253 | type(datetime) :: date1,date2 1254 | type(tm_struct) :: ctime 1255 | type(timedelta) :: timediff 1256 | 1257 | ! Return code for strptime 1258 | character(len=1) :: rc 1259 | 1260 | ! Example times in "YYYYMMDD hhmmss" format 1261 | character(len=15) :: str1 = "20130512 091519" 1262 | character(len=15) :: str2 = "20131116 120418" 1263 | 1264 | ! Get tm_struct instance from str1 1265 | rc = c_strptime(str1,"%Y%m%d %H%M%S"//char(0),ctime) 1266 | date1 = tm2date(ctime) 1267 | 1268 | ! Get tm_struct instance from str2 1269 | rc = c_strptime(str2,"%Y%m%d %H%M%S"//char(0),ctime) 1270 | date2 = tm2date(ctime) 1271 | 1272 | timediff = date2-date1 1273 | 1274 | print *, timediff 1275 | print *, timediff % total_seconds() 1276 | ``` 1277 | 1278 | This example outputs the following: 1279 | 1280 | ``` 1281 | 188 2 48 58 1000 1282 | 16253339.0000000 1283 | ``` 1284 | 1285 | #### See also 1286 | 1287 | * [*strptime*](#strptime) 1288 | 1289 | * [*tm2date*](#tm2date) 1290 | 1291 | [Back to top](#top) 1292 |
1293 | 1294 | ### date2num 1295 | 1296 | ```fortran 1297 | pure elemental real(kind=real_dp) function date2num(d) 1298 | type(datetime),intent(in) :: d 1299 | ``` 1300 | 1301 | Returns the number of days since `0001-01-01 00:00:00 UTC`, 1302 | given a [*datetime*](#datetime) instance `d`. 1303 | 1304 | This function is similar in what it returns to analogous functions 1305 | in Python ([*matplotlib.dates.date2num*](http://matplotlib.org/api/dates_api.html#matplotlib.dates.date2num)) 1306 | and MATLAB's [*datenum*](http://www.mathworks.com/help/matlab/ref/datenum.html). 1307 | Note that [*matplotlib.dates.date2num*](http://matplotlib.org/api/dates_api.html#matplotlib.dates.date2num) returns the number of days since `0001-01-01 00:00:00 UTC` plus `1` 1308 | (for historical reasons), 1309 | and MATLAB's [*datenum*](http://www.mathworks.com/help/matlab/ref/datenum.html) 1310 | returns the number of days since `0000-01-01 00:00:00 UTC`. 1311 | In *datetime-fortran*, we choose the reference time of `0001-01-01 00:00:00 UTC` 1312 | as we consider it to be the least astonishing for the average user. 1313 | Thus, MATLAB and Python users should be cautious when using 1314 | *datetime-fortran*'s [*date2num()*](#date2num) function. 1315 | 1316 | Since version 1.0.5, [date2num](#date2num) is timezone aware, i.e. 1317 | the datetime instance is first converted to UTC before calculating 1318 | the number of days. 1319 | 1320 | [date2num](#date2num) is the inverse function of [num2date](#num2date), 1321 | so by definition, `a % utc() == num2date(date2num(a))` evaluates as `.true.` 1322 | for any `datetime` instance `a`. 1323 | 1324 | #### Arguments 1325 | 1326 | `d` A [*datetime*](#datetime) instance. 1327 | 1328 | #### Return value 1329 | 1330 | `date2num` A `REAL(KIND=real64)` number of days since `0001-01-01 00:00:00 UTC`. 1331 | 1332 | #### Example usage 1333 | 1334 | ```fortran 1335 | use datetime_module,only:datetime,date2num 1336 | 1337 | type(datetime) :: a 1338 | 1339 | ! Initialize: 1340 | a = datetime(2013,1,1,6) 1341 | 1342 | print *, date2num(a) ! 734869.25000000000 1343 | ``` 1344 | 1345 | #### See also 1346 | 1347 | * [*datetime*](#datetime) 1348 | 1349 | * [*num2date*](#num2date) 1350 | 1351 | [Back to top](#top) 1352 |
1353 | 1354 | ### datetimeRange 1355 | 1356 | ```fortran 1357 | pure function datetimeRange(d0,d1,t) 1358 | type(datetime), intent(in) :: d0 1359 | type(datetime), intent(in) :: d1 1360 | type(timedelta),intent(in) :: t 1361 | ``` 1362 | 1363 | Given start and end [*datetime*](#datetime) instances `d0` and `d1`, 1364 | and time increment as [*timedelta*](#timedelta) instance `t`, 1365 | returns an array of datetime instances. 1366 | The number of elements is the number of whole time increments 1367 | contained between datetimes `d0` and `d1`. 1368 | 1369 | #### Arguments 1370 | 1371 | `d0` A [*datetime*](#datetime) instance with start time. Will be the first element 1372 | of the resulting array. 1373 | 1374 | `d1` A [*datetime*](#datetime) instance with end time. Will be the equal to or greater than 1375 | the last element of the resulting array. 1376 | 1377 | `t` A [*timedelta*](#timedelta) instance being the time increment for the resulting array. 1378 | 1379 | #### Return value 1380 | 1381 | `datetimeRange` An array of [*datetime*](#datetime) instances of length 1382 | `floor((d1-d0)/t)+1` 1383 | 1384 | #### Example usage 1385 | 1386 | ```fortran 1387 | type(datetime) :: a,b 1388 | type(timedelta) :: td 1389 | 1390 | type(datetime),dimension(:),allocatable :: dtRange 1391 | 1392 | a = datetime(2014,5,1) 1393 | b = datetime(2014,5,3) 1394 | td = timedelta(days=1) 1395 | 1396 | dtRange = datetimeRange(a,b,td) 1397 | 1398 | ! Returns: 1399 | ! 1400 | ! dtRange = [datetime(2014,5,1), 1401 | ! datetime(2014,5,2), 1402 | ! datetime(2014,5,3)] 1403 | 1404 | a = datetime(2014,5,1) 1405 | b = datetime(2014,5,3) 1406 | td = timedelta(hours=7) 1407 | 1408 | dtRange = datetimeRange(a,b,td) 1409 | 1410 | ! Returns: 1411 | ! 1412 | ! dtRange = [datetime(2014,5,1,0), 1413 | ! datetime(2014,5,1,7), 1414 | ! datetime(2014,5,1,14), 1415 | ! datetime(2014,5,1,21), 1416 | ! datetime(2014,5,2, 4), 1417 | ! datetime(2014,5,2,11), 1418 | ! datetime(2014,5,2,18)] 1419 | ``` 1420 | 1421 | #### See also 1422 | 1423 | * [*datetime*](#datetime) 1424 | 1425 | * [*timedelta*](#timedelta) 1426 | 1427 | [Back to top](#top) 1428 |
1429 | 1430 | ### daysInMonth 1431 | 1432 | ```fortran 1433 | pure elemental integer function daysInMonth(month,year) 1434 | integer,intent(in) :: month 1435 | integer,intent(in) :: year 1436 | ``` 1437 | 1438 | Returns the number of days in month for a given month and year. 1439 | This function is declared as `elemental`, so it can be called 1440 | with scalar or n-dimensional array arguments. 1441 | 1442 | #### Arguments 1443 | 1444 | `month` Integer number of month in year. Valid values are in the range [1-12]. 1445 | 1446 | `year` Integer year. 1447 | 1448 | #### Return value 1449 | 1450 | Returns an integer number of days in requested month and year. 1451 | Returns `0` if `month` is not in valid range. 1452 | 1453 | #### Example usage 1454 | 1455 | ```fortran 1456 | use datetime_module,only:daysInMonth 1457 | 1458 | ! January on leap year: 1459 | print *, daysInMonth(1,2012) ! 31 1460 | 1461 | ! February on leap year: 1462 | print *, daysInMonth(2,2012) ! 29 1463 | 1464 | ! February on non-leap year 1465 | print *, daysInMonth(2,2013) ! 28 1466 | ``` 1467 | 1468 | #### See also 1469 | 1470 | * [*daysInYear*](#daysinyear) 1471 | 1472 | [Back to top](#top) 1473 |
1474 | 1475 | ### daysInYear 1476 | 1477 | ```fortran 1478 | pure elemental integer Function daysInYear(year) 1479 | integer,intent(in) :: year 1480 | ``` 1481 | 1482 | Given an integer `year`, returns an integer number of days in that year. 1483 | Calls the [*isLeapYear*](#isleapyear) function. 1484 | 1485 | #### Arguments 1486 | 1487 | `year` An `integer` scalar or array containing the desired year number(s). 1488 | 1489 | #### Return value 1490 | 1491 | `daysInYear` An `integer` scalar or array. Represents the number of days in `year`. 1492 | 1493 | #### Example usage 1494 | 1495 | ```fortran 1496 | use datetime_module,only:daysInYear 1497 | 1498 | ! Leap year: 1499 | print *, daysInYear(2012) ! 366 1500 | 1501 | ! Non-leap year: 1502 | print *, daysInYear(2013) ! 365 1503 | ``` 1504 | 1505 | #### See also 1506 | 1507 | * [*daysInMonth*](#daysinmonth) 1508 | 1509 | * [*isLeapYear*](#isleapyear) 1510 | 1511 | [Back to top](#top) 1512 |
1513 | 1514 | ### isLeapYear 1515 | 1516 | ```fortran 1517 | pure elemental logical function isLeapYear(year) 1518 | integer,intent(in) :: year 1519 | ``` 1520 | 1521 | Returns a `logical` value indicating whether the reqested year is a leap year. 1522 | 1523 | #### Arguments 1524 | 1525 | `year` An `integer` scalar or array representing year number. 1526 | 1527 | #### Return value 1528 | 1529 | `isLeapYear` A `logical` scalar or array indicating whether a given year is leap year. 1530 | 1531 | #### Example usage 1532 | 1533 | ```fortran 1534 | use datetime_module,only:isLeapYear 1535 | 1536 | ! Leap year: 1537 | print *, isLeapYear(2012) ! .true. 1538 | 1539 | ! Non-leap year: 1540 | print *, isLeapYear(2013) ! .false. 1541 | ``` 1542 | 1543 | #### See also 1544 | 1545 | * [*daysInYear*](#daysInYear) 1546 | 1547 | [Back to top](#top) 1548 |
1549 | 1550 | ### num2date 1551 | 1552 | ```fortran 1553 | pure elemental type(datetime) function num2date(num) 1554 | real(kind=real_dp),intent(in) :: num 1555 | ``` 1556 | 1557 | Given the number of days since `0001-01-01 00:00:00 UTC`, returns a 1558 | correspoding [datetime](#datetime) instance. 1559 | 1560 | This function is similar to analogous function 1561 | in Python ([*matplotlib.dates.num2date*](http://matplotlib.org/api/dates_api.html#matplotlib.dates.num2date)). 1562 | 1563 | [num2date](#num2date) is the inverse function of [date2num](#date2num), 1564 | so by definition, `a == num2date(date2num(a))` evaluates as `.true.` 1565 | for any `datetime` instance `a`. 1566 | Similarly, `b == date2num(num2date(b))` evaluates as `.true.` 1567 | for any variable `b` of type `real(kind=real64)`. 1568 | 1569 | #### Arguments 1570 | 1571 | `num` Number of days since `0001-01-01 00:00:00 UTC`. 1572 | 1573 | #### Return value 1574 | 1575 | `num2date` A [*datetime*](#datetime) instance. 1576 | 1577 | #### Example usage 1578 | 1579 | ```fortran 1580 | use datetime_module,only:datetime,num2date 1581 | 1582 | type(datetime) :: a 1583 | 1584 | a = num2date(734869.25d0) ! a becomes datetime(2013,1,1,6,0,0,0) 1585 | ``` 1586 | 1587 | #### See also 1588 | 1589 | * [*date2num*](#date2num) 1590 | 1591 | * [*datetime*](#datetime) 1592 | 1593 | [Back to top](#top) 1594 |
1595 | 1596 | ### strptime 1597 | 1598 | ```fortran 1599 | type(datetime) function strptime(str,format) 1600 | character(len=*),intent(in) :: str 1601 | character(len=*),intent(in) :: format 1602 | ``` 1603 | 1604 | A wrapper function around [*c_strptime*](#c_strptime). 1605 | Given a character string `str` with the format `format`, returns 1606 | an appropriate *datetime* instance containing that time information. 1607 | This function is analogous to Python's *datetime.datetime.strptime()* function. 1608 | Available since version 0.3.0. 1609 | 1610 | #### Arguments 1611 | 1612 | `str` is the character string containing date and time information. 1613 | 1614 | `format` is the character string containing any combination of regular characters and special format specifiers, 1615 | describing the date and time information in `str`. 1616 | 1617 | #### Return value 1618 | 1619 | Upon successful completion, [*strptime*](#strptime) returns the [*datetime*](#datetime) 1620 | instance corresponding to the time information contained in *str*. 1621 | 1622 | #### Example usage 1623 | 1624 | Extracting time difference between two time strings using [*strptime*](#strptime): 1625 | 1626 | ```fortran 1627 | use datetime_module 1628 | 1629 | type(datetime) :: date1,date2 1630 | type(timedelta) :: timediff 1631 | 1632 | ! Example times in "YYYYMMDD hhmmss" format 1633 | character(len=15) :: str1 = "20130512 091519" 1634 | character(len=15) :: str2 = "20131116 120418" 1635 | 1636 | date1 = strptime(str1,"%Y%m%d %H%M%S") 1637 | date2 = strptime(str2,"%Y%m%d %H%M%S") 1638 | 1639 | timediff = date2-date1 1640 | 1641 | print *, timediff 1642 | print *, timediff%total_seconds() 1643 | ``` 1644 | 1645 | This example outputs the following: 1646 | 1647 | ``` 1648 | 188 2 48 58 1000 1649 | 16253339.0000000 1650 | ``` 1651 | This is the same example as in [*c_strptime*](#c_strptime) but with fewer 1652 | necessary steps. 1653 | 1654 | #### See also 1655 | 1656 | * [*c_strptime*](#c_strptime) 1657 | 1658 | * [*tm2date*](#tm2date) 1659 | 1660 | [Back to top](#top) 1661 |
1662 | 1663 | ### tm2date 1664 | 1665 | ```fortran 1666 | pure elemental type(datetime) function tm2date(ctime) 1667 | type(tm_struct),intent(in) :: ctime 1668 | ``` 1669 | 1670 | Given a [*tm_struct*](#tm_struct) instance, 1671 | returns a corresponding [*datetime*](#datetime) instance. 1672 | Mostly useful for obtaining a *datetime* instance after a *tm_struct* 1673 | is returned from [*strptime*](#strptime). 1674 | 1675 | #### Arguments 1676 | 1677 | `ctime` A `tm_struct` instance. 1678 | 1679 | #### Return value 1680 | 1681 | `tm2date` A `datetime` instance. 1682 | 1683 | #### Example usage 1684 | 1685 | See example usage for [*strptime*](#strptime). 1686 | 1687 | #### See also 1688 | 1689 | * [*datetime*](#datetime) 1690 | 1691 | * [*tm_struct*](#tm_struct) 1692 | 1693 | * [*strptime*](#strptime) 1694 | 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 |
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 |
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 |
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 | 1845 | [Back to top](#top) 1846 |
1847 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 1.8.0 2 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /cmake/check_strftime.cmake: -------------------------------------------------------------------------------- 1 | include(CheckFortranSourceRuns) 2 | 3 | check_fortran_source_runs( 4 | "program test 5 | use iso_c_binding 6 | implicit none 7 | type,bind(c) :: tm_struct 8 | integer(C_INT) :: tm_sec=0,tm_min=0,tm_hour=0,tm_mday=1,tm_mon=0,tm_year=100,tm_wday=0,tm_yday=0,tm_isdst=0 9 | end type 10 | interface 11 | type(C_PTR) function strftime(str, slen, format, tm) bind(c) 12 | import 13 | character(kind=c_char), intent(out) :: str(*) 14 | integer(c_int), value, intent(in) :: slen 15 | character(kind=c_char), intent(in) :: format(*) 16 | type(tm_struct), intent(in) :: tm 17 | end function 18 | end interface 19 | 20 | type(C_PTR) :: rc 21 | character(kind=c_char,len=10) :: str 22 | character(kind=c_char,len=8) :: fmt = '%G %V %u' 23 | type(tm_struct) :: tm 24 | rc = strftime(str, len(str, kind=C_INT), fmt // c_null_char, tm) 25 | if(.not. c_associated(rc)) error stop 'strftime failed' 26 | print '(a)', str(:index(str, c_null_char)-1) 27 | end program" 28 | HAVE_C_STRFTIME 29 | ) 30 | -------------------------------------------------------------------------------- /cmake/check_strptime.cmake: -------------------------------------------------------------------------------- 1 | # some compilers (e.g. MinGW) may not have strptime in C 2 | 3 | include(CheckFortranSourceCompiles) 4 | 5 | check_fortran_source_compiles( 6 | "program test 7 | use iso_c_binding 8 | implicit none 9 | type,bind(c) :: tm_struct 10 | integer(C_INT) :: tm_sec,tm_min,tm_hour,tm_mday,tm_mon,tm_year,tm_wday,tm_yday,tm_isdst 11 | end type 12 | interface 13 | integer(C_INT) function strptime(str,format,tm) bind(C) 14 | import 15 | character(kind=c_char), intent(in) :: str(*), format(*) 16 | type(tm_struct), intent(out) :: tm 17 | end function 18 | end interface 19 | 20 | integer(C_INT) :: rc 21 | character(kind=c_char, len=20), parameter :: str = '2018-01-01 12:00:00' 22 | character(kind=c_char, len=18), parameter :: fmt = '%Y-%m-%d %H:%M:%S' 23 | type(tm_struct) :: tm 24 | 25 | rc = strptime(str,fmt,tm) 26 | 27 | end program" 28 | HAVE_C_STRPTIME 29 | SRC_EXT f90 30 | ) 31 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_PREREQ([2.69]) 2 | AC_INIT([datetime-fortran], [1.7.1]) 3 | AC_CONFIG_SRCDIR([src/datetime_module.f90]) 4 | 5 | # foreign flag tells that we are not conforming to the GNU standards 6 | # and hence can ommit NEWS, README, COPYING 7 | AM_INIT_AUTOMAKE([foreign]) 8 | 9 | # for package configuration 10 | PKG_PROG_PKG_CONFIG 11 | PKG_INSTALLDIR 12 | 13 | AC_PROG_FC([gfortran], [Fortran 90]) # we need a Fortran 90 compiler 14 | # If we are using gfortran, but user hasn't set FCFLAGS for 'configure' 15 | # then override autotool defaults 16 | AS_IF([test x$FC = xgfortran -a x$ac_cv_env_FCFLAGS_set = x], 17 | AC_SUBST([FCFLAGS], ["-Wall"]) 18 | [# Set some flags automatically if using gfortran]) 19 | 20 | AC_PROG_RANLIB # needed to create libraries 21 | 22 | AC_CONFIG_FILES([Makefile src/Makefile tests/Makefile]) 23 | # Generate a PC file so that others can easily detect our libraries: 24 | AC_CONFIG_FILES([datetime-fortran.pc:datetime-fortran.pc.in]) 25 | 26 | AC_OUTPUT 27 | -------------------------------------------------------------------------------- /datetime-fortran.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | 3 | Name: @PACKAGE_NAME@ 4 | Description: A Fortran library that provides time and date manipulation facilities 5 | Version: @PACKAGE_VERSION@ 6 | Libs: -L${prefix}/lib -ldatetime 7 | Cflags: -I${prefix}/include -------------------------------------------------------------------------------- /example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(add_and_subtract add_and_subtract.f90) 2 | target_link_libraries(add_and_subtract datetime) 3 | add_test(NAME "add_and_subtract" COMMAND $) 4 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # datetime-fortran examples 2 | 3 | Examples of what datetime-fortran can do. 4 | -------------------------------------------------------------------------------- /example/add_and_subtract.f90: -------------------------------------------------------------------------------- 1 | program add_and_subtract 2 | !! Adds and subtracts datetimes 3 | use datetime_module, only : timedelta, datetime 4 | 5 | implicit none 6 | 7 | type (datetime) :: a, b 8 | type (timedelta) :: t 9 | 10 | !> add 86400 seconds 11 | a = datetime(2020, 2, 28) 12 | b = a + timedelta(seconds=86400) 13 | if (b /= datetime(2020, 2, 29)) error stop 'expected 2020-02-29' 14 | 15 | !> subtract 16 | b = b - timedelta(seconds=35) 17 | if (b /= datetime(2020, 2, 28, 23, 59, 25)) error stop 'expected 2020-02-28T23:59:25' 18 | 19 | !> total_seconds is floating point 20 | t = b - b - timedelta(milliseconds=234) 21 | 22 | if (abs(t%total_seconds() + 0.234) > 0.0001) error stop 'expected -0.234 seconds' 23 | 24 | end program add_and_subtract 25 | -------------------------------------------------------------------------------- /fpm.toml: -------------------------------------------------------------------------------- 1 | name = "datetime" 2 | version = "1.8.0" 3 | license = "MIT" 4 | author = "Milan Curcic" 5 | maintainer = "caomaco@gmail.com" 6 | copyright = "2013-2023 datetime-fortran contributors" 7 | -------------------------------------------------------------------------------- /scrub-clean.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Try to get rid of a lot of stuff generated by all the autotools 4 | 5 | clean_dir () { (cd $1 ; rm -f Makefile Makefile.in *.o *.mod *.a datetime_tests ;); } 6 | 7 | rm -f configure 8 | 9 | rm -f datetime-fortran.pc 10 | rm -f *.gz aclocal.m4 depcomp install-sh missing \ 11 | autoscan.log Makefile Makefile.in test-driver 12 | rm -f config.log config.status configure.scan 13 | 14 | rm -rf autom4te.cache 15 | 16 | clean_dir src 17 | clean_dir tests 18 | rm -f tests/*log tests/*.trs 19 | rm -f test-driver 20 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | nodist_include_HEADERS = datetime_module.mod 2 | #nodist_HEADERS = datetime_module.mod 3 | #nodist_SOURCES = *.mod 4 | 5 | lib_LIBRARIES = libdatetime.a 6 | 7 | libdatetime_a_SOURCES = datetime_module.f90 8 | 9 | CLEANFILES = *.mod 10 | 11 | # Mostly unchanged from original upstream Makefile 12 | OBJS = datetime_module.o 13 | 14 | .f90.o: 15 | $(FC) -c $(FCFLAGS) $< 16 | 17 | libdatetime.a : datetime_module.o 18 | ar ruv libdatetime.a $(OBJS) 19 | 20 | datetime_module.mod : datetime_module.o 21 | 22 | #install-exec-hook : datetime_module.mod 23 | # cp datetime_module.mod $(libdir) 24 | 25 | #uninstall-hook : 26 | # rm $(libdir)/datetime_module.mod 27 | 28 | datetime_module.o : datetime_module.f90 29 | -------------------------------------------------------------------------------- /src/datetime_module.f90: -------------------------------------------------------------------------------- 1 | module datetime_module 2 | 3 | use, intrinsic :: iso_fortran_env, only: int64, real32, real64, & 4 | stderr => error_unit 5 | use, intrinsic :: iso_c_binding, only: c_char, c_int, c_int64_t, c_null_char, c_associated, C_PTR 6 | 7 | implicit none 8 | 9 | private 10 | 11 | public :: datetime, timedelta, clock 12 | public :: date2num 13 | public :: datetimeRange 14 | public :: daysInMonth 15 | public :: daysInYear 16 | public :: isLeapYear 17 | public :: num2date 18 | public :: machinetimezone 19 | public :: strptime 20 | public :: epochdatetime 21 | public :: localtime 22 | public :: gmtime 23 | public :: tm2date 24 | public :: tm_struct 25 | public :: c_strftime 26 | public :: c_strptime 27 | 28 | real(real64), parameter :: zero = 0._real64, one = 1._real64 29 | 30 | ! Constant multipliers to transform a number of some time unit to another 31 | real(real64), parameter :: d2h = 24._real64 ! day -> hour 32 | real(real64), parameter :: h2d = one / d2h ! hour -> day 33 | real(real64), parameter :: d2m = d2h * 60._real64 ! day -> minute 34 | real(real64), parameter :: m2d = one / d2m ! minute -> day 35 | real(real64), parameter :: m2h = one / 60 ! minute -> hour 36 | real(real64), parameter :: s2d = m2d / 60 ! second -> day 37 | real(real64), parameter :: d2s = 86400._real64 ! day -> second 38 | real(real64), parameter :: h2s = 3600._real64 ! hour -> second 39 | real(real64), parameter :: s2h = one / h2s ! second -> hour 40 | real(real64), parameter :: m2s = 60._real64 ! minute -> second 41 | real(real64), parameter :: s2m = one / m2s ! second -> minute 42 | 43 | integer, parameter :: MAXSTRLEN = 99 ! maximum string length for strftime 44 | 45 | type :: datetime 46 | 47 | private 48 | 49 | integer :: year = 1 ! year [1-HUGE(year)] 50 | integer :: month = 1 ! month in year [1-12] 51 | integer :: day = 1 ! day in month [1-31] 52 | integer :: hour = 0 ! hour in day [0-23] 53 | integer :: minute = 0 ! minute in hour [0-59] 54 | integer :: second = 0 ! second in minute [0-59] 55 | integer :: millisecond = 0 ! milliseconds in second [0-999] 56 | real(real64) :: tz = 0 ! timezone offset from UTC [hours] 57 | 58 | contains 59 | 60 | ! getter functions 61 | procedure, pass(self), public :: getYear 62 | procedure, pass(self), public :: getMonth 63 | procedure, pass(self), public :: getDay 64 | procedure, pass(self), public :: getHour 65 | procedure, pass(self), public :: getMinute 66 | procedure, pass(self), public :: getSecond 67 | procedure, pass(self), public :: getMillisecond 68 | procedure, pass(self), public :: getTz 69 | 70 | ! public methods 71 | procedure, pass(self), public :: isocalendar 72 | procedure, pass(self), public :: isoformat 73 | procedure, pass(self), public :: isValid 74 | procedure, nopass, public :: now 75 | procedure, pass(self), public :: secondsSinceEpoch 76 | procedure, pass(self), public :: strftime 77 | procedure, pass(self), public :: tm 78 | procedure, pass(self), public :: tzOffset 79 | procedure, pass(self), public :: isoweekday 80 | procedure, pass(self), public :: isoweekdayLong 81 | procedure, pass(self), public :: isoweekdayShort 82 | procedure, pass(self), public :: utc 83 | procedure, pass(self), public :: weekday 84 | procedure, pass(self), public :: weekdayLong 85 | procedure, pass(self), public :: weekdayShort 86 | procedure, pass(self), public :: yearday 87 | 88 | ! private methods 89 | procedure, pass(self), private :: addMilliseconds 90 | procedure, pass(self), private :: addSeconds 91 | procedure, pass(self), private :: addMinutes 92 | procedure, pass(self), private :: addHours 93 | procedure, pass(self), private :: addDays 94 | 95 | ! operator overloading procedures 96 | procedure, pass(d0), private :: datetime_plus_timedelta 97 | procedure, pass(d0), private :: timedelta_plus_datetime 98 | procedure, pass(d0), private :: datetime_minus_datetime 99 | procedure, pass(d0), private :: datetime_minus_timedelta 100 | procedure, pass(d0), private :: datetime_eq 101 | procedure, pass(d0), private :: datetime_neq 102 | procedure, pass(d0), private :: datetime_gt 103 | procedure, pass(d0), private :: datetime_ge 104 | procedure, pass(d0), private :: datetime_lt 105 | procedure, pass(d0), private :: datetime_le 106 | 107 | generic :: operator(+) => datetime_plus_timedelta, & 108 | timedelta_plus_datetime 109 | generic :: operator(-) => datetime_minus_datetime, & 110 | datetime_minus_timedelta 111 | generic :: operator(==) => datetime_eq 112 | generic :: operator(/=) => datetime_neq 113 | generic :: operator(>) => datetime_gt 114 | generic :: operator(>=) => datetime_ge 115 | generic :: operator(<) => datetime_lt 116 | generic :: operator(<=) => datetime_le 117 | 118 | end type datetime 119 | 120 | interface datetime 121 | module procedure :: datetime_constructor 122 | endinterface datetime 123 | 124 | type :: timedelta 125 | private 126 | 127 | integer :: days = 0 128 | integer :: hours = 0 129 | integer :: minutes = 0 130 | integer :: seconds = 0 131 | integer :: milliseconds = 0 132 | 133 | contains 134 | 135 | procedure, pass(self), public :: getDays 136 | procedure, pass(self), public :: getHours 137 | procedure, pass(self), public :: getMinutes 138 | procedure, pass(self), public :: getSeconds 139 | procedure, pass(self), public :: getMilliseconds 140 | 141 | procedure, public :: total_seconds 142 | 143 | procedure, private :: timedelta_plus_timedelta 144 | procedure, private :: timedelta_minus_timedelta 145 | procedure, private :: unary_minus_timedelta 146 | procedure, private :: timedelta_eq 147 | procedure, private :: timedelta_neq 148 | procedure, private :: timedelta_gt 149 | procedure, private :: timedelta_ge 150 | procedure, private :: timedelta_lt 151 | procedure, private :: timedelta_le 152 | 153 | generic :: operator(+) => timedelta_plus_timedelta 154 | generic :: operator(-) => timedelta_minus_timedelta, unary_minus_timedelta 155 | generic :: operator(==) => timedelta_eq 156 | generic :: operator(/=) => timedelta_neq 157 | generic :: operator(>) => timedelta_gt 158 | generic :: operator(>=) => timedelta_ge 159 | generic :: operator(<) => timedelta_lt 160 | generic :: operator(<=) => timedelta_le 161 | 162 | end type timedelta 163 | 164 | interface timedelta 165 | module procedure :: timedelta_constructor 166 | endinterface timedelta 167 | 168 | type,bind(c) :: tm_struct 169 | ! Derived type for compatibility with C and C++ struct tm. 170 | ! Enables calling strftime and strptime using iso_c_binding. 171 | ! See http://www.cplusplus.com/reference/ctime/tm for reference. 172 | integer(c_int) :: tm_sec = 0 ! Seconds [0-60] (1 leap second) 173 | integer(c_int) :: tm_min = 0 ! Minutes [0-59] 174 | integer(c_int) :: tm_hour = 0 ! Hours [0-23] 175 | integer(c_int) :: tm_mday = 0 ! Day [1-31] 176 | integer(c_int) :: tm_mon = 0 ! Month [0-11] 177 | integer(c_int) :: tm_year = 0 ! Year - 1900 178 | integer(c_int) :: tm_wday = 0 ! Day of week [0-6] 179 | integer(c_int) :: tm_yday = 0 ! Days in year [0-365] 180 | integer(c_int) :: tm_isdst = 0 ! DST [-1/0/1] 181 | end type tm_struct 182 | 183 | interface 184 | 185 | type(c_ptr) function c_strftime(str, slen, format, tm) bind(c, name='strftime') 186 | ! Returns a formatted time string, given input time struct and format. 187 | ! See https://www.cplusplus.com/reference/ctime/strftime for reference. 188 | import :: c_char, c_int, tm_struct, C_PTR 189 | character(kind=c_char), intent(out) :: str(*) ! result string 190 | integer(c_int), value, intent(in) :: slen ! string length 191 | character(kind=c_char), intent(in) :: format(*) ! time format 192 | type(tm_struct), intent(in) :: tm ! tm_struct instance 193 | end function c_strftime 194 | 195 | integer(c_int) function c_strptime(str,format,tm) bind(c,name='strptime') 196 | ! Interface to POSIX strptime. 197 | ! Returns a time struct object based on the input time string str and format. 198 | ! See http://man7.org/linux/man-pages/man3/strptime.3.html for reference. 199 | import :: c_char, c_int, tm_struct 200 | character(kind=c_char), intent(in) :: str(*) ! input string 201 | character(kind=c_char), intent(in) :: format(*) ! time format 202 | type(tm_struct), intent(out) :: tm ! result tm_struct 203 | end function c_strptime 204 | 205 | end interface 206 | 207 | 208 | type :: clock 209 | type(datetime) :: startTime 210 | type(datetime) :: stopTime 211 | type(datetime) :: currentTime 212 | type(timedelta) :: tickInterval 213 | logical :: alarm = .false. 214 | logical :: started = .false. 215 | logical :: stopped = .false. 216 | contains 217 | procedure :: reset 218 | procedure :: tick 219 | end type clock 220 | 221 | contains 222 | 223 | 224 | pure elemental subroutine reset(self) 225 | ! Resets the clock to its start time. 226 | class(clock), intent(in out) :: self 227 | self % currentTime = self % startTime 228 | self % started = .false. 229 | self % stopped = .false. 230 | end subroutine reset 231 | 232 | 233 | pure elemental subroutine tick(self) 234 | ! Increments the currentTime of the clock instance by one tickInterval. 235 | class(clock), intent(in out) :: self 236 | if (self % stopped) return 237 | if (.not. self % started) then 238 | self % started = .true. 239 | self % currentTime = self % startTime 240 | end if 241 | self % currentTime = self % currentTime + self % tickInterval 242 | if (self % currentTime >= self % stopTime) self % stopped = .true. 243 | end subroutine tick 244 | 245 | 246 | pure elemental type(datetime) function datetime_constructor( & 247 | year, month, day, hour, minute, second, millisecond, tz) 248 | ! Constructor function for the `datetime` class. 249 | integer, intent(in), optional :: year, month, day, hour, minute, second, millisecond 250 | real(real64), intent(in), optional :: tz ! timezone offset in hours 251 | 252 | datetime_constructor % year = 1 253 | if (present(year)) datetime_constructor % year = year 254 | 255 | datetime_constructor % month = 1 256 | if (present(month)) datetime_constructor % month = month 257 | 258 | datetime_constructor % day = 1 259 | if (present(day)) datetime_constructor % day = day 260 | 261 | datetime_constructor % hour = 0 262 | if (present(hour)) datetime_constructor % hour = hour 263 | 264 | datetime_constructor % minute = 0 265 | if (present(minute)) datetime_constructor % minute = minute 266 | 267 | datetime_constructor % second = 0 268 | if (present(second)) datetime_constructor % second = second 269 | 270 | datetime_constructor % millisecond = 0 271 | if (present(millisecond)) datetime_constructor % millisecond = millisecond 272 | 273 | datetime_constructor % tz = 0 274 | if (present(tz)) datetime_constructor % tz = tz 275 | 276 | end function datetime_constructor 277 | 278 | 279 | pure elemental integer function getYear(self) 280 | ! Returns the year component 281 | class(datetime), intent(in) :: self 282 | getYear = self % year 283 | end function getYear 284 | 285 | 286 | pure elemental integer function getMonth(self) 287 | ! Returns the year component 288 | class(datetime), intent(in) :: self 289 | getMonth = self % month 290 | end function getMonth 291 | 292 | 293 | pure elemental integer function getDay(self) 294 | ! Returns the year component 295 | class(datetime), intent(in) :: self 296 | getDay = self % day 297 | end function getDay 298 | 299 | 300 | pure elemental integer function getHour(self) 301 | ! Returns the year component 302 | class(datetime), intent(in) :: self 303 | getHour = self % hour 304 | end function getHour 305 | 306 | 307 | pure elemental integer function getMinute(self) 308 | ! Returns the year component 309 | class(datetime), intent(in) :: self 310 | getMinute = self % minute 311 | end function getMinute 312 | 313 | 314 | pure elemental integer function getSecond(self) 315 | ! Returns the year component 316 | class(datetime), intent(in) :: self 317 | getSecond = self % second 318 | end function getSecond 319 | 320 | 321 | pure elemental integer function getMillisecond(self) 322 | ! Returns the year component 323 | class(datetime), intent(in) :: self 324 | getMillisecond = self % millisecond 325 | end function getMillisecond 326 | 327 | 328 | pure elemental real(real64) function getTz(self) 329 | ! Returns the timezone offset component 330 | class(datetime), intent(in) :: self 331 | getTz = self % tz 332 | end function getTz 333 | 334 | 335 | pure elemental subroutine addMilliseconds(self, ms) 336 | ! Adds an integer number of milliseconds to self. Called by `datetime` 337 | ! addition (`+`) and subtraction (`-`) operators. 338 | class(datetime), intent(in out) :: self 339 | integer, intent(in) :: ms 340 | self % millisecond = self % millisecond + ms 341 | do 342 | if (self % millisecond >= 1000) then 343 | call self % addSeconds(self % millisecond / 1000) 344 | self % millisecond = mod(self % millisecond, 1000) 345 | else if (self % millisecond < 0) then 346 | call self % addSeconds(self % millisecond / 1000 - 1) 347 | self % millisecond = mod(self % millisecond, 1000) + 1000 348 | else 349 | exit 350 | end if 351 | end do 352 | end subroutine addMilliseconds 353 | 354 | 355 | pure elemental subroutine addSeconds(self, s) 356 | ! Adds an integer number of seconds to self. Called by `datetime` 357 | ! addition (`+`) and subtraction (`-`) operators. 358 | class(datetime), intent(in out) :: self 359 | integer, intent(in) :: s 360 | self % second = self % second + s 361 | do 362 | if (self % second >= 60) then 363 | call self % addMinutes(self % second / 60) 364 | self % second = mod(self % second, 60) 365 | else if (self % second < 0) then 366 | call self % addMinutes(self % second / 60 - 1) 367 | self % second = mod(self % second, 60) + 60 368 | else 369 | exit 370 | end if 371 | end do 372 | end subroutine addSeconds 373 | 374 | 375 | pure elemental subroutine addMinutes(self,m) 376 | ! Adds an integer number of minutes to self. Called by `datetime` 377 | ! addition (`+`) and subtraction (`-`) operators. 378 | class(datetime), intent(in out) :: self 379 | integer, intent(in) :: m 380 | self % minute = self % minute + m 381 | do 382 | if (self % minute >= 60) then 383 | call self % addHours(self % minute / 60) 384 | self % minute = mod(self % minute, 60) 385 | else if (self % minute < 0) then 386 | call self % addHours(self % minute / 60 - 1) 387 | self % minute = mod(self % minute, 60) + 60 388 | else 389 | exit 390 | end if 391 | end do 392 | end subroutine addMinutes 393 | 394 | 395 | pure elemental subroutine addHours(self,h) 396 | ! Adds an integer number of hours to self. Called by `datetime` 397 | ! addition (`+`) and subtraction (`-`) operators. 398 | class(datetime), intent(in out) :: self 399 | integer, intent(in) :: h 400 | self % hour = self % hour + h 401 | do 402 | if (self % hour >= 24) then 403 | call self % addDays(self % hour / 24) 404 | self % hour = mod(self % hour, 24) 405 | else if (self % hour < 0) then 406 | call self % addDays(self % hour / 24 - 1) 407 | self % hour = mod(self % hour, 24) + 24 408 | else 409 | exit 410 | end if 411 | end do 412 | end subroutine addHours 413 | 414 | 415 | pure elemental subroutine addDays(self, d) 416 | ! Adds an integer number of dayss to self. Called by `datetime` 417 | ! addition (`+`) and subtraction (`-`) operators. 418 | class(datetime), intent(in out) :: self 419 | integer, intent(in) :: d 420 | integer :: daysInCurrentMonth 421 | self % day = self % day + d 422 | do 423 | daysInCurrentMonth = daysInMonth(self % month, self % year) 424 | if (self % day > daysInCurrentMonth) then 425 | self % day = self % day - daysInCurrentMonth 426 | self % month = self % month+1 427 | if (self % month > 12) then 428 | self % year = self % year + self % month/12 429 | self % month = mod(self % month, 12) 430 | end if 431 | else if (self % day < 1) then 432 | self % month = self % month-1 433 | if (self % month < 1) then 434 | self % year = self % year + self % month / 12 - 1 435 | self % month = 12 + mod(self % month, 12) 436 | end if 437 | self % day = self % day + daysInMonth(self % month, self % year) 438 | else 439 | exit 440 | end if 441 | end do 442 | end subroutine addDays 443 | 444 | 445 | pure elemental character(23) function isoformat(self,sep) 446 | ! Returns character string with time in ISO 8601 format. 447 | class(datetime), intent(in) :: self 448 | character, intent(in), optional :: sep 449 | character :: separator 450 | 451 | separator = 'T' 452 | if (present(sep)) separator = sep 453 | 454 | ! TODO below is a bit cumbersome and was implemented 455 | ! at a time before the interface to strftime. Now we 456 | ! could do something like: 457 | ! 458 | ! isoformat = self % strftime('%Y-%m-%d'//separator//'%H:%M:%S') 459 | ! 460 | isoformat = int2str(self % year, 4)//'-'// & 461 | int2str(self % month, 2)//'-'// & 462 | int2str(self % day, 2)//separator//& 463 | int2str(self % hour, 2)//':'// & 464 | int2str(self % minute, 2)//':'// & 465 | int2str(self % second, 2)//'.'// & 466 | int2str(self % millisecond,3) 467 | 468 | end function isoformat 469 | 470 | 471 | pure elemental logical function isValid(self) 472 | ! Checks whether the `datetime` instance has valid component values. 473 | ! Returns `.true.` if the `datetime` instance is valid, and `.false.` 474 | ! otherwise. 475 | class(datetime), intent(in) :: self 476 | 477 | ! assume valid 478 | isValid = .true. 479 | 480 | if (self % year < 1) then 481 | isValid = .false. 482 | return 483 | end if 484 | 485 | if (self % month < 1 .or. self % month > 12) then 486 | isValid = .false. 487 | return 488 | end if 489 | 490 | if (self % day < 1 .or. & 491 | self % day > daysInMonth(self % month,self % year)) then 492 | isValid = .false. 493 | return 494 | end if 495 | 496 | if (self % hour < 0 .or. self % hour > 23) then 497 | isValid = .false. 498 | return 499 | end if 500 | 501 | if (self % minute < 0 .or. self % minute > 59) then 502 | isValid = .false. 503 | return 504 | end if 505 | 506 | if (self % second < 0 .or. self % second > 59) then 507 | isValid = .false. 508 | return 509 | end if 510 | 511 | if (self % millisecond < 0 .or. self % millisecond > 999) then 512 | isValid = .false. 513 | return 514 | end if 515 | 516 | end function isValid 517 | 518 | 519 | type(datetime) function now() 520 | ! Returns a `datetime` instance with current time. 521 | character(5) :: zone 522 | integer :: values(8) 523 | integer :: hour, minute 524 | 525 | ! Obtain local machine time zone information 526 | call date_and_time(zone=zone, values=values) 527 | 528 | read(zone(1:3), '(i3)') hour 529 | read(zone(4:5), '(i2)') minute 530 | 531 | now = datetime(year = values(1), month = values(2), day = values(3), & 532 | hour = values(5), minute = values(6), second = values(7), & 533 | millisecond = values(8)) 534 | 535 | now % tz = hour + minute * m2h 536 | 537 | end function now 538 | 539 | 540 | pure elemental integer function weekday(self) 541 | ! Returns the day of the week calculated using Zeller's congruence. 542 | ! Returned value is an integer scalar in the range [0-6], such that: 543 | ! 544 | ! 0: Sunday 545 | ! 1: Monday 546 | ! 2: Tuesday 547 | ! 3: Wednesday 548 | ! 4: Thursday 549 | ! 5: Friday 550 | ! 6: Saturday 551 | class(datetime), intent(in) :: self 552 | integer :: year, month, j, k 553 | 554 | year = self % year 555 | month = self % month 556 | 557 | if (month <= 2) then 558 | month = month + 12 559 | year = year - 1 560 | end if 561 | 562 | j = year / 100 563 | k = mod(year, 100) 564 | 565 | weekday = mod(self % day + ((month + 1) * 26) / 10 + k + k / 4 + j / 4 + 5 * j, 7) -1 566 | 567 | if (weekday < 0) weekday = 6 568 | 569 | end function weekday 570 | 571 | 572 | pure elemental integer function isoweekday(self) 573 | ! Returns the day of the week per ISO 8601 returned from weekday(). 574 | ! Returned value is an integer scalar in the range [1-7]. 575 | class(datetime), intent(in) :: self 576 | isoweekday = self % weekday() 577 | if (isoweekday == 0) isoweekday = 7 578 | end function isoweekday 579 | 580 | 581 | pure elemental character(9) function weekdayLong(self) 582 | ! Returns the full name of the day of the week. 583 | class(datetime), intent(in) :: self 584 | character(9), parameter :: & 585 | days(*) = ['Sunday ', 'Monday ', 'Tuesday ','Wednesday', & 586 | 'Thursday ', 'Friday ', 'Saturday '] 587 | weekdayLong = days(self % weekday() + 1) 588 | end function weekdayLong 589 | 590 | 591 | pure elemental character(9) function isoweekdayLong(self) 592 | ! Returns the full name of the day of the week for ISO 8601 593 | ! ordered weekdays. 594 | class(datetime), intent(in) :: self 595 | character(9), parameter :: & 596 | days(7) = ['Monday ','Tuesday ','Wednesday','Thursday ', & 597 | 'Friday ','Saturday ','Sunday '] 598 | isoweekdayLong = days(self % isoweekday()) 599 | end function isoweekdayLong 600 | 601 | 602 | pure elemental character(3) function weekdayShort(self) 603 | ! Returns the short (3-letter) name of the day of the week. 604 | class(datetime), intent(in) :: self 605 | character(3), parameter :: days(7) = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] 606 | weekdayShort = days(self % weekday() + 1) 607 | end function weekdayShort 608 | 609 | 610 | pure elemental character(3) function isoweekdayShort(self) 611 | ! Returns the short (3-letter) name of the day of the week 612 | ! based on ISO 8601 ordering. 613 | class(datetime), intent(in) :: self 614 | character(3), parameter :: days(7) = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] 615 | isoweekdayShort = days(self % isoweekday()) 616 | end function isoweekdayShort 617 | 618 | 619 | function isocalendar(self) 620 | ! Returns an array of 3 integers, year, week number, and week day, 621 | ! as defined by ISO 8601 week date. Essentially a wrapper around C 622 | ! `strftime` function. 623 | class(datetime), intent(in) :: self 624 | integer :: isocalendar(3) 625 | integer :: year, week, wday 626 | type(C_PTR) :: rc 627 | character(20) :: string 628 | 629 | rc = c_strftime(string, len(string), '%G %V %u' // c_null_char, self % tm()) 630 | if (.not. c_associated(rc)) then 631 | write(stderr,*) "ERROR:datetime:strftime: format: %G %V %u" 632 | return 633 | endif 634 | 635 | read(string(1:4), '(i4)') year 636 | read(string(6:7), '(i2)') week 637 | read(string(9:9), '(i1)') wday 638 | 639 | isocalendar = [year, week, wday] 640 | 641 | end function isocalendar 642 | 643 | 644 | integer(int64) function secondsSinceEpoch(self) 645 | ! Returns an integer number of seconds since the UNIX Epoch (1 Jan 1970). 646 | ! Since Windows does not have strftime('%s'), we implement this using 647 | ! datetime itself. 648 | class(datetime), intent(in) :: self 649 | type(timedelta) :: delta 650 | type(datetime) :: this_time, unix_time 651 | integer :: sign, hours, minutes, tzsec 652 | 653 | this_time = datetime(self%year, self%month, self%day, & 654 | self%hour, self%minute, self%second) 655 | unix_time = datetime(1970, 1, 1, 0, 0, 0) 656 | delta = this_time - unix_time 657 | secondsSinceEpoch = delta%total_seconds() 658 | 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 | 679 | end function secondsSinceEpoch 680 | 681 | 682 | function strftime(self, format) 683 | ! Wrapper around C and C++ `strftime` function. 684 | class(datetime), intent(in) :: self 685 | character(*), intent(in) :: format 686 | character(:), allocatable :: strftime 687 | type(C_PTR) :: rc 688 | character(MAXSTRLEN) :: resultString 689 | resultString = "" 690 | rc = c_strftime(resultString, len(resultString), trim(format) // c_null_char, & 691 | self % tm()) 692 | if (.not. c_associated(rc)) write(stderr, '(a)') "ERROR:datetime:strftime: format: " // trim(format) 693 | strftime = resultString(1:len_trim(resultString)-1) !< strip null 694 | end function strftime 695 | 696 | 697 | pure elemental type(tm_struct) function tm(self) 698 | ! Returns a `tm_struct` instance of the current `datetime`. 699 | class(datetime), intent(in) :: self 700 | tm % tm_sec = self % second 701 | tm % tm_min = self % minute 702 | tm % tm_hour = self % hour 703 | tm % tm_mday = self % day 704 | tm % tm_mon = self % month - 1 705 | tm % tm_year = self % year - 1900 706 | tm % tm_wday = self % weekday() 707 | tm % tm_yday = self % yearday() - 1 708 | tm % tm_isdst = -1 709 | end function tm 710 | 711 | 712 | pure elemental character(5) function tzOffset(self) 713 | ! Returns a character string with timezone offset in hours from UTC, 714 | ! in format +/-[hh][mm]. 715 | class(datetime), intent(in) :: self 716 | integer :: hours,minutes 717 | 718 | if (self % tz < 0) then 719 | tzOffset(1:1) = '-' 720 | else 721 | tzOffset(1:1) = '+' 722 | end if 723 | 724 | hours = int(abs(self % tz)) 725 | minutes = nint((abs(self % tz) - hours) * 60) 726 | 727 | if (minutes == 60) then 728 | minutes = 0 729 | hours = hours + 1 730 | end if 731 | 732 | write(tzOffset(2:5), '(2i2.2)') hours, minutes 733 | 734 | end function tzOffset 735 | 736 | 737 | pure elemental type(datetime) function utc(self) 738 | ! Returns the `datetime` instance at Coordinated Universal Time (UTC). 739 | class(datetime), intent(in) :: self 740 | integer :: hours, minutes, sgn 741 | hours = int(abs(self % tz)) 742 | minutes = nint((abs(self % tz) - hours) * 60) 743 | sgn = int(sign(one, self % tz)) 744 | utc = self - timedelta(hours=sgn * hours, minutes=sgn * minutes) 745 | utc % tz = 0 746 | end function utc 747 | 748 | 749 | pure elemental integer function yearday(self) 750 | ! Returns the integer day of the year (ordinal date). 751 | class(datetime), intent(in) :: self 752 | integer :: month 753 | yearday = 0 754 | do month = 1, self % month-1 755 | yearday = yearday + daysInMonth(month, self % year) 756 | end do 757 | yearday = yearday + self % day 758 | end function yearday 759 | 760 | 761 | pure elemental function datetime_plus_timedelta(d0,t) result(d) 762 | ! Adds a `timedelta` instance to a `datetime` instance, and returns a 763 | ! new `datetime` instance. Overloads the operator `+`. 764 | class(datetime), intent(in) :: d0 765 | class(timedelta), intent(in) :: t 766 | type(datetime) :: d 767 | 768 | integer :: milliseconds, seconds, minutes, hours, days 769 | 770 | d = datetime(year = d0 % getYear(), & 771 | month = d0 % getMonth(), & 772 | day = d0 % getDay(), & 773 | hour = d0 % getHour(), & 774 | minute = d0 % getMinute(), & 775 | second = d0 % getSecond(), & 776 | millisecond = d0 % getMillisecond(), & 777 | tz = d0 % getTz()) 778 | 779 | milliseconds = t % getMilliseconds() 780 | seconds = t % getSeconds() 781 | minutes = t % getMinutes() 782 | hours = t % getHours() 783 | days = t % getDays() 784 | 785 | if (milliseconds /= 0) call d % addMilliseconds(milliseconds) 786 | if (seconds /= 0) call d % addSeconds(seconds) 787 | if (minutes /= 0) call d % addMinutes(minutes) 788 | if (hours /= 0) call d % addHours(hours) 789 | if (days /= 0) call d % addDays(days) 790 | 791 | end function datetime_plus_timedelta 792 | 793 | 794 | pure elemental function timedelta_plus_datetime(t,d0) result(d) 795 | ! Adds a `timedelta` instance to a `datetime` instance, and returns a 796 | ! new `datetime` instance. Overloads the operator `+`. 797 | class(timedelta), intent(in) :: t 798 | class(datetime), intent(in) :: d0 799 | type(datetime) :: d 800 | d = d0 + t 801 | end function timedelta_plus_datetime 802 | 803 | 804 | pure elemental function datetime_minus_timedelta(d0,t) result(d) 805 | ! Subtracts a `timedelta` instance from a `datetime` instance and 806 | ! returns a new `datetime` instance. Overloads the operator `-`. 807 | class(datetime), intent(in) :: d0 808 | class(timedelta), intent(in) :: t 809 | type(datetime) :: d 810 | d = d0 + (-t) 811 | end function datetime_minus_timedelta 812 | 813 | 814 | pure elemental function datetime_minus_datetime(d0,d1) result(t) 815 | ! Subtracts a `datetime` instance from another `datetime` instance, 816 | ! and returns a `timedelta` instance. Overloads the operator `-`. 817 | class(datetime), intent(in) :: d0, d1 818 | type(timedelta) :: t 819 | real(real64) :: daysDiff 820 | integer :: days,hours,minutes,seconds,milliseconds 821 | integer :: sign_ 822 | 823 | daysDiff = date2num(d0)-date2num(d1) 824 | 825 | if (daysDiff < 0) then 826 | sign_ = -1 827 | daysDiff = ABS(daysDiff) 828 | else 829 | sign_ = 1 830 | end if 831 | 832 | days = int(daysDiff) 833 | hours = int((daysDiff-days)*d2h) 834 | minutes = int((daysDiff-days-hours*h2d)*d2m) 835 | seconds = int((daysDiff-days-hours*h2d-minutes*m2d)*d2s) 836 | milliseconds = nint((daysDiff-days-hours*h2d-minutes*m2d& 837 | -seconds*s2d)*d2s*1e3_real64) 838 | 839 | t = timedelta(sign_*days,sign_*hours,sign_*minutes,sign_*seconds,& 840 | sign_*milliseconds) 841 | 842 | end function datetime_minus_datetime 843 | 844 | 845 | pure elemental logical function datetime_gt(d0,d1) result(res) 846 | ! `datetime` comparison operator that returns `.true.` if `d0` is 847 | ! greater than `d1` and `.false.` otherwise. Overloads the 848 | ! operator `>`. 849 | class(datetime), intent(in) :: d0, d1 850 | type(datetime) :: d0_utc, d1_utc 851 | 852 | ! Convert to UTC before making comparison 853 | d0_utc = d0 % utc() 854 | d1_utc = d1 % utc() 855 | 856 | ! Compare years 857 | if (d0_utc % year > d1_utc % year) then 858 | res = .true. 859 | else if (d0_utc % year < d1_utc % year) then 860 | res = .false. 861 | else 862 | 863 | ! Compare months 864 | if (d0_utc % month > d1_utc % month) then 865 | res = .true. 866 | else if (d0_utc % month < d1_utc % month) then 867 | res = .false. 868 | else 869 | 870 | ! Compare days 871 | if (d0_utc % day > d1_utc % day) then 872 | res = .true. 873 | else if (d0_utc % day < d1_utc % day) then 874 | res = .false. 875 | else 876 | 877 | ! Compare hours 878 | if (d0_utc % hour > d1_utc % hour) then 879 | res = .true. 880 | else if (d0_utc % hour < d1_utc % hour) then 881 | res = .false. 882 | else 883 | 884 | ! Compare minutes 885 | if (d0_utc % minute > d1_utc % minute) then 886 | res = .true. 887 | else if (d0_utc % minute < d1_utc % minute) then 888 | res = .false. 889 | else 890 | 891 | ! Compare seconds 892 | if (d0_utc % second > d1_utc % second) then 893 | res = .true. 894 | else if (d0_utc % second < d1_utc % second) then 895 | res = .false. 896 | else 897 | 898 | ! Compare milliseconds 899 | if (d0_utc % millisecond > d1_utc % millisecond) then 900 | res = .true. 901 | else 902 | res = .false. 903 | end if 904 | 905 | end if 906 | end if 907 | end if 908 | end if 909 | end if 910 | end if 911 | 912 | end function datetime_gt 913 | 914 | 915 | pure elemental logical function datetime_lt(d0,d1) result(res) 916 | ! `datetime` comparison operator that returns `.true.` if `d0` is 917 | ! less than `d1` and `.false.` otherwise. Overloads the operator `<`. 918 | class(datetime), intent(in) :: d0, d1 919 | res = d1 > d0 920 | end function datetime_lt 921 | 922 | 923 | pure elemental logical function datetime_eq(d0,d1) result(res) 924 | ! `datetime` comparison operator that returns `.true.` if `d0` is 925 | ! equal to `d1` and `.false.` otherwise. Overloads the operator `==`. 926 | class(datetime), intent(in) :: d0, d1 927 | type(datetime) :: d0_utc, d1_utc 928 | 929 | ! Convert to UTC before making comparison 930 | d0_utc = d0 % utc() 931 | d1_utc = d1 % utc() 932 | 933 | res = d0_utc % year == d1_utc % year .and. & 934 | d0_utc % month == d1_utc % month .and. & 935 | d0_utc % day == d1_utc % day .and. & 936 | d0_utc % hour == d1_utc % hour .and. & 937 | d0_utc % minute == d1_utc % minute .and. & 938 | d0_utc % second == d1_utc % second .and. & 939 | d0_utc % millisecond == d1_utc % millisecond 940 | 941 | end function datetime_eq 942 | 943 | 944 | pure elemental logical function datetime_neq(d0,d1) result(res) 945 | ! `datetime` comparison operator that eturns `.true.` if `d0` is 946 | ! not equal to `d1` and `.false.` otherwise. Overloads the operator `/=`. 947 | class(datetime), intent(in) :: d0, d1 948 | res = .not. d0 == d1 949 | end function datetime_neq 950 | 951 | 952 | pure elemental logical function datetime_ge(d0,d1) result(res) 953 | ! `datetime` comparison operator. Returns `.true.` if `d0` is greater 954 | ! than or equal to `d1` and `.false.` otherwise. Overloads the 955 | ! operator `>=`. 956 | class(datetime), intent(in) :: d0, d1 957 | res = d0 > d1 .or. d0 == d1 958 | end function datetime_ge 959 | 960 | 961 | pure elemental logical function datetime_le(d0,d1) result(res) 962 | ! `datetime` comparison operator. Returns `.true.` if `d0` is less 963 | ! than or equal to `d1`, and `.false.` otherwise. Overloads the 964 | ! operator `<=`. 965 | class(datetime), intent(in) :: d0, d1 966 | res = d1 > d0 .or. d0 == d1 967 | end function datetime_le 968 | 969 | 970 | pure elemental logical function isLeapYear(year) 971 | ! Returns `.true.` if year is leap year and `.false.` otherwise. 972 | integer, intent(in) :: year 973 | isLeapYear = (mod(year,4) == 0 .and. .not. mod(year,100) == 0)& 974 | .or. (mod(year,400) == 0) 975 | end function isLeapYear 976 | 977 | 978 | pure function datetimeRange(d0, d1, t) 979 | ! Given start and end `datetime` instances `d0` and `d1` and time 980 | ! increment as `timedelta` instance `t`, returns an array of 981 | ! `datetime` instances. The number of elements is the number of whole 982 | ! time increments contained between datetimes `d0` and `d1`. 983 | type(datetime), intent(in) :: d0, d1 984 | type(timedelta), intent(in) :: t 985 | real(real64) :: datenum0, datenum1, eps, increment 986 | type(datetime), allocatable :: datetimeRange(:) 987 | integer :: n, nm 988 | eps = 1e-10_real64 989 | datenum0 = date2num(d0) 990 | datenum1 = date2num(d1) 991 | increment = t % total_seconds() * s2d 992 | nm = floor((datenum1 - datenum0 + eps) / increment) + 1 993 | allocate(datetimeRange(nm)) 994 | do n = 1, nm 995 | datetimeRange(n) = num2date(datenum0 + (n - 1) * increment) 996 | end do 997 | end function datetimeRange 998 | 999 | 1000 | pure elemental integer function daysInMonth(month,year) 1001 | ! Given integer month and year, returns an integer number 1002 | ! of days in that particular month. 1003 | integer, intent(in) :: month, year 1004 | 1005 | integer, parameter :: days(*) = [31, 28, 31, 30, 31, 30, & 1006 | 31, 31, 30, 31, 30, 31] 1007 | 1008 | if (month < 1 .or. month > 12) then 1009 | ! Should raise an error and abort here, however we want to keep 1010 | ! the pure and elemental attributes. Make sure this function is 1011 | ! called with the month argument in range. 1012 | daysInMonth = 0 1013 | return 1014 | end if 1015 | 1016 | if (month == 2 .and. isLeapYear(year)) then 1017 | daysInMonth = 29 1018 | else 1019 | daysInMonth = days(month) 1020 | end if 1021 | 1022 | end function daysInMonth 1023 | 1024 | 1025 | pure elemental integer function daysInYear(year) 1026 | ! Returns the number of days in year. 1027 | integer, intent(in) :: year 1028 | if (isLeapYear(year)) then 1029 | daysInYear = 366 1030 | else 1031 | daysInYear = 365 1032 | end if 1033 | end function daysInYear 1034 | 1035 | 1036 | pure elemental real(real64) function date2num(d) 1037 | ! Given a datetime instance d, returns number of days since 1038 | ! `0001-01-01 00:00:00`, taking into account the timezone offset. 1039 | type(datetime), intent(in) :: d 1040 | type(datetime) :: d_utc 1041 | integer :: year 1042 | 1043 | ! Convert to UTC first 1044 | d_utc = d % utc() 1045 | 1046 | ! d_utc % year must be positive: 1047 | if (d_utc % year < 1) then 1048 | date2num = 0 1049 | return 1050 | end if 1051 | 1052 | date2num = 0 1053 | do year = 1,d_utc % year-1 1054 | date2num = date2num + daysInYear(year) 1055 | end do 1056 | 1057 | date2num = date2num & 1058 | + d_utc % yearday() & 1059 | + d_utc % hour*h2d & 1060 | + d_utc % minute*m2d& 1061 | + (d_utc % second+1e-3_real64*d_utc % millisecond)*s2d 1062 | 1063 | end function date2num 1064 | 1065 | 1066 | pure elemental type(datetime) function num2date(num) 1067 | ! Given number of days since `0001-01-01 00:00:00`, returns a 1068 | ! correspoding `datetime` instance. 1069 | real(real64), intent(in) :: num 1070 | integer :: year, month, day, hour, minute, second, millisecond 1071 | real(real64) :: days, totseconds 1072 | 1073 | ! num must be positive 1074 | if (num < 0) then 1075 | num2date = datetime(1) 1076 | return 1077 | end if 1078 | 1079 | days = num 1080 | 1081 | year = 1 1082 | do 1083 | if (int(days) <= daysInYear(year))exit 1084 | days = days-daysInYear(year) 1085 | year = year+1 1086 | end do 1087 | 1088 | month = 1 1089 | do 1090 | if (inT(days) <= daysInMonth(month,year))exit 1091 | days = days-daysInMonth(month,year) 1092 | month = month+1 1093 | end do 1094 | 1095 | day = int(days) 1096 | totseconds = (days-day)*d2s 1097 | hour = int(totseconds*s2h) 1098 | minute = int((totseconds-hour*h2s)*s2m) 1099 | second = int(totseconds-hour*h2s-minute*m2s) 1100 | millisecond = nint((totseconds-int(totseconds))*1e3_real64) 1101 | 1102 | num2date = datetime(year,month,day,hour,minute,second,millisecond,tz=zero) 1103 | 1104 | ! Handle a special case caused by floating-point arithmethic: 1105 | if (num2date % millisecond == 1000) then 1106 | num2date % millisecond = 0 1107 | call num2date % addSeconds(1) 1108 | end if 1109 | 1110 | if (num2date % second == 60) then 1111 | num2date % second = 0 1112 | call num2date % addMinutes(1) 1113 | end if 1114 | if (num2date % minute == 60) then 1115 | num2date % minute = 0 1116 | call num2date % addHours(1) 1117 | end if 1118 | if (num2date % hour == 24) then 1119 | num2date % hour = 0 1120 | call num2date % addDays(1) 1121 | end if 1122 | 1123 | end function num2date 1124 | 1125 | 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) 1146 | ! A wrapper function around C/C++ strptime function. 1147 | ! Returns a `datetime` instance. 1148 | character(*), intent(in) :: str, format 1149 | real(real64), intent(in), optional :: tz 1150 | integer :: rc 1151 | type(tm_struct) :: tm 1152 | rc = c_strptime(trim(str) // c_null_char, trim(format) // c_null_char, tm) 1153 | if (rc == 0) then 1154 | write(stderr, *) "ERROR:datetime:strptime: failed to parse string: ", str 1155 | return 1156 | endif 1157 | strptime = tm2date(tm,tz) 1158 | end function strptime 1159 | 1160 | 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) 1204 | ! Given a `tm_struct` instance, returns a corresponding `datetime` 1205 | ! instance. 1206 | type(tm_struct), intent(in) :: ctime 1207 | real(real64), intent(in), optional :: tz ! time zone 1208 | 1209 | tm2date % millisecond = 0 1210 | tm2date % second = ctime % tm_sec 1211 | tm2date % minute = ctime % tm_min 1212 | tm2date % hour = ctime % tm_hour 1213 | tm2date % day = ctime % tm_mday 1214 | tm2date % month = ctime % tm_mon+1 1215 | tm2date % year = ctime % tm_year+1900 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 1227 | 1228 | end function tm2date 1229 | 1230 | 1231 | pure function int2str(i, length) 1232 | ! Converts an integer `i` into a character string of requested length, 1233 | ! pre-pending zeros if necessary. 1234 | integer, intent(in) :: i, length 1235 | character(length) :: int2str 1236 | character(2) :: string 1237 | write(string, '(i2)') length 1238 | write(int2str, '(i' // string // '.' // string //')') i 1239 | end function int2str 1240 | 1241 | 1242 | pure elemental type(timedelta) function timedelta_constructor( & 1243 | days, hours, minutes, seconds, milliseconds) 1244 | ! Constructor function for the `timedelta` class. 1245 | integer, intent(in), optional :: days, hours, minutes, seconds, milliseconds 1246 | 1247 | timedelta_constructor % days = 0 1248 | if (present(days)) timedelta_constructor % days = days 1249 | 1250 | timedelta_constructor % hours = 0 1251 | if (present(hours)) timedelta_constructor % hours = hours 1252 | 1253 | timedelta_constructor % minutes = 0 1254 | if (present(minutes)) timedelta_constructor % minutes = minutes 1255 | 1256 | timedelta_constructor % seconds = 0 1257 | if (present(seconds)) timedelta_constructor % seconds = seconds 1258 | 1259 | timedelta_constructor % milliseconds = 0 1260 | if (present(milliseconds)) timedelta_constructor % milliseconds = milliseconds 1261 | 1262 | end function timedelta_constructor 1263 | 1264 | 1265 | pure elemental integer function getDays(self) 1266 | ! Returns the number of days. 1267 | class(timedelta), intent(in) :: self 1268 | getDays = self % days 1269 | end function getDays 1270 | 1271 | 1272 | pure elemental integer function getHours(self) 1273 | ! Returns the number of hours. 1274 | class(timedelta), intent(in) :: self 1275 | getHours = self % hours 1276 | end function getHours 1277 | 1278 | 1279 | pure elemental integer function getMinutes(self) 1280 | ! Returns the number of minutes. 1281 | class(timedelta), intent(in) :: self 1282 | getMinutes = self % minutes 1283 | end function getMinutes 1284 | 1285 | 1286 | pure elemental integer function getSeconds(self) 1287 | ! Returns the number of seconds. 1288 | class(timedelta), intent(in) :: self 1289 | getSeconds = self % seconds 1290 | end function getSeconds 1291 | 1292 | 1293 | pure elemental integer function getMilliseconds(self) 1294 | ! Returns the number of milliseconds. 1295 | class(timedelta), intent(in) :: self 1296 | getMilliseconds = self % milliseconds 1297 | end function getMilliseconds 1298 | 1299 | 1300 | pure elemental real(real64) function total_seconds(self) 1301 | ! Returns a total number of seconds contained in a `timedelta` 1302 | ! instance. 1303 | class(timedelta), intent(in) :: self 1304 | total_seconds = self % days*86400._real64 & 1305 | + self % hours*3600._real64 & 1306 | + self % minutes*60._real64 & 1307 | + self % seconds & 1308 | + self % milliseconds*1e-3_real64 1309 | end function total_seconds 1310 | 1311 | 1312 | pure elemental type(timedelta) function timedelta_plus_timedelta(t0,t1) result(t) 1313 | ! Adds two `timedelta` instances together and returns a `timedelta` 1314 | ! instance. Overloads the operator `+`. 1315 | class(timedelta), intent(in) :: t0, t1 1316 | t = timedelta(days = t0 % days + t1 % days, & 1317 | hours = t0 % hours + t1 % hours, & 1318 | minutes = t0 % minutes + t1 % minutes, & 1319 | seconds = t0 % seconds + t1 % seconds, & 1320 | milliseconds = t0 % milliseconds + t1 % milliseconds) 1321 | end function timedelta_plus_timedelta 1322 | 1323 | 1324 | pure elemental type(timedelta) function timedelta_minus_timedelta(t0,t1) result(t) 1325 | ! Subtracts a `timedelta` instance from another. Returns a 1326 | ! `timedelta` instance. Overloads the operator `-`. 1327 | class(timedelta), intent(in) :: t0, t1 1328 | t = t0 + (-t1) 1329 | end function timedelta_minus_timedelta 1330 | 1331 | 1332 | pure elemental type(timedelta) function unary_minus_timedelta(t0) result(t) 1333 | ! Takes a negative of a `timedelta` instance. Overloads the operator `-`. 1334 | class(timedelta), intent(in) :: t0 1335 | t % days = -t0 % days 1336 | t % hours = -t0 % hours 1337 | t % minutes = -t0 % minutes 1338 | t % seconds = -t0 % seconds 1339 | t % milliseconds = -t0 % milliseconds 1340 | end function unary_minus_timedelta 1341 | 1342 | 1343 | pure elemental logical function timedelta_eq(td0,td1) result(res) 1344 | ! `timedelta` object comparison operator. Returns `.true.` if `td0` 1345 | ! is equal to `td1` and `.false.` otherwise. Overloads the operator 1346 | ! `==`. 1347 | class(timedelta), intent(in) :: td0, td1 1348 | res = td0 % total_seconds() == td1 % total_seconds() 1349 | end function timedelta_eq 1350 | 1351 | 1352 | pure elemental logical function timedelta_neq(td0,td1) result(res) 1353 | ! `timedelta` object comparison operator. Returns `.true.` if `td0` 1354 | ! is not equal to `td1` and `.false.` otherwise. Overloads the 1355 | ! operator `/=`. 1356 | class(timedelta), intent(in) :: td0, td1 1357 | res = td0 % total_seconds() /= td1 % total_seconds() 1358 | end function timedelta_neq 1359 | 1360 | 1361 | pure elemental logical function timedelta_gt(td0,td1) result(res) 1362 | ! `timedelta` object comparison operator. Returns `.true.` if 1363 | ! `td0` is greater than `td1` and `.false.` otherwise. Overloads the 1364 | ! operator `>`. 1365 | class(timedelta), intent(in) :: td0, td1 1366 | res = td0 % total_seconds() > td1 % total_seconds() 1367 | end function timedelta_gt 1368 | 1369 | 1370 | pure elemental logical function timedelta_ge(td0,td1) result(res) 1371 | ! `timedelta` object comparison operator. Returns `.true.` if `td0` 1372 | ! is greater than or equal to `td1` and `.false.` otherwise. 1373 | ! Overloads the operator >=. 1374 | class(timedelta), intent(in) :: td0, td1 1375 | res = td0 % total_seconds() >= td1 % total_seconds() 1376 | end function timedelta_ge 1377 | 1378 | 1379 | pure elemental logical function timedelta_lt(td0,td1) result(res) 1380 | ! `timedelta` object comparison operator. Returns `.true.` if `td0` 1381 | ! is less than `td1` and `.false.` otherwise. Overloads the operator 1382 | ! `<`. 1383 | class(timedelta), intent(in) :: td0, td1 1384 | res = td0 % total_seconds() < td1 % total_seconds() 1385 | end function timedelta_lt 1386 | 1387 | 1388 | pure elemental logical function timedelta_le(td0,td1) result(res) 1389 | ! `timedelta` object comparison operator. Returns `.true.` if `td0` 1390 | ! is less than or equal to `td1` and `.false.` otherwise. Overloads 1391 | ! the operator `<=`. 1392 | class(timedelta), intent(in) :: td0, td1 1393 | res = td0 % total_seconds() <= td1 % total_seconds() 1394 | end function timedelta_le 1395 | 1396 | end module datetime_module 1397 | -------------------------------------------------------------------------------- /src/strptime.cpp: -------------------------------------------------------------------------------- 1 | // https://stackoverflow.com/a/33542189 2 | #include 3 | #include 4 | #include 5 | 6 | extern "C" char* strptime(const char* s, 7 | const char* f, 8 | struct tm* tm) { 9 | // Isn't the C++ standard lib nice? std::get_time is defined such that its 10 | // format parameters are the exact same as strptime. Of course, we have to 11 | // create a string stream first, and imbue it with the current C locale, and 12 | // we also have to make sure we return the right things if it fails, or 13 | // if it succeeds, but this is still far simpler an implementation than any 14 | // of the versions in any of the C standard libraries. 15 | std::istringstream input(s); 16 | input.imbue(std::locale(setlocale(LC_ALL, nullptr))); 17 | input >> std::get_time(tm, f); 18 | if (input.fail()) { 19 | return nullptr; 20 | } 21 | return (char*)(s + input.tellg()); 22 | } -------------------------------------------------------------------------------- /test/Makefile.am: -------------------------------------------------------------------------------- 1 | noinst_PROGRAMS = datetime_tests 2 | EXTRA_DIST = tests-env.sh 3 | datetime_tests_SOURCES = datetime_tests.f90 4 | CLEANFILES = *.mod 5 | 6 | # Mostly unchanged from original upstream Makefile 7 | LIB = ../src 8 | INCLUDE = ../src 9 | OBJ = datetime_tests.o 10 | 11 | .f90.o: 12 | $(FC) -c $(FCFLAGS) -I$(INCLUDE) $< 13 | 14 | datetime_tests$(EXEEXT): datetime_tests.o 15 | $(FC) $(FCFLAGS) $(OBJ) -L$(LIB) -ldatetime -o $@ 16 | 17 | #bin_SCRIPTS = . tests/tests-env.sh 18 | AM_TESTS_ENVIRONMENT = . ./tests-env.sh 19 | AM_TESTS_FD_REDIRECT = 9>&2 20 | TESTS = tests-env.sh 21 | -------------------------------------------------------------------------------- /test/datetime_tests.f90: -------------------------------------------------------------------------------- 1 | module datetime_tests 2 | use datetime_module 3 | use, intrinsic :: iso_fortran_env, only: int64, real64 4 | 5 | implicit none 6 | 7 | private 8 | public :: assert, test_datetime 9 | 10 | contains 11 | 12 | logical function assert(condition, test_name) 13 | logical, intent(in) :: condition 14 | character(*), intent(in) :: test_name 15 | character(60) :: output_test_name 16 | 17 | assert = condition 18 | output_test_name = test_name 19 | 20 | if (assert) then 21 | print *, 'test ' // output_test_name // ': ' // & 22 | char(27) // '[32mPASS' // char(27) // '[0m' 23 | else 24 | print '(a)', 'test ' // output_test_name // ': ' // & 25 | char(27) // '[31mFAIL' // char(27) // '[0m' 26 | end if 27 | 28 | end function assert 29 | 30 | 31 | subroutine initialize_tests(tests, ntests) 32 | logical, allocatable, intent(in out) :: tests(:) 33 | integer, intent(in) :: ntests 34 | if (allocated(tests)) deallocate(tests) 35 | allocate(tests(ntests)) 36 | end subroutine initialize_tests 37 | 38 | 39 | subroutine report_tests(tests, test_failed) 40 | ! Takes the test counter as input and reports 41 | ! the total number of test passes and failures. 42 | logical, intent(in) :: tests(:) 43 | logical, optional, intent(out) :: test_failed 44 | 45 | integer :: n, ntests, nsuccess, nfailure 46 | 47 | ntests = size(tests) 48 | 49 | nsuccess = 0 50 | nfailure = 0 51 | do n = 1, ntests 52 | if (tests(n)) then 53 | nsuccess = nsuccess + 1 54 | else 55 | nfailure = nfailure + 1 56 | end if 57 | end do 58 | 59 | print '(a, i3, a)', 'Ran a total of ', ntests, ' tests.' 60 | print '(i3, a, i3, a)', nsuccess, ' tests PASSED, ', & 61 | nfailure, ' tests FAILED.' 62 | 63 | if (present(test_failed)) then 64 | test_failed = .false. 65 | if (nfailure /= 0) test_failed = .true. 66 | end if 67 | 68 | end subroutine report_tests 69 | 70 | 71 | subroutine test_datetime 72 | type(datetime) :: a 73 | type(timedelta) :: td 74 | type(clock) :: c 75 | 76 | type(datetime), allocatable :: dtRange(:) 77 | 78 | real(real64) :: eps = tiny(1._real64) 79 | logical, allocatable :: tests(:) 80 | logical :: test_failed 81 | integer :: i, n, ntests 82 | 83 | print * 84 | 85 | ! Test counter; modify if adding new tests 86 | ntests = 196 87 | 88 | call initialize_tests(tests, ntests) 89 | 90 | n = 1 91 | 92 | ! Empty constructor 93 | tests(n) = assert(datetime() == datetime(1, 1, 1), & 94 | 'empty datetime() constructor') 95 | n = n + 1 96 | 97 | ! Empty time initialization 98 | tests(n) = assert(datetime(2014, 1, 1) == datetime(2014, 1, 1, 0, 0, 0, 0), & 99 | 'semi-empty datetime() constructor') 100 | n = n + 1 101 | 102 | ! Increment milliseconds 103 | tests(n) = assert(datetime(2014, 1, 1, 0, 0, 0, 0) + timedelta(milliseconds= 100) & 104 | == datetime(2014, 1, 1, 0, 0, 0, 100), & 105 | 'datetime + timedelta(milliseconds = 100)') 106 | n = n + 1 107 | 108 | ! Decrement milliseconds 109 | tests(n) = assert(datetime(2014, 1, 1, 0, 0, 0, 0) + timedelta(milliseconds=-100) & 110 | == datetime(2013, 12, 31, 23, 59, 59, 900), & 111 | 'datetime + timedelta(milliseconds = -100)') 112 | n = n + 1 113 | 114 | ! Increment seconds 115 | tests(n) = assert(datetime(2014, 1, 1, 0, 0, 0, 0) + timedelta(seconds=1) & 116 | == datetime(2014, 1, 1, 0, 0, 1, 0), & 117 | 'datetime + timedelta(seconds = 1)') 118 | n = n + 1 119 | 120 | ! Decrement seconds 121 | tests(n) = assert(datetime(2014, 1, 1, 0, 0, 0, 0) + timedelta(seconds=-1) & 122 | == datetime(2013, 12, 31, 23, 59, 59, 0), & 123 | 'datetime + timedelta(seconds = -1)') 124 | n = n + 1 125 | 126 | ! Increment minutes 127 | tests(n) = assert(datetime(2014, 1, 1, 0, 0, 0, 0) + timedelta(minutes=1) & 128 | == datetime(2014, 1, 1, 0, 1, 0, 0), & 129 | 'datetime + timedelta(minutes = 1)') 130 | n = n + 1 131 | 132 | ! Decrement minutes 133 | tests(n) = assert(datetime(2014, 1, 1, 0, 0, 0, 0) + timedelta(minutes=-1) & 134 | == datetime(2013, 12, 31, 23, 59, 0, 0), & 135 | 'datetime + timedelta(minutes = -1)') 136 | n = n + 1 137 | 138 | ! Increment hours 139 | tests(n) = assert(datetime(2014, 1, 1, 0, 0, 0, 0) + timedelta(hours=1) & 140 | == datetime(2014, 1, 1, 1, 0, 0, 0), & 141 | 'datetime + timedelta(hours = 1)') 142 | n = n + 1 143 | 144 | ! Decrement hours 145 | tests(n) = assert(datetime(2014, 1, 1, 0, 0, 0, 0) + timedelta(hours=-1) & 146 | == datetime(2013, 12, 31, 23, 0, 0, 0), & 147 | 'datetime + timedelta(hours = -1)') 148 | n = n + 1 149 | 150 | ! Increment days 151 | tests(n) = assert(datetime(2014, 1, 1, 0, 0, 0, 0) + timedelta(days= 1) & 152 | == datetime(2014, 1, 2, 0, 0, 0, 0), & 153 | 'datetime + timedelta(days = 1)') 154 | n = n + 1 155 | 156 | ! Decrement days 157 | tests(n) = assert(datetime(2014, 1, 1, 0, 0, 0, 0) + timedelta(days=-1)& 158 | == datetime(2013, 12, 31, 0, 0, 0, 0), & 159 | 'datetime + timedelta(days = -1)') 160 | n = n + 1 161 | 162 | ! Test various overflow situations 163 | 164 | a = datetime(2014, 1, 1, 0, 0, 0) 165 | 166 | tests(n) = assert(a+timedelta(seconds=3) == a+timedelta(milliseconds=3000), & 167 | 'Seconds overflow in addMilliseconds (3000 milliseconds)') 168 | n = n + 1 169 | 170 | tests(n) = assert(a-timedelta(seconds=3) == a-timedelta(milliseconds=3000), & 171 | 'Seconds overflow in addMilliseconds (-3000 milliseconds)') 172 | n = n + 1 173 | 174 | tests(n) = assert(a+timedelta(minutes=6) == a+timedelta(seconds=360), & 175 | 'Minutes overflow in addSeconds (360 seconds)') 176 | n = n + 1 177 | 178 | tests(n) = assert(a-timedelta(minutes=6) == a-timedelta(seconds=360), & 179 | 'Minutes overflow in addSeconds (-360 seconds)') 180 | n = n + 1 181 | 182 | tests(n) = assert(a+timedelta(hours=6) == a+timedelta(minutes=360), & 183 | 'Hours overflow in addMinutes (360 minutes)') 184 | n = n + 1 185 | 186 | tests(n) = assert(a-timedelta(hours=6) == a-timedelta(minutes=360), & 187 | 'Hours overflow in addMinutes (-360 minutes)') 188 | n = n + 1 189 | 190 | tests(n) = assert(a+timedelta(days=3) == a+timedelta(hours=72), & 191 | 'Days overflow in addHours (72 hours)') 192 | n = n + 1 193 | 194 | tests(n) = assert(a-timedelta(days=3) == a-timedelta(hours=72), & 195 | 'Days overflow in addHours (-72 hours)') 196 | n = n + 1 197 | 198 | print '(71("-"))' 199 | 200 | ! Test subtracting into previous months: 201 | tests(n) = assert(datetime(2014, 2, 1, 0, 0, 0, 0) + timedelta(days=-1)& 202 | == datetime(2014, 1, 31, 0, 0, 0, 0), & 203 | 'decrement datetime into January') 204 | n = n + 1 205 | 206 | tests(n) = assert(datetime(2014, 3, 1, 0, 0, 0, 0) + timedelta(days=-1)& 207 | == datetime(2014, 2, 28, 0, 0, 0, 0), & 208 | 'decrement datetime into February') 209 | n = n + 1 210 | 211 | tests(n) = assert(datetime(2014, 4, 1, 0, 0, 0, 0) + timedelta(days=-1)& 212 | == datetime(2014, 3, 31, 0, 0, 0, 0), & 213 | 'decrement datetime into March') 214 | n = n + 1 215 | 216 | tests(n) = assert(datetime(2014, 5, 1, 0, 0, 0, 0) + timedelta(days=-1)& 217 | == datetime(2014, 4, 30, 0, 0, 0, 0), & 218 | 'decrement datetime into April') 219 | n = n + 1 220 | 221 | tests(n) = assert(datetime(2014, 6, 1, 0, 0, 0, 0) + timedelta(days=-1)& 222 | == datetime(2014, 5, 31, 0, 0, 0, 0), & 223 | 'decrement datetime into May') 224 | n = n + 1 225 | 226 | tests(n) = assert(datetime(2014, 7, 1, 0, 0, 0, 0) + timedelta(days=-1)& 227 | == datetime(2014, 6, 30, 0, 0, 0, 0), & 228 | 'decrement datetime into June') 229 | n = n + 1 230 | 231 | tests(n) = assert(datetime(2014, 8, 1, 0, 0, 0, 0) + timedelta(days=-1)& 232 | == datetime(2014, 7, 31, 0, 0, 0, 0), & 233 | 'decrement datetime into July') 234 | n = n + 1 235 | 236 | tests(n) = assert(datetime(2014, 9, 1, 0, 0, 0, 0) + timedelta(days=-1)& 237 | == datetime(2014, 8, 31, 0, 0, 0, 0), & 238 | 'decrement datetime into August') 239 | n = n + 1 240 | 241 | tests(n) = assert(datetime(2014, 10, 1, 0, 0, 0, 0) + timedelta(days=-1)& 242 | == datetime(2014, 9, 30, 0, 0, 0, 0), & 243 | 'decrement datetime into September') 244 | n = n + 1 245 | 246 | tests(n) = assert(datetime(2014, 11, 1, 0, 0, 0, 0) + timedelta(days=-1)& 247 | == datetime(2014, 10, 31, 0, 0, 0, 0), & 248 | 'decrement datetime into October') 249 | n = n + 1 250 | 251 | tests(n) = assert(datetime(2014, 12, 1, 0, 0, 0, 0) + timedelta(days=-1)& 252 | == datetime(2014, 11, 30, 0, 0, 0, 0), & 253 | 'decrement datetime into November') 254 | n = n + 1 255 | 256 | tests(n) = assert(datetime(2015, 1, 1, 0, 0, 0, 0) + timedelta(days=-1)& 257 | == datetime(2014, 12, 31, 0, 0, 0, 0), & 258 | 'decrement datetime into December') 259 | n = n + 1 260 | 261 | print '(71("-"))' 262 | 263 | ! datetime difference 264 | tests(n) = assert(datetime(2014, 1, 2)-datetime(2014, 1, 1)& 265 | == timedelta(days=1), & 266 | 'datetime-datetime == timedelta(days = 1)') 267 | n = n + 1 268 | 269 | tests(n) = assert(datetime(2014, 1, 1, 2)-datetime(2014, 1, 1, 1)& 270 | == timedelta(hours=1), & 271 | 'datetime-datetime == timedelta(hours = 1)') 272 | n = n + 1 273 | 274 | tests(n) = assert(datetime(2014, 1, 1, 1, 2)-datetime(2014, 1, 1, 1, 1)& 275 | == timedelta(minutes=1), & 276 | 'datetime-datetime == timedelta(minutes = 1)') 277 | n = n + 1 278 | 279 | tests(n) = assert(datetime(2014, 1, 1, 1, 1, 2)-datetime(2014, 1, 1, 1, 1, 1)& 280 | == timedelta(seconds=1), & 281 | 'datetime-datetime == timedelta(seconds = 1)') 282 | n = n + 1 283 | 284 | tests(n) = assert(datetime(2014, 1, 1, 1, 1, 1, 2)& 285 | -datetime(2014, 1, 1, 1, 1, 1, 1)& 286 | == timedelta(milliseconds=1), & 287 | 'datetime-datetime == timedelta(milliseconds = 1)') 288 | n = n + 1 289 | 290 | print '(71("-"))' 291 | 292 | ! datetime comparison tests 293 | tests(n) = assert(datetime(2014, 1, 2, 3, 4, 5, 6) & 294 | == datetime(2014, 1, 2, 3, 4, 5, 6), & 295 | 'datetime == datetime') 296 | n = n + 1 297 | 298 | tests(n) = assert(datetime(2014, 1, 2, 9, 4, 5, 6, tz=6._real64) & 299 | == datetime(2014, 1, 2, 3, 4, 5, 6, tz=0._real64), & 300 | 'datetime == datetime, timezone test 1') 301 | n = n + 1 302 | 303 | tests(n) = assert(datetime(2014, 1, 2, 3, 4, 5, 6, tz=-6._real64) & 304 | == datetime(2014, 1, 2, 9, 4, 5, 6, tz= 0._real64), & 305 | 'datetime == datetime, timezone test 2') 306 | n = n + 1 307 | 308 | tests(n) = assert(datetime(2013, 1, 2, 3, 4, 5, 6) & 309 | /= datetime(2014, 1, 2, 3, 4, 5, 6), & 310 | 'datetime /= datetime') 311 | n = n + 1 312 | 313 | tests(n) = assert(datetime(2014, 1, 2, 4, 4, 5, 6) & 314 | > datetime(2014, 1, 2, 3, 4, 5, 6), & 315 | 'datetime > datetime') 316 | n = n + 1 317 | 318 | tests(n) = assert(datetime(2014, 1, 2, 4, 4, 5, 6) & 319 | >= datetime(2014, 1, 2, 3, 4, 5, 6), & 320 | 'datetime >= datetime (greater)') 321 | n = n + 1 322 | 323 | tests(n) = assert(datetime(2014, 1, 2, 3, 4, 5, 6) & 324 | >= datetime(2014, 1, 2, 3, 4, 5, 6), & 325 | 'datetime >= datetime (equal)') 326 | n = n + 1 327 | 328 | tests(n) = assert(datetime(2014, 1, 2, 3, 4, 5, 6) & 329 | < datetime(2014, 1, 2, 4, 4, 5, 6), & 330 | 'datetime < datetime') 331 | n = n + 1 332 | 333 | tests(n) = assert(datetime(2014, 1, 2, 3, 4, 5, 6) & 334 | <= datetime(2014, 1, 2, 4, 4, 5, 6), & 335 | 'datetime <= datetime (less)') 336 | n = n + 1 337 | 338 | tests(n) = assert(datetime(2014, 1, 2, 3, 4, 5, 6) & 339 | <= datetime(2014, 1, 2, 3, 4, 5, 6), & 340 | 'datetime <= datetime (equal)') 341 | n = n + 1 342 | 343 | print '(71("-"))' 344 | 345 | ! Test datetime % isoformat() 346 | 347 | a = datetime(2014, 1, 1, 0, 0, 0, 0) 348 | 349 | tests(n) = assert(a % isoformat() == '2014-01-01T00:00:00.000', & 350 | 'datetime % isoformat, default separator') 351 | n = n + 1 352 | 353 | tests(n) = assert(a % isoformat('T') == '2014-01-01T00:00:00.000', & 354 | 'datetime % isoformat, T separator') 355 | n = n + 1 356 | 357 | tests(n) = assert(a % isoformat(' ') == '2014-01-01 00:00:00.000', & 358 | 'datetime % isoformat, blank separator') 359 | n = n + 1 360 | 361 | print '(71("-"))' 362 | 363 | ! Test datetime % strftime() and strptime() 364 | 365 | a = datetime(2014, 1, 2, 3, 4, 5) 366 | 367 | tests(n) = assert(a % strftime('%Y-%m-%d %H:%M:%S')& 368 | == '2014-01-02 03:04:05', & 369 | 'datetime % strftime') 370 | n = n + 1 371 | 372 | tests(n) = assert(strptime('2014-01-02 03:04:05', '%Y-%m-%d %H:%M:%S')& 373 | == datetime(2014, 1, 2, 3, 4, 5), & 374 | 'datetime % strptime') 375 | n = n + 1 376 | 377 | tests(n) = assert(strptime(a % strftime('%Y-%m-%d %H:%M:%S'), & 378 | '%Y-%m-%d %H:%M:%S') == a, & 379 | 'strptime(datetime % strftime(fmt), fmt) == datetime') 380 | n = n + 1 381 | 382 | block 383 | type(tm_struct) :: tm 384 | a = strptime("1970/1/1 0:0:0","%Y/%m/%d %H:%M:%S",tz=9.0_real64) 385 | tests(n) = assert(a % getTz() == 9.0_real64, & 386 | 'strptime with `tz`, 9.0_real64') 387 | end block 388 | n = n + 1 389 | 390 | print '(71("-"))' 391 | 392 | ! datetime % isocalendar: test all examples from 393 | ! http://en.wikipedia.org/wiki/ISO_week_date 394 | 395 | a = datetime(2005, 1, 1) 396 | tests(n) = assert(ALL(a % isocalendar() == [2004, 53, 6]), & 397 | 'datetime(2005, 1, 1) % isocalendar() == [2004, 53, 6]') 398 | n = n + 1 399 | 400 | a = datetime(2005, 1, 2) 401 | tests(n) = assert(ALL(a % isocalendar() == [2004, 53, 7]), & 402 | 'datetime(2005, 1, 2) % isocalendar() == [2004, 53, 7]') 403 | n = n + 1 404 | 405 | a = datetime(2005, 12, 31) 406 | tests(n) = assert(ALL(a % isocalendar() == [2005, 52, 6]), & 407 | 'datetime(2005, 12, 31) % isocalendar() == [2005, 52, 6]') 408 | n = n + 1 409 | 410 | a = datetime(2007, 1, 1) 411 | tests(n) = assert(ALL(a % isocalendar() == [2007, 1, 1]), & 412 | 'datetime(2007, 1, 1) % isocalendar() == [2007, 1, 1]') 413 | n = n + 1 414 | 415 | a = datetime(2007, 12, 30) 416 | tests(n) = assert(ALL(a % isocalendar() == [2007, 52, 7]), & 417 | 'datetime(2007, 12, 30) % isocalendar() == [2007, 52, 7]') 418 | n = n + 1 419 | 420 | a = datetime(2007, 12, 31) 421 | tests(n) = assert(ALL(a % isocalendar() == [2008, 1, 1]), & 422 | 'datetime(2007, 12, 31) % isocalendar() == [2008, 1, 1]') 423 | n = n + 1 424 | 425 | a = datetime(2008, 1, 1) 426 | tests(n) = assert(ALL(a % isocalendar() == [2008, 1, 2]), & 427 | 'datetime(2008, 1, 1) % isocalendar() == [2008, 1, 2]') 428 | n = n + 1 429 | 430 | a = datetime(2008, 12, 28) 431 | tests(n) = assert(ALL(a % isocalendar() == [2008, 52, 7]), & 432 | 'datetime(2008, 12, 28) % isocalendar() == [2008, 52, 7]') 433 | n = n + 1 434 | 435 | a = datetime(2008, 12, 29) 436 | tests(n) = assert(ALL(a % isocalendar() == [2009, 1, 1]), & 437 | 'datetime(2008, 12, 29) % isocalendar() == [2009, 1, 1]') 438 | n = n + 1 439 | 440 | a = datetime(2008, 12, 30) 441 | tests(n) = assert(ALL(a % isocalendar() == [2009, 1, 2]), & 442 | 'datetime(2008, 12, 30) % isocalendar() == [2009, 1, 2]') 443 | n = n + 1 444 | 445 | a = datetime(2008, 12, 31) 446 | tests(n) = assert(ALL(a % isocalendar() == [2009, 1, 3]), & 447 | 'datetime(2008, 12, 31) % isocalendar() == [2009, 1, 3]') 448 | n = n + 1 449 | 450 | a = datetime(2009, 1, 1) 451 | tests(n) = assert(ALL(a % isocalendar() == [2009, 1, 4]), & 452 | 'datetime(2009, 1, 1) % isocalendar() == [2009, 1, 4]') 453 | n = n + 1 454 | 455 | a = datetime(2009, 12, 31) 456 | tests(n) = assert(ALL(a % isocalendar() == [2009, 53, 4]), & 457 | 'datetime(2009, 12, 31) % isocalendar() == [2009, 53, 4]') 458 | n = n + 1 459 | 460 | a = datetime(2010, 1, 1) 461 | tests(n) = assert(ALL(a % isocalendar() == [2009, 53, 5]), & 462 | 'datetime(2010, 1, 1) % isocalendar() == [2009, 53, 5]') 463 | n = n + 1 464 | 465 | a = datetime(2010, 1, 2) 466 | tests(n) = assert(ALL(a % isocalendar() == [2009, 53, 6]), & 467 | 'datetime(2010, 1, 2) % isocalendar() == [2009, 53, 6]') 468 | n = n + 1 469 | 470 | a = datetime(2010, 1, 3) 471 | tests(n) = assert(ALL(a % isocalendar() == [2009, 53, 7]), & 472 | 'datetime(2010, 1, 3) % isocalendar() == [2009, 53, 7]') 473 | n = n + 1 474 | 475 | print '(71("-"))' 476 | 477 | ! datetime % isValid() 478 | 479 | a = datetime(1, 1, 1) 480 | tests(n) = assert(a % isValid(), 'datetime(1, 1, 1) is valid') 481 | n = n + 1 482 | 483 | a = datetime(0, 1, 1) 484 | tests(n) = assert(.not. a % isValid(), 'datetime(0, 1, 1) is not valid') 485 | n = n + 1 486 | 487 | a = datetime(-1, 1, 1) 488 | tests(n) = assert(.not. a % isValid(), 'datetime(-1, 1, 1) is not valid') 489 | n = n + 1 490 | 491 | a = datetime(2014, 1, 1) 492 | tests(n) = assert(a % isValid(), 'datetime(2014, 1, 1) is valid') 493 | n = n + 1 494 | 495 | a = datetime(2014, 0, 1) 496 | tests(n) = assert(.not. a % isValid(), 'datetime(2014, 0, 1) is not valid') 497 | n = n + 1 498 | 499 | a = datetime(2014, 1, 0) 500 | tests(n) = assert(.not. a % isValid(), 'datetime(2014, 1, 0) is not valid') 501 | n = n + 1 502 | 503 | a = datetime(2014, 2, 28) 504 | tests(n) = assert(a % isValid(), 'datetime(2014, 2, 28) is valid') 505 | n = n + 1 506 | 507 | a = datetime(2014, 2, 29) 508 | tests(n) = assert(.not. a % isValid(), 'datetime(2014, 2, 29) is not valid') 509 | n = n + 1 510 | 511 | a = datetime(2012, 2, 29) 512 | tests(n) = assert(a % isValid(), 'datetime(2012, 2, 29) is valid') 513 | n = n + 1 514 | 515 | a = datetime(2012, 3, 31) 516 | tests(n) = assert(a % isValid(), 'datetime(2012, 3, 31) is valid') 517 | n = n + 1 518 | 519 | a = datetime(2012, 3, 32) 520 | tests(n) = assert(.not. a % isValid(), 'datetime(2012, 3, 32) is not valid') 521 | n = n + 1 522 | 523 | a = datetime(2012, 3, 31, 0, 0, 0) 524 | tests(n) = assert(a % isValid(), 'datetime(2012, 3, 31, 0, 0, 0) is valid') 525 | n = n + 1 526 | 527 | a = datetime(2012, 3, 31, 24, 0, 0) 528 | tests(n) = assert(.not. a % isValid(), 'datetime(2012, 3, 31, 24, 0, 0) is not valid') 529 | n = n + 1 530 | 531 | a = datetime(2012, 3, 31, 0, 60, 0) 532 | tests(n) = assert(.not. a % isValid(), 'datetime(2012, 3, 31, 0, 60, 0) is not valid') 533 | n = n + 1 534 | 535 | a = datetime(2012, 3, 31, 0, 0, 60) 536 | tests(n) = assert(.not. a % isValid(), 'datetime(2012, 3, 31, 0, 0, 60) is not valid') 537 | n = n + 1 538 | 539 | a = datetime(2012, 3, 31, 0, 0, 0, 1000) 540 | tests(n) = assert(.not. a % isValid(), 'datetime(2012, 3, 31, 0, 0, 0, 1000) is not valid') 541 | n = n + 1 542 | 543 | print '(71("-"))' 544 | 545 | ! datetime % secondsSinceEpoch 546 | a = datetime(1970, 1, 1, 0, 0, 0) 547 | 548 | tests(n) = assert(a % secondsSinceEpoch() == 0, & 549 | 'datetime % secondsSinceEpoch(), 0 seconds') 550 | n = n + 1 551 | 552 | a = datetime(1970, 1, 1, 1, 0, 0) 553 | tests(n) = assert(a % secondsSinceEpoch() == 3600, & 554 | 'datetime % secondsSinceEpoch(), 1 hour') 555 | n = n + 1 556 | 557 | a = datetime(1969, 12, 31, 23, 0, 0) 558 | tests(n) = assert(a % secondsSinceEpoch() == -3600, & 559 | 'datetime % secondsSinceEpoch(), -1 hour') 560 | n = n + 1 561 | 562 | a = datetime(2070, 1, 1) 563 | tests(n) = assert(a % secondsSinceEpoch() == 3155760000_int64, & 564 | 'datetime % secondsSinceEpoch(), 100 years') 565 | n = n + 1 566 | 567 | a = datetime(1970, 1, 1, 0, 0, 0, 0, tz=9.0_real64) 568 | tests(n) = assert(a % secondsSinceEpoch() == -32400_int64, & 569 | 'datetime % secondsSinceEpoch(), with tz=9.0') 570 | n = n + 1 571 | 572 | ! localtime(epoc_time, tz) 573 | 574 | print '(71("-"))' 575 | 576 | block 577 | type(tm_struct) :: tm 578 | integer(int64) :: epoc 579 | a = datetime(1970, 1, 1, 0, 0, 0, 0, tz=machinetimezone()) 580 | tm = a % tm() 581 | epoc = a % secondsSinceEpoch() 582 | tests(n) = assert(a == localtime(epoc, machinetimezone()), & 583 | "localtime from epoc (with your timezone)") 584 | end block 585 | n = n + 1 586 | 587 | block 588 | type(tm_struct) :: tm 589 | type(datetime) :: b 590 | td = timedelta(hours=int(machinetimezone())) 591 | a = datetime(1970, 1, 1, 0, 0, 0, 0, tz=machinetimezone()) 592 | tm = a % tm() 593 | b = gmtime(a % secondsSinceEpoch()) 594 | a = a - td 595 | !print*,"a",a%isoformat() 596 | !print*,"g",b%isoformat(),b%getYear(),b%getDay() 597 | tests(n) = assert(a%isoformat() == b%isoformat(), "gmtime") 598 | end block 599 | n = n + 1 600 | 601 | print '(71("-"))' 602 | 603 | ! datetime % tzOffset 604 | 605 | a = datetime(2014, 1, 1, 0, 0, 0, tz=0._real64) 606 | tests(n) = assert(a % tzOffset() == '+0000', & 607 | 'datetime % tzOffset(), +0000') 608 | n = n + 1 609 | 610 | a = datetime(2014, 1, 1, 0, 0, 0, tz=-3.5_real64) 611 | tests(n) = assert(a % tzOffset() == '-0330', & 612 | 'datetime % tzOffset(), -0330') 613 | n = n + 1 614 | 615 | a = datetime(2014, 1, 1, 0, 0, 0, tz=5.75_real64) 616 | tests(n) = assert(a % tzOffset() == '+0545', & 617 | 'datetime % tzOffset(), +0545') 618 | n = n + 1 619 | 620 | print '(71("-"))' 621 | 622 | ! datetime % utc() 623 | 624 | a = datetime(2014, 1, 1, 0, 0, 0, tz=0._real64) 625 | tests(n) = assert(a % utc() == a, 'datetime % utc(), +0000') 626 | n = n + 1 627 | 628 | !a = datetime(2014, 1, 1, 0, 0, 0, tz=6.) 629 | !b = a-timedelta(hours=6) 630 | !b % tz = 0 631 | !tests(n) = assert(a % utc() == b, 'datetime % utc(), +0600') 632 | !n = n + 1 633 | 634 | !a = datetime(2014, 1, 1, 0, 0, 0, tz=-6.) 635 | !b = a+timedelta(hours=6) 636 | !b % tz = 0 637 | !tests(n) = assert(a % utc() == b, 'datetime % utc(), -0600') 638 | !!n = n + 1 639 | 640 | print '(71("-"))' 641 | 642 | ! datetime % weekday() 643 | 644 | a = datetime(2014, 1, 1) 645 | tests(n) = assert(a % weekday() == 3, 'datetime % weekday(), Wednesday') 646 | n = n + 1 647 | 648 | a = datetime(2014, 1, 2) 649 | tests(n) = assert(a % weekday() == 4, 'datetime % weekday(), Thursday') 650 | n = n + 1 651 | 652 | a = datetime(2014, 1, 3) 653 | tests(n) = assert(a % weekday() == 5, 'datetime % weekday(), Friday') 654 | n = n + 1 655 | 656 | a = datetime(2014, 1, 4) 657 | tests(n) = assert(a % weekday() == 6, 'datetime % weekday(), Saturday') 658 | n = n + 1 659 | 660 | a = datetime(2014, 1, 5) 661 | tests(n) = assert(a % weekday() == 0, 'datetime % weekday(), Sunday') 662 | n = n + 1 663 | 664 | a = datetime(2014, 1, 6) 665 | tests(n) = assert(a % weekday() == 1, 'datetime % weekday(), Monday') 666 | n = n + 1 667 | 668 | a = datetime(2014, 1, 7) 669 | tests(n) = assert(a % weekday() == 2, 'datetime % weekday(), Tuesday') 670 | n = n + 1 671 | 672 | print '(71("-"))' 673 | 674 | ! datetime % isoweekday() 675 | 676 | a = datetime(2014, 1, 1) 677 | tests(n) = assert(a % isoweekday() == 3, 'datetime % isoweekday(), Wednesday') 678 | n = n + 1 679 | 680 | a = datetime(2014, 1, 2) 681 | tests(n) = assert(a % isoweekday() == 4, 'datetime % isoweekday(), Thursday') 682 | n = n + 1 683 | 684 | a = datetime(2014, 1, 3) 685 | tests(n) = assert(a % isoweekday() == 5, 'datetime % isoweekday(), Friday') 686 | n = n + 1 687 | 688 | a = datetime(2014, 1, 4) 689 | tests(n) = assert(a % isoweekday() == 6, 'datetime % isoweekday(), Saturday') 690 | n = n + 1 691 | 692 | a = datetime(2014, 1, 5) 693 | tests(n) = assert(a % isoweekday() == 7, 'datetime % isoweekday(), Sunday') 694 | n = n + 1 695 | 696 | a = datetime(2014, 1, 6) 697 | tests(n) = assert(a % isoweekday() == 1, 'datetime % isoweekday(), Monday') 698 | n = n + 1 699 | 700 | a = datetime(2014, 1, 7) 701 | tests(n) = assert(a % isoweekday() == 2, 'datetime % isoweekday(), Tuesday') 702 | n = n + 1 703 | 704 | print '(71("-"))' 705 | 706 | ! datetime % weekdayLong() 707 | 708 | a = datetime(2014, 1, 1) 709 | tests(n) = assert(a % weekdayLong() == 'Wednesday', & 710 | 'datetime % weekdayLong(), Wednesday') 711 | n = n + 1 712 | 713 | a = datetime(2014, 1, 2) 714 | tests(n) = assert(a % weekdayLong() == 'Thursday', & 715 | 'datetime % weekdayLong(), Thursday') 716 | n = n + 1 717 | 718 | a = datetime(2014, 1, 3) 719 | tests(n) = assert(a % weekdayLong() == 'Friday', & 720 | 'datetime % weekdayLong(), Friday') 721 | n = n + 1 722 | 723 | a = datetime(2014, 1, 4) 724 | tests(n) = assert(a % weekdayLong() == 'Saturday', & 725 | 'datetime % weekdayLong(), Saturday') 726 | n = n + 1 727 | 728 | a = datetime(2014, 1, 5) 729 | tests(n) = assert(a % weekdayLong() == 'Sunday', & 730 | 'datetime % weekdayLong(), Sunday') 731 | n = n + 1 732 | 733 | a = datetime(2014, 1, 6) 734 | tests(n) = assert(a % weekdayLong() == 'Monday', & 735 | 'datetime % weekdayLong(), Monday') 736 | n = n + 1 737 | 738 | a = datetime(2014, 1, 7) 739 | tests(n) = assert(a % weekdayLong() == 'Tuesday', & 740 | 'datetime % weekdayLong(), Tuesday') 741 | n = n + 1 742 | 743 | print '(71("-"))' 744 | 745 | ! datetime % isoweekdayLong() 746 | 747 | a = datetime(2014, 1, 1) 748 | tests(n) = assert(a % isoweekdayLong() == 'Wednesday', & 749 | 'datetime % isoweekdayLong(), Wednesday') 750 | n = n + 1 751 | 752 | a = datetime(2014, 1, 2) 753 | tests(n) = assert(a % isoweekdayLong() == 'Thursday', & 754 | 'datetime % isoweekdayLong(), Thursday') 755 | n = n + 1 756 | 757 | a = datetime(2014, 1, 3) 758 | tests(n) = assert(a % isoweekdayLong() == 'Friday', & 759 | 'datetime % isoweekdayLong(), Friday') 760 | n = n + 1 761 | 762 | a = datetime(2014, 1, 4) 763 | tests(n) = assert(a % isoweekdayLong() == 'Saturday', & 764 | 'datetime % isoweekdayLong(), Saturday') 765 | n = n + 1 766 | 767 | a = datetime(2014, 1, 5) 768 | tests(n) = assert(a % isoweekdayLong() == 'Sunday', & 769 | 'datetime % isoweekdayLong(), Sunday') 770 | n = n + 1 771 | 772 | a = datetime(2014, 1, 6) 773 | tests(n) = assert(a % isoweekdayLong() == 'Monday', & 774 | 'datetime % isoweekdayLong(), Monday') 775 | n = n + 1 776 | 777 | a = datetime(2014, 1, 7) 778 | tests(n) = assert(a % isoweekdayLong() == 'Tuesday', & 779 | 'datetime % isoweekdayLong(), Tuesday') 780 | n = n + 1 781 | 782 | print '(71("-"))' 783 | 784 | ! datetime % weekdayShort() 785 | 786 | a = datetime(2014, 1, 1) 787 | tests(n) = assert(a % weekdayShort() == 'Wed', & 788 | 'datetime % weekdayShort(), Wed') 789 | n = n + 1 790 | 791 | a = datetime(2014, 1, 2) 792 | tests(n) = assert(a % weekdayShort() == 'Thu', & 793 | 'datetime % weekdayShort(), Thu') 794 | n = n + 1 795 | 796 | a = datetime(2014, 1, 3) 797 | tests(n) = assert(a % weekdayShort() == 'Fri', & 798 | 'datetime % weekdayShort(), Fri') 799 | n = n + 1 800 | 801 | a = datetime(2014, 1, 4) 802 | tests(n) = assert(a % weekdayShort() == 'Sat', & 803 | 'datetime % weekdayShort(), Sat') 804 | n = n + 1 805 | 806 | a = datetime(2014, 1, 5) 807 | tests(n) = assert(a % weekdayShort() == 'Sun', & 808 | 'datetime % weekdayShort(), Sun') 809 | n = n + 1 810 | 811 | a = datetime(2014, 1, 6) 812 | tests(n) = assert(a % weekdayShort() == 'Mon', & 813 | 'datetime % weekdayShort(), Mon') 814 | n = n + 1 815 | 816 | a = datetime(2014, 1, 7) 817 | tests(n) = assert(a % weekdayShort() == 'Tue', & 818 | 'datetime % weekdayShort(), Tue') 819 | n = n + 1 820 | 821 | print '(71("-"))' 822 | 823 | ! datetime % isoweekdayShort() 824 | 825 | a = datetime(2014, 1, 1) 826 | tests(n) = assert(a % isoweekdayShort() == 'Wed', & 827 | 'datetime % isoweekdayShort(), Wed') 828 | n = n + 1 829 | 830 | a = datetime(2014, 1, 2) 831 | tests(n) = assert(a % isoweekdayShort() == 'Thu', & 832 | 'datetime % isoweekdayShort(), Thu') 833 | n = n + 1 834 | 835 | a = datetime(2014, 1, 3) 836 | tests(n) = assert(a % isoweekdayShort() == 'Fri', & 837 | 'datetime % isoweekdayShort(), Fri') 838 | n = n + 1 839 | 840 | a = datetime(2014, 1, 4) 841 | tests(n) = assert(a % isoweekdayShort() == 'Sat', & 842 | 'datetime % isoweekdayShort(), Sat') 843 | n = n + 1 844 | 845 | a = datetime(2014, 1, 5) 846 | tests(n) = assert(a % isoweekdayShort() == 'Sun', & 847 | 'datetime % isoweekdayShort(), Sun') 848 | n = n + 1 849 | 850 | a = datetime(2014, 1, 6) 851 | tests(n) = assert(a % isoweekdayShort() == 'Mon', & 852 | 'datetime % isoweekdayShort(), Mon') 853 | n = n + 1 854 | 855 | a = datetime(2014, 1, 7) 856 | tests(n) = assert(a % isoweekdayShort() == 'Tue', & 857 | 'datetime % isoweekdayShort(), Tue') 858 | n = n + 1 859 | 860 | print '(71("-"))' 861 | 862 | ! datetime % yearday() 863 | 864 | a = datetime(2014, 1, 1) 865 | tests(n) = assert(a % yearday() == 1, & 866 | 'datetime(2014, 1, 1) % yearday() == 1') 867 | n = n + 1 868 | 869 | a = datetime(2014, 2, 1) 870 | tests(n) = assert(a % yearday() == 32, & 871 | 'datetime(2014, 2, 1) % yearday() == 32') 872 | n = n + 1 873 | 874 | a = datetime(2014, 3, 1) 875 | tests(n) = assert(a % yearday() == 60, & 876 | 'datetime(2014, 3, 1) % yearday() == 60') 877 | n = n + 1 878 | 879 | a = datetime(2014, 4, 1) 880 | tests(n) = assert(a % yearday() == 91, & 881 | 'datetime(2014, 4, 1) % yearday() == 91') 882 | n = n + 1 883 | 884 | a = datetime(2014, 5, 1) 885 | tests(n) = assert(a % yearday() == 121, & 886 | 'datetime(2014, 5, 1) % yearday() == 121') 887 | n = n + 1 888 | 889 | a = datetime(2014, 6, 1) 890 | tests(n) = assert(a % yearday() == 152, & 891 | 'datetime(2014, 6, 1) % yearday() == 152') 892 | n = n + 1 893 | 894 | a = datetime(2014, 7, 1) 895 | tests(n) = assert(a % yearday() == 182, & 896 | 'datetime(2014, 7, 1) % yearday() == 182') 897 | n = n + 1 898 | 899 | a = datetime(2014, 8, 1) 900 | tests(n) = assert(a % yearday() == 213, & 901 | 'datetime(2014, 8, 1) % yearday() == 213') 902 | n = n + 1 903 | 904 | a = datetime(2014, 9, 1) 905 | tests(n) = assert(a % yearday() == 244, & 906 | 'datetime(2014, 9, 1) % yearday() == 244') 907 | n = n + 1 908 | 909 | a = datetime(2014, 10, 1) 910 | tests(n) = assert(a % yearday() == 274, & 911 | 'datetime(2014, 10, 1) % yearday() == 275') 912 | n = n + 1 913 | 914 | a = datetime(2014, 11, 1) 915 | tests(n) = assert(a % yearday() == 305, & 916 | 'datetime(2014, 11, 1) % yearday() == 305') 917 | n = n + 1 918 | 919 | a = datetime(2014, 12, 1) 920 | tests(n) = assert(a % yearday() == 335, & 921 | 'datetime(2014, 12, 1) % yearday() == 335') 922 | n = n + 1 923 | 924 | print '(71("-"))' 925 | 926 | ! Timedelta tests 927 | tests(n) = assert(timedelta() == timedelta(0, 0, 0, 0, 0), & 928 | 'timedelta empty constructor') 929 | n = n + 1 930 | 931 | td = timedelta(milliseconds=1) 932 | tests(n) = assert(td % total_seconds() >= 1D-3-eps & 933 | .AND. td % total_seconds() <= 1D-3+eps, & 934 | 'timedelta % total_seconds(), milliseconds conversion') 935 | n = n + 1 936 | 937 | td = timedelta(seconds=1) 938 | tests(n) = assert(td % total_seconds() == 1, & 939 | 'timedelta % total_seconds(), seconds conversion') 940 | n = n + 1 941 | 942 | td = timedelta(minutes=1) 943 | tests(n) = assert(td % total_seconds() == 60, & 944 | 'timedelta % total_seconds(), minutes conversion') 945 | n = n + 1 946 | 947 | td = timedelta(hours=1) 948 | tests(n) = assert(td % total_seconds() == 3600, & 949 | 'timedelta % total_seconds(), hours conversion') 950 | n = n + 1 951 | 952 | td = timedelta(days=1) 953 | tests(n) = assert(td % total_seconds() == 86400, & 954 | 'timedelta % total_seconds(), days conversion') 955 | n = n + 1 956 | 957 | print '(71("-"))' 958 | 959 | ! Test date2num and num2date 960 | 961 | a = a % now() 962 | tests(n) = assert(a % utc() == num2date(date2num(a)), & 963 | 'datetime % utc() == num2date(date2num(datetime)) (now)') 964 | n = n + 1 965 | 966 | ! Test for overflowing month 967 | a = datetime(2014, 11, 30, 1) 968 | tests(n) = assert(a == num2date(date2num(a)), & 969 | 'datetime == num2date(date2num(datetime)) (overflowing month)') 970 | n = n + 1 971 | 972 | ! Test for overflowing year 973 | a = datetime(2014, 12, 31, 1) 974 | tests(n) = assert(a == num2date(date2num(a)), & 975 | 'datetime == num2date(date2num(datetime)) (overflowing year)') 976 | n = n + 1 977 | 978 | print '(71("-"))' 979 | 980 | ! datetimeRange tests 981 | 982 | allocate(dtRange(3)) 983 | 984 | dtRange = [datetime(2014, 1, 1), & 985 | datetime(2014, 1, 2), & 986 | datetime(2014, 1, 3)] 987 | tests(n) = assert(ALL(datetimeRange(datetime(2014, 1, 1), & 988 | datetime(2014, 1, 3), & 989 | timedelta(days=1)) & 990 | == dtRange), & 991 | 'datetimeRange, day increment') 992 | n = n + 1 993 | 994 | dtRange = [datetime(2014, 1, 1, 0), & 995 | datetime(2014, 1, 1, 1), & 996 | datetime(2014, 1, 1, 2)] 997 | tests(n) = assert(ALL(datetimeRange(datetime(2014, 1, 1, 0), & 998 | datetime(2014, 1, 1, 2), & 999 | timedelta(hours=1)) & 1000 | == dtRange), & 1001 | 'datetimeRange, hour increment') 1002 | n = n + 1 1003 | 1004 | dtRange = [datetime(2014, 1, 1, 0, 0), & 1005 | datetime(2014, 1, 1, 0, 1), & 1006 | datetime(2014, 1, 1, 0, 2)] 1007 | tests(n) = assert(ALL(datetimeRange(datetime(2014, 1, 1, 0, 0), & 1008 | datetime(2014, 1, 1, 0, 2), & 1009 | timedelta(minutes=1)) & 1010 | == dtRange), & 1011 | 'datetimeRange, minute increment') 1012 | n = n + 1 1013 | 1014 | dtRange = [datetime(2014, 1, 1, 0, 0, 0), & 1015 | datetime(2014, 1, 1, 0, 0, 1), & 1016 | datetime(2014, 1, 1, 0, 0, 2)] 1017 | tests(n) = assert(ALL(datetimeRange(datetime(2014, 1, 1, 0, 0, 0), & 1018 | datetime(2014, 1, 1, 0, 0, 2), & 1019 | timedelta(seconds=1)) & 1020 | == dtRange), & 1021 | 'datetimeRange, second increment') 1022 | n = n + 1 1023 | 1024 | deallocate(dtRange) 1025 | allocate(dtRange(7)) 1026 | 1027 | dtRange = [datetime(2012, 1, 1, 0, 0, 0), & 1028 | datetime(2012, 1, 1, 1, 0, 0), & 1029 | datetime(2012, 1, 1, 2, 0, 0), & 1030 | datetime(2012, 1, 1, 3, 0, 0), & 1031 | datetime(2012, 1, 1, 4, 0, 0), & 1032 | datetime(2012, 1, 1, 5, 0, 0), & 1033 | datetime(2012, 1, 1, 6, 0, 0)] 1034 | tests(n) = assert(ALL(datetimeRange(datetime(2012, 1, 1, 0, 0, 0), & 1035 | datetime(2012, 1, 1, 6, 0, 0), & 1036 | timedelta(hours=1)) == dtRange) & 1037 | .AND. SIZE(datetimeRange(datetime(2012, 1, 1, 0, 0, 0), & 1038 | datetime(2012, 1, 1, 6, 0, 0), & 1039 | timedelta(hours=1))) & 1040 | == SIZE(dtRange), 'datetimeRange, rounding test') 1041 | n = n + 1 1042 | 1043 | print '(71("-"))' 1044 | 1045 | ! isLeapYear tests 1046 | 1047 | tests(n) = assert(.not. isLeapYear(1), 'isLeapYear(1) == F') 1048 | n = n + 1 1049 | 1050 | tests(n) = assert(isLeapYear(4), 'isLeapYear(4) == T') 1051 | n = n + 1 1052 | 1053 | tests(n) = assert(.not. isLeapYear(100), 'isLeapYear(100) == F') 1054 | n = n + 1 1055 | 1056 | tests(n) = assert(isLeapYear(400), 'isLeapYear(400) == T') 1057 | n = n + 1 1058 | 1059 | tests(n) = assert(isLeapYear(2000), 'isLeapYear(2000) == T') 1060 | n = n + 1 1061 | 1062 | tests(n) = assert(.not. isLeapYear(2014), 'isLeapYear(2014) == F') 1063 | n = n + 1 1064 | 1065 | print '(71("-"))' 1066 | 1067 | ! daysInYear 1068 | 1069 | tests(n) = assert(daysInYear(2014) == 365, 'daysInYear(2014) == 365') 1070 | n = n + 1 1071 | 1072 | tests(n) = assert(daysInYear(2012) == 366, 'daysInYear(2012) == 366') 1073 | n = n + 1 1074 | 1075 | tests(n) = assert(daysInYear(2000) == 366, 'daysInYear(2000) == 366') 1076 | n = n + 1 1077 | 1078 | tests(n) = assert(daysInYear(1900) == 365, 'daysInYear(1900) == 365') 1079 | n = n + 1 1080 | 1081 | print '(71("-"))' 1082 | 1083 | ! daysInMonth 1084 | 1085 | tests(n) = assert(daysInMonth(1, 2014) == 31, 'daysInMonth(1, 2014) == 31') 1086 | n = n + 1 1087 | 1088 | tests(n) = assert(daysInMonth(2, 2014) == 28, 'daysInMonth(2, 2014) == 28') 1089 | n = n + 1 1090 | 1091 | tests(n) = assert(daysInMonth(2, 2012) == 29, 'daysInMonth(2, 2012) == 29') 1092 | n = n + 1 1093 | 1094 | tests(n) = assert(daysInMonth(3, 2014) == 31, 'daysInMonth(3, 2014) == 31') 1095 | n = n + 1 1096 | 1097 | tests(n) = assert(daysInMonth(4, 2014) == 30, 'daysInMonth(4, 2014) == 30') 1098 | n = n + 1 1099 | 1100 | tests(n) = assert(daysInMonth(5, 2014) == 31, 'daysInMonth(5, 2014) == 31') 1101 | n = n + 1 1102 | 1103 | tests(n) = assert(daysInMonth(6, 2014) == 30, 'daysInMonth(6, 2014) == 30') 1104 | n = n + 1 1105 | 1106 | tests(n) = assert(daysInMonth(7, 2014) == 31, 'daysInMonth(7, 2014) == 31') 1107 | n = n + 1 1108 | 1109 | tests(n) = assert(daysInMonth(8, 2014) == 31, 'daysInMonth(8, 2014) == 31') 1110 | n = n + 1 1111 | 1112 | tests(n) = assert(daysInMonth(9, 2014) == 30, 'daysInMonth(9, 2014) == 30') 1113 | n = n + 1 1114 | 1115 | tests(n) = assert(daysInMonth(10, 2014) == 31, 'daysInMonth(10, 2014) == 31') 1116 | n = n + 1 1117 | 1118 | tests(n) = assert(daysInMonth(11, 2014) == 30, 'daysInMonth(11, 2014) == 30') 1119 | n = n + 1 1120 | 1121 | tests(n) = assert(daysInMonth(12, 2014) == 31, 'daysInMonth(12, 2014) == 31') 1122 | n = n + 1 1123 | 1124 | print '(71("-"))' 1125 | 1126 | ! clock tests 1127 | 1128 | ! Initialize a clock with an hourly tick interval 1129 | c = clock(startTime = datetime(2014, 1, 1, 0, 0, 0), & 1130 | stopTime = datetime(2014, 1, 2, 0, 0, 0), & 1131 | currentTime = datetime(2014, 1, 1, 0, 0, 0), & 1132 | tickInterval = timedelta(hours=1)) 1133 | 1134 | tests(n) = assert(.not. c % started, 'clock % started == F (before tick)') 1135 | n = n + 1 1136 | 1137 | call c % tick() 1138 | 1139 | tests(n) = assert(c % started, 'clock % started == T (after 1 tick)') 1140 | n = n + 1 1141 | 1142 | tests(n) = assert(.not. c % stopped, 'clock % stopped == F (after 1 tick)') 1143 | n = n + 1 1144 | 1145 | ! Tick 23 times 1146 | do i = 1, 23 1147 | call c % tick() 1148 | end do 1149 | 1150 | tests(n) = assert(c % currentTime == c % stopTime, & 1151 | 'clock % currentTime == clock % stopTime (after 24 ticks)') 1152 | n = n + 1 1153 | 1154 | tests(n) = assert(c % stopped, 'clock % stopped == T (after 24 ticks)') 1155 | n = n + 1 1156 | 1157 | ! Reset clock 1158 | call c % reset() 1159 | 1160 | tests(n) = assert(.not. c % started, 'clock % started == F (after reset)') 1161 | n = n + 1 1162 | 1163 | tests(n) = assert(.not. c % started, 'clock % stopped == F (after reset)') 1164 | n = n + 1 1165 | 1166 | tests(n) = assert(c % currentTime == c % startTime, & 1167 | 'clock % currentTime == clock % startTime (after reset)') 1168 | n = n + 1 1169 | 1170 | print '(71("-"))' 1171 | 1172 | test_failed = .false. 1173 | 1174 | call report_tests(tests, test_failed) 1175 | 1176 | if (test_failed) stop 1 1177 | 1178 | end subroutine test_datetime 1179 | 1180 | end module datetime_tests 1181 | 1182 | 1183 | program run_tests 1184 | use datetime_tests, only: test_datetime 1185 | call test_datetime() 1186 | end program run_tests 1187 | -------------------------------------------------------------------------------- /test/tests-env.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # remove escape codes 4 | 5 | OS=$(uname -s) 6 | if [ "X$OS" = "XDarwin" ]; then 7 | noesc () { sed 's/\[[0-9]*m*//g' ; } 8 | else 9 | # http://www.commandlinefu.com/commands/view/3584/remove-color-codes-special-characters-with-sed 10 | noesc () { sed -r 's/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g' ; } 11 | fi 12 | 13 | LOG=tests-env.sh.log 14 | ./datetime_tests | noesc > $LOG 15 | 16 | TRS=tests-env.sh.trs 17 | 18 | # extract lines that end in PASS or FAIL 19 | pof () { awk '/PASS$/ {print $0} ; /FAIL$/ {print $0}' ; } 20 | 21 | # back to front. Replace the first word by the last 22 | btf () { awk '{$1=$NF;}1'; } 23 | 24 | rmres () { sed 's/: PASS$//' | sed 's/: FAIL$//' ; } 25 | 26 | pof <$LOG | btf | rmres | awk '{print ":test-result: " $0}' >$TRS 27 | 28 | --------------------------------------------------------------------------------