Skip to content

Commit 97f1515

Browse files
author
Jeff Whitaker
authored
Merge pull request #586 from ckhroulev/issue-584
360_day and 365_day calendars: support negative Julian days. See #584.
2 parents c0810ba + 3f56a2d commit 97f1515

File tree

3 files changed

+62
-4
lines changed

3 files changed

+62
-4
lines changed

Changelog

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
* Use valid_min/valid_max/valid_range attributes when defining mask
66
(issue #576). Values outside the valid range are considered to
77
be missing when defining the mask.
8+
* Fix for issue #584 (add support for dates before -4712-1-1 in 360_day
9+
and 365_day calendars to netcdftime.utime).
810

911
version 1.2.4 (tag v1.2.4rel)
1012
==============================

netcdftime/netcdftime.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,10 @@ def _DateFromNoLeapDay(JD):
393393
# based on redate.py by David Finlayson.
394394

395395
if JD < 0:
396-
raise ValueError('Julian Day must be positive')
396+
year_offset = int(-JD) // 365 + 1
397+
JD += year_offset * 365
398+
else:
399+
year_offset = 0
397400

398401
dayofwk = int(math.fmod(int(JD + 1.5), 7))
399402
(F, Z) = math.modf(JD + 0.5)
@@ -428,7 +431,17 @@ def _DateFromNoLeapDay(JD):
428431
(sfrac, seconds) = math.modf(mfrac * 60.0)
429432
microseconds = sfrac*1.e6
430433

431-
return datetime(year, month, int(days), int(hours), int(minutes),
434+
if year_offset > 0:
435+
# correct dayofwk
436+
437+
# 365 mod 7 = 1, so the day of the week changes by one day for
438+
# every year in year_offset
439+
dayofwk -= int(math.fmod(year_offset, 7))
440+
441+
if dayofwk < 0:
442+
dayofwk += 7
443+
444+
return datetime(year - year_offset, month, int(days), int(hours), int(minutes),
432445
int(seconds), int(microseconds),dayofwk, dayofyr)
433446

434447

@@ -495,7 +508,10 @@ def _DateFrom360Day(JD):
495508
"""
496509

497510
if JD < 0:
498-
raise ValueError('Julian Day must be positive')
511+
year_offset = int(-JD) // 360 + 1
512+
JD += year_offset * 360
513+
else:
514+
year_offset = 0
499515

500516
#jd = int(360. * (year + 4716)) + int(30. * (month - 1)) + day
501517
(F, Z) = math.modf(JD)
@@ -511,7 +527,7 @@ def _DateFrom360Day(JD):
511527
(sfrac, seconds) = math.modf(mfrac * 60.0)
512528
microseconds = sfrac*1.e6
513529

514-
return datetime(year, month, int(days), int(hours), int(minutes),
530+
return datetime(year - year_offset, month, int(days), int(hours), int(minutes),
515531
int(seconds), int(microseconds), -1, dayofyr)
516532

517533

test/tst_netcdftime.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,5 +659,45 @@ def test_issue444(self):
659659
assert(index == 11)
660660
f.close()
661661

662+
class issue584TestCase(unittest.TestCase):
663+
"""Regression tests for issue #584."""
664+
converters = None
665+
666+
def setUp(self):
667+
self.converters = {"360_day" : utime("days since 1-1-1", "360_day"),
668+
"365_day" : utime("days since 1-1-1", "365_day")}
669+
670+
def test_roundtrip(self):
671+
"Test roundtrip conversion (num2date <-> date2num) using 360_day and 365_day calendars."
672+
673+
# Pick a date and time outside of the range of the Julian calendar.
674+
date = datetimex(-5000, 1, 1, 12)
675+
676+
for calendar in ["360_day", "365_day"]:
677+
converter = self.converters[calendar]
678+
self.assertEqual(date, converter.num2date(converter.date2num(date)))
679+
680+
def test_dayofwk(self):
681+
"Test computation of dayofwk in the 365_day calendar."
682+
683+
converter = self.converters["365_day"]
684+
685+
# Pick the date corresponding to the Julian day of 1.0 to test
686+
# the transision from positive to negative Julian days.
687+
julian_day = converter.date2num(datetimex(-4712, 1, 2, 12))
688+
689+
old_date = converter.num2date(julian_day)
690+
for delta_year in range(1, 101): # 100 years cover several 7-year cycles
691+
date = converter.num2date(julian_day - delta_year * 365)
692+
693+
# test that the day of the week changes by one every year (except
694+
# for wrapping around every 7 years, of course)
695+
if date.dayofwk == 6:
696+
self.assertEqual(old_date.dayofwk, 0)
697+
else:
698+
self.assertEqual(old_date.dayofwk - date.dayofwk, 1)
699+
700+
old_date = date
701+
662702
if __name__ == '__main__':
663703
unittest.main()

0 commit comments

Comments
 (0)