Hello community,

here is the log from the commit of package python-croniter for openSUSE:Factory 
checked in at 2017-08-28 15:15:57
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-croniter (Old)
 and      /work/SRC/openSUSE:Factory/.python-croniter.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-croniter"

Mon Aug 28 15:15:57 2017 rev:6 rq:518656 version:0.3.17

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-croniter/python-croniter.changes  
2016-12-08 00:29:35.000000000 +0100
+++ /work/SRC/openSUSE:Factory/.python-croniter.new/python-croniter.changes     
2017-08-28 15:17:19.223417956 +0200
@@ -1,0 +2,19 @@
+Fri Aug 25 07:05:44 UTC 2017 - [email protected]
+
+- update to 0.3.17:
+  - DOW occurence sharp style support.
+  - Better test suite
+  - DST support
+  - fix bug around multiple conditions and range_val in
+    _get_prev_nearest_diff.
+  - issue #69: added day_or option to change behavior when day-of-month and
+    day-of-week is given
+  - `Real fix for #34
+  - `Modernize test infra
+  - `Release as a universal wheel
+  - `Raise ValueError on negative numbers
+  - `Compare types using "issubclass" instead of exact match
+  - `Implement step cron with a variable base
+- convert to singlespec
+
+-------------------------------------------------------------------

Old:
----
  croniter-0.3.12.tar.gz

New:
----
  croniter-0.3.17.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-croniter.spec ++++++
--- /var/tmp/diff_new_pack.BVEWaj/_old  2017-08-28 15:17:22.171003769 +0200
+++ /var/tmp/diff_new_pack.BVEWaj/_new  2017-08-28 15:17:22.171003769 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package python-croniter
 #
-# Copyright (c) 2016 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany.
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -16,28 +16,28 @@
 #
 
 
+%{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           python-croniter
-Version:        0.3.12
+Version:        0.3.17
 Release:        0
 Summary:        Croniter provides iteration for datetime object with cron like 
format
 License:        MIT
 Group:          Development/Languages/Python
 Url:            http://github.com/kiorky/croniter
-Source:         
https://pypi.io/packages/source/c/croniter/croniter-%{version}.tar.gz
-BuildRequires:  python-devel
-BuildRequires:  python-setuptools
+Source:         
https://files.pythonhosted.org/packages/source/c/croniter/croniter-%{version}.tar.gz
+BuildRequires:  %{python_module devel}
+BuildRequires:  %{python_module setuptools}
+BuildRequires:  fdupes
+BuildRequires:  python-rpm-macros
 BuildRequires:  unzip
 # Test requirements:
-BuildRequires:  python-nose
-BuildRequires:  python-python-dateutil
-BuildRequires:  python-pytz
+BuildRequires:  %{python_module nose}
+BuildRequires:  %{python_module python-dateutil}
+BuildRequires:  %{python_module pytz}
 Requires:       python-python-dateutil
-BuildRoot:      %{_tmppath}/%{name}-%{version}-build
-%if 0%{?suse_version} && 0%{?suse_version} <= 1110
-%{!?python_sitelib: %global python_sitelib %(python -c "from 
distutils.sysconfig import get_python_lib; print get_python_lib()")}
-%else
 BuildArch:      noarch
-%endif
+
+%python_subpackages
 
 %description
 croniter provides iteration for datetime object with cron like format.
@@ -46,15 +46,16 @@
 %setup -q -n croniter-%{version}
 
 %build
-python setup.py build
+%python_build
 
 %install
-python setup.py install --prefix=%{_prefix} --root=%{buildroot}
+%python_install
+%python_expand %fdupes %{buildroot}%{$python_sitelib}
 
 %check
-nosetests
+%python_exec %{_bindir}/nosetests
 
-%files
+%files %{python_files}
 %defattr(-,root,root,-)
 %doc README.rst docs/LICENSE
 %{python_sitelib}/*

++++++ croniter-0.3.12.tar.gz -> croniter-0.3.17.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/croniter-0.3.12/MANIFEST.in 
new/croniter-0.3.17/MANIFEST.in
--- old/croniter-0.3.12/MANIFEST.in     2016-03-10 21:31:22.000000000 +0100
+++ new/croniter-0.3.17/MANIFEST.in     2017-05-22 12:11:46.000000000 +0200
@@ -1,6 +1,7 @@
-include *.txt *.cfg *.rst
+include *.txt *.cfg *.rst *.ini
 
 recursive-include docs *
+recursive-include requirements *
 recursive-include src *
 
 global-exclude *pyc
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/croniter-0.3.12/PKG-INFO new/croniter-0.3.17/PKG-INFO
--- old/croniter-0.3.12/PKG-INFO        2016-03-10 21:31:22.000000000 +0100
+++ new/croniter-0.3.17/PKG-INFO        2017-05-22 12:11:46.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: croniter
-Version: 0.3.12
+Version: 0.3.17
 Summary: croniter provides iteration for datetime object with cron like format
 Home-page: http://github.com/kiorky/croniter
 Author: Matsumoto Taichi, kiorky
@@ -12,7 +12,7 @@
         .. contents::
         
         
-        croniter provides iteration for datetime object with cron like format.
+        croniter provides iteration for the datetime object with a cron like 
format.
         
         ::
         
@@ -33,12 +33,12 @@
         Usage
         ============
         
-        Simple example of usage is followings::
+        A simple example::
         
             >>> from croniter import croniter
             >>> from datetime import datetime
             >>> base = datetime(2010, 1, 25, 4, 46)
-            >>> iter = croniter('*/5 * * * *', base)  # every 5 minites
+            >>> iter = croniter('*/5 * * * *', base)  # every 5 minutes
             >>> print iter.get_next(datetime)   # 2010-01-25 04:50:00
             >>> print iter.get_next(datetime)   # 2010-01-25 04:55:00
             >>> print iter.get_next(datetime)   # 2010-01-25 05:00:00
@@ -47,21 +47,42 @@
             >>> print iter.get_next(datetime)   # 2010-01-26 04:02:00
             >>> print iter.get_next(datetime)   # 2010-01-30 04:02:00
             >>> print iter.get_next(datetime)   # 2010-02-02 04:02:00
-        
-        All you need to know is constructor and get_next, these signature are 
following::
-        
-            >>> def __init__(self, cron_format, start_time=time.time())
-        
-        croniter iterate along with 'cron_format' from 'start_time'.
-        cron_format is 'min hour day month day_of_week', and please refer to
-        http://en.wikipedia.org/wiki/Cron for details.::
+            >>>
+            >>> iter = croniter('2 4 1 * wed', base)  # 04:02 on every 
Wednesday OR on 1st day of month
+            >>> print iter.get_next(datetime)   # 2010-01-27 04:02:00
+            >>> print iter.get_next(datetime)   # 2010-02-01 04:02:00
+            >>> print iter.get_next(datetime)   # 2010-02-03 04:02:00
+            >>>
+            >>> iter = croniter('2 4 1 * wed', base, day_or=False)  # 04:02 on 
every 1st day of the month if it is a Wednesday
+            >>> print iter.get_next(datetime)   # 2010-09-01 04:02:00
+            >>> print iter.get_next(datetime)   # 2010-12-01 04:02:00
+            >>> print iter.get_next(datetime)   # 2011-06-01 04:02:00
+            >>> iter = croniter('0 0 * * sat#1,sun#2', base)
+            >>> print iter.get_next(datetime)   # datetime.datetime(2010, 2, 
6, 0, 0)
+        
+        All you need to know is how to use the constructor and the ``get_next``
+        method, the signature of these methods are listed below::
+        
+            >>> def __init__(self, cron_format, start_time=time.time(), 
day_or=True)
+        
+        croniter iterates along with ``cron_format`` from ``start_time``.
+        ``cron_format`` is **min hour day month day_of_week**, you can refer to
+        http://en.wikipedia.org/wiki/Cron for more details. The ``day_or``
+        switch is used to control how croniter handles **day** and 
**day_of_week**
+        entries. Default option is the cron behaviour, which connects those
+        values using **OR**. If the switch is set to False, the values are 
connected
+        using **AND**. This behaves like fcron and enables you to e.g. define 
a job that
+        executes each 2nd friday of a month by setting the days of month and 
the
+        weekday.
+        ::
         
             >>> def get_next(self, ret_type=float)
         
-        get_next return next time in iteration with 'ret_type'.
-        And ret_type accept only 'float' or 'datetime'.
+        get_next calculates the next value according to the cron expression and
+        returns an object of type ``ret_type``. ``ret_type`` should be a 
``float`` or a
+        ``datetime`` object.
         
-        Now, supported get_prev method. (>= 0.2.0)::
+        Supported added for ``get_prev`` method. (>= 0.2.0)::
         
             >>> base = datetime(2010, 8, 25)
             >>> itr = croniter('0 0 1 * *', base)
@@ -69,6 +90,12 @@
             >>> print itr.get_prev(datetime)  # 2010-07-01 00:00:00
             >>> print itr.get_prev(datetime)  # 2010-06-01 00:00:00
         
+        About DST
+        =========
+        Be sure to init your croniter instance with a TZ aware datetime for 
this to work !::
+        
+            >>> local_date = tz.localize(datetime(2017, 3, 26))
+            >>> val = croniter('0 0 * * *', local_date).get_next(datetime)
         
         Develop this package
         ====================
@@ -77,25 +104,28 @@
         
             git clone https://github.com/kiorky/croniter.git
             cd croniter
-            python bootstrap.py -d
-            bin/buildout -vvvvvvN
-            bin/test
+            virtualenv --no-site-packages venv
+            . venv/bin/activate
+            pip install --upgrade -r requirements/test.txt
+            py.test src
         
         
         Make a new release
         ====================
-        We use zest.fullreleaser, a great releaser infrastructure.
+        We use zest.fullreleaser, a great release infrastructure.
         
-        Do and follow the instructions
+        Do and follow these instructions
         ::
         
-            bin/fullrelease
+            . venv/bin/activate
+            pip install --upgrade -r requirements/release.txt
+            fullrelease
         
         
         Contributors
         ===============
-        Thank you to all who have contributed to this project!
-        If you contributed and not listed below please let me know.
+        Thanks to all who have contributed to this project!
+        If you have contributed and your name is not listed below please let 
me know.
         
             - mrmachine
             - Hinnack
@@ -114,6 +144,50 @@
         Changelog
         ==============
         
+        0.3.17 (2017-05-22)
+        -------------------
+        - DOW occurence sharp style support.
+          [kiorky, Kengo Seki <[email protected]>]
+        
+        
+        0.3.16 (2017-03-15)
+        -------------------
+        
+        - Better test suite [mrcrilly@github]
+        - DST support [kiorky]
+        
+        0.3.15 (2017-02-16)
+        -------------------
+        
+        - fix bug around multiple conditions and range_val in
+          _get_prev_nearest_diff.
+          [abeja-yuki@github]
+        
+        0.3.14 (2017-01-25)
+        -------------------
+        
+        - issue #69: added day_or option to change behavior when day-of-month 
and
+          day-of-week is given
+          [Andreas Vogl <[email protected]>]
+        
+        
+        
+        0.3.13 (2016-11-01)
+        -------------------
+        
+        - `Real fix for #34 <https://github.com/taichino/croniter/pull/73>`_
+          [kiorky@github]
+        - `Modernize test infra 
<https://github.com/taichino/croniter/pull/72>`_
+          [kiorky@github]
+        - `Release as a universal wheel 
<https://github.com/kiorky/croniter/pull/16>`_
+          [adamchainz@github]
+        - `Raise ValueError on negative numbers 
<https://github.com/taichino/croniter/pull/63>`_
+          [josegonzalez@github]
+        - `Compare types using "issubclass" instead of exact match 
<https://github.com/taichino/croniter/pull/70>`_
+          [darkk@github]
+        - `Implement step cron with a variable base 
<https://github.com/taichino/croniter/pull/60>`_
+          [josegonzalez@github]
+        
         0.3.12 (2016-03-10)
         -------------------
         - support setting ret_type in __init__ [Brent Tubbs 
<[email protected]>]
@@ -185,4 +259,6 @@
 Classifier: License :: OSI Approved :: MIT License
 Classifier: Operating System :: POSIX
 Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 3
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/croniter-0.3.12/README.rst 
new/croniter-0.3.17/README.rst
--- old/croniter-0.3.12/README.rst      2016-03-10 21:31:22.000000000 +0100
+++ new/croniter-0.3.17/README.rst      2017-05-22 12:11:46.000000000 +0200
@@ -4,7 +4,7 @@
 .. contents::
 
 
-croniter provides iteration for datetime object with cron like format.
+croniter provides iteration for the datetime object with a cron like format.
 
 ::
 
@@ -25,12 +25,12 @@
 Usage
 ============
 
-Simple example of usage is followings::
+A simple example::
 
     >>> from croniter import croniter
     >>> from datetime import datetime
     >>> base = datetime(2010, 1, 25, 4, 46)
-    >>> iter = croniter('*/5 * * * *', base)  # every 5 minites
+    >>> iter = croniter('*/5 * * * *', base)  # every 5 minutes
     >>> print iter.get_next(datetime)   # 2010-01-25 04:50:00
     >>> print iter.get_next(datetime)   # 2010-01-25 04:55:00
     >>> print iter.get_next(datetime)   # 2010-01-25 05:00:00
@@ -39,21 +39,42 @@
     >>> print iter.get_next(datetime)   # 2010-01-26 04:02:00
     >>> print iter.get_next(datetime)   # 2010-01-30 04:02:00
     >>> print iter.get_next(datetime)   # 2010-02-02 04:02:00
-
-All you need to know is constructor and get_next, these signature are 
following::
-
-    >>> def __init__(self, cron_format, start_time=time.time())
-
-croniter iterate along with 'cron_format' from 'start_time'.
-cron_format is 'min hour day month day_of_week', and please refer to
-http://en.wikipedia.org/wiki/Cron for details.::
+    >>>
+    >>> iter = croniter('2 4 1 * wed', base)  # 04:02 on every Wednesday OR on 
1st day of month
+    >>> print iter.get_next(datetime)   # 2010-01-27 04:02:00
+    >>> print iter.get_next(datetime)   # 2010-02-01 04:02:00
+    >>> print iter.get_next(datetime)   # 2010-02-03 04:02:00
+    >>>
+    >>> iter = croniter('2 4 1 * wed', base, day_or=False)  # 04:02 on every 
1st day of the month if it is a Wednesday
+    >>> print iter.get_next(datetime)   # 2010-09-01 04:02:00
+    >>> print iter.get_next(datetime)   # 2010-12-01 04:02:00
+    >>> print iter.get_next(datetime)   # 2011-06-01 04:02:00
+    >>> iter = croniter('0 0 * * sat#1,sun#2', base)
+    >>> print iter.get_next(datetime)   # datetime.datetime(2010, 2, 6, 0, 0)
+
+All you need to know is how to use the constructor and the ``get_next``
+method, the signature of these methods are listed below::
+
+    >>> def __init__(self, cron_format, start_time=time.time(), day_or=True)
+
+croniter iterates along with ``cron_format`` from ``start_time``.
+``cron_format`` is **min hour day month day_of_week**, you can refer to
+http://en.wikipedia.org/wiki/Cron for more details. The ``day_or``
+switch is used to control how croniter handles **day** and **day_of_week**
+entries. Default option is the cron behaviour, which connects those
+values using **OR**. If the switch is set to False, the values are connected
+using **AND**. This behaves like fcron and enables you to e.g. define a job 
that
+executes each 2nd friday of a month by setting the days of month and the
+weekday.
+::
 
     >>> def get_next(self, ret_type=float)
 
-get_next return next time in iteration with 'ret_type'.
-And ret_type accept only 'float' or 'datetime'.
+get_next calculates the next value according to the cron expression and
+returns an object of type ``ret_type``. ``ret_type`` should be a ``float`` or a
+``datetime`` object.
 
-Now, supported get_prev method. (>= 0.2.0)::
+Supported added for ``get_prev`` method. (>= 0.2.0)::
 
     >>> base = datetime(2010, 8, 25)
     >>> itr = croniter('0 0 1 * *', base)
@@ -61,6 +82,12 @@
     >>> print itr.get_prev(datetime)  # 2010-07-01 00:00:00
     >>> print itr.get_prev(datetime)  # 2010-06-01 00:00:00
 
+About DST
+=========
+Be sure to init your croniter instance with a TZ aware datetime for this to 
work !::
+
+    >>> local_date = tz.localize(datetime(2017, 3, 26))
+    >>> val = croniter('0 0 * * *', local_date).get_next(datetime)
 
 Develop this package
 ====================
@@ -69,25 +96,28 @@
 
     git clone https://github.com/kiorky/croniter.git
     cd croniter
-    python bootstrap.py -d
-    bin/buildout -vvvvvvN
-    bin/test
+    virtualenv --no-site-packages venv
+    . venv/bin/activate
+    pip install --upgrade -r requirements/test.txt
+    py.test src
 
 
 Make a new release
 ====================
-We use zest.fullreleaser, a great releaser infrastructure.
+We use zest.fullreleaser, a great release infrastructure.
 
-Do and follow the instructions
+Do and follow these instructions
 ::
 
-    bin/fullrelease
+    . venv/bin/activate
+    pip install --upgrade -r requirements/release.txt
+    fullrelease
 
 
 Contributors
 ===============
-Thank you to all who have contributed to this project!
-If you contributed and not listed below please let me know.
+Thanks to all who have contributed to this project!
+If you have contributed and your name is not listed below please let me know.
 
     - mrmachine
     - Hinnack
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/croniter-0.3.12/buildout.cfg 
new/croniter-0.3.17/buildout.cfg
--- old/croniter-0.3.12/buildout.cfg    2016-03-10 21:31:22.000000000 +0100
+++ new/croniter-0.3.17/buildout.cfg    1970-01-01 01:00:00.000000000 +0100
@@ -1,118 +0,0 @@
-[buildout]
-package-name = croniter
-develop = .
-versions=versions
-parts =
-    scripts
-    omelette
-    coverage
-    report
-    test
-    report-xml
-    code-analysis
-extensions =
-    mr.developer
-auto-checkout =
-test-eggs= ${buildout:package-name} [test]
-eggs = ${buildout:package-name}
-
-[sources]
-
-[code-analysis]
-recipe = plone.recipe.codeanalysis
-directory = ${buildout:directory}/src
-flake8-ignore=C901
-
-[scripts]
-recipe=zc.recipe.egg
-eggs = ${buildout:eggs}
-    zest.releaser
-    ipython
-    tox
-    nose
-interpreter = scripts
-
-[test]
-recipe=zc.recipe.egg
-eggs = ${scripts:eggs}
-        croniter[test]
-arguments= ['croniter']
-scripts=test
-entry-points=
-    test=nose:run_exit
-
-[coverage]
-recipe = zc.recipe.egg
-eggs = coverage
-initialization =
-include = '--source=${buildout:directory}/src'
-sys.argv = sys.argv[:] + ['run', include, 'bin/test', '--xml']
-
-[report]
-recipe = zc.recipe.egg
-eggs = coverage
-scripts = coverage=report
-initialization =
-sys.argv = sys.argv[:] + ['html', '-i']
-
-[report-xml]
-recipe = zc.recipe.egg
-eggs = coverage
-scripts = coverage=report-xml
-initialization =
-sys.argv = sys.argv[:] + ['xml', '-i']
-
-[omelette]
-recipe = collective.recipe.omelette
-eggs = ${scripts:eggs}
-
-[versions]
-pytz = 2013.9
-python-dateutil = 2.2
-
-# buildout & test infra
-zc.buildout = 2.2.1
-zc.recipe.egg = 2.0.1
-buildout-versions = 1.7
-collective.recipe.omelette = 0.16
-coverage = 3.7.1
-ipython = 1.1.0
-mr.developer = 1.28
-plone.testing = 4.0.9
-zc.recipe.testrunner = 2.0.0
-zest.releaser = 3.50
-six = 1.5.2
-unittest2 = 0.5.1
-zope.exceptions = 4.0.6
-zope.interface = 4.0.5
-zope.testing = 4.1.2
-zope.testrunner = 4.4.1
-zope.contentprovider = 4.0.0a1
-zope.event = 4.0.2
-zope.i18n = 4.0.0a4
-zope.pagetemplate = 4.0.4
-zope.proxy = 4.1.3
-zope.publisher = 4.0.0a4
-zope.schema = 4.4.0
-zope.security = 4.0.0
-zope.tales = 4.0.2
-zope.traversing = 4.0.0a3
-zptlint = 0.2.4
-mccabe = 0.2.1
-pep8 = 1.4.6
-plone.recipe.codeanalysis = 1.0b6
-pyflakes = 0.7.3
-zope.contenttype = 4.0.1
-zope.i18nmessageid = 4.0.2
-zope.location = 4.0.2
-zope.tal = 4.0.0
-Unidecode = 0.04.14
-flake8 = 2.1.0
-i18ndude = 3.3.3
-ordereddict = 1.1
-plone.i18n = 2.0.9
-transaction = 1.4.1
-zope.browser = 2.0.2
-zope.component = 4.1.0
-zope.configuration = 4.0.2
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/croniter-0.3.12/docs/CHANGES.rst 
new/croniter-0.3.17/docs/CHANGES.rst
--- old/croniter-0.3.12/docs/CHANGES.rst        2016-03-10 21:31:22.000000000 
+0100
+++ new/croniter-0.3.17/docs/CHANGES.rst        2017-05-22 12:11:46.000000000 
+0200
@@ -1,6 +1,50 @@
 Changelog
 ==============
 
+0.3.17 (2017-05-22)
+-------------------
+- DOW occurence sharp style support.
+  [kiorky, Kengo Seki <[email protected]>]
+
+
+0.3.16 (2017-03-15)
+-------------------
+
+- Better test suite [mrcrilly@github]
+- DST support [kiorky]
+
+0.3.15 (2017-02-16)
+-------------------
+
+- fix bug around multiple conditions and range_val in
+  _get_prev_nearest_diff.
+  [abeja-yuki@github]
+
+0.3.14 (2017-01-25)
+-------------------
+
+- issue #69: added day_or option to change behavior when day-of-month and
+  day-of-week is given
+  [Andreas Vogl <[email protected]>]
+
+
+
+0.3.13 (2016-11-01)
+-------------------
+
+- `Real fix for #34 <https://github.com/taichino/croniter/pull/73>`_
+  [kiorky@github]
+- `Modernize test infra <https://github.com/taichino/croniter/pull/72>`_
+  [kiorky@github]
+- `Release as a universal wheel <https://github.com/kiorky/croniter/pull/16>`_
+  [adamchainz@github]
+- `Raise ValueError on negative numbers 
<https://github.com/taichino/croniter/pull/63>`_
+  [josegonzalez@github]
+- `Compare types using "issubclass" instead of exact match 
<https://github.com/taichino/croniter/pull/70>`_
+  [darkk@github]
+- `Implement step cron with a variable base 
<https://github.com/taichino/croniter/pull/60>`_
+  [josegonzalez@github]
+
 0.3.12 (2016-03-10)
 -------------------
 - support setting ret_type in __init__ [Brent Tubbs <[email protected]>]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/croniter-0.3.12/py3.cfg new/croniter-0.3.17/py3.cfg
--- old/croniter-0.3.12/py3.cfg 2016-03-10 21:31:22.000000000 +0100
+++ new/croniter-0.3.17/py3.cfg 1970-01-01 01:00:00.000000000 +0100
@@ -1,9 +0,0 @@
-[buildout]
-extends=buildout.cfg
-parts-=
-    omelette
-    code-analysis
-[scripts]
-eggs-=
-    ipython
-    zest.releaser
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/croniter-0.3.12/requirements/base.txt 
new/croniter-0.3.17/requirements/base.txt
--- old/croniter-0.3.12/requirements/base.txt   1970-01-01 01:00:00.000000000 
+0100
+++ new/croniter-0.3.17/requirements/base.txt   2017-05-22 12:11:46.000000000 
+0200
@@ -0,0 +1,2 @@
+python_dateutil
+-e .
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/croniter-0.3.12/requirements/release.txt 
new/croniter-0.3.17/requirements/release.txt
--- old/croniter-0.3.12/requirements/release.txt        1970-01-01 
01:00:00.000000000 +0100
+++ new/croniter-0.3.17/requirements/release.txt        2017-05-22 
12:11:46.000000000 +0200
@@ -0,0 +1,2 @@
+-r base.txt
+zest.releaser[recommended]>=6.7
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/croniter-0.3.12/requirements/test.txt 
new/croniter-0.3.17/requirements/test.txt
--- old/croniter-0.3.12/requirements/test.txt   1970-01-01 01:00:00.000000000 
+0100
+++ new/croniter-0.3.17/requirements/test.txt   2017-05-22 12:11:46.000000000 
+0200
@@ -0,0 +1,8 @@
+-r base.txt
+pytz
+pytest>=3.0.3
+tox>=2.4.1
+coverage>=4.2
+mock>=2.0.0  # For Python 2
+coveralls
+flake8
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/croniter-0.3.12/setup.cfg 
new/croniter-0.3.17/setup.cfg
--- old/croniter-0.3.12/setup.cfg       2016-03-10 21:31:22.000000000 +0100
+++ new/croniter-0.3.17/setup.cfg       2017-05-22 12:11:46.000000000 +0200
@@ -1,3 +1,21 @@
+[bdist_wheel]
+universal = 1
+
+[coverage:run]
+source = .
+branch = True
+omit = 
+       .tox/*
+       setup.py
+       **/**/tests*
+       **/**/test_*.py
+
+[coverage:report]
+exclude_lines = 
+       pragma: no cover
+       raise AssertionError
+       raise NotImplementedError
+
 [egg_info]
 tag_build = 
 tag_date = 0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/croniter-0.3.12/setup.py new/croniter-0.3.17/setup.py
--- old/croniter-0.3.12/setup.py        2016-03-10 21:31:22.000000000 +0100
+++ new/croniter-0.3.17/setup.py        2017-05-22 12:11:46.000000000 +0200
@@ -8,6 +8,12 @@
         os.path.join('.', *rnames)
     ).read()
 
+install_requires = [
+    a.strip()
+    for a in read('requirements/base.txt').splitlines()
+    if a.strip() and not a.startswith(('#', '-'))
+]
+
 long_description = "\n\n".join(
     [
         read('README.rst'),
@@ -17,7 +23,7 @@
 
 setup(
     name='croniter',
-    version='0.3.12',
+    version='0.3.17',
     py_modules=['croniter', ],
     description=(
         'croniter provides iteration for datetime '
@@ -28,10 +34,7 @@
     author_email='[email protected], [email protected]',
     url='http://github.com/kiorky/croniter',
     keywords='datetime, iterator, cron',
-    install_requires=[
-        "python-dateutil",
-        "setuptools",
-    ],
+    install_requires=install_requires,
     license="MIT License",
     classifiers=[
         "Development Status :: 4 - Beta",
@@ -39,13 +42,10 @@
         "License :: OSI Approved :: MIT License",
         "Operating System :: POSIX",
         "Programming Language :: Python",
+        "Programming Language :: Python :: 2",
+        "Programming Language :: Python :: 3",
         "Topic :: Software Development :: Libraries :: Python Modules"],
     packages=find_packages('src'),
     package_dir={'': 'src'},
     include_package_data=True,
-    extras_require={
-        'test': [
-            "pytz",
-        ],
-    },
 )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/croniter-0.3.12/src/croniter/__init__.py 
new/croniter-0.3.17/src/croniter/__init__.py
--- old/croniter-0.3.12/src/croniter/__init__.py        2016-03-10 
21:31:22.000000000 +0100
+++ new/croniter-0.3.17/src/croniter/__init__.py        2017-05-22 
12:11:46.000000000 +0200
@@ -1,4 +1,9 @@
 # -*- coding: utf-8 -*-
 from __future__ import absolute_import
-from .croniter import croniter
+from .croniter import (
+    croniter,
+    CroniterBadDateError,  # noqa
+    CroniterBadCronError,  # noqa
+    CroniterNotAlphaError  # noqa
+)  # noqa
 croniter.__name__  # make flake8 happy
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/croniter-0.3.12/src/croniter/croniter.py 
new/croniter-0.3.17/src/croniter/croniter.py
--- old/croniter-0.3.12/src/croniter/croniter.py        2016-03-10 
21:31:22.000000000 +0100
+++ new/croniter-0.3.17/src/croniter/croniter.py        2017-05-22 
12:11:46.000000000 +0200
@@ -7,13 +7,25 @@
 import datetime
 from dateutil.relativedelta import relativedelta
 from dateutil.tz import tzutc
+import calendar
 
+step_search_re = re.compile(r'^([^-]+)-([^-/]+)(/(.*))?$')
 search_re = re.compile(r'^([^-]+)-([^-/]+)(/(.*))?$')
 only_int_re = re.compile(r'^\d+$')
 any_int_re = re.compile(r'^\d+')
 star_or_int_re = re.compile(r'^(\d+|\*)$')
 
-__all__ = ('croniter',)
+
+class CroniterBadCronError(ValueError):
+    '''.'''
+
+
+class CroniterBadDateError(ValueError):
+    '''.'''
+
+
+class CroniterNotAlphaError(ValueError):
+    '''.'''
 
 
 class croniter(object):
@@ -52,8 +64,11 @@
     bad_length = 'Exactly 5 or 6 columns has to be specified for iterator' \
                  'expression.'
 
-    def __init__(self, expr_format, start_time=None, ret_type=float):
+    def __init__(self, expr_format, start_time=None, ret_type=float,
+                 day_or=True):
         self._ret_type = ret_type
+        self._day_or = day_or
+
         if start_time is None:
             start_time = time()
 
@@ -62,13 +77,15 @@
             self.tzinfo = start_time.tzinfo
             start_time = self._datetime_to_timestamp(start_time)
 
+        self.start_time = start_time
         self.cur = start_time
         self.exprs = expr_format.split()
 
         if len(self.exprs) != 5 and len(self.exprs) != 6:
-            raise ValueError(self.bad_length)
+            raise CroniterBadCronError(self.bad_length)
 
         expanded = []
+        nth_weekday_of_month = {}
 
         for i, expr in enumerate(self.exprs):
             e_list = expr.split(',')
@@ -76,42 +93,53 @@
 
             while len(e_list) > 0:
                 e = e_list.pop()
-                t = re.sub(r'^\*(/.+)$', r'%d-%d\1' % (
+
+                if i == 4:
+                    e, sep, nth = str(e).partition('#')
+                    if nth and not re.match(r'[1-5]', nth):
+                        raise CroniterBadDateError(
+                            "[{0}] is not acceptable".format(expr_format))
+
+                t = re.sub(r'^\*(\/.+)$', r'%d-%d\1' % (
                     self.RANGES[i][0],
                     self.RANGES[i][1]),
                     str(e))
                 m = search_re.search(t)
 
+                if not m:
+                    t = re.sub(r'^(.+)\/(.+)$', r'\1-%d/\2' % (
+                        self.RANGES[i][1]),
+                        str(e))
+                    m = step_search_re.search(t)
+
                 if m:
                     (low, high, step) = m.group(1), m.group(2), m.group(4) or 1
 
                     if not any_int_re.search(low):
-                        low = "{0}".format(self.ALPHACONV[i][low.lower()])
+                        low = "{0}".format(self._alphaconv(i, low))
 
                     if not any_int_re.search(high):
-                        high = "{0}".format(self.ALPHACONV[i][high.lower()])
+                        high = "{0}".format(self._alphaconv(i, high))
 
                     if (
                         not low or not high or int(low) > int(high)
                         or not only_int_re.search(str(step))
                     ):
-                        raise ValueError(
+                        raise CroniterBadDateError(
                             "[{0}] is not acceptable".format(expr_format))
 
                     low, high, step = map(int, [low, high, step])
-                    e_list += range(low, high + 1, step)
-                    # other solution
-                    #try:
-                    #    for j in xrange(int(low), int(high) + 1):
-                    #        if j % int(step) == 0:
-                    #            e_list.append(j)
-                    #except NameError:
-                    #    for j in range(int(low), int(high) + 1):
-                    #        if j % int(step) == 0:
-                    #            e_list.append(j)
+                    rng = range(low, high + 1, step)
+                    e_list += (["{0}#{1}".format(item, nth) for item in rng]
+                        if i == 4 and nth else rng)
                 else:
+                    if t.startswith('-'):
+                        raise CroniterBadCronError(
+                            "[{0}] is not acceptable,\
+                            negative numbers not allowed".format(
+                                        expr_format))
                     if not star_or_int_re.search(t):
-                        t = self.ALPHACONV[i][t.lower()]
+                        t = self._alphaconv(i, t)
 
                     try:
                         t = int(t)
@@ -126,17 +154,31 @@
                         and (int(t) < self.RANGES[i][0] or
                              int(t) > self.RANGES[i][1])
                     ):
-                        raise ValueError(
+                        raise CroniterBadCronError(
                             "[{0}] is not acceptable, out of range".format(
                                 expr_format))
 
                     res.append(t)
 
+                    if i == 4 and nth:
+                        if t not in nth_weekday_of_month:
+                            nth_weekday_of_month[t] = set()
+                        nth_weekday_of_month[t].add(int(nth))
+
             res.sort()
             expanded.append(['*'] if (len(res) == 1
                                       and res[0] == '*')
                             else res)
+
         self.expanded = expanded
+        self.nth_weekday_of_month = nth_weekday_of_month
+
+    def _alphaconv(self, index, key):
+        try:
+            return self.ALPHACONV[index][key.lower()]
+        except KeyError:
+            raise CroniterNotAlphaError(
+                "[{0}] is not acceptable".format(" ".join(self.exprs)))
 
     def get_next(self, ret_type=None):
         return self._get_next(ret_type or self._ret_type, is_prev=False)
@@ -146,7 +188,7 @@
 
     def get_current(self, ret_type=None):
         ret_type = ret_type or self._ret_type
-        if ret_type == datetime.datetime:
+        if issubclass(ret_type,  datetime.datetime):
             return self._timestamp_to_datetime(self.cur)
         return self.cur
 
@@ -205,35 +247,57 @@
 
     def _get_next(self, ret_type=None, is_prev=False):
         expanded = self.expanded[:]
+        nth_weekday_of_month = self.nth_weekday_of_month.copy()
 
         ret_type = ret_type or self._ret_type
 
-        if ret_type not in (float, datetime.datetime):
+        if not issubclass(ret_type, (float, datetime.datetime)):
             raise TypeError("Invalid ret_type, only 'float' or 'datetime' "
                             "is acceptable.")
 
-        if expanded[2][0] != '*' and expanded[4][0] != '*':
+        # exception to support day of month and day of week as defined in cron
+        if (expanded[2][0] != '*' and expanded[4][0] != '*') and self._day_or:
             bak = expanded[4]
             expanded[4] = ['*']
-            t1 = self._calc(self.cur, expanded, is_prev)
+            t1 = self._calc(self.cur, expanded, nth_weekday_of_month, is_prev)
             expanded[4] = bak
             expanded[2] = ['*']
 
-            t2 = self._calc(self.cur, expanded, is_prev)
+            t2 = self._calc(self.cur, expanded, nth_weekday_of_month, is_prev)
             if not is_prev:
                 result = t1 if t1 < t2 else t2
             else:
                 result = t1 if t1 > t2 else t2
         else:
-            result = self._calc(self.cur, expanded, is_prev)
-        self.cur = result
+            result = self._calc(self.cur, expanded, nth_weekday_of_month, 
is_prev)
 
-        if ret_type == datetime.datetime:
-            result = self._timestamp_to_datetime(result)
+        # DST Handling for cron job spanning accross days
+        dtstarttime = self._timestamp_to_datetime(self.start_time)
+        dtresult = self._timestamp_to_datetime(result)
+        dtresult_utcoffset = dtresult.utcoffset() or datetime.timedelta(0)
+        dtstarttime_utcoffset = (
+            dtstarttime.utcoffset() or datetime.timedelta(0))
+        hours_before_midnight = 24 - dtstarttime.hour
+        lag_hours = (
+            self._timedelta_to_seconds(dtresult - dtstarttime) / (60*60)
+        )
+        if (
+            lag_hours >= hours_before_midnight and
+            (dtresult_utcoffset or dtstarttime_utcoffset) and
+            (dtresult_utcoffset != dtstarttime_utcoffset)
+        ):
+            lag = self._timedelta_to_seconds(
+                dtresult_utcoffset - dtstarttime_utcoffset
+            )
+            dtresult = dtresult - datetime.timedelta(seconds=lag)
+            result = self._datetime_to_timestamp(dtresult)
 
+        self.cur = result
+        if issubclass(ret_type, datetime.datetime):
+            result = dtresult
         return result
 
-    def _calc(self, now, expanded, is_prev):
+    def _calc(self, now, expanded, nth_weekday_of_month, is_prev):
         if is_prev:
             nearest_diff_method = self._get_prev_nearest_diff
             sign = -1
@@ -244,7 +308,7 @@
         offset = len(expanded) == 6 and 1 or 60
         dst = now = self._timestamp_to_datetime(now + sign * offset)
 
-        day, month, year = dst.day, dst.month, dst.year
+        month, year = dst.month, dst.year
         current_year = now.year
         DAYS = self.DAYS
 
@@ -275,7 +339,7 @@
                 days = DAYS[month - 1]
                 if month == 2 and self.is_leap(year) is True:
                     days += 1
-                if 'l' in expanded[2] and days==d.day:
+                if 'l' in expanded[2] and days == d.day:
                     return False, d
 
                 if is_prev:
@@ -310,6 +374,55 @@
                     return True, d
             return False, d
 
+        def proc_day_of_week_nth(d):
+            if '*' in nth_weekday_of_month:
+                s = nth_weekday_of_month['*']
+                for i in range(0, 7):
+                    if i in nth_weekday_of_month:
+                        nth_weekday_of_month[i].update(s)
+                    else:
+                        nth_weekday_of_month[i] = s
+                del nth_weekday_of_month['*']
+
+            candidates = []
+            for wday, nth in nth_weekday_of_month.items():
+                w = (wday + 6) % 7
+                c = calendar.Calendar(w).monthdayscalendar(d.year, d.month)
+                if c[0][0] == 0: c.pop(0)
+                for n in nth:
+                    if len(c) < n:
+                        continue
+                    candidate = c[n - 1][0]
+                    if (
+                        (is_prev and candidate <= d.day) or
+                        (not is_prev and d.day <= candidate)
+                    ):
+                        candidates.append(candidate)
+
+            if not candidates:
+                if is_prev:
+                    d += relativedelta(days=-d.day,
+                                       hour=23, minute=59, second=59)
+                else:
+                    days = DAYS[month - 1]
+                    if month == 2 and self.is_leap(year) is True:
+                        days += 1
+                    d += relativedelta(days=(days - d.day + 1),
+                                       hour=0, minute=0, second=0)
+                return True, d
+
+            candidates.sort()
+            diff_day = (candidates[-1] if is_prev else candidates[0]) - d.day
+            if diff_day != 0:
+                if is_prev:
+                    d += relativedelta(days=diff_day,
+                                       hour=23, minute=59, second=59)
+                else:
+                    d += relativedelta(days=diff_day,
+                                       hour=0, minute=0, second=0)
+                return True, d
+            return False, d
+
         def proc_hour(d):
             if expanded[1][0] != '*':
                 diff_hour = nearest_diff_method(d.hour, expanded[1], 24)
@@ -346,7 +459,8 @@
 
         procs = [proc_month,
                  proc_day_of_month,
-                 proc_day_of_week,
+                 (proc_day_of_week_nth if nth_weekday_of_month
+                     else proc_day_of_week),
                  proc_hour,
                  proc_minute,
                  proc_second]
@@ -356,14 +470,16 @@
             for proc in procs:
                 (changed, dst) = proc(dst)
                 if changed:
-                    day, month, year = dst.day, dst.month, dst.year
+                    month, year = dst.month, dst.year
                     next = True
                     break
             if next:
                 continue
             return self._datetime_to_timestamp(dst.replace(microsecond=0))
 
-        raise Exception("failed to find prev date")
+        if is_prev:
+            raise CroniterBadDateError("failed to find prev date")
+        raise CroniterBadDateError("failed to find next date")
 
     def _get_next_nearest(self, x, to_check):
         small = [item for item in to_check if item < x]
@@ -399,7 +515,13 @@
             return -x
         candidate = candidates[0]
         for c in candidates:
-            if c < range_val:
+            # fixed: c < range_val
+            # this code will reject all 31 day of month, 12 month, 59 second,
+            # 23 hour and so on.
+            # if candidates has just a element, this will not harmful.
+            # but candidates have multiple elements, then values equal to
+            # range_val will rejected.
+            if c <= range_val:
                 candidate = c
                 break
 
@@ -410,10 +532,3 @@
             return True
         else:
             return False
-
-if __name__ == '__main__':
-
-    base = datetime.datetime(2010, 1, 25)
-    itr = croniter('0 0 1 * *', base)
-    n1 = itr.get_next(datetime.datetime)
-    print(n1)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/croniter-0.3.12/src/croniter/tests/test_croniter.py 
new/croniter-0.3.17/src/croniter/tests/test_croniter.py
--- old/croniter-0.3.12/src/croniter/tests/test_croniter.py     2016-03-10 
21:31:22.000000000 +0100
+++ new/croniter-0.3.17/src/croniter/tests/test_croniter.py     2017-05-22 
12:11:46.000000000 +0200
@@ -1,12 +1,11 @@
-#!/usr/bin/python
+#!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
 import unittest
-from datetime import datetime
+from datetime import datetime, timedelta
 from time import sleep
 import pytz
-from croniter import croniter
-
+from croniter import croniter, CroniterBadDateError
 from croniter.tests import base
 
 
@@ -146,6 +145,49 @@
         self.assertEqual(n3.day, 3)
         self.assertEqual(n3.year, 2010)
 
+    def testNthWeekDay(self):
+        base = datetime(2010, 2, 25)
+        itr = croniter('0 0 * * sat#1', base)
+        n1 = itr.get_next(datetime)
+        self.assertEqual(n1.isoweekday(), 6)
+        self.assertEqual(n1.day, 6)
+        self.assertEqual(n1.month, 3)
+        n2 = itr.get_next(datetime)
+        self.assertEqual(n2.isoweekday(), 6)
+        self.assertEqual(n2.day, 3)
+        self.assertEqual(n2.month, 4)
+
+        base = datetime(2010, 1, 25)
+        itr = croniter('0 0 * * wed#5', base)
+        n1 = itr.get_next(datetime)
+        self.assertEqual(n1.month, 3)
+        self.assertEqual(n1.day, 31)
+        self.assertEqual(n1.year, 2010)
+        n2 = itr.get_next(datetime)
+        self.assertEqual(n2.month, 6)
+        self.assertEqual(n2.day, 30)
+        self.assertEqual(n2.year, 2010)
+        n3 = itr.get_next(datetime)
+        self.assertEqual(n3.month, 9)
+        self.assertEqual(n3.day, 29)
+        self.assertEqual(n3.year, 2010)
+
+    def testWeekDayDayAnd(self):
+        base = datetime(2010, 1, 25)
+        itr = croniter('0 0 1 * mon', base, day_or=False)
+        n1 = itr.get_next(datetime)
+        self.assertEqual(n1.month, 2)
+        self.assertEqual(n1.day, 1)
+        self.assertEqual(n1.year, 2010)
+        n2 = itr.get_next(datetime)
+        self.assertEqual(n2.month, 3)
+        self.assertEqual(n2.day, 1)
+        self.assertEqual(n2.year, 2010)
+        n3 = itr.get_next(datetime)
+        self.assertEqual(n3.month, 11)
+        self.assertEqual(n3.day, 1)
+        self.assertEqual(n3.year, 2010)
+
     def testMonth(self):
         base = datetime(2010, 1, 25)
         itr = croniter('0 0 1 * *', base)
@@ -228,10 +270,12 @@
         self.assertRaises(TypeError, itr.get_next, str)
         self.assertRaises(ValueError, croniter, '* * * *')
         self.assertRaises(ValueError, croniter, '* * 5-1 * *')
-        self.assertRaises(KeyError, croniter, '* * * janu-jun *')
+        self.assertRaises(ValueError, croniter, '-90 * * * *')
+        self.assertRaises(ValueError, croniter, 'a * * * *')
+        self.assertRaises(ValueError, croniter, '* * * janu-jun *')
 
     def testSundayToThursdayWithAlphaConversion(self):
-        base = datetime(2010, 8, 25, 15, 56) #wednesday
+        base = datetime(2010, 8, 25, 15, 56)  # wednesday
         itr = croniter("30 22 * * sun-thu", base)
         next = itr.get_next(datetime)
 
@@ -306,6 +350,30 @@
         self.assertEqual(prev3.hour, 0)
         self.assertEqual(prev3.minute, 0)
 
+    def testPrevNthWeekDay(self):
+        base = datetime(2010, 8, 25, 15, 56)
+        itr = croniter('0 0 * * sat#1,sun#2', base)
+        prev1 = itr.get_prev(datetime)
+        self.assertEqual(prev1.year, base.year)
+        self.assertEqual(prev1.month, base.month)
+        self.assertEqual(prev1.day, 8)
+        self.assertEqual(prev1.hour, 0)
+        self.assertEqual(prev1.minute, 0)
+
+        prev2 = itr.get_prev(datetime)
+        self.assertEqual(prev2.year, base.year)
+        self.assertEqual(prev2.month, base.month)
+        self.assertEqual(prev2.day, 7)
+        self.assertEqual(prev2.hour, 0)
+        self.assertEqual(prev2.minute, 0)
+
+        prev3 = itr.get_prev(datetime)
+        self.assertEqual(prev3.year, base.year)
+        self.assertEqual(prev3.month, 7)
+        self.assertEqual(prev3.day, 11)
+        self.assertEqual(prev3.hour, 0)
+        self.assertEqual(prev3.minute, 0)
+
     def testPrevWeekDay2(self):
         base = datetime(2010, 8, 25, 15, 56)
         itr = croniter('10 0 * * 0', base)
@@ -388,6 +456,112 @@
         self.assertEqual(n6.month, 2)
         self.assertEqual(n6.day, 16)
 
+    def test_bug34(self):
+        base = datetime(2012, 2, 24, 0, 0, 0)
+        itr = croniter('* * 31 2 *', base)
+        try:
+            itr.get_next(datetime)
+        except (CroniterBadDateError,) as ex:
+            self.assertEqual("{0}".format(ex),
+                             'failed to find next date')
+
+    def testBug57(self):
+        base = datetime(2012, 2, 24, 0, 0, 0)
+        itr = croniter('0 4/6 * * *', base)
+        n1 = itr.get_next(datetime)
+        self.assertEqual(n1.hour, 4)
+        self.assertEqual(n1.minute, 0)
+        self.assertEqual(n1.month, 2)
+        self.assertEqual(n1.day, 24)
+
+        n1 = itr.get_prev(datetime)
+        self.assertEqual(n1.hour, 22)
+        self.assertEqual(n1.minute, 0)
+        self.assertEqual(n1.month, 2)
+        self.assertEqual(n1.day, 23)
+
+        itr = croniter('0 0/6 * * *', base)
+        n1 = itr.get_next(datetime)
+        self.assertEqual(n1.hour, 6)
+        self.assertEqual(n1.minute, 0)
+        self.assertEqual(n1.month, 2)
+        self.assertEqual(n1.day, 24)
+
+        n1 = itr.get_prev(datetime)
+        self.assertEqual(n1.hour, 0)
+        self.assertEqual(n1.minute, 0)
+        self.assertEqual(n1.month, 2)
+        self.assertEqual(n1.day, 24)
+
+    def test_multiple_months(self):
+        base = datetime(2016, 3, 1, 0, 0, 0)
+        itr = croniter('0 0 1 3,6,9,12 *', base)
+        n1 = itr.get_next(datetime)
+        self.assertEqual(n1.hour, 0)
+        self.assertEqual(n1.month, 6)
+        self.assertEqual(n1.day, 1)
+        self.assertEqual(n1.year, 2016)
+
+        base = datetime(2016, 2, 15, 0, 0, 0)
+        itr = croniter('0 0 1 3,6,9,12 *', base)
+        n1 = itr.get_next(datetime)
+        self.assertEqual(n1.hour, 0)
+        self.assertEqual(n1.month, 3)
+        self.assertEqual(n1.day, 1)
+        self.assertEqual(n1.year, 2016)
+
+        base = datetime(2016, 12, 3, 10, 0, 0)
+        itr = croniter('0 0 1 3,6,9,12 *', base)
+        n1 = itr.get_next(datetime)
+        self.assertEqual(n1.hour, 0)
+        self.assertEqual(n1.month, 3)
+        self.assertEqual(n1.day, 1)
+        self.assertEqual(n1.year, 2017)
+
+        # The result with this parameters was incorrect.
+        # self.assertEqual(p1.month, 12
+        # AssertionError: 9 != 12
+        base = datetime(2016, 3, 1, 0, 0, 0)
+        itr = croniter('0 0 1 3,6,9,12 *', base)
+        p1 = itr.get_prev(datetime)
+        self.assertEqual(p1.hour, 0)
+        self.assertEqual(p1.month, 12)
+        self.assertEqual(p1.day, 1)
+        self.assertEqual(p1.year, 2015)
+
+        # check my change resolves another hidden bug.
+        base = datetime(2016, 2, 1, 0, 0, 0)
+        itr = croniter('0 0 1,15,31 * *', base)
+        p1 = itr.get_prev(datetime)
+        self.assertEqual(p1.hour, 0)
+        self.assertEqual(p1.month, 1)
+        self.assertEqual(p1.day, 31)
+        self.assertEqual(p1.year, 2016)
+
+        base = datetime(2016, 6, 1, 0, 0, 0)
+        itr = croniter('0 0 1 3,6,9,12 *', base)
+        p1 = itr.get_prev(datetime)
+        self.assertEqual(p1.hour, 0)
+        self.assertEqual(p1.month, 3)
+        self.assertEqual(p1.day, 1)
+        self.assertEqual(p1.year, 2016)
+
+        base = datetime(2016, 3, 1, 0, 0, 0)
+        itr = croniter('0 0 1 1,3,6,9,12 *', base)
+        p1 = itr.get_prev(datetime)
+        self.assertEqual(p1.hour, 0)
+        self.assertEqual(p1.month, 1)
+        self.assertEqual(p1.day, 1)
+        self.assertEqual(p1.year, 2016)
+
+        base = datetime(2016, 3, 1, 0, 0, 0)
+        itr = croniter('0 0 1 1,3,6,9,12 *', base)
+        p1 = itr.get_prev(datetime)
+        self.assertEqual(p1.hour, 0)
+        self.assertEqual(p1.month, 1)
+        self.assertEqual(p1.day, 1)
+        self.assertEqual(p1.year, 2016)
+
     def test_rangeGenerator(self):
         base = datetime(2013, 3, 4, 0, 0)
         itr = croniter('1-9/2 0 1 * *', base)
@@ -467,13 +641,15 @@
         itr = croniter('* * * * *')
         sleep(.01)
         itr2 = croniter('* * * * *')
-        self.assertGreater(itr2.cur, itr.cur)
+        # Greater dosnt exists in py26
+        self.assertTrue(itr2.cur > itr.cur)
 
     def assertScheduleTimezone(self, callback, expected_schedule):
         for expected_date, expected_offset in expected_schedule:
             d = callback()
             self.assertEqual(expected_date, d.replace(tzinfo=None))
-            self.assertEqual(expected_offset, 
croniter._timedelta_to_seconds(d.utcoffset()))
+            self.assertEqual(expected_offset,
+                             croniter._timedelta_to_seconds(d.utcoffset()))
 
     def testTimezoneWinterTime(self):
         tz = pytz.timezone('Europe/Athens')
@@ -515,6 +691,47 @@
         ct = croniter('*/30 * * * *', tz.localize(start))
         self.assertScheduleTimezone(lambda: ct.get_prev(datetime), 
reversed(expected_schedule))
 
+    def test_std_dst(self):
+        """
+        DST tests
+
+        This fixes https://github.com/taichino/croniter/issues/82
+
+        """
+        tz = pytz.timezone('Europe/Warsaw')
+        # -> 2017-03-26 01:59+1:00 -> 03:00+2:00
+        local_date = tz.localize(datetime(2017, 3, 26))
+        val = croniter('0 0 * * *', local_date).get_next(datetime)
+        self.assertEqual(val, tz.localize(datetime(2017, 3, 27)))
+        #
+        local_date = tz.localize(datetime(2017, 3, 26, 1))
+        cr = croniter('0 * * * *', local_date)
+        val = cr.get_next(datetime)
+        self.assertEqual(val, tz.localize(datetime(2017, 3, 26, 3)))
+        val = cr.get_current(datetime)
+        self.assertEqual(val, tz.localize(datetime(2017, 3, 26, 3)))
+
+        # -> 2017-10-29 02:59+2:00 -> 02:00+1:00
+        local_date = tz.localize(datetime(2017, 10, 29))
+        val = croniter('0 0 * * *', local_date).get_next(datetime)
+        self.assertEqual(val, tz.localize(datetime(2017, 10, 30)))
+        local_date = tz.localize(datetime(2017, 10, 29, 1, 59))
+        val = croniter('0 * * * *', local_date).get_next(datetime)
+        self.assertEqual(
+            val.replace(tzinfo=None),
+            tz.localize(datetime(2017, 10, 29, 2)).replace(tzinfo=None))
+        local_date = tz.localize(datetime(2017, 10, 29, 2))
+        val = croniter('0 * * * *', local_date).get_next(datetime)
+        self.assertEqual(val, tz.localize(datetime(2017, 10, 29, 3)))
+        local_date = tz.localize(datetime(2017, 10, 29, 3))
+        val = croniter('0 * * * *', local_date).get_next(datetime)
+        self.assertEqual(val, tz.localize(datetime(2017, 10, 29, 4)))
+        local_date = tz.localize(datetime(2017, 10, 29, 4))
+        val = croniter('0 * * * *', local_date).get_next(datetime)
+        self.assertEqual(val, tz.localize(datetime(2017, 10, 29, 5)))
+        local_date = tz.localize(datetime(2017, 10, 29, 5))
+        val = croniter('0 * * * *', local_date).get_next(datetime)
+        self.assertEqual(val, tz.localize(datetime(2017, 10, 29, 6)))
 
 if __name__ == '__main__':
     unittest.main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/croniter-0.3.12/src/croniter.egg-info/PKG-INFO 
new/croniter-0.3.17/src/croniter.egg-info/PKG-INFO
--- old/croniter-0.3.12/src/croniter.egg-info/PKG-INFO  2016-03-10 
21:31:22.000000000 +0100
+++ new/croniter-0.3.17/src/croniter.egg-info/PKG-INFO  2017-05-22 
12:11:46.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: croniter
-Version: 0.3.12
+Version: 0.3.17
 Summary: croniter provides iteration for datetime object with cron like format
 Home-page: http://github.com/kiorky/croniter
 Author: Matsumoto Taichi, kiorky
@@ -12,7 +12,7 @@
         .. contents::
         
         
-        croniter provides iteration for datetime object with cron like format.
+        croniter provides iteration for the datetime object with a cron like 
format.
         
         ::
         
@@ -33,12 +33,12 @@
         Usage
         ============
         
-        Simple example of usage is followings::
+        A simple example::
         
             >>> from croniter import croniter
             >>> from datetime import datetime
             >>> base = datetime(2010, 1, 25, 4, 46)
-            >>> iter = croniter('*/5 * * * *', base)  # every 5 minites
+            >>> iter = croniter('*/5 * * * *', base)  # every 5 minutes
             >>> print iter.get_next(datetime)   # 2010-01-25 04:50:00
             >>> print iter.get_next(datetime)   # 2010-01-25 04:55:00
             >>> print iter.get_next(datetime)   # 2010-01-25 05:00:00
@@ -47,21 +47,42 @@
             >>> print iter.get_next(datetime)   # 2010-01-26 04:02:00
             >>> print iter.get_next(datetime)   # 2010-01-30 04:02:00
             >>> print iter.get_next(datetime)   # 2010-02-02 04:02:00
-        
-        All you need to know is constructor and get_next, these signature are 
following::
-        
-            >>> def __init__(self, cron_format, start_time=time.time())
-        
-        croniter iterate along with 'cron_format' from 'start_time'.
-        cron_format is 'min hour day month day_of_week', and please refer to
-        http://en.wikipedia.org/wiki/Cron for details.::
+            >>>
+            >>> iter = croniter('2 4 1 * wed', base)  # 04:02 on every 
Wednesday OR on 1st day of month
+            >>> print iter.get_next(datetime)   # 2010-01-27 04:02:00
+            >>> print iter.get_next(datetime)   # 2010-02-01 04:02:00
+            >>> print iter.get_next(datetime)   # 2010-02-03 04:02:00
+            >>>
+            >>> iter = croniter('2 4 1 * wed', base, day_or=False)  # 04:02 on 
every 1st day of the month if it is a Wednesday
+            >>> print iter.get_next(datetime)   # 2010-09-01 04:02:00
+            >>> print iter.get_next(datetime)   # 2010-12-01 04:02:00
+            >>> print iter.get_next(datetime)   # 2011-06-01 04:02:00
+            >>> iter = croniter('0 0 * * sat#1,sun#2', base)
+            >>> print iter.get_next(datetime)   # datetime.datetime(2010, 2, 
6, 0, 0)
+        
+        All you need to know is how to use the constructor and the ``get_next``
+        method, the signature of these methods are listed below::
+        
+            >>> def __init__(self, cron_format, start_time=time.time(), 
day_or=True)
+        
+        croniter iterates along with ``cron_format`` from ``start_time``.
+        ``cron_format`` is **min hour day month day_of_week**, you can refer to
+        http://en.wikipedia.org/wiki/Cron for more details. The ``day_or``
+        switch is used to control how croniter handles **day** and 
**day_of_week**
+        entries. Default option is the cron behaviour, which connects those
+        values using **OR**. If the switch is set to False, the values are 
connected
+        using **AND**. This behaves like fcron and enables you to e.g. define 
a job that
+        executes each 2nd friday of a month by setting the days of month and 
the
+        weekday.
+        ::
         
             >>> def get_next(self, ret_type=float)
         
-        get_next return next time in iteration with 'ret_type'.
-        And ret_type accept only 'float' or 'datetime'.
+        get_next calculates the next value according to the cron expression and
+        returns an object of type ``ret_type``. ``ret_type`` should be a 
``float`` or a
+        ``datetime`` object.
         
-        Now, supported get_prev method. (>= 0.2.0)::
+        Supported added for ``get_prev`` method. (>= 0.2.0)::
         
             >>> base = datetime(2010, 8, 25)
             >>> itr = croniter('0 0 1 * *', base)
@@ -69,6 +90,12 @@
             >>> print itr.get_prev(datetime)  # 2010-07-01 00:00:00
             >>> print itr.get_prev(datetime)  # 2010-06-01 00:00:00
         
+        About DST
+        =========
+        Be sure to init your croniter instance with a TZ aware datetime for 
this to work !::
+        
+            >>> local_date = tz.localize(datetime(2017, 3, 26))
+            >>> val = croniter('0 0 * * *', local_date).get_next(datetime)
         
         Develop this package
         ====================
@@ -77,25 +104,28 @@
         
             git clone https://github.com/kiorky/croniter.git
             cd croniter
-            python bootstrap.py -d
-            bin/buildout -vvvvvvN
-            bin/test
+            virtualenv --no-site-packages venv
+            . venv/bin/activate
+            pip install --upgrade -r requirements/test.txt
+            py.test src
         
         
         Make a new release
         ====================
-        We use zest.fullreleaser, a great releaser infrastructure.
+        We use zest.fullreleaser, a great release infrastructure.
         
-        Do and follow the instructions
+        Do and follow these instructions
         ::
         
-            bin/fullrelease
+            . venv/bin/activate
+            pip install --upgrade -r requirements/release.txt
+            fullrelease
         
         
         Contributors
         ===============
-        Thank you to all who have contributed to this project!
-        If you contributed and not listed below please let me know.
+        Thanks to all who have contributed to this project!
+        If you have contributed and your name is not listed below please let 
me know.
         
             - mrmachine
             - Hinnack
@@ -114,6 +144,50 @@
         Changelog
         ==============
         
+        0.3.17 (2017-05-22)
+        -------------------
+        - DOW occurence sharp style support.
+          [kiorky, Kengo Seki <[email protected]>]
+        
+        
+        0.3.16 (2017-03-15)
+        -------------------
+        
+        - Better test suite [mrcrilly@github]
+        - DST support [kiorky]
+        
+        0.3.15 (2017-02-16)
+        -------------------
+        
+        - fix bug around multiple conditions and range_val in
+          _get_prev_nearest_diff.
+          [abeja-yuki@github]
+        
+        0.3.14 (2017-01-25)
+        -------------------
+        
+        - issue #69: added day_or option to change behavior when day-of-month 
and
+          day-of-week is given
+          [Andreas Vogl <[email protected]>]
+        
+        
+        
+        0.3.13 (2016-11-01)
+        -------------------
+        
+        - `Real fix for #34 <https://github.com/taichino/croniter/pull/73>`_
+          [kiorky@github]
+        - `Modernize test infra 
<https://github.com/taichino/croniter/pull/72>`_
+          [kiorky@github]
+        - `Release as a universal wheel 
<https://github.com/kiorky/croniter/pull/16>`_
+          [adamchainz@github]
+        - `Raise ValueError on negative numbers 
<https://github.com/taichino/croniter/pull/63>`_
+          [josegonzalez@github]
+        - `Compare types using "issubclass" instead of exact match 
<https://github.com/taichino/croniter/pull/70>`_
+          [darkk@github]
+        - `Implement step cron with a variable base 
<https://github.com/taichino/croniter/pull/60>`_
+          [josegonzalez@github]
+        
         0.3.12 (2016-03-10)
         -------------------
         - support setting ret_type in __init__ [Brent Tubbs 
<[email protected]>]
@@ -185,4 +259,6 @@
 Classifier: License :: OSI Approved :: MIT License
 Classifier: Operating System :: POSIX
 Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 3
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/croniter-0.3.12/src/croniter.egg-info/SOURCES.txt 
new/croniter-0.3.17/src/croniter.egg-info/SOURCES.txt
--- old/croniter-0.3.12/src/croniter.egg-info/SOURCES.txt       2016-03-10 
21:31:22.000000000 +0100
+++ new/croniter-0.3.17/src/croniter.egg-info/SOURCES.txt       2017-05-22 
12:11:46.000000000 +0200
@@ -1,10 +1,13 @@
 MANIFEST.in
 README.rst
-buildout.cfg
-py3.cfg
+setup.cfg
 setup.py
+tox.ini
 docs/CHANGES.rst
 docs/LICENSE
+requirements/base.txt
+requirements/release.txt
+requirements/test.txt
 src/croniter/__init__.py
 src/croniter/croniter.py
 src/croniter.egg-info/PKG-INFO
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/croniter-0.3.12/src/croniter.egg-info/requires.txt 
new/croniter-0.3.17/src/croniter.egg-info/requires.txt
--- old/croniter-0.3.12/src/croniter.egg-info/requires.txt      2016-03-10 
21:31:22.000000000 +0100
+++ new/croniter-0.3.17/src/croniter.egg-info/requires.txt      2017-05-22 
12:11:46.000000000 +0200
@@ -1,5 +1 @@
-python-dateutil
-setuptools
-
-[test]
-pytz
+python_dateutil
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/croniter-0.3.12/tox.ini new/croniter-0.3.17/tox.ini
--- old/croniter-0.3.12/tox.ini 1970-01-01 01:00:00.000000000 +0100
+++ new/croniter-0.3.17/tox.ini 2017-05-22 12:11:46.000000000 +0200
@@ -0,0 +1,22 @@
+[tox]
+minversion = 2.3
+envlist =
+    {py26,py27,py34,py35}-std
+    py27-coverage
+skipsdist = true
+
+[testenv]
+usedevelop = true
+deps =
+    -r{toxinidir}/requirements/test.txt
+whitelist_externals = /bin/sh
+setenv =
+    COVERAGE_FILE={envdir}/coverage_report
+changedir = src
+commands =
+    {py26,py27,py34,py35,py36}-std: py.test -v .
+    {py27,py34,py35,py36}-std: flake8 src/croniter/croniter.py
+    py27-coverage: coverage erase
+    py27-coverage: sh -c 'cd .. && coverage run $(which py.test) -v src'
+    py27-coverage: coverage report
+


Reply via email to