OB.DAAC Logo
NASA Logo
Ocean Color Science Software

ocssw V2022
calendars.f95
Go to the documentation of this file.
1 module calendars
2 
3  implicit none
4 
5 ! Restrict access to module components.
6 ! All are private unless otherwise stated.
7  private
8 
9  public :: datetime, gdatetime, operator(-), operator(+), operator(<), &
10  & operator(>), operator(/=), operator(==), operator(<=), operator(>=)
11 
14 
21 
22 
23 ! Define custom data types
24 ! Fixed date and time
25  type :: datetime
26  integer :: day ! in days
27  integer :: time ! in milliseconds
28  end type datetime
29 
30 ! Gregorian date and time
31  type :: gdatetime
32  integer :: year
33  integer :: month
34  integer :: mday
35  integer :: hour
36  integer :: min
37  integer :: sec
38  integer :: msec
39  integer :: tzone ! in hours, tzone = localtime - utc, range=[0,23]
40  end type gdatetime
41 
42 ! Define calendar epochs
43  integer, private, parameter :: GREG_EPOCH = 1
44  integer, private, parameter :: FIXED_EPOCH = 0
45 
46 ! Gregorian constants
47  integer, parameter :: JAN = 1
48  integer, parameter :: FEB = 2
49  integer, parameter :: MAR = 3
50  integer, parameter :: APR = 4
51  integer, parameter :: MAY = 5
52  integer, parameter :: JUN = 6
53  integer, parameter :: JUL = 7
54  integer, parameter :: AUG = 8
55  integer, parameter :: SEP = 9
56  integer, parameter :: OCT = 10
57  integer, parameter :: NOV = 11
58  integer, parameter :: DEC = 12
59 
60  integer, parameter :: SUN = 0
61  integer, parameter :: MON = sun + 1
62  integer, parameter :: TUE = sun + 2
63  integer, parameter :: WED = sun + 3
64  integer, parameter :: THU = sun + 4
65  integer, parameter :: FRI = sun + 5
66  integer, parameter :: SAT = sun + 6
67 
68 !---------------------------------------------------------------------------------------------------
69  interface operator (-)
70  module procedure subtract
71  end interface
72 
73  interface operator (+)
74  module procedure add
75  end interface
76 
77  interface operator (>)
78  module procedure greater_than
79  end interface
80 
81  interface operator (>=)
82  module procedure greater_than_equal
83  end interface
84 
85  interface operator (<)
86  module procedure less_than
87  end interface
88 
89  interface operator (<=)
90  module procedure less_than_equal
91  end interface
92 
93  contains
94 
95 !---------------------------------------------------------------------------------------------------
96 ! FIXED FUNCTIONS
97 !---------------------------------------------------------------------------------------------------
98 
99  type (datetime) function subtract(dt1, dt2)
100  type (datetime), intent(in) :: dt1, dt2
101 
102  subtract = datetime(dt1%day - dt2%day, dt1%time - dt2%time)
103  if (subtract%time >= 86400000) then
104  subtract%day = subtract%day + int(subtract%time/86400000)
105  subtract%time = mod(subtract%time,86400000)
106  end if
107  if (subtract%time < 0) then
108  subtract%day = subtract%day + int((subtract%time)/86400000) - 1
109  subtract%time = 86400000 + mod(subtract%time,86400000)
110  end if
111 
112  if (subtract%time == 86400000) then
113  subtract%time = 0
114  subtract%day = subtract%day + 1
115  end if
116 
117  return
118 
119  end function subtract
120 
121  type (datetime) function add(dt1, dt2)
122  type (datetime), intent(in) :: dt1, dt2
123 
124  add = datetime(dt1%day + dt2%day, dt1%time + dt2%time)
125  if (add%time >= 86400000) then
126  add%day = add%day + int(add%time/86400000)
127  add%time = mod(add%time,86400000)
128  end if
129  if (add%time < 0) then
130  add%day = add%day + int(add%time/86400000) - 1
131  add%time = 86400000 + mod(add%time, 86400000)
132  end if
133 
134  return
135 
136  end function add
137 
138  logical function greater_than(dt1, dt2)
139  type (datetime), intent(in) :: dt1, dt2
140 
141  greater_than = .false.
142  if (dt1%day > dt2%day) then
143  greater_than = .true.
144  else if (dt1%day == dt2%day) then
145  if (dt1%time > dt2%time) then
146  greater_than = .true.
147  end if
148  end if
149 
150  return
151 
152  end function greater_than
153 
154  logical function greater_than_equal(dt1, dt2)
155  type (datetime), intent(in) :: dt1, dt2
156 
157  greater_than_equal = .false.
158  if (dt1 > dt2) then
159  greater_than_equal = .true.
160  else if (dt1%day == dt2%day .AND. dt1%time == dt2%time) then
161  greater_than_equal = .true.
162  end if
163 
164  return
165 
166  end function greater_than_equal
167 
168  logical function less_than(dt1, dt2)
169  type (datetime), intent(in) :: dt1, dt2
170 
171  less_than = .false.
172  if (dt1%day < dt2%day) then
173  less_than = .true.
174  else if (dt1%day == dt2%day) then
175  if (dt1%time < dt2%time) then
176  less_than = .true.
177  end if
178  end if
179 
180  return
181 
182  end function less_than
183 
184  logical function less_than_equal(dt1, dt2)
185  type (datetime), intent(in) :: dt1, dt2
186 
187  less_than_equal = .false.
188  if (dt1 < dt2) then
189  less_than_equal = .true.
190  else if (dt1%day == dt2%day .AND. dt1%time == dt2%time) then
191  less_than_equal = .true.
192  end if
193 
194  return
195  end function less_than_equal
196 
197  integer function rd(day)
198  implicit none
199  integer, intent(in) :: day
200 
201  rd = day - fixed_epoch
202 
203  end function rd
204 
205  integer function day_of_week_from_fixed(dt1)
206  implicit none
207 
208  type(datetime), intent(in) :: dt1
209 
210  day_of_week_from_fixed = mod(dt1%day - fixed_epoch - sun, 7)
211 
212  return
213  end function day_of_week_from_fixed
214 
215 ! Return the value of dt1 in seconds
216  real function seconds_from_fixed(dt1)
217  implicit none
218 
219  type(datetime), intent(in) :: dt1
220 
221  seconds_from_fixed = dt1%day * 86400 + dt1%time/1000.0
222 
223  return
224  end function seconds_from_fixed
225 
226 !---------------------------------------------------------------------------------------------------
227 ! TIME FUNCTIONS
228 !---------------------------------------------------------------------------------------------------
229 
230 ! returns local mean time offset from UTC in milliseconds
231  integer function timezone_from_longitude(lon)
232  implicit none
233 
234  real, intent(in) :: lon
235 
236  integer, parameter :: msec_per_lon = 240000
237 
238  timezone_from_longitude = lon * msec_per_lon
239 
240  return
241 
242  end function timezone_from_longitude
243 
244  type(datetime) function universal_from_local(dt1, lon)
245  implicit none
246 
247  type(datetime), intent(in) :: dt1
248  real, intent(in) :: lon
249 
250  universal_from_local = dt1 - datetime(0, timezone_from_longitude(lon))
251 
252  return
253 
254  end function universal_from_local
255 
256  type(datetime) function local_from_universal(dt1, lon)
257  implicit none
258 
259  type(datetime), intent(in) :: dt1
260  real, intent(in) :: lon
261 
262  type(datetime) :: dt2
263 
264  local_from_universal = dt1 + datetime(0, timezone_from_longitude(lon))
265 
266  return
267 
268  end function local_from_universal
269 
270 ! tz = UTC offset where standard_time = universal_time + tz (in milliseconds)
271  type(datetime) function standard_from_universal(dt1, tz)
272  type(datetime), intent(in) :: dt1
273  integer, intent(in) :: tz
274 
275  standard_from_universal = dt1 + datetime(0, tz)
276  return
277 
278  end function standard_from_universal
279 
280  type(datetime) function universal_from_standard(dt1, tz)
281  type(datetime), intent(in) :: dt1
282  integer, intent(in) :: tz
283 
284  universal_from_standard = dt1 - datetime(0, tz)
285  return
286 
287  end function universal_from_standard
288 
289 !---------------------------------------------------------------------------------------------------
290 ! GREGORIAN FUNCTIONS
291 !---------------------------------------------------------------------------------------------------
292 
293  logical function gregorian_leap_year(yr)
294  implicit none
295  integer, intent(in) :: yr
296 
297  if ((mod(yr,4) == 0 .AND. mod(yr,100) /= 0) .OR. (mod(yr,400) == 0)) then
298  gregorian_leap_year = .true.
299  else
300  gregorian_leap_year = .false.
301  end if
302 
303  return
304 
305  end function gregorian_leap_year
306 
307 
308  type(datetime) function fixed_from_gregorian(gdt1)
309  implicit none
310 
311  type(gdatetime), intent(in) :: gdt1
312  type(datetime) :: dt1
313  integer :: offset
314 
315  if (gdt1%month <= 2) then
316  offset = 0
317  else if (gregorian_leap_year(gdt1%year)) then
318  offset = -1
319  else
320  offset = -2
321  end if
322 
323  fixed_from_gregorian%day = greg_epoch - 1 + 365*(gdt1%year - 1) + floor((gdt1%year - 1)/4.0) - &
324  & floor((gdt1%year-1)/100.0) + floor((gdt1%year - 1)/400.0) + &
325  & floor((1.0/12.0)*(367*gdt1%month - 362)) + offset + gdt1%mday
327 
328  return
329 
330  end function fixed_from_gregorian
331 
332  integer function time_from_gregorian(gdt1)
333  implicit none
334 
335  type(gdatetime), intent(in) :: gdt1
336 
337  time_from_gregorian = gdt1%hour*3600000 + gdt1%min*60000 + gdt1%sec*1000 + gdt1%msec
338 
339  return
340  end function time_from_gregorian
341 
342  type(gdatetime) function gregorian_from_time(dt1)
343  implicit none
344 
345  type(datetime), intent(in) :: dt1
346  type(gdatetime) :: gdt1
347 
348  gdt1%hour = dt1%time / 3600000
349  gdt1%min = (dt1%time - gdt1%hour*3600000) / 60000
350  gdt1%sec = (dt1%time - gdt1%hour*3600000 - gdt1%min*60000) / 1000
351  gdt1%msec = dt1%time - gdt1%hour*3600000 - gdt1%min*60000 - gdt1%sec*1000
352 
353  gregorian_from_time = gdt1
354 
355  return
356  end function gregorian_from_time
357 
358  type(datetime) function gregorian_year_start(yr)
359  implicit none
360  integer, intent(in) :: yr
361  type(gdatetime) :: gdt1
362 
363  gdt1 = gdatetime(yr, jan, 1, 0, 0, 0, 0, 0)
365 
366  return
367  end function gregorian_year_start
368 
369  type(datetime) function gregorian_year_end(yr)
370  implicit none
371  integer, intent(in) :: yr
372  type(gdatetime) :: gdt1
373 
374  gdt1 = gdatetime(yr, dec, 31, 23, 59, 59, 86400000, 0)
376 
377  return
378 
379  end function gregorian_year_end
380 
381  subroutine gregorian_year_range(yr, yrange)
382  integer, intent(in) :: yr
383  integer, dimension(:), intent(inout) :: yrange
384  type(datetime) :: dt1, dt2
385 
386  dt1 = gregorian_year_start(yr)
387  dt2 = gregorian_year_end(yr)
388  yrange = (/dt1%day, dt2%day/)
389 
390  return
391 
392  end subroutine gregorian_year_range
393 
394  integer function gregorian_year_from_fixed(dt1)
395  implicit none
396 
397  type (datetime), intent(in) :: dt1
398 
399  integer :: d0, d1, d2, d3
400  integer :: n1, n4, n100, n400
401  integer :: year
402 
403  d0 = dt1%day - greg_epoch
404  n400 = d0/146097
405  d1 = mod(d0, 146097)
406  n100 = d1/36524
407  d2 = mod(d1, 36524)
408  n4 = d2/1461
409  d3 = mod(d2, 1461)
410  n1 = d3/365
411  year = 400*n400 + 100*n100 + 4*n4 + n1
412 
413  if (n100 == 4 .OR. n1 == 4) then
415  else
416  gregorian_year_from_fixed = year + 1
417  endif
418 
419  return
420  end function gregorian_year_from_fixed
421 
422  type(gdatetime) function gregorian_from_fixed(dt1)
423  implicit none
424 
425  type(datetime), intent(in) :: dt1
426 
427  integer :: yr, pdays, correction, mo, mdy
428  type(gdatetime) :: gdt1
429  type(datetime) :: dt2
430 
431  yr = gregorian_year_from_fixed(dt1)
432  dt2 = gregorian_year_start(yr)
433  pdays = dt1%day - dt2%day
434 
435  gdt1 = gdatetime(yr, mar, 1, 0, 0, 0, 0, 0)
436  dt2 = fixed_from_gregorian(gdt1)
437  if (dt1%day < dt2%day) then
438  correction = 0
439  else if (gregorian_leap_year(yr)) then
440  correction = 1
441  else
442  correction = 2
443  end if
444 
445  mo = floor((1.0/367.0) * (12 * (pdays+correction) + 373))
446 
447  gdt1 = gdatetime(yr, mo, 1, 0, 0, 0, 0, 0)
448  dt2 = fixed_from_gregorian(gdt1)
449  mdy = 1 + dt1%day - dt2%day
450 
451  gdt1 = gregorian_from_time(dt1)
452 
453  gregorian_from_fixed = gdatetime(yr, mo, mdy, gdt1%hour, gdt1%min, gdt1%sec, &
454  & gdt1%msec, 0)
455 
456  return
457  end function gregorian_from_fixed
458 
459  type(datetime) function gregorian_date_difference(gdt1, gdt2)
460  implicit none
461 
462  type(gdatetime), intent(in) :: gdt1, gdt2
463 
465 
466  end function gregorian_date_difference
467 
468  type(gdatetime) function gregorian_from_doy(yr, doy)
469  implicit none
470 
471  integer, intent(in) :: yr, doy
472  type(datetime) :: dt1
473  type(gdatetime) :: gdt1
474 
475  dt1 = fixed_from_doy(yr, doy)
477 
478  return
479 
480  end function gregorian_from_doy
481 
482  integer function doy_from_gregorian(gdt1)
483  implicit none
484 
485  type(gdatetime), intent(in) :: gdt1
486  type(gdatetime) :: gdt2
487  type(datetime) :: dt1
488 
489  gdt2 = gdatetime(gdt1%year-1, dec, 31, 0, 0, 0, 0, 0)
490  dt1 = gregorian_date_difference(gdt2, gdt1)
491  doy_from_gregorian = dt1%day
492 
493  return
494 
495  end function doy_from_gregorian
496 
497  integer function doy_from_fixed(dt1)
498  implicit none
499 
500  type(datetime), intent(in) :: dt1
501 
502  integer :: d0, d1, d2, d3
503  integer :: n1, n100
504 
505  d0 = dt1%day - greg_epoch
506  d1 = mod(d0,146097)
507  n100 = d1/36524
508  d2 = mod(d1, 36524)
509  d3 = mod(d2, 1461)
510  n1 = d3/365
511 
512  if (n1 /= 4 .AND. n100 /= 4) then
513  doy_from_fixed = mod(d3,365) + 1
514  else
515  doy_from_fixed = 366
516  end if
517 
518  return
519 
520  end function doy_from_fixed
521 
522  type(datetime) function fixed_from_doy(yr, doy)
523  implicit none
524 
525  integer, intent(in) :: doy, yr
526  type(gdatetime) :: gdt1
527  type(datetime) :: dt1
528 
529  gdt1 = gdatetime(yr-1, dec, 31, 0, 0, 0, 0, 0)
530  fixed_from_doy = fixed_from_gregorian(gdt1) + datetime(doy,0)
531 
532  return
533 
534  end function fixed_from_doy
535 
536 ! Compare given year and day of year to beginning and ending of each season.
537 ! Return the season: 1 = Winter, 2 = Spring, 3 = Summer, 4 = Fall
538  integer function season_from_doy(yr, doy)
539  implicit none
540 
541  integer, intent(in) :: yr
542  integer, intent(in) :: doy
543 
544  type(datetime) :: dt1, sstart, sstop
545  type(gdatetime) :: gdt1
546 
547  dt1 = fixed_from_doy(yr,doy)
548  gdt1 = gregorian_from_fixed(dt1)
549 
550  select case (gdt1%month)
551  case(1,2,12) ! Winter
552  season_from_doy = 1
553 
554  case(3:5) ! Spring
555  season_from_doy = 2
556 
557  case(6:8) ! Summer
558  season_from_doy = 3
559 
560  case(9:11) ! Fall
561  season_from_doy = 4
562 
563  case default ! Uh oh
564  print *, "ERROR: Invalid month specified: ", gdt1%month
565  season_from_doy = -1
566 
567  end select
568 
569 ! Winter
570 ! sstart = fixed_from_gregorian(gdatetime(yr-1,DEC,1,0,0,0,0,0))
571 ! sstop = fixed_from_gregorian(gdatetime(yr,MAR,1,0,0,0,0,0))
572 ! if (dt1 >= sstart .AND. dt1 < sstop) then
573 ! season_from_doy = 1
574 ! return
575 ! end if
576 !
577 ! Spring
578 ! sstart = fixed_from_gregorian(gdatetime(yr,MAR,1,0,0,0,0,0))
579 ! sstop = fixed_from_gregorian(gdatetime(yr,JUN,1,0,0,0,0,0))
580 ! if (dt1 >= sstart .AND. dt1 < sstop) then
581 ! season_from_doy = 2
582 ! return
583 ! end if
584 !
585 ! Summer
586 ! sstart = fixed_from_gregorian(gdatetime(yr,JUN,1,0,0,0,0,0))
587 ! sstop = fixed_from_gregorian(gdatetime(yr,SEP,1,0,0,0,0,0))
588 ! if (dt1 >= sstart .AND. dt1 < sstop) then
589 ! season_from_doy = 3
590 ! return
591 ! end if
592 !
593 ! Fall
594 ! sstart = fixed_from_gregorian(gdatetime(yr,SEP,1,0,0,0,0,0))
595 ! sstop = fixed_from_gregorian(gdatetime(yr,NOV,1,0,0,0,0,0))
596 ! if (dt1 >= sstart .AND. dt1 < sstop) then
597 ! season_from_doy = 4
598 ! return
599 ! end if
600 !
601 ! Something went wrong.
602 ! season_from_doy = -1
603  return
604 
605  end function season_from_doy
606 !---------------------------------------------------------------------------------------------------
607 ! TAI-93 FUNCTIONS
608 !---------------------------------------------------------------------------------------------------
609 ! TAI-93 is a time standard used in EOS data files. Time is specified as seconds since
610 ! 1993-01-01, aka January 1, 1993.
611 
612 ! TODO: set up and return a status code somehow.
613  integer function tai93_from_fixed(dt)
614  type(datetime), intent(in) :: dt
615 
616  type(datetime) :: tai_epoch, dt1
617  type(gdatetime) :: gdt1
618 
619  tai93_from_fixed = -999
620  gdt1 = gdatetime(2043, 1, 1, 0, 0, 0, 0, 0)
621  dt1 = fixed_from_gregorian(gdt1)
622  if (dt >= dt1) then
623  print *, "ERROR: Specified datetime out of range for TAI93 values (max=2043Jan1)"
624  return
625  end if
626 
627  gdt1 = gdatetime(1993, 1, 1, 0, 0, 0, 0, 0)
628  tai_epoch = fixed_from_gregorian(gdt1)
629  dt1 = dt - tai_epoch
630 
631  tai93_from_fixed = dt1%day * 86400 + int(dt1%time/1000)
632 
633  return
634  end function tai93_from_fixed
635 
636  type(datetime) function fixed_from_tai93(tai)
637  integer, intent(in) :: tai
638  type(datetime) :: tai_epoch
639  type(gdatetime) :: gdt1
640  integer :: dtai, mstai
641  gdt1 = gdatetime(1993,1,1,0,0,0,0,0)
642  tai_epoch = fixed_from_gregorian(gdt1)
643 
644  dtai = tai / 86400
645  mstai = mod(tai,86400)*1000
646  fixed_from_tai93 = tai_epoch + datetime(dtai, mstai)
647 
648  return
649  end function fixed_from_tai93
650 
651 end module calendars
type(datetime) function, public gregorian_year_end(yr)
Definition: calendars.f95:370
real function, public seconds_from_fixed(dt1)
Definition: calendars.f95:217
type(datetime) function, public fixed_from_gregorian(gdt1)
Definition: calendars.f95:309
subroutine, public gregorian_year_range(yr, yrange)
Definition: calendars.f95:382
type(datetime) function, public universal_from_standard(dt1, tz)
Definition: calendars.f95:281
logical function, public gregorian_leap_year(yr)
Definition: calendars.f95:294
integer function, public season_from_doy(yr, doy)
Definition: calendars.f95:539
type(datetime) function, public fixed_from_doy(yr, doy)
Definition: calendars.f95:523
type(gdatetime) function, public gregorian_from_doy(yr, doy)
Definition: calendars.f95:469
type(datetime) function, public local_from_universal(dt1, lon)
Definition: calendars.f95:257
type(datetime) function, public standard_from_universal(dt1, tz)
Definition: calendars.f95:272
type(datetime) function, public gregorian_year_start(yr)
Definition: calendars.f95:359
integer function, public doy_from_gregorian(gdt1)
Definition: calendars.f95:483
type(datetime) function, public universal_from_local(dt1, lon)
Definition: calendars.f95:245
type(datetime) function, public gregorian_date_difference(gdt1, gdt2)
Definition: calendars.f95:460
type(gdatetime) function, public gregorian_from_time(dt1)
Definition: calendars.f95:343
type(gdatetime) function, public gregorian_from_fixed(dt1)
Definition: calendars.f95:423
integer function, public doy_from_fixed(dt1)
Definition: calendars.f95:498
integer function, public day_of_week_from_fixed(dt1)
Definition: calendars.f95:206
integer function, public timezone_from_longitude(lon)
Definition: calendars.f95:232
integer function, public gregorian_year_from_fixed(dt1)
Definition: calendars.f95:395
type(datetime) function, public fixed_from_tai93(tai)
Definition: calendars.f95:637
integer function, public tai93_from_fixed(dt)
Definition: calendars.f95:614
integer function, public time_from_gregorian(gdt1)
Definition: calendars.f95:333