SUEWS API Site
Documentation of SUEWS source code
suews_util_datetime.f95
Go to the documentation of this file.
1! Courtesy of wavebitscientific
2! https://wavebitscientific.github.io/datetime-fortran/
3
4!
5! datetime-fortran - A Fortran library for date and time manipulation
6! Copyright (c) 2013-2017, Wavebit Scientific LLC
7! All rights reserved.
8!
9! Licensed under the BSD 3-clause license. See LICENSE for details.
10!
12!=======================================================================
13!
14! mod_strftime: Interfaces to strftime and strptime procedures from
15! from C/C++ standard library.
16!
17!=======================================================================
18
19 use, INTRINSIC :: iso_c_binding, only: c_char, c_int
20
21 IMPLICIT NONE
22
23 PRIVATE
24
25 PUBLIC :: tm_struct
26 PUBLIC :: c_strftime
27 PUBLIC :: c_strptime
28
29 TYPE, BIND(c) :: tm_struct
30
31 !! A derived type provided for compatibility with C/C++ time struct.
32 !! Allows for calling strftime and strptime procedures through the
33 !! iso_c_binding.
34
35 INTEGER(kind=C_INT) :: tm_sec !! Seconds [0-60] (1 leap second)
36 INTEGER(kind=C_INT) :: tm_min !! Minutes [0-59]
37 INTEGER(kind=C_INT) :: tm_hour !! Hours [0-23]
38 INTEGER(kind=C_INT) :: tm_mday !! Day [1-31]
39 INTEGER(kind=C_INT) :: tm_mon !! Month [0-11]
40 INTEGER(kind=C_INT) :: tm_year !! Year - 1900
41 INTEGER(kind=C_INT) :: tm_wday !! Day of week [0-6]
42 INTEGER(kind=C_INT) :: tm_yday !! Days in year [0-365]
43 INTEGER(kind=C_INT) :: tm_isdst !! DST [-1/0/1]
44
45 END TYPE tm_struct
46!=======================================================================
47
48 INTERFACE
49
50 !! Interface to C procedures strftime and strptime through
51 !! iso_c_binding.
52
53 FUNCTION c_strftime(str, slen, FORMAT, tm) &
54 bind(c, name='strftime') result(rc)
55
56 !! Returns a formatted time string, given input time struct and
57 !! format. Refer to C standard library documentation for more
58 !! information.
59
60 IMPORT :: c_char, c_int
61 IMPORT :: tm_struct
62
63 IMPLICIT NONE
64
65 ! Arguments
66 CHARACTER(kind=C_CHAR), DIMENSION(*), INTENT(out) :: str !! result string
67 INTEGER(kind=C_INT), VALUE, INTENT(in) :: slen !! string length
68 CHARACTER(kind=C_CHAR), DIMENSION(*), INTENT(in) :: format !! time format
69 TYPE(tm_struct), INTENT(in) :: tm !! tm_struct instance
70 INTEGER(kind=C_INT) :: rc !! return code
71
72 END FUNCTION c_strftime
73
74 FUNCTION c_strptime(str, FORMAT, tm) bind(c, name='strptime') RESULT(rc)
75
76 !! Returns a time struct object based on the input time string str,
77 !! formatted using format. Refer to C standard library documentation
78 !! for more information.
79
80 IMPORT :: c_char, c_int
81 IMPORT :: tm_struct
82
83 IMPLICIT NONE
84
85 ! Arguments
86 CHARACTER(kind=C_CHAR), DIMENSION(*), INTENT(in) :: str !! input string
87 CHARACTER(kind=C_CHAR), DIMENSION(*), INTENT(in) :: format !! time format
88 TYPE(tm_struct), INTENT(out) :: tm !! result tm_struct
89 INTEGER(kind=C_INT) :: rc !! return code
90
91 END FUNCTION c_strptime
92
93 END INTERFACE
94!=======================================================================
95END MODULE mod_strftime
96
97!
98! datetime-fortran - A Fortran library for date and time manipulation
99! Copyright (c) 2013-2016, Wavebit Scientific LLC
100! All rights reserved.
101!
102! Licensed under the BSD-3 clause license. See LICENSE for details.
103!
105!=======================================================================
106!
107! mod_constants: Basic constants and time conversion factors.
108!
109!=======================================================================
110
111 use, INTRINSIC :: iso_fortran_env, only: real32, real64
112
113 IMPLICIT NONE
114
115 PRIVATE
116
117 PUBLIC :: zero, one, d2h, h2d, d2m, m2d, m2h, s2d, d2s, h2s, s2h, m2s, s2m, maxstrlen
118
119 REAL(kind=real64), PARAMETER :: zero = 0_real64 !! 0
120 REAL(kind=real64), PARAMETER :: one = 1_real64 !! 1
121
122! Constant multipliers that transform a number
123! of some time unit to another:
124 REAL(kind=real64), PARAMETER :: d2h = 24_real64 !! day -> hour
125 REAL(kind=real64), PARAMETER :: h2d = one/d2h !! hour -> day
126 REAL(kind=real64), PARAMETER :: d2m = d2h*60_real64 !! day -> minute
127 REAL(kind=real64), PARAMETER :: m2d = one/d2m !! minute -> day
128 REAL(kind=real64), PARAMETER :: m2h = one/60_real64 !! minute -> hour
129 REAL(kind=real64), PARAMETER :: s2d = m2d/60_real64 !! second -> day
130 REAL(kind=real64), PARAMETER :: d2s = 86400_real64 !! day -> second
131 REAL(kind=real64), PARAMETER :: h2s = 3600_real64 !! hour -> second
132 REAL(kind=real64), PARAMETER :: s2h = one/h2s !! second -> hour
133 REAL(kind=real64), PARAMETER :: m2s = 60_real64 !! minute -> second
134 REAL(kind=real64), PARAMETER :: s2m = one/m2s !! second -> minute
135
136! Maximum string length for strftime.
137! Constant for now; may become a preprocessor macro later.
138 INTEGER, PARAMETER :: maxstrlen = 99
139
140!=======================================================================
141END MODULE mod_constants
142
143!
144! datetime-fortran - A Fortran library for date and time manipulation
145! Copyright (c) 2013-2017, Wavebit Scientific LLC
146! All rights reserved.
147!
148! Licensed under the BSD 3-clause license. See LICENSE for details.
149!
151!=======================================================================
152!
153! mod_timedelta: Module that provides the timedelta class and its
154! type-bound methods and operators.
155!
156!=======================================================================
157
158 use, INTRINSIC :: iso_fortran_env, only: real32, real64
159
160 IMPLICIT NONE
161
162 PRIVATE
163
164 PUBLIC :: timedelta
165
166 TYPE :: timedelta
167
168 !! Class of objects that define difference between two datetime
169 !! instances.
170
171 PRIVATE
172
173 INTEGER :: days = 0 !! number of days
174 INTEGER :: hours = 0 !! number of hours
175 INTEGER :: minutes = 0 !! number of minutes
176 INTEGER :: seconds = 0 !! number of seconds
177 INTEGER :: milliseconds = 0 !! number of milliseconds
178
179 CONTAINS
180
181 ! getter functions
182 PROCEDURE, pass(self), PUBLIC :: getdays
183 PROCEDURE, pass(self), PUBLIC :: gethours
184 PROCEDURE, pass(self), PUBLIC :: getminutes
185 PROCEDURE, pass(self), PUBLIC :: getseconds
186 PROCEDURE, pass(self), PUBLIC :: getmilliseconds
187
188 ! public methods
189 PROCEDURE, PUBLIC :: total_seconds
190
191 ! operator overloading procedures
192 PROCEDURE, PRIVATE :: timedelta_plus_timedelta
193 PROCEDURE, PRIVATE :: timedelta_minus_timedelta
194 PROCEDURE, PRIVATE :: unary_minus_timedelta
195 PROCEDURE, PRIVATE :: eq
196 PROCEDURE, PRIVATE :: neq
197 PROCEDURE, PRIVATE :: gt
198 PROCEDURE, PRIVATE :: ge
199 PROCEDURE, PRIVATE :: lt
200 PROCEDURE, PRIVATE :: le
201
202 generic :: OPERATOR(+) => timedelta_plus_timedelta
203 generic :: OPERATOR(-) => timedelta_minus_timedelta, &
205 generic :: OPERATOR(==) => eq
206 generic :: OPERATOR(/=) => neq
207 generic :: OPERATOR(>) => gt
208 generic :: OPERATOR(>=) => ge
209 generic :: OPERATOR(<) => lt
210 generic :: OPERATOR(<=) => le
211
212 END TYPE timedelta
213
214 INTERFACE timedelta
215 MODULE PROCEDURE :: timedelta_constructor
216 END INTERFACE timedelta
217
218!=======================================================================
219CONTAINS
220
221 PURE ELEMENTAL TYPE(timedelta) function timedelta_constructor(days, &
222 hours, minutes, seconds, milliseconds)
223
224 !! Constructor function for the `timedelta` class.
225
226 INTEGER, INTENT(in), OPTIONAL :: days !! number of days
227 INTEGER, INTENT(in), OPTIONAL :: hours !! number of hours
228 INTEGER, INTENT(in), OPTIONAL :: minutes !! number of minutes
229 INTEGER, INTENT(in), OPTIONAL :: seconds !! number of seconds
230 INTEGER, INTENT(in), OPTIONAL :: milliseconds !! number of milliseconds
231
232 IF (PRESENT(days)) THEN
233 timedelta_constructor%days = days
234 ELSE
235 timedelta_constructor%days = 0
236 END IF
237
238 IF (PRESENT(hours)) THEN
239 timedelta_constructor%hours = hours
240 ELSE
241 timedelta_constructor%hours = 0
242 END IF
243
244 IF (PRESENT(minutes)) THEN
245 timedelta_constructor%minutes = minutes
246 ELSE
247 timedelta_constructor%minutes = 0
248 END IF
249
250 IF (PRESENT(seconds)) THEN
251 timedelta_constructor%seconds = seconds
252 ELSE
253 timedelta_constructor%seconds = 0
254 END IF
255
256 IF (PRESENT(milliseconds)) THEN
257 timedelta_constructor%milliseconds = milliseconds
258 ELSE
259 timedelta_constructor%milliseconds = 0
260 END IF
261
262 END FUNCTION timedelta_constructor
263
264! timedelta getters
265!=======================================================================
266
267 PURE ELEMENTAL INTEGER FUNCTION getdays(self)
268 !! Returns the number of days.
269 CLASS(timedelta), INTENT(in) :: self !! `timedelta` instance
270 getdays = self%days
271 END FUNCTION getdays
272
273 PURE ELEMENTAL INTEGER FUNCTION gethours(self)
274 !! Returns the number of hours.
275 CLASS(timedelta), INTENT(in) :: self !! `timedelta` instance
276 gethours = self%hours
277 END FUNCTION gethours
278
279 PURE ELEMENTAL INTEGER FUNCTION getminutes(self)
280 !! Returns the number of minutes.
281 CLASS(timedelta), INTENT(in) :: self !! `timedelta` instance
282 getminutes = self%minutes
283 END FUNCTION getminutes
284
285 PURE ELEMENTAL INTEGER FUNCTION getseconds(self)
286 !! Returns the number of seconds.
287 CLASS(timedelta), INTENT(in) :: self !! `timedelta` instance
288 getseconds = self%seconds
289 END FUNCTION getseconds
290
291 PURE ELEMENTAL INTEGER FUNCTION getmilliseconds(self)
292 !! Returns the number of milliseconds.
293 CLASS(timedelta), INTENT(in) :: self !! `timedelta` instance
294 getmilliseconds = self%milliseconds
295 END FUNCTION getmilliseconds
296
297 PURE ELEMENTAL REAL(kind=REAL64) FUNCTION total_seconds(self)
298
299 !! Returns a total number of seconds contained in a `timedelta`
300 !! instance.
301
302 CLASS(timedelta), INTENT(in) :: self !! `timedelta` instance
303
304 total_seconds = self%days*86400._real64 &
305 + self%hours*3600._real64 &
306 + self%minutes*60._real64 &
307 + self%seconds &
308 + self%milliseconds*1e-3_real64
309
310 END FUNCTION total_seconds
311
312 PURE ELEMENTAL FUNCTION timedelta_plus_timedelta(t0, t1) RESULT(t)
313
314 !! Adds two `timedelta` instances together and returns a `timedelta`
315 !! instance. Overloads the operator `+`.
316
317 CLASS(timedelta), INTENT(in) :: t0 !! lhs `timedelta` instance
318 TYPE(timedelta), INTENT(in) :: t1 !! rhs `timedelta` instance
319 TYPE(timedelta) :: t !! result
320
321 t = timedelta(days=t0%days + t1%days, &
322 hours=t0%hours + t1%hours, &
323 minutes=t0%minutes + t1%minutes, &
324 seconds=t0%seconds + t1%seconds, &
325 milliseconds=t0%milliseconds + t1%milliseconds)
326
327 END FUNCTION timedelta_plus_timedelta
328
329 PURE ELEMENTAL FUNCTION timedelta_minus_timedelta(t0, t1) RESULT(t)
330
331 !! Subtracts a `timedelta` instance from another. Returns a
332 !! `timedelta` instance. Overloads the operator `-`.
333
334 CLASS(timedelta), INTENT(in) :: t0 !! lhs `timedelta` instance
335 TYPE(timedelta), INTENT(in) :: t1 !! lhs `timedelta` instance
336 TYPE(timedelta) :: t !! result
337
338 t = t0 + (-t1)
339
340 END FUNCTION timedelta_minus_timedelta
341
342 PURE ELEMENTAL FUNCTION unary_minus_timedelta(t0) RESULT(t)
343
344 !! Takes a negative of a `timedelta` instance. Overloads the operator
345 !! `-`.
346
347 CLASS(timedelta), INTENT(in) :: t0 !! `timedelta` instance
348 TYPE(timedelta) :: t !! result
349
350 t%days = -t0%days
351 t%hours = -t0%hours
352 t%minutes = -t0%minutes
353 t%seconds = -t0%seconds
354 t%milliseconds = -t0%milliseconds
355
356 END FUNCTION unary_minus_timedelta
357
358 PURE ELEMENTAL LOGICAL FUNCTION eq(td0, td1)
359
360 !! `timedelta` object comparison operator. Returns `.true.` if `td0`
361 !! is equal to `td1` and `.false.` otherwise. Overloads the operator
362 !! `==`.
363
364 CLASS(timedelta), INTENT(in) :: td0 !! lhs `timedelta` instance
365 TYPE(timedelta), INTENT(in) :: td1 !! rhs `timedelta` instance
366
367 eq = td0%total_seconds() == td1%total_seconds()
368
369 END FUNCTION eq
370
371 PURE ELEMENTAL LOGICAL FUNCTION neq(td0, td1)
372
373 !! `timedelta` object comparison operator. Returns `.true.` if `td0`
374 !! is not equal to `td1` and `.false.` otherwise. Overloads the
375 !! operator `/=`.
376
377 CLASS(timedelta), INTENT(in) :: td0 !! lhs `timedelta` instance
378 TYPE(timedelta), INTENT(in) :: td1 !! rhs `timedelta` instance
379
380 neq = .NOT. (td0%total_seconds() == td1%total_seconds())
381
382 END FUNCTION neq
383
384 PURE ELEMENTAL LOGICAL FUNCTION gt(td0, td1)
385
386 !! `timedelta` object comparison operator. Returns `.true.` if
387 !! `td0` is greater than `td1` and `.false.` otherwise. Overloads the
388 !! operator `>`.
389
390 CLASS(timedelta), INTENT(in) :: td0 !! lhs `timedelta` instance
391 TYPE(timedelta), INTENT(in) :: td1 !! rhs `timedelta` instance
392
393 gt = td0%total_seconds() > td1%total_seconds()
394
395 END FUNCTION gt
396
397 PURE ELEMENTAL LOGICAL FUNCTION ge(td0, td1)
398
399 !! `timedelta` object comparison operator. Returns `.true.` if `td0`
400 !! is greater than or equal to `td1` and `.false.` otherwise.
401 !! Overloads the operator >=.
402
403 CLASS(timedelta), INTENT(in) :: td0 !! lhs `timedelta` instance
404 TYPE(timedelta), INTENT(in) :: td1 !! rhs `timedelta` instance
405
406 ge = td0%total_seconds() >= td1%total_seconds()
407
408 END FUNCTION ge
409
410 PURE ELEMENTAL LOGICAL FUNCTION lt(td0, td1)
411
412 !! `timedelta` object comparison operator. Returns `.true.` if `td0`
413 !! is less than `td1` and `.false.` otherwise. Overloads the operator
414 !! `<`.
415
416 CLASS(timedelta), INTENT(in) :: td0 !! lhs `timedelta` instance
417 TYPE(timedelta), INTENT(in) :: td1 !! rhs `timedelta` instance
418
419 lt = td0%total_seconds() < td1%total_seconds()
420
421 END FUNCTION lt
422
423 PURE ELEMENTAL LOGICAL FUNCTION le(td0, td1)
424
425 !! `timedelta` object comparison operator. Returns `.true.` if `td0`
426 !! is less than or equal to `td1` and `.false.` otherwise. Overloads
427 !! the operator `<=`.
428
429 CLASS(timedelta), INTENT(in) :: td0 !! lhs `timedelta` instance
430 TYPE(timedelta), INTENT(in) :: td1 !! rhs `timedelta` instance
431
432 le = td0%total_seconds() <= td1%total_seconds()
433
434 END FUNCTION le
435!=======================================================================
436END MODULE mod_timedelta
437
438!
439! datetime-fortran - A Fortran library for date and time manipulation
440! Copyright (c) 2013-2017, Wavebit Scientific LLC
441! All rights reserved.
442!
443! Licensed under the BSD 3-clause license. See LICENSE for details.
444!
446!=======================================================================
447!
448! mod_datetime: Module that provides the datetime class and its
449! type-bound methods and operators. At the time being,
450! this module also includes some procedures not
451! associated with datetime.
452!
453!=======================================================================
454
455 use, INTRINSIC :: iso_fortran_env, only: real32, real64
456 use, INTRINSIC :: iso_c_binding, only: c_char, c_int, c_null_char
457 USE mod_timedelta, ONLY: timedelta
459 USE mod_constants
460
461 IMPLICIT NONE
462
463 PRIVATE
464
465 PUBLIC :: datetime
466 PUBLIC :: date2num
467 PUBLIC :: datetimerange
468 PUBLIC :: daysinmonth
469 PUBLIC :: daysinyear
470 PUBLIC :: isleapyear
471 PUBLIC :: num2date
472 PUBLIC :: strptime
473 PUBLIC :: tm2date
474
475 TYPE :: datetime
476
477 !! Main datetime class for date and time representation.
478
479 PRIVATE
480
481 INTEGER :: year = 1 !! year [1-HUGE(year)]
482 INTEGER :: month = 1 !! month in year [1-12]
483 INTEGER :: day = 1 !! day in month [1-31]
484 INTEGER :: hour = 0 !! hour in day [0-23]
485 INTEGER :: minute = 0 !! minute in hour [0-59]
486 INTEGER :: second = 0 !! second in minute [0-59]
487 INTEGER :: millisecond = 0 !! milliseconds in second [0-999]
488
489 REAL(kind=real64) :: tz = 0 !! timezone offset from UTC [hours]
490
491 CONTAINS
492
493 ! getter functions
494 PROCEDURE, pass(self), PUBLIC :: getyear
495 PROCEDURE, pass(self), PUBLIC :: getmonth
496 PROCEDURE, pass(self), PUBLIC :: getday
497 PROCEDURE, pass(self), PUBLIC :: gethour
498 PROCEDURE, pass(self), PUBLIC :: getminute
499 PROCEDURE, pass(self), PUBLIC :: getsecond
500 PROCEDURE, pass(self), PUBLIC :: getmillisecond
501 PROCEDURE, pass(self), PUBLIC :: gettz
502
503 ! public methods
504 PROCEDURE, pass(self), PUBLIC :: isocalendar
505 PROCEDURE, pass(self), PUBLIC :: isoformat
506 PROCEDURE, pass(self), PUBLIC :: isvalid
507 PROCEDURE, NOPASS, PUBLIC :: now
508 PROCEDURE, pass(self), PUBLIC :: secondssinceepoch
509 PROCEDURE, pass(self), PUBLIC :: strftime
510 PROCEDURE, pass(self), PUBLIC :: tm
511 PROCEDURE, pass(self), PUBLIC :: tzoffset
512 PROCEDURE, pass(self), PUBLIC :: utc
513 PROCEDURE, pass(self), PUBLIC :: weekday
514 PROCEDURE, pass(self), PUBLIC :: isoweekday
515 PROCEDURE, pass(self), PUBLIC :: weekdaylong
516 PROCEDURE, pass(self), PUBLIC :: isoweekdaylong
517 PROCEDURE, pass(self), PUBLIC :: weekdayshort
518 PROCEDURE, pass(self), PUBLIC :: isoweekdayshort
519 PROCEDURE, pass(self), PUBLIC :: yearday
520
521 ! private methods
522 PROCEDURE, pass(self), PRIVATE :: addmilliseconds
523 PROCEDURE, pass(self), PRIVATE :: addseconds
524 PROCEDURE, pass(self), PRIVATE :: addminutes
525 PROCEDURE, pass(self), PRIVATE :: addhours
526 PROCEDURE, pass(self), PRIVATE :: adddays
527
528 ! operator overloading procedures
529 PROCEDURE, pass(d0), PRIVATE :: datetime_plus_timedelta
530 PROCEDURE, pass(d0), PRIVATE :: timedelta_plus_datetime
531 PROCEDURE, pass(d0), PRIVATE :: datetime_minus_datetime
532 PROCEDURE, pass(d0), PRIVATE :: datetime_minus_timedelta
533 PROCEDURE, pass(d0), PRIVATE :: eq
534 PROCEDURE, pass(d0), PRIVATE :: neq
535 PROCEDURE, pass(d0), PRIVATE :: gt
536 PROCEDURE, pass(d0), PRIVATE :: ge
537 PROCEDURE, pass(d0), PRIVATE :: lt
538 PROCEDURE, pass(d0), PRIVATE :: le
539
540 generic :: OPERATOR(+) => datetime_plus_timedelta, &
542 generic :: OPERATOR(-) => datetime_minus_datetime, &
544 generic :: OPERATOR(==) => eq
545 generic :: OPERATOR(/=) => neq
546 generic :: OPERATOR(>) => gt
547 generic :: OPERATOR(>=) => ge
548 generic :: OPERATOR(<) => lt
549 generic :: OPERATOR(<=) => le
550
551 END TYPE datetime
552
553 INTERFACE datetime
554 MODULE PROCEDURE :: datetime_constructor
555 END INTERFACE datetime
556
557!=======================================================================
558CONTAINS
559
560 PURE ELEMENTAL TYPE(datetime) function datetime_constructor(year, month, &
561 day, hour, minute, second, millisecond, tz)
562
563 !! Constructor function for the `datetime` class.
564
565 INTEGER, INTENT(in), OPTIONAL :: year !! year
566 INTEGER, INTENT(in), OPTIONAL :: month !! month
567 INTEGER, INTENT(in), OPTIONAL :: day !! day
568 INTEGER, INTENT(in), OPTIONAL :: hour !! hour
569 INTEGER, INTENT(in), OPTIONAL :: minute !! minute
570 INTEGER, INTENT(in), OPTIONAL :: second !! second
571 INTEGER, INTENT(in), OPTIONAL :: millisecond !! millisecond
572 REAL(kind=real64), INTENT(in), OPTIONAL :: tz !! timezone offset in hours
573
574 IF (PRESENT(year)) THEN
575 datetime_constructor%year = year
576 ELSE
577 datetime_constructor%year = 1
578 END IF
579
580 IF (PRESENT(month)) THEN
581 datetime_constructor%month = month
582 ELSE
583 datetime_constructor%month = 1
584 END IF
585
586 IF (PRESENT(day)) THEN
587 datetime_constructor%day = day
588 ELSE
590 END IF
591
592 IF (PRESENT(hour)) THEN
593 datetime_constructor%hour = hour
594 ELSE
595 datetime_constructor%hour = 0
596 END IF
597
598 IF (PRESENT(minute)) THEN
599 datetime_constructor%minute = minute
600 ELSE
601 datetime_constructor%minute = 0
602 END IF
603
604 IF (PRESENT(second)) THEN
605 datetime_constructor%second = second
606 ELSE
607 datetime_constructor%second = 0
608 END IF
609
610 IF (PRESENT(millisecond)) THEN
611 datetime_constructor%millisecond = millisecond
612 ELSE
613 datetime_constructor%millisecond = 0
614 END IF
615
616 IF (PRESENT(tz)) THEN
618 ELSE
620 END IF
621
622 END FUNCTION datetime_constructor
623
624! datetime getters
625!=======================================================================
626
627 PURE ELEMENTAL INTEGER FUNCTION getyear(self)
628 !! Returns the year component
629 CLASS(datetime), INTENT(in) :: self !! `datetime` instance
630 getyear = self%year
631 END FUNCTION getyear
632
633 PURE ELEMENTAL INTEGER FUNCTION getmonth(self)
634 !! Returns the year component
635 CLASS(datetime), INTENT(in) :: self !! `datetime` instance
636 getmonth = self%month
637 END FUNCTION getmonth
638
639 PURE ELEMENTAL INTEGER FUNCTION getday(self)
640 !! Returns the year component
641 CLASS(datetime), INTENT(in) :: self !! `datetime` instance
642 getday = self%day
643 END FUNCTION getday
644
645 PURE ELEMENTAL INTEGER FUNCTION gethour(self)
646 !! Returns the year component
647 CLASS(datetime), INTENT(in) :: self !! `datetime` instance
648 gethour = self%hour
649 END FUNCTION gethour
650
651 PURE ELEMENTAL INTEGER FUNCTION getminute(self)
652 !! Returns the year component
653 CLASS(datetime), INTENT(in) :: self !! `datetime` instance
654 getminute = self%minute
655 END FUNCTION getminute
656
657 PURE ELEMENTAL INTEGER FUNCTION getsecond(self)
658 !! Returns the year component
659 CLASS(datetime), INTENT(in) :: self !! `datetime` instance
660 getsecond = self%second
661 END FUNCTION getsecond
662
663 PURE ELEMENTAL INTEGER FUNCTION getmillisecond(self)
664 !! Returns the year component
665 CLASS(datetime), INTENT(in) :: self !! `datetime` instance
666 getmillisecond = self%millisecond
667 END FUNCTION getmillisecond
668
669 PURE ELEMENTAL REAL(kind=REAL64) FUNCTION gettz(self)
670 !! Returns the timezone offset component
671 CLASS(datetime), INTENT(in) :: self !! `datetime` instance
672 gettz = self%tz
673 END FUNCTION gettz
674
675 PURE ELEMENTAL SUBROUTINE addmilliseconds(self, ms)
676
677 !! Adds an integer number of milliseconds to self. Called by `datetime`
678 !! addition (`+`) and subtraction (`-`) operators.
679
680 CLASS(datetime), INTENT(inout) :: self !! `datetime` instance
681 INTEGER, INTENT(in) :: ms !! number of milliseconds to add
682
683 self%millisecond = self%millisecond + ms
684
685 DO
686 IF (self%millisecond >= 1000) THEN
687 CALL self%addSeconds(self%millisecond/1000)
688 self%millisecond = mod(self%millisecond, 1000)
689 ELSEIF (self%millisecond < 0) THEN
690 CALL self%addSeconds(self%millisecond/1000 - 1)
691 self%millisecond = mod(self%millisecond, 1000) + 1000
692 ELSE
693 EXIT
694 END IF
695 END DO
696
697 END SUBROUTINE addmilliseconds
698
699! datetime-bound methods
700!=======================================================================
701
702 PURE ELEMENTAL SUBROUTINE addseconds(self, s)
703
704 !! Adds an integer number of seconds to self. Called by `datetime`
705 !! addition (`+`) and subtraction (`-`) operators.
706
707 CLASS(datetime), INTENT(inout) :: self !! `datetime` instance
708 INTEGER, INTENT(in) :: s !! number of seconds to add
709
710 self%second = self%second + s
711
712 DO
713 IF (self%second >= 60) THEN
714 CALL self%addMinutes(self%second/60)
715 self%second = mod(self%second, 60)
716 ELSEIF (self%second < 0) THEN
717 CALL self%addMinutes(self%second/60 - 1)
718 self%second = mod(self%second, 60) + 60
719 ELSE
720 EXIT
721 END IF
722 END DO
723
724 END SUBROUTINE addseconds
725
726 PURE ELEMENTAL SUBROUTINE addminutes(self, m)
727
728 !! Adds an integer number of minutes to self. Called by `datetime`
729 !! addition (`+`) and subtraction (`-`) operators.
730
731 CLASS(datetime), INTENT(inout) :: self !! `datetime` instance
732 INTEGER, INTENT(in) :: m !! number of minutes to add
733
734 self%minute = self%minute + m
735
736 DO
737 IF (self%minute >= 60) THEN
738 CALL self%addHours(self%minute/60)
739 self%minute = mod(self%minute, 60)
740 ELSEIF (self%minute < 0) THEN
741 CALL self%addHours(self%minute/60 - 1)
742 self%minute = mod(self%minute, 60) + 60
743 ELSE
744 EXIT
745 END IF
746 END DO
747
748 END SUBROUTINE addminutes
749
750 PURE ELEMENTAL SUBROUTINE addhours(self, h)
751
752 !! Adds an integer number of hours to self. Called by `datetime`
753 !! addition (`+`) and subtraction (`-`) operators.
754
755 CLASS(datetime), INTENT(inout) :: self !! `datetime` instance
756 INTEGER, INTENT(in) :: h !! number of hours to add
757
758 self%hour = self%hour + h
759
760 DO
761 IF (self%hour >= 24) THEN
762 CALL self%addDays(self%hour/24)
763 self%hour = mod(self%hour, 24)
764 ELSEIF (self%hour < 0) THEN
765 CALL self%addDays(self%hour/24 - 1)
766 self%hour = mod(self%hour, 24) + 24
767 ELSE
768 EXIT
769 END IF
770 END DO
771
772 END SUBROUTINE addhours
773
774 PURE ELEMENTAL SUBROUTINE adddays(self, d)
775
776 !! Adds an integer number of dayss to self. Called by `datetime`
777 !! addition (`+`) and subtraction (`-`) operators.
778
779 CLASS(datetime), INTENT(inout) :: self !! `datetime` instance
780 INTEGER, INTENT(in) :: d !! number of days to add
781
782 INTEGER :: daysincurrentmonth
783
784 self%day = self%day + d
785 DO
786 daysincurrentmonth = daysinmonth(self%month, self%year)
787 IF (self%day > daysincurrentmonth) THEN
788 self%day = self%day - daysincurrentmonth
789 self%month = self%month + 1
790 IF (self%month > 12) THEN
791 self%year = self%year + self%month/12
792 self%month = mod(self%month, 12)
793 END IF
794 ELSEIF (self%day < 1) THEN
795 self%month = self%month - 1
796 IF (self%month < 1) THEN
797 self%year = self%year + self%month/12 - 1
798 self%month = 12 + mod(self%month, 12)
799 END IF
800 self%day = self%day + daysinmonth(self%month, self%year)
801 ELSE
802 EXIT
803 END IF
804 END DO
805
806 END SUBROUTINE adddays
807
808 PURE ELEMENTAL CHARACTER(len=23) FUNCTION isoformat(self, sep)
809
810 !! Returns character string with time in ISO 8601 format.
811
812 CLASS(datetime), INTENT(in) :: self !! `datetime instance`
813 CHARACTER(len=1), INTENT(in), OPTIONAL :: sep
814 !! separator character, 'T' is default
815
816 CHARACTER(len=1) :: separator
817
818 IF (PRESENT(sep)) THEN
819 separator = sep
820 ELSE
821 separator = 'T'
822 END IF
823
824 ! TODO below is a bit cumbersome and was implemented
825 ! at a time before the interface to strftime. Now we
826 ! could do something like:
827 !
828 ! isoformat = self % strftime('%Y-%m-%d'//separator//'%H:%M:%S')
829 !
830 isoformat = int2str(self%year, 4)//'-'// &
831 int2str(self%month, 2)//'-'// &
832 int2str(self%day, 2)//separator// &
833 int2str(self%hour, 2)//':'// &
834 int2str(self%minute, 2)//':'// &
835 int2str(self%second, 2)//'.'// &
836 int2str(self%millisecond, 3)
837
838 END FUNCTION isoformat
839
840 PURE ELEMENTAL LOGICAL FUNCTION isvalid(self)
841
842 !! Checks whether the `datetime` instance has valid component values.
843 !! Returns `.true.` if the `datetime` instance is valid, and `.false.`
844 !! otherwise.
845
846 CLASS(datetime), INTENT(in) :: self !! `datetime` instance
847
848 ! assume valid
849 isvalid = .true.
850
851 IF (self%year < 1) THEN
852 isvalid = .false.
853 RETURN
854 END IF
855
856 IF (self%month < 1 .OR. self%month > 12) THEN
857 isvalid = .false.
858 RETURN
859 END IF
860
861 IF (self%day < 1 .OR. &
862 self%day > daysinmonth(self%month, self%year)) THEN
863 isvalid = .false.
864 RETURN
865 END IF
866
867 IF (self%hour < 0 .OR. self%hour > 23) THEN
868 isvalid = .false.
869 RETURN
870 END IF
871
872 IF (self%minute < 0 .OR. self%minute > 59) THEN
873 isvalid = .false.
874 RETURN
875 END IF
876
877 IF (self%second < 0 .OR. self%second > 59) THEN
878 isvalid = .false.
879 RETURN
880 END IF
881
882 IF (self%millisecond < 0 .OR. self%millisecond > 999) THEN
883 isvalid = .false.
884 RETURN
885 END IF
886
887 END FUNCTION isvalid
888
889 TYPE(datetime) function now()
890
891 !! Returns a `datetime` instance with current time.
892 !! No input arguments.
893
894 CHARACTER(len=5) :: zone
895 INTEGER, DIMENSION(8) :: values
896
897 INTEGER :: hour, minute
898
899 ! Obtain local machine time zone information
900 CALL date_and_time(zone=zone, values=values)
901
902 READ (unit=zone(1:3), fmt='(I3)') hour
903 READ (unit=zone(4:5), fmt='(I2)') minute
904
905 now = datetime(year=values(1), &
906 month=values(2), &
907 day=values(3), &
908 hour=values(5), &
909 minute=values(6), &
910 second=values(7), &
911 millisecond=values(8))
912
913 now%tz = hour + minute*m2h
914
915 END FUNCTION now
916
917 PURE ELEMENTAL INTEGER FUNCTION weekday(self)
918
919 !! Returns the day of the week calculated using Zeller's congruence.
920 !! Returned value is an integer scalar in the range [0-6], such that:
921 !!
922 !! 0: Sunday
923 !! 1: Monday
924 !! 2: Tuesday
925 !! 3: Wednesday
926 !! 4: Thursday
927 !! 5: Friday
928 !! 6: Saturday
929
930 CLASS(datetime), INTENT(in) :: self !! `datetime` instance
931
932 INTEGER :: year, month
933 INTEGER :: j, k
934
935 year = self%year
936 month = self%month
937
938 IF (month <= 2) THEN
939 month = month + 12
940 year = year - 1
941 END IF
942
943 j = year/100
944 k = mod(year, 100)
945
946 weekday = mod(self%day + ((month + 1)*26)/10 + k + k/4 + j/4 + 5*j, 7) - 1
947
948 IF (weekday < 0) weekday = 6
949
950 END FUNCTION weekday
951
952 PURE ELEMENTAL INTEGER FUNCTION isoweekday(self)
953
954 !! Returns the day of the week per ISO 8601 returned from weekday().
955 !! Returned value is an integer scalar in the range [1-7], such that:
956 !!
957 !! 1: Monday
958 !! 2: Tuesday
959 !! 3: Wednesday
960 !! 4: Thursday
961 !! 5: Friday
962 !! 6: Saturday
963 !! 7: Sunday
964
965 CLASS(datetime), INTENT(in) :: self !! `datetime` instance
966
967 isoweekday = self%weekday()
968
969 IF (isoweekday == 0) THEN
970 isoweekday = 7
971 END IF
972
973 END FUNCTION isoweekday
974
975 PURE ELEMENTAL CHARACTER(len=9) FUNCTION weekdaylong(self)
976
977 !! Returns the full name of the day of the week.
978
979 CLASS(datetime), INTENT(in) :: self !! `datetime` instance
980
981 CHARACTER(len=9), PARAMETER, DIMENSION(7) :: &
982 days = ['Sunday ', 'Monday ', 'Tuesday ', 'Wednesday', &
983 'Thursday ', 'Friday ', 'Saturday ']
984
985 weekdaylong = days(self%weekday() + 1)
986
987 END FUNCTION weekdaylong
988
989 PURE ELEMENTAL CHARACTER(len=9) FUNCTION isoweekdaylong(self)
990
991 !! Returns the full name of the day of the week for ISO 8601
992 !! ordered weekdays.
993
994 CLASS(datetime), INTENT(in) :: self !! `datetime` instance
995
996 CHARACTER(len=9), PARAMETER, DIMENSION(7) :: &
997 days = ['Monday ', 'Tuesday ', 'Wednesday', 'Thursday ', &
998 'Friday ', 'Saturday ', 'Sunday ']
999
1000 isoweekdaylong = days(self%isoweekday())
1001
1002 END FUNCTION isoweekdaylong
1003
1004 PURE ELEMENTAL CHARACTER(len=3) FUNCTION weekdayshort(self)
1005
1006 !! Returns the short (3-letter) name of the day of the week.
1007
1008 CLASS(datetime), INTENT(in) :: self !! `datetime` instance
1009
1010 CHARACTER(len=3), PARAMETER, DIMENSION(7) :: &
1011 days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
1012
1013 weekdayshort = days(self%weekday() + 1)
1014
1015 END FUNCTION weekdayshort
1016
1017 PURE ELEMENTAL CHARACTER(len=3) FUNCTION isoweekdayshort(self)
1018
1019 !! Returns the short (3-letter) name of the day of the week
1020 !! based on ISO 8601 ordering.
1021
1022 CLASS(datetime), INTENT(in) :: self !! `datetime` instance
1023
1024 CHARACTER(len=3), PARAMETER, DIMENSION(7) :: &
1025 days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
1026
1027 isoweekdayshort = days(self%isoweekday())
1028
1029 END FUNCTION isoweekdayshort
1030
1031 FUNCTION isocalendar(self)
1032
1033 !! Returns an array of 3 integers, year, week number, and week day,
1034 !! as defined by ISO 8601 week date. Essentially a wrapper around C
1035 !! `strftime` function.
1036
1037 CLASS(datetime), INTENT(in) :: self !! `datetime` instance
1038
1039 INTEGER, DIMENSION(3) :: isocalendar
1040 INTEGER :: year, week, wday
1041 INTEGER :: rc
1042 CHARACTER(len=20) :: string
1043
1044 rc = c_strftime(string, len(string), '%G %V %u'//c_null_char, &
1045 self%tm())
1046
1047 READ (unit=string(1:4), fmt='(I4)') year
1048 READ (unit=string(6:7), fmt='(I2)') week
1049 READ (unit=string(9:9), fmt='(I1)') wday
1050
1051 isocalendar = [year, week, wday]
1052
1053 END FUNCTION isocalendar
1054
1055 INTEGER FUNCTION secondssinceepoch(self)
1056
1057 !! Returns an integer number of seconds since the UNIX Epoch,
1058 !! `1970-01-01 00:00:00`. Note that this is a wrapper around C's
1059 !! `strftime('%s')`, so the number of seconds will reflect the time
1060 !! zone of the local machine on which the function is being called.
1061
1062 CLASS(datetime), INTENT(in) :: self !! `datetime` instance
1063
1064 CHARACTER(len=11) :: string
1065
1066 string = self%strftime('%s')
1067 READ (unit=string, fmt='(I10)') secondssinceepoch
1068
1069 END FUNCTION secondssinceepoch
1070
1071 FUNCTION strftime(self, FORMAT)
1072
1073 !! Wrapper around C/C++ `strftime` function.
1074
1075 CLASS(datetime), INTENT(in) :: self !! `datetime` instance
1076 CHARACTER(len=*), INTENT(in) :: format !! format string
1077
1078 CHARACTER(len=:), ALLOCATABLE :: strftime
1079
1080 INTEGER :: n, rc
1081 CHARACTER(len=MAXSTRLEN) :: resultstring
1082
1083 resultstring = ""
1084 rc = c_strftime(resultstring, maxstrlen, trim(format)//c_null_char, &
1085 self%tm())
1086 strftime = trim(resultstring)
1087 n = len(strftime)
1088 strftime = strftime(1:n - 1)
1089
1090 END FUNCTION strftime
1091
1092 PURE ELEMENTAL TYPE(tm_struct) function tm(self)
1093
1094 !! Returns a `tm_struct` instance of the current `datetime`.
1095
1096 CLASS(datetime), INTENT(in) :: self !! `datetime` instance
1097
1098 tm%tm_sec = self%second
1099 tm%tm_min = self%minute
1100 tm%tm_hour = self%hour
1101 tm%tm_mday = self%day
1102 tm%tm_mon = self%month - 1
1103 tm%tm_year = self%year - 1900
1104 tm%tm_wday = self%weekday()
1105 tm%tm_yday = self%yearday() - 1
1106 tm%tm_isdst = -1
1107
1108 END FUNCTION tm
1109
1110 PURE ELEMENTAL CHARACTER(len=5) FUNCTION tzoffset(self)
1111
1112 !! Returns a character string with timezone offset in hours from UTC,
1113 !! in format +/-[hh][mm].
1114
1115 CLASS(datetime), INTENT(in) :: self !! `datetime` instance
1116
1117 INTEGER :: hours, minutes
1118
1119 IF (self%tz < 0) THEN
1120 tzoffset(1:1) = '-'
1121 ELSE
1122 tzoffset(1:1) = '+'
1123 END IF
1124
1125 hours = int(abs(self%tz))
1126 minutes = nint((abs(self%tz) - hours)*60)
1127
1128 IF (minutes == 60) THEN
1129 minutes = 0
1130 hours = hours + 1
1131 END IF
1132
1133 WRITE (unit=tzoffset(2:5), fmt='(2I2.2)') hours, minutes
1134
1135 END FUNCTION tzoffset
1136
1137 PURE ELEMENTAL TYPE(datetime) function utc(self)
1138
1139 !! Returns the `datetime` instance at Coordinated Universal Time (UTC).
1140
1141 CLASS(datetime), INTENT(in) :: self !! `datetime` instance
1142
1143 INTEGER :: hours, minutes, sgn
1144
1145 hours = int(abs(self%tz))
1146 minutes = nint((abs(self%tz) - hours)*60)
1147 sgn = int(sign(one, self%tz))
1148
1149 utc = self - timedelta(hours=sgn*hours, minutes=sgn*minutes)
1150 utc%tz = 0
1151
1152 END FUNCTION utc
1153
1154 PURE ELEMENTAL INTEGER FUNCTION yearday(self)
1155
1156 !! Returns the integer day of the year (ordinal date).
1157
1158 CLASS(datetime), INTENT(in) :: self !! `datetime` instance
1159
1160 INTEGER :: month
1161
1162 yearday = 0
1163 DO month = 1, self%month - 1
1164 yearday = yearday + daysinmonth(month, self%year)
1165 END DO
1166 yearday = yearday + self%day
1167
1168 END FUNCTION yearday
1169
1170! datetime operators
1171!=======================================================================
1172
1173 PURE ELEMENTAL FUNCTION datetime_plus_timedelta(d0, t) RESULT(d)
1174
1175 !! Adds a `timedelta` instance to a `datetime` instance, and returns a
1176 !! new `datetime` instance. Overloads the operator `+`.
1177
1178 CLASS(datetime), INTENT(in) :: d0 !! `datetime` instance
1179 CLASS(timedelta), INTENT(in) :: t !! `timedelta` instance
1180 TYPE(datetime) :: d
1181
1182 INTEGER :: milliseconds, seconds, minutes, hours, days
1183
1184 d = datetime(year=d0%getYear(), &
1185 month=d0%getMonth(), &
1186 day=d0%getDay(), &
1187 hour=d0%getHour(), &
1188 minute=d0%getMinute(), &
1189 second=d0%getSecond(), &
1190 millisecond=d0%getMillisecond(), &
1191 tz=d0%getTz())
1192
1193 milliseconds = t%getMilliseconds()
1194 seconds = t%getSeconds()
1195 minutes = t%getMinutes()
1196 hours = t%getHours()
1197 days = t%getDays()
1198
1199 IF (milliseconds /= 0) CALL d%addMilliseconds(milliseconds)
1200 IF (seconds /= 0) CALL d%addSeconds(seconds)
1201 IF (minutes /= 0) CALL d%addMinutes(minutes)
1202 IF (hours /= 0) CALL d%addHours(hours)
1203 IF (days /= 0) CALL d%addDays(days)
1204
1205 END FUNCTION datetime_plus_timedelta
1206
1207 PURE ELEMENTAL FUNCTION timedelta_plus_datetime(t, d0) RESULT(d)
1208
1209 !! Adds a `timedelta` instance to a `datetime` instance, and returns a
1210 !! new `datetime` instance. Overloads the operator `+`.
1211
1212 CLASS(timedelta), INTENT(in) :: t !! `timedelta` instance
1213 CLASS(datetime), INTENT(in) :: d0 !! `datetime` instance
1214 TYPE(datetime) :: d
1215
1216 d = d0 + t
1217
1218 END FUNCTION timedelta_plus_datetime
1219
1220 PURE ELEMENTAL FUNCTION datetime_minus_timedelta(d0, t) RESULT(d)
1221
1222 !! Subtracts a `timedelta` instance from a `datetime` instance and
1223 !! returns a new `datetime` instance. Overloads the operator `-`.
1224
1225 CLASS(datetime), INTENT(in) :: d0 !! `datetime` instance
1226 CLASS(timedelta), INTENT(in) :: t !! `timedelta` instance
1227 TYPE(datetime) :: d
1228
1229 d = d0 + (-t)
1230
1231 END FUNCTION datetime_minus_timedelta
1232
1233 PURE ELEMENTAL FUNCTION datetime_minus_datetime(d0, d1) RESULT(t)
1234
1235 !! Subtracts a `datetime` instance from another `datetime` instance,
1236 !! and returns a `timedelta` instance. Overloads the operator `-`.
1237
1238 CLASS(datetime), INTENT(in) :: d0 !! lhs `datetime` instance
1239 CLASS(datetime), INTENT(in) :: d1 !! rhs `datetime` instance
1240 TYPE(timedelta) :: t
1241
1242 REAL(kind=real64) :: daysdiff
1243 INTEGER :: days, hours, minutes, seconds, milliseconds
1244 INTEGER :: sign_
1245
1246 daysdiff = date2num(d0) - date2num(d1)
1247
1248 IF (daysdiff < 0) THEN
1249 sign_ = -1
1250 daysdiff = abs(daysdiff)
1251 ELSE
1252 sign_ = 1
1253 END IF
1254
1255 days = int(daysdiff)
1256 hours = int((daysdiff - days)*d2h)
1257 minutes = int((daysdiff - days - hours*h2d)*d2m)
1258 seconds = int((daysdiff - days - hours*h2d - minutes*m2d)*d2s)
1259 milliseconds = nint((daysdiff - days - hours*h2d - minutes*m2d &
1260 - seconds*s2d)*d2s*1e3_real64)
1261
1262 t = timedelta(sign_*days, sign_*hours, sign_*minutes, sign_*seconds, &
1263 sign_*milliseconds)
1264
1265 END FUNCTION datetime_minus_datetime
1266
1267 PURE ELEMENTAL LOGICAL FUNCTION gt(d0, d1)
1268
1269 !! `datetime` comparison operator that eturns `.true.` if `d0` is
1270 !! greater than `d1` and `.false.` otherwise. Overloads the
1271 !! operator `>`.
1272
1273 CLASS(datetime), INTENT(in) :: d0 !! lhs `datetime` instance
1274 CLASS(datetime), INTENT(in) :: d1 !! rhs `datetime` instance
1275
1276 TYPE(datetime) :: d0_utc, d1_utc
1277
1278 ! Convert to UTC before making comparison
1279 d0_utc = d0%utc()
1280 d1_utc = d1%utc()
1281
1282 ! Year comparison block
1283 IF (d0_utc%year > d1_utc%year) THEN
1284 gt = .true.
1285 ELSEIF (d0_utc%year < d1_utc%year) THEN
1286 gt = .false.
1287 ELSE
1288
1289 ! Month comparison block
1290 IF (d0_utc%month > d1_utc%month) THEN
1291 gt = .true.
1292 ELSEIF (d0_utc%month < d1_utc%month) THEN
1293 gt = .false.
1294 ELSE
1295
1296 ! Day comparison block
1297 IF (d0_utc%day > d1_utc%day) THEN
1298 gt = .true.
1299 ELSEIF (d0_utc%day < d1_utc%day) THEN
1300 gt = .false.
1301 ELSE
1302
1303 ! Hour comparison block
1304 IF (d0_utc%hour > d1_utc%hour) THEN
1305 gt = .true.
1306 ELSEIF (d0_utc%hour < d1_utc%hour) THEN
1307 gt = .false.
1308 ELSE
1309
1310 ! Minute comparison block
1311 IF (d0_utc%minute > d1_utc%minute) THEN
1312 gt = .true.
1313 ELSEIF (d0_utc%minute < d1_utc%minute) THEN
1314 gt = .false.
1315 ELSE
1316
1317 ! Second comparison block
1318 IF (d0_utc%second > d1_utc%second) THEN
1319 gt = .true.
1320 ELSEIF (d0_utc%second < d1_utc%second) THEN
1321 gt = .false.
1322 ELSE
1323
1324 ! Millisecond comparison block
1325 IF (d0_utc%millisecond > d1_utc%millisecond) THEN
1326 gt = .true.
1327 ELSE
1328 gt = .false.
1329 END IF
1330
1331 END IF
1332 END IF
1333 END IF
1334 END IF
1335 END IF
1336 END IF
1337
1338 END FUNCTION gt
1339
1340 PURE ELEMENTAL LOGICAL FUNCTION lt(d0, d1)
1341
1342 !! `datetime` comparison operator that returns `.true.` if `d0` is
1343 !! less than `d1` and `.false.` otherwise. Overloads the operator `<`.
1344
1345 CLASS(datetime), INTENT(in) :: d0 !! lhs `datetime` instance
1346 CLASS(datetime), INTENT(in) :: d1 !! rhs `datetime` instance
1347
1348 lt = d1 > d0
1349
1350 END FUNCTION lt
1351
1352 PURE ELEMENTAL LOGICAL FUNCTION eq(d0, d1)
1353
1354 !! `datetime` comparison operator that returns `.true.` if `d0` is
1355 !! equal to `d1` and `.false.` otherwise. Overloads the operator `==`.
1356
1357 CLASS(datetime), INTENT(in) :: d0 !! lhs `datetime` instance
1358 CLASS(datetime), INTENT(in) :: d1 !! rhs `datetime` instance
1359
1360 TYPE(datetime) :: d0_utc, d1_utc
1361
1362 ! Convert to UTC before making comparison
1363 d0_utc = d0%utc()
1364 d1_utc = d1%utc()
1365
1366 eq = d0_utc%year == d1_utc%year .AND. &
1367 d0_utc%month == d1_utc%month .AND. &
1368 d0_utc%day == d1_utc%day .AND. &
1369 d0_utc%hour == d1_utc%hour .AND. &
1370 d0_utc%minute == d1_utc%minute .AND. &
1371 d0_utc%second == d1_utc%second .AND. &
1372 d0_utc%millisecond == d1_utc%millisecond
1373
1374 END FUNCTION eq
1375
1376 PURE ELEMENTAL LOGICAL FUNCTION neq(d0, d1)
1377
1378 !! `datetime` comparison operator that eturns `.true.` if `d0` is
1379 !! not equal to `d1` and `.false.` otherwise. Overloads the operator `/=`.
1380
1381 CLASS(datetime), INTENT(in) :: d0 !! lhs `datetime` instance
1382 CLASS(datetime), INTENT(in) :: d1 !! rhs `datetime` instance
1383
1384 neq = .NOT. d0 == d1
1385
1386 END FUNCTION neq
1387
1388 PURE ELEMENTAL LOGICAL FUNCTION ge(d0, d1)
1389
1390 !! `datetime` comparison operator. Returns `.true.` if `d0` is greater
1391 !! than or equal to `d1` and `.false.` otherwise. Overloads the
1392 !! operator `>=`.
1393
1394 CLASS(datetime), INTENT(in) :: d0 !! lhs `datetime` instance
1395 CLASS(datetime), INTENT(in) :: d1 !! rhs `datetime` instance
1396
1397 ge = d0 > d1 .OR. d0 == d1
1398
1399 END FUNCTION ge
1400
1401 PURE ELEMENTAL LOGICAL FUNCTION le(d0, d1)
1402
1403 !! `datetime` comparison operator. Returns `.true.` if `d0` is less
1404 !! than or equal to `d1`, and `.false.` otherwise. Overloads the
1405 !! operator `<=`.
1406
1407 CLASS(datetime), INTENT(in) :: d0 !! lhs `datetime` instance
1408 CLASS(datetime), INTENT(in) :: d1 !! rhs `datetime` instance
1409
1410 le = d1 > d0 .OR. d0 == d1
1411
1412 END FUNCTION le
1413
1414! public procedures
1415!=======================================================================
1416
1417 PURE ELEMENTAL LOGICAL FUNCTION isleapyear(year)
1418
1419 !! Returns `.true.` if year is leap year and `.false.` otherwise.
1420
1421 INTEGER, INTENT(in) :: year !! year
1422
1423 isleapyear = (mod(year, 4) == 0 .AND. .NOT. mod(year, 100) == 0) &
1424 .OR. (mod(year, 400) == 0)
1425
1426 END FUNCTION isleapyear
1427
1428 PURE FUNCTION datetimerange(d0, d1, t)
1429
1430 !! Given start and end `datetime` instances `d0` and `d1` and time
1431 !! increment as `timedelta` instance `t`, returns an array of
1432 !! `datetime` instances. The number of elements is the number of whole
1433 !! time increments contained between datetimes `d0` and `d1`.
1434
1435 TYPE(datetime), INTENT(in) :: d0 !! start time
1436 TYPE(datetime), INTENT(in) :: d1 !! end time
1437 TYPE(timedelta), INTENT(in) :: t !! time increment
1438
1439 REAL(kind=real64) :: datenum0, datenum1, increment
1440 REAL(kind=real64) :: eps
1441
1442 TYPE(datetime), DIMENSION(:), ALLOCATABLE :: datetimerange
1443
1444 INTEGER :: n, nm
1445
1446 eps = 1e-10_real64
1447
1448 datenum0 = date2num(d0)
1449 datenum1 = date2num(d1)
1450
1451 increment = t%total_seconds()*s2d
1452
1453 nm = floor((datenum1 - datenum0 + eps)/increment) + 1
1454
1455 ALLOCATE (datetimerange(nm))
1456
1457 DO n = 1, nm
1458 datetimerange(n) = num2date(datenum0 + (n - 1)*increment)
1459 END DO
1460
1461 END FUNCTION datetimerange
1462
1463 PURE ELEMENTAL INTEGER FUNCTION daysinmonth(month, year)
1464
1465 !! Given integer month and year, returns an integer number
1466 !! of days in that particular month.
1467
1468 INTEGER, INTENT(in) :: month !! month
1469 INTEGER, INTENT(in) :: year !! year
1470
1471 INTEGER, PARAMETER, DIMENSION(12) :: &
1472 days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
1473
1474 IF (month < 1 .OR. month > 12) THEN
1475 ! Should raise an error and abort here, however we want to keep
1476 ! the pure and elemental attributes. Make sure this function is
1477 ! called with the month argument in range.
1478 daysinmonth = 0
1479 RETURN
1480 END IF
1481
1482 IF (month == 2 .AND. isleapyear(year)) THEN
1483 daysinmonth = 29
1484 ELSE
1485 daysinmonth = days(month)
1486 END IF
1487
1488 END FUNCTION daysinmonth
1489
1490 PURE ELEMENTAL INTEGER FUNCTION daysinyear(year)
1491
1492 !! Returns the number of days in year.
1493
1494 INTEGER, INTENT(in) :: year !! year
1495
1496 IF (isleapyear(year)) THEN
1497 daysinyear = 366
1498 ELSE
1499 daysinyear = 365
1500 END IF
1501
1502 END FUNCTION daysinyear
1503
1504 PURE ELEMENTAL REAL(kind=REAL64) FUNCTION date2num(d)
1505
1506 !! Given a datetime instance d, returns number of days since
1507 !! `0001-01-01 00:00:00`, taking into account the timezone offset.
1508
1509 TYPE(datetime), INTENT(in) :: d !! `datetime` instance
1510
1511 TYPE(datetime) :: d_utc
1512 INTEGER :: year
1513
1514 ! Convert to UTC first
1515 d_utc = d%utc()
1516
1517 ! d_utc % year must be positive:
1518 IF (d_utc%year < 1) THEN
1519 date2num = 0
1520 RETURN
1521 END IF
1522
1523 date2num = 0
1524 DO year = 1, d_utc%year - 1
1525 date2num = date2num + daysinyear(year)
1526 END DO
1527
1528 date2num = date2num &
1529 + d_utc%yearday() &
1530 + d_utc%hour*h2d &
1531 + d_utc%minute*m2d &
1532 + (d_utc%second + 1e-3_real64*d_utc%millisecond)*s2d
1533
1534 END FUNCTION date2num
1535
1536 PURE ELEMENTAL TYPE(datetime) function num2date(num)
1537
1538 !! Given number of days since `0001-01-01 00:00:00`, returns a
1539 !! correspoding `datetime` instance.
1540
1541 REAL(kind=real64), INTENT(in) :: num
1542 !! number of days since `0001-01-01 00:00:00`
1543
1544 INTEGER :: year, month, day, hour, minute, second, millisecond
1545 REAL(kind=real64) :: days, totseconds
1546
1547 ! num must be positive:
1548 IF (num < 0) THEN
1549 num2date = datetime(1)
1550 RETURN
1551 END IF
1552
1553 days = num
1554
1555 year = 1
1556 DO
1557 IF (int(days) <= daysinyear(year)) EXIT
1558 days = days - daysinyear(year)
1559 year = year + 1
1560 END DO
1561
1562 month = 1
1563 DO
1564 IF (int(days) <= daysinmonth(month, year)) EXIT
1565 days = days - daysinmonth(month, year)
1566 month = month + 1
1567 END DO
1568
1569 day = int(days)
1570 totseconds = (days - day)*d2s
1571 hour = int(totseconds*s2h)
1572 minute = int((totseconds - hour*h2s)*s2m)
1573 second = int(totseconds - hour*h2s - minute*m2s)
1574 millisecond = nint((totseconds - int(totseconds))*1e3_real64)
1575
1576 num2date = datetime(year, month, day, hour, minute, second, millisecond, tz=zero)
1577
1578 ! Handle a special case caused by floating-point arithmethic:
1579 IF (num2date%millisecond == 1000) THEN
1580 num2date%millisecond = 0
1581 CALL num2date%addSeconds(1)
1582 END IF
1583
1584 IF (num2date%second == 60) THEN
1585 num2date%second = 0
1586 CALL num2date%addMinutes(1)
1587 END IF
1588 IF (num2date%minute == 60) THEN
1589 num2date%minute = 0
1590 CALL num2date%addHours(1)
1591 END IF
1592 IF (num2date%hour == 60) THEN
1593 num2date%hour = 0
1594 CALL num2date%addDays(1)
1595 END IF
1596
1597 END FUNCTION num2date
1598
1599 TYPE(datetime) function strptime(str, format)
1600
1601 !! A wrapper function around C/C++ strptime function.
1602 !! Returns a `datetime` instance.
1603
1604 CHARACTER(len=*), INTENT(in) :: str !! time string
1605 CHARACTER(len=*), INTENT(in) :: format !! time format
1606
1607 INTEGER :: rc
1608 TYPE(tm_struct) :: tm
1609
1610 rc = c_strptime(trim(str)//c_null_char, trim(format)//c_null_char, tm)
1612
1613 END FUNCTION strptime
1614
1615 PURE ELEMENTAL TYPE(datetime) function tm2date(ctime)
1616
1617 !! Given a `tm_struct` instance, returns a corresponding `datetime`
1618 !! instance.
1619
1620 TYPE(tm_struct), INTENT(in) :: ctime !! C-style time struct
1621
1622 tm2date%millisecond = 0
1623 tm2date%second = ctime%tm_sec
1624 tm2date%minute = ctime%tm_min
1625 tm2date%hour = ctime%tm_hour
1626 tm2date%day = ctime%tm_mday
1627 tm2date%month = ctime%tm_mon + 1
1628 tm2date%year = ctime%tm_year + 1900
1629 tm2date%tz = 0
1630
1631 END FUNCTION tm2date
1632
1633! private procedures
1634!=======================================================================
1635
1636 PURE FUNCTION int2str(i, length)
1637
1638 !! Converts an integer `i` into a character string of requested length,
1639 !! pre-pending zeros if necessary.
1640
1641 INTEGER, INTENT(in) :: i !! integer to convert to string
1642 INTEGER, INTENT(in) :: length !! desired length of string
1643
1644 CHARACTER(len=length) :: int2str
1645 CHARACTER(len=2) :: string
1646
1647 WRITE (unit=string, fmt='(I2)') length
1648 WRITE (unit=int2str, fmt='(I'//string//'.'//string//')') i
1649
1650 END FUNCTION int2str
1651!=======================================================================
1652END MODULE mod_datetime
1653
1654!
1655! datetime-fortran - A Fortran library for date and time manipulation
1656! Copyright (c) 2013-2017, Wavebit Scientific LLC
1657! All rights reserved.
1658!
1659! Licensed under the BSD 3-clause license. See LICENSE for details.
1660!
1662!=======================================================================
1663!
1664! mod_clock
1665!
1666!=======================================================================
1667
1668 use, INTRINSIC :: iso_fortran_env, only: real32, real64
1669 use, INTRINSIC :: iso_c_binding, only: c_char, c_int, c_null_char
1670 USE mod_datetime, ONLY: datetime
1671 USE mod_timedelta, ONLY: timedelta
1672
1673 IMPLICIT NONE
1674
1675 PRIVATE
1676
1677! Derived types:
1678 PUBLIC :: clock
1679
1680 TYPE :: clock
1681
1682 !! A clock object with a start, stop and current times, tick interval
1683 !! and tick methods.
1684
1685 TYPE(datetime) :: starttime
1686 TYPE(datetime) :: stoptime
1687 TYPE(datetime) :: currenttime
1688
1689 TYPE(timedelta) :: tickinterval
1690
1691 ! May become Alarm class in some future release;
1692 ! for now, just a switch
1693 LOGICAL :: alarm = .false.
1694
1695 ! Clock status flags
1696 LOGICAL :: started = .false.
1697 LOGICAL :: stopped = .false.
1698
1699 CONTAINS
1700
1701 PROCEDURE :: reset
1702 PROCEDURE :: tick
1703
1704 END TYPE clock
1705!=======================================================================
1706CONTAINS
1707
1708!=======================================================================
1709 PURE ELEMENTAL SUBROUTINE reset(self)
1710
1711 !! Resets the clock to its start time.
1712
1713 CLASS(clock), INTENT(inout) :: self
1714
1715 self%currentTime = self%startTime
1716
1717 self%started = .false.
1718 self%stopped = .false.
1719
1720 END SUBROUTINE reset
1721!=======================================================================
1722
1723!=======================================================================
1724 PURE ELEMENTAL SUBROUTINE tick(self)
1725
1726 !! Increments the currentTime of the clock instance by one tickInterval.
1727
1728 CLASS(clock), INTENT(inout) :: self
1729
1730 IF (self%stopped) THEN
1731 RETURN
1732 END IF
1733
1734 IF (.NOT. self%started) THEN
1735 self%started = .true.
1736 self%currentTime = self%startTime
1737 END IF
1738
1739 self%currentTime = self%currentTime + self%tickInterval
1740
1741 IF (self%currentTime >= self%stopTime) THEN
1742 self%stopped = .true.
1743 END IF
1744
1745 END SUBROUTINE tick
1746!=======================================================================
1747END MODULE mod_clock
1748
1749! datetime-fortran - A Fortran library for date and time manipulation
1750! Copyright (c) 2013-2017, Wavebit Scientific LLC
1751! All rights reserved.
1752!
1753! Licensed under the BSD-3 clause license. See LICENSE for details.
1754!
1756
1757!! Provides entry point to all items defined in datetime, timedelta,
1758!! clock and strftime modules.
1759
1760 USE mod_datetime
1761 USE mod_timedelta
1762 USE mod_clock
1763 USE mod_strftime
1764
1765END MODULE datetime_module
pure elemental subroutine reset(self)
pure elemental subroutine tick(self)
real(kind=real64), parameter, public h2s
real(kind=real64), parameter, public m2s
real(kind=real64), parameter, public s2m
real(kind=real64), parameter, public d2m
integer, parameter, public maxstrlen
real(kind=real64), parameter, public s2h
real(kind=real64), parameter, public s2d
real(kind=real64), parameter, public h2d
real(kind=real64), parameter, public m2h
real(kind=real64), parameter, public m2d
real(kind=real64), parameter, public d2s
real(kind=real64), parameter, public one
real(kind=real64), parameter, public zero
real(kind=real64), parameter, public d2h
pure elemental integer function getminute(self)
pure elemental real(kind=real64) function, public date2num(d)
pure elemental logical function eq(d0, d1)
pure elemental logical function neq(d0, d1)
pure elemental subroutine adddays(self, d)
pure elemental type(datetime) function datetime_plus_timedelta(d0, t)
type(datetime) function now()
pure elemental logical function le(d0, d1)
pure elemental integer function gethour(self)
pure elemental real(kind=real64) function gettz(self)
pure elemental type(datetime) function datetime_minus_timedelta(d0, t)
pure elemental integer function weekday(self)
pure elemental logical function, public isleapyear(year)
pure elemental subroutine addmilliseconds(self, ms)
integer function, dimension(3) isocalendar(self)
pure elemental integer function getmonth(self)
pure elemental subroutine addseconds(self, s)
pure elemental integer function getsecond(self)
pure elemental type(datetime) function datetime_constructor(year, month, day, hour, minute, second, millisecond, tz)
pure character(len=length) function int2str(i, length)
type(datetime) function, public strptime(str, format)
pure elemental type(timedelta) function datetime_minus_datetime(d0, d1)
pure elemental type(tm_struct) function tm(self)
pure elemental logical function ge(d0, d1)
pure elemental subroutine addminutes(self, m)
pure elemental integer function isoweekday(self)
integer function secondssinceepoch(self)
pure elemental logical function isvalid(self)
pure elemental subroutine addhours(self, h)
pure elemental logical function gt(d0, d1)
pure elemental type(datetime) function timedelta_plus_datetime(t, d0)
pure elemental logical function lt(d0, d1)
pure elemental integer function yearday(self)
pure elemental character(len=9) function weekdaylong(self)
pure elemental character(len=5) function tzoffset(self)
pure type(datetime) function, dimension(:), allocatable, public datetimerange(d0, d1, t)
pure elemental integer function getmillisecond(self)
pure elemental character(len=3) function weekdayshort(self)
pure elemental type(datetime) function utc(self)
pure elemental integer function, public daysinmonth(month, year)
pure elemental integer function getyear(self)
pure elemental character(len=23) function isoformat(self, sep)
pure elemental type(datetime) function, public num2date(num)
pure elemental character(len=9) function isoweekdaylong(self)
pure elemental type(datetime) function, public tm2date(ctime)
pure elemental integer function, public daysinyear(year)
character(len=:) function, allocatable strftime(self, format)
pure elemental integer function getday(self)
pure elemental character(len=3) function isoweekdayshort(self)
pure elemental type(timedelta) function timedelta_plus_timedelta(t0, t1)
pure elemental integer function getdays(self)
pure elemental integer function gethours(self)
pure elemental logical function le(td0, td1)
pure elemental logical function ge(td0, td1)
pure elemental integer function getminutes(self)
pure elemental logical function eq(td0, td1)
pure elemental logical function neq(td0, td1)
pure elemental type(timedelta) function timedelta_constructor(days, hours, minutes, seconds, milliseconds)
pure elemental real(kind=real64) function total_seconds(self)
pure elemental logical function lt(td0, td1)
pure elemental integer function getmilliseconds(self)
pure elemental type(timedelta) function timedelta_minus_timedelta(t0, t1)
pure elemental integer function getseconds(self)
pure elemental type(timedelta) function unary_minus_timedelta(t0)
pure elemental logical function gt(td0, td1)