diff --git a/Changelog b/Changelog index 559cc525..0da1b0d6 100644 --- a/Changelog +++ b/Changelog @@ -10,9 +10,10 @@ version 1.5.1 (not yet released) * '360_day' was missing from list of 'idealized' calendars. * fixed a bug that led to subclasses losing their type identity upon pickling (issue #251, PR #252). - * Change default behavior of proleptic_gregorian to has_year_zero=T to - conform to upcoming changes in CF v1.9 (PR #238). Issue warning when trying - to create a cftime.datetime instance that is not allowed in CF. + * Change default behavior of proleptic_gregorian to has_year_zero=T + (to be consistent with ISO-8601 since CF does not specify the year zero convention + for this calendar). Issue warning when trying to + to create a cftime.datetime instance that is not allowed in CF (PR #238). version 1.5.0 (release tag v1.5.0.rel) diff --git a/README.md b/README.md index c4664097..1380aca1 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,14 @@ Time-handling functionality from netcdf4-python ## News For details on the latest updates, see the [Changelog](https://github.com/Unidata/cftime/blob/master/Changelog). +10/1/2021: Version 1.5.1 released. Changed default behavior of ``proleptic_gregorian`` +to has_year_zero=T (since it is allowed in ISO-8601 and CF does not specify the +year zero convention for this calendar). Raise warning message when trying +to create a calendar that is not supported by CF version 1.9 (no years < 1 +allowed for 'standard'/'gregorian' or 'julian' calendars). +Added support for "common_year" and "common_years" units for "noleap" +and "365_day" calendars. + 5/20/2021: Version 1.5.0 released. Includes support for astronomical year numbering (including the year zero) for real-world calendars ('julian', 'gregorian'/'standard', and 'proleptic_gregorian') using 'has_year_zero' `cftime.datetime` kwarg. diff --git a/docs/_build/doctrees/api.doctree b/docs/_build/doctrees/api.doctree index 67e79299..efedee63 100644 Binary files a/docs/_build/doctrees/api.doctree and b/docs/_build/doctrees/api.doctree differ diff --git a/docs/_build/doctrees/environment.pickle b/docs/_build/doctrees/environment.pickle index 01209583..7a700b49 100644 Binary files a/docs/_build/doctrees/environment.pickle and b/docs/_build/doctrees/environment.pickle differ diff --git a/docs/_build/doctrees/installing.doctree b/docs/_build/doctrees/installing.doctree index ca2c5c47..1bd988fe 100644 Binary files a/docs/_build/doctrees/installing.doctree and b/docs/_build/doctrees/installing.doctree differ diff --git a/docs/_build/html/api.html b/docs/_build/html/api.html index ab6c43ea..9dfeee1e 100644 --- a/docs/_build/html/api.html +++ b/docs/_build/html/api.html @@ -4,7 +4,8 @@ - + + API — cftime 1.0 documentation @@ -31,7 +32,7 @@
-
+

API

@@ -87,7 +88,8 @@

units: a string of the form <time units> since <reference time> describing the time units. <time units> can be days, hours, minutes, seconds, milliseconds or microseconds. <reference time> is the time -origin. months_since is allowed only for the 360_day calendar.

+origin. months since is allowed only for the 360_day calendar +and common_years since is allowed only for the 365_day calendar.

calendar: describes the calendar to be used in the time calculations. All the values currently defined in the CF metadata convention are supported. @@ -103,6 +105,9 @@ CF version 1.9 conventions (False for ‘julian’, ‘gregorian’/’standard’, True for ‘proleptic_gregorian’ (ISO 8601) and True for the idealized calendars ‘noleap’/’365_day’, ‘360_day’, 366_day’/’all_leap’) +Note that CF v1.9 does not specifically mention whether year zero +is allowed in the proleptic_gregorian calendar, but ISO-8601 has +a year zero so we have adopted this as the default. The defaults can only be over-ridden for the real-world calendars, for the the idealized calendars the year zero always exists and the has_year_zero kwarg is ignored. @@ -202,7 +207,7 @@

Return (integer) julian day ordinal.

Day 0 starts at noon January 1 of the year -4713 for the julian, gregorian and standard calendars (year -4712 if year -zero allowd).

+zero allowed).

Day 0 starts at noon on November 24 of the year -4714 for the proleptic gregorian calendar (year -4713 if year zero allowed).

Day 0 starts at noon on January 1 of the year zero is for the @@ -225,7 +230,8 @@

units: a string of the form <time units> since <reference time> describing the time units. <time units> can be days, hours, minutes, seconds, milliseconds or microseconds. <reference time> is the time -origin. months_since is allowed only for the 360_day calendar.

+origin. months since is allowed only for the 360_day calendar +and common_years since is allowed only for the 365_day calendar.

calendar: describes the calendar used in the time calculations. All the values currently defined in the CF metadata convention are supported. @@ -297,7 +303,7 @@ correspond to the closest times.

-
+
diff --git a/docs/_build/html/installing.html b/docs/_build/html/installing.html index d85dd985..48738b0f 100644 --- a/docs/_build/html/installing.html +++ b/docs/_build/html/installing.html @@ -4,7 +4,8 @@ - + + Installation — cftime 1.0 documentation @@ -32,16 +33,16 @@
-
+

Installation

-
+

Required dependencies

  • Python: >3.2

  • numpy (1.7 or later)

-
-
+
+

Instructions

The easiest way to get everything installed is to use conda command line tool:

$ conda install cftime
@@ -58,16 +59,16 @@ 

Instructions
$ pip install cftime
 

-
-
+
+

Developing

When developing we recommend cloning the GitHub repository, building the extension in-place with cython 0.19 or later python setup.py build_ext --inplace

and running the test suite to check if the changes are passing the tests pytest --pyargs test

-
-
+ + diff --git a/docs/_build/html/searchindex.js b/docs/_build/html/searchindex.js index 9a0e108c..400b8307 100644 --- a/docs/_build/html/searchindex.js +++ b/docs/_build/html/searchindex.js @@ -1 +1 @@ -Search.setIndex({docnames:["api","index","installing"],envversion:{"sphinx.domains.c":2,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":3,"sphinx.domains.index":1,"sphinx.domains.javascript":2,"sphinx.domains.math":2,"sphinx.domains.python":2,"sphinx.domains.rst":2,"sphinx.domains.std":1,"sphinx.ext.intersphinx":1,sphinx:56},filenames:["api.rst","index.rst","installing.rst"],objects:{"":{cftime:[0,0,0,"-"]},"cftime.datetime":{change_calendar:[0,3,1,""],fromordinal:[0,3,1,""],isoformat:[0,3,1,""],replace:[0,3,1,""],strftime:[0,3,1,""],timetuple:[0,3,1,""],toordinal:[0,3,1,""]},cftime:{date2index:[0,1,1,""],date2num:[0,1,1,""],datetime:[0,2,1,""],num2date:[0,1,1,""],num2pydate:[0,1,1,""],time2index:[0,1,1,""]}},objnames:{"0":["py","module","Python module"],"1":["py","function","Python function"],"2":["py","class","Python class"],"3":["py","method","Python method"]},objtypes:{"0":"py:module","1":"py:function","2":"py:class","3":"py:method"},terms:{"100":[],"1582":0,"1st":0,"360_dai":0,"365_dai":0,"366_dai":0,"4712":0,"4713":0,"4714":0,"8601":0,"boolean":[],"class":0,"default":[0,2],"int":0,"new":[0,2],"return":0,"static":0,"true":0,For:0,Has:0,The:[0,2],Then:2,__add__:0,__format__:0,__repr__:0,__str__:0,__sub__:0,_cftime:[],accuraci:0,after:0,all:0,all_leap:0,allow:0,allowd:0,also:2,alwai:0,api:1,appear:2,appli:0,approxim:0,arg:[],argument:0,arrai:0,associ:0,assum:0,astronom:0,attribut:0,auto:0,awar:0,base:0,befor:[0,2],behavior:0,being:2,between:0,blank:0,breakpoint:0,build:2,build_ext:2,calcul:0,calendar:0,can:0,cannot:0,cartopi:2,cfconvent:[],cftime:[0,2],chang:2,change_calendar:0,channel:2,check:2,climat:1,clone:2,closest:0,command:2,commun:2,compar:[],comparison:0,complet:0,comput:[],conda:2,conform:[0,1],contain:0,control:0,convent:[0,1],correspond:0,creat:0,ctime:0,current:0,cython:2,dai:0,date2index:0,date2num:0,date:0,datefromjuliandai:[],datetim:0,datetimeallleap:[],datetimegregorian:[],datetimejulian:[],datetimenoleap:[],datetimeprolepticgregorian:[],dayofwk:0,dayofyr:0,daysinmonth:0,decod:1,defin:0,depend:1,describ:0,determin:[],develop:1,differ:[],difficult:2,direct:0,document:0,don:2,dst:0,easiest:2,either:0,entri:0,equival:0,error:0,even:0,everyth:2,exact:0,exist:0,explicit:0,extens:2,fall:[],fals:0,field:0,file:1,first:[0,2],flag:0,follow:[],forecast:1,forg:2,form:0,format:0,found:0,fraction:0,from:0,fromordin:0,get:2,github:2,given:0,gregorian:0,has_year_zero:0,have:[0,2],histor:0,hour:0,http:[],ideal:0,ignor:0,includ:0,increas:0,index:[0,1],indic:0,inplac:2,input:0,instal:1,instanc:0,instruct:1,integ:0,invers:0,iso:0,isoformat:0,its:0,januari:0,jdai:0,julian:0,juliandayfromd:[],just:0,keyword:0,kwarg:0,last:[],later:2,librari:1,like:[],line:2,list:0,localtim:0,mai:2,maintain:2,match:0,mean:0,metadata:0,method:0,microsecond:0,millisecond:0,mimic:0,minut:0,mix:0,modul:1,month:0,months_sinc:0,must:0,nativ:0,nctime:0,nearest:0,need:[0,2],netcdf:[0,1],no_leap:[],noleap:0,none:0,noon:0,note:0,novemb:0,num2dat:0,num2pyd:0,number:0,numer:0,numpi:2,object:0,offset:0,one:0,onli:0,only_use_cftime_datetim:0,only_use_python_datetim:0,oper:0,option:0,order:0,ordin:0,org:[],origin:0,other:0,otherwis:0,over:0,overload:0,page:1,part:0,pass:2,perfectli:0,phoni:[],pip:2,place:2,possibl:0,preced:0,produc:0,prolept:0,proleptic_gregorian:0,pyarg:2,pynio:2,pytest:2,python:[0,1,2],rais:0,real:0,recommend:2,refer:0,releas:2,replac:0,repositori:2,repres:0,requir:1,return_tupl:[],ridden:0,rist:[],run:2,same:0,search:1,second:0,section:0,see:0,select:0,self:0,sep:0,sequenc:0,set:0,setup:2,should:0,sinc:0,some:0,specif:0,specifi:0,standard:0,start:0,store:0,strftime:0,string:0,strptime:0,struct_tim:0,subclass:0,subtract:[],suit:2,support:0,sure:2,synonym:0,system:0,test:2,than:0,thei:0,thi:0,time2index:0,time:[0,1],timedelta:0,timespec:0,timetupl:0,tool:2,toordin:0,unit:[0,1],unless:0,updat:2,use:[0,2],use_only_python_datetim:0,used:0,uses:[],using:[0,2],utc:0,valid:0,valu:[0,1],variabl:[0,1],versa:[],version:0,vice:[],wai:2,weekdai:0,when:2,where:0,whether:[],which:0,within:0,work:0,world:0,ydai:0,year:0,you:2,zero:0,zone:0},titles:["API","cftime","Installation"],titleterms:{api:0,cftime:1,content:1,depend:2,develop:2,indic:1,instal:2,instruct:2,requir:2,tabl:1}}) \ No newline at end of file +Search.setIndex({docnames:["api","index","installing"],envversion:{"sphinx.domains.c":2,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":3,"sphinx.domains.index":1,"sphinx.domains.javascript":2,"sphinx.domains.math":2,"sphinx.domains.python":2,"sphinx.domains.rst":2,"sphinx.domains.std":1,"sphinx.ext.intersphinx":1,sphinx:56},filenames:["api.rst","index.rst","installing.rst"],objects:{"":{cftime:[0,0,0,"-"]},"cftime.datetime":{change_calendar:[0,3,1,""],fromordinal:[0,3,1,""],isoformat:[0,3,1,""],replace:[0,3,1,""],strftime:[0,3,1,""],timetuple:[0,3,1,""],toordinal:[0,3,1,""]},cftime:{date2index:[0,1,1,""],date2num:[0,1,1,""],datetime:[0,2,1,""],num2date:[0,1,1,""],num2pydate:[0,1,1,""],time2index:[0,1,1,""]}},objnames:{"0":["py","module","Python module"],"1":["py","function","Python function"],"2":["py","class","Python class"],"3":["py","method","Python method"]},objtypes:{"0":"py:module","1":"py:function","2":"py:class","3":"py:method"},terms:{"100":[],"1582":0,"1st":0,"360_dai":0,"365_dai":0,"366_dai":0,"4712":0,"4713":0,"4714":0,"8601":0,"boolean":[],"class":0,"default":[0,2],"int":0,"new":[0,2],"return":0,"static":0,"true":0,For:0,Has:0,The:[0,2],Then:2,__add__:0,__format__:0,__repr__:0,__str__:0,__sub__:0,_cftime:[],accuraci:0,adopt:0,after:0,all:0,all_leap:0,allow:0,allowd:[],also:2,alwai:0,api:1,appear:2,appli:0,approxim:0,arg:[],argument:0,arrai:0,associ:0,assum:0,astronom:0,attribut:0,auto:0,awar:0,base:0,befor:[0,2],behavior:0,being:2,between:0,blank:0,breakpoint:0,build:2,build_ext:2,calcul:0,calendar:0,can:0,cannot:0,cartopi:2,cfconvent:[],cftime:[0,2],chang:2,change_calendar:0,channel:2,check:2,climat:1,clone:2,closest:0,command:2,common_year:0,commun:2,compar:[],comparison:0,complet:0,comput:[],conda:2,conform:[0,1],contain:0,control:0,convent:[0,1],correspond:0,creat:0,ctime:0,current:0,cython:2,dai:0,date2index:0,date2num:0,date:0,datefromjuliandai:[],datetim:0,datetimeallleap:[],datetimegregorian:[],datetimejulian:[],datetimenoleap:[],datetimeprolepticgregorian:[],dayofwk:0,dayofyr:0,daysinmonth:0,decod:1,defin:0,depend:1,describ:0,determin:[],develop:1,differ:[],difficult:2,direct:0,document:0,doe:0,don:2,dst:0,easiest:2,either:0,entri:0,equival:0,error:0,even:0,everyth:2,exact:0,exist:0,explicit:0,extens:2,fall:[],fals:0,field:0,file:1,first:[0,2],flag:0,follow:[],forecast:1,forg:2,form:0,format:0,found:0,fraction:0,from:0,fromordin:0,get:2,github:2,given:0,gregorian:0,has:0,has_year_zero:0,have:[0,2],histor:0,hour:0,http:[],ideal:0,ignor:0,includ:0,increas:0,index:[0,1],indic:0,inplac:2,input:0,instal:1,instanc:0,instruct:1,integ:0,invers:0,iso:0,isoformat:0,its:0,januari:0,jdai:0,julian:0,juliandayfromd:[],just:0,keyword:0,kwarg:0,last:[],later:2,librari:1,like:[],line:2,list:0,localtim:0,mai:2,maintain:2,match:0,mean:0,mention:0,metadata:0,method:0,microsecond:0,millisecond:0,mimic:0,minut:0,mix:0,modul:1,month:0,months_sinc:[],must:0,nativ:0,nctime:0,nearest:0,need:[0,2],netcdf:[0,1],no_leap:[],noleap:0,none:0,noon:0,note:0,novemb:0,num2dat:0,num2pyd:0,number:0,numer:0,numpi:2,object:0,offset:0,one:0,onli:0,only_use_cftime_datetim:0,only_use_python_datetim:0,oper:0,option:0,order:0,ordin:0,org:[],origin:0,other:0,otherwis:0,over:0,overload:0,page:1,part:0,pass:2,perfectli:0,phoni:[],pip:2,place:2,possibl:0,preced:0,produc:0,prolept:0,proleptic_gregorian:0,pyarg:2,pynio:2,pytest:2,python:[0,1,2],rais:0,real:0,recommend:2,refer:0,releas:2,replac:0,repositori:2,repres:0,requir:1,return_tupl:[],ridden:0,rist:[],run:2,same:0,search:1,second:0,section:0,see:0,select:0,self:0,sep:0,sequenc:0,set:0,setup:2,should:0,sinc:0,some:0,specif:0,specifi:0,standard:0,start:0,store:0,strftime:0,string:0,strptime:0,struct_tim:0,subclass:0,subtract:[],suit:2,support:0,sure:2,synonym:0,system:0,test:2,than:0,thei:0,thi:0,time2index:0,time:[0,1],timedelta:0,timespec:0,timetupl:0,tool:2,toordin:0,unit:[0,1],unless:0,updat:2,use:[0,2],use_only_python_datetim:0,used:0,uses:[],using:[0,2],utc:0,valid:0,valu:[0,1],variabl:[0,1],versa:[],version:0,vice:[],wai:2,weekdai:0,when:2,where:0,whether:0,which:0,within:0,work:0,world:0,ydai:0,year:0,you:2,zero:0,zone:0},titles:["API","cftime","Installation"],titleterms:{api:0,cftime:1,content:1,depend:2,develop:2,indic:1,instal:2,instruct:2,requir:2,tabl:1}}) \ No newline at end of file diff --git a/src/cftime/_cftime.pyx b/src/cftime/_cftime.pyx index cd1df3bf..88d95d3d 100644 --- a/src/cftime/_cftime.pyx +++ b/src/cftime/_cftime.pyx @@ -163,6 +163,9 @@ def date2num(dates,units,calendar=None,has_year_zero=None): CF version 1.9 conventions (False for 'julian', 'gregorian'/'standard', True for 'proleptic_gregorian' (ISO 8601) and True for the idealized calendars 'noleap'/'365_day', '360_day', 366_day'/'all_leap') + Note that CF v1.9 does not specifically mention whether year zero + is allowed in the proleptic_gregorian calendar, but ISO-8601 has + a year zero so we have adopted this as the default. The defaults can only be over-ridden for the real-world calendars, for the the idealized calendars the year zero always exists and the has_year_zero kwarg is ignored. @@ -1029,9 +1032,12 @@ The default format of the string produced by strftime is controlled by self.form has_year_zero=True else: has_year_zero = _year_zero_defaults(calendar) - # warn if requested date not allowed by CF. - if (calendar in ['julian','gregorian','standard'] and year <= 0) or\ - (calendar == 'proleptic_gregorian' and not has_year_zero and year < 1): + # warn if requested date not allowed by CF + # (no years < 1 in mixed Julian/Gregorian calendar). + # CF version 1.9 does not specify whether a year zero should exist + # for the proleptic_gregorian calendar. IS0 8601 uses proleptic_gregorian + # and has a year zero, so for now this is the default in cftime. + if calendar in ['julian','gregorian','standard'] and year <= 0: warnings.warn(cfwarnmsg,category=CFWarning) # raise exception if year zero requested but has_year_zero set # to False (issue #248). diff --git a/test/test_cftime.py b/test/test_cftime.py index 22a689ff..c5de9406 100644 --- a/test/test_cftime.py +++ b/test/test_cftime.py @@ -1398,67 +1398,6 @@ def not_comparable_5(): for func in [not_comparable_1, not_comparable_2, not_comparable_3, not_comparable_4, not_comparable_5]: self.assertRaises(TypeError, func) - @pytest.mark.skipif(sys.version_info.major != 2, - reason='python2 specific, non-comparable test') - def test_richcmp_py2(self): - class Rich(object): - """Dummy class with traditional rich comparison support.""" - def __lt__(self, other): - raise NotImplementedError('__lt__') - def __le__(self, other): - raise NotImplementedError('__le__') - def __eq__(self, other): - raise NotImplementedError('__eq__') - def __ne__(self, other): - raise NotImplementedError('__ne__') - def __gt__(self, other): - raise NotImplementedError('__gt__') - def __ge__(self, other): - raise NotImplementedError('__ge__') - - class CythonRich(object): - """Dummy class with spoof cython rich comparison support.""" - def __richcmp__(self, other): - """ - This method is never called. However it is introspected - by the cftime.datetime.__richcmp__ method, which will then - return NotImplemented, causing Python to call this classes - __cmp__ method as a back-stop, and hence spoofing the - cython specific rich comparison behaviour. - """ - pass - def __cmp__(self, other): - raise NotImplementedError('__richcmp__') - - class Pass(object): - """Dummy class with no rich comparison support whatsoever.""" - pass - - class Pass___cmp__(object): - """Dummy class that delegates all comparisons.""" - def __cmp__(self, other): - return NotImplemented - - # Test LHS operand comparison operator processing. - for op, expected in [(operator.gt, '__lt__'), (operator.ge, '__le__'), - (operator.eq, '__eq__'), (operator.ne, '__ne__'), - (operator.lt, '__gt__'), (operator.le, '__ge__')]: - with self.assertRaisesRegex(NotImplementedError, expected): - op(self.date1_365_day, Rich()) - - with self.assertRaisesRegex(NotImplementedError, '__richcmp__'): - op(self.date1_365_day, CythonRich()) - - # Test RHS operand comparison operator processing. - for op in [operator.gt, operator.ge, operator.eq, operator.ne, - operator.lt, operator.le]: - with self.assertRaisesRegex(TypeError, 'cannot compare'): - op(Pass(), self.date1_365_day) - - with self.assertRaisesRegex(TypeError, 'cannot compare'): - op(Pass___cmp__(), self.date1_365_day) - - class issue17TestCase(unittest.TestCase): """Regression tests for issue #17/#669.""" # issue 17 / 699: timezone formats not supported correctly