datec 0.1__tar.gz → 0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- datec-0.2/.gitignore +105 -0
- datec-0.2/LICENSE +21 -0
- datec-0.2/PKG-INFO +110 -0
- datec-0.2/README.md +86 -0
- {datec-0.1 → datec-0.2}/datec/__init__.py +54 -41
- {datec-0.1 → datec-0.2}/datec/__main__.py +1 -1
- datec-0.2/pyproject.toml +42 -0
- datec-0.2/requirements-dev.in +9 -0
- datec-0.2/tests/datec_test.py +82 -0
- datec-0.1/PKG-INFO +0 -92
- datec-0.1/datec.egg-info/PKG-INFO +0 -92
- datec-0.1/datec.egg-info/SOURCES.txt +0 -9
- datec-0.1/datec.egg-info/dependency_links.txt +0 -1
- datec-0.1/datec.egg-info/entry_points.txt +0 -3
- datec-0.1/datec.egg-info/requires.txt +0 -1
- datec-0.1/datec.egg-info/top_level.txt +0 -1
- datec-0.1/setup.cfg +0 -4
- datec-0.1/setup.py +0 -32
datec-0.2/.gitignore
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
requirements-dev.txt
|
|
13
|
+
develop-eggs/
|
|
14
|
+
dist/
|
|
15
|
+
downloads/
|
|
16
|
+
eggs/
|
|
17
|
+
.eggs/
|
|
18
|
+
lib/
|
|
19
|
+
lib64/
|
|
20
|
+
parts/
|
|
21
|
+
sdist/
|
|
22
|
+
var/
|
|
23
|
+
wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.coverage
|
|
43
|
+
.coverage.*
|
|
44
|
+
.cache
|
|
45
|
+
nosetests.xml
|
|
46
|
+
coverage.xml
|
|
47
|
+
*.cover
|
|
48
|
+
.hypothesis/
|
|
49
|
+
.pytest_cache/
|
|
50
|
+
|
|
51
|
+
# Translations
|
|
52
|
+
*.mo
|
|
53
|
+
*.pot
|
|
54
|
+
|
|
55
|
+
# Django stuff:
|
|
56
|
+
*.log
|
|
57
|
+
local_settings.py
|
|
58
|
+
db.sqlite3
|
|
59
|
+
|
|
60
|
+
# Flask stuff:
|
|
61
|
+
instance/
|
|
62
|
+
.webassets-cache
|
|
63
|
+
|
|
64
|
+
# Scrapy stuff:
|
|
65
|
+
.scrapy
|
|
66
|
+
|
|
67
|
+
# Sphinx documentation
|
|
68
|
+
docs/_build/
|
|
69
|
+
|
|
70
|
+
# PyBuilder
|
|
71
|
+
target/
|
|
72
|
+
|
|
73
|
+
# Jupyter Notebook
|
|
74
|
+
.ipynb_checkpoints
|
|
75
|
+
|
|
76
|
+
# pyenv
|
|
77
|
+
.python-version
|
|
78
|
+
|
|
79
|
+
# celery beat schedule file
|
|
80
|
+
celerybeat-schedule
|
|
81
|
+
|
|
82
|
+
# SageMath parsed files
|
|
83
|
+
*.sage.py
|
|
84
|
+
|
|
85
|
+
# Environments
|
|
86
|
+
.env
|
|
87
|
+
.venv
|
|
88
|
+
env/
|
|
89
|
+
venv/
|
|
90
|
+
ENV/
|
|
91
|
+
env.bak/
|
|
92
|
+
venv.bak/
|
|
93
|
+
|
|
94
|
+
# Spyder project settings
|
|
95
|
+
.spyderproject
|
|
96
|
+
.spyproject
|
|
97
|
+
|
|
98
|
+
# Rope project settings
|
|
99
|
+
.ropeproject
|
|
100
|
+
|
|
101
|
+
# mkdocs documentation
|
|
102
|
+
/site
|
|
103
|
+
|
|
104
|
+
# mypy
|
|
105
|
+
.mypy_cache/
|
datec-0.2/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2019 isaacto
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
datec-0.2/PKG-INFO
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: datec
|
|
3
|
+
Version: 0.2
|
|
4
|
+
Summary: Date Command
|
|
5
|
+
Project-URL: Homepage, https://github.com/isaacto/datec
|
|
6
|
+
Project-URL: Repository, https://github.com/isaacto/datec.git
|
|
7
|
+
Project-URL: Issues, https://github.com/isaacto/datec/issues
|
|
8
|
+
Author-email: Isaac To <isaac.to@gmail.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Requires-Dist: python-dateutil
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# Date command: A command-based date computation engine
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
You can install the package simply by
|
|
30
|
+
|
|
31
|
+
pip install datec
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
datec allows you to use "date commands" to modify datetime's by adding
|
|
36
|
+
to them, like this:
|
|
37
|
+
|
|
38
|
+
datetime.datetime.now() + datec.Period(2, 'week')
|
|
39
|
+
|
|
40
|
+
A date command can be parsed from strings using the parse() function,
|
|
41
|
+
which create a command from a string representation. This forms the
|
|
42
|
+
basis of the datec command, which is a command-line program to output
|
|
43
|
+
datetime after applying date commands. In general the date
|
|
44
|
+
representation is NxYYYY-mm-ddTHH:MM:SS.ffffff, where unspecified
|
|
45
|
+
parts are omitted leaving the symbols intact, like "2x-2-29T3::." (see
|
|
46
|
+
the following for the meaning). If the fractional part is not
|
|
47
|
+
specified the "." may be omitted, if all time parts are not specified
|
|
48
|
+
the "T::." can be omitted, if all date parts are not specified the
|
|
49
|
+
"--T" can be omitted, and if Nx may be omitted in some cases for
|
|
50
|
+
setting a partial datetime or weekday. There are a couple other more
|
|
51
|
+
formats like +3week and -2wed for shifting by period and weekday.
|
|
52
|
+
|
|
53
|
+
Date commands are in two forms: period shifting commands and partial
|
|
54
|
+
datetime shifting commands. The first type is more familiar: they
|
|
55
|
+
look like
|
|
56
|
+
|
|
57
|
+
* +2week (shift the datetime forward by 2 week)
|
|
58
|
+
* -1month (shift the datetime backward by 1 month)
|
|
59
|
+
|
|
60
|
+
Period is one of year, month, week, day, hour, minute and second,
|
|
61
|
+
represented by an object of the Period class. Fractional numbers are
|
|
62
|
+
acceptable except for year and month. If shifting a period leads to
|
|
63
|
+
an invalid date (e.g., shift backward 1 month from 2019-07-31), it
|
|
64
|
+
moves backwards the closest valid date (here, 2019-06-30). In general
|
|
65
|
+
the parts finer than the shifted part is unaffected (e.g., shifting 1
|
|
66
|
+
month from 2019-07-31 02:00 gives you 2019-06-30 02:00).
|
|
67
|
+
|
|
68
|
+
Partial datetime shifting is less familiar. It looks like:
|
|
69
|
+
|
|
70
|
+
* 12:: (set the hour number to 12)
|
|
71
|
+
* +2x12:: (move forward to the second hour 12)
|
|
72
|
+
* +4x--31 (move forward to the fourth occurrence of day 31 of a month)
|
|
73
|
+
* -3x-02-29 (move backward to the third occurrence of February 29)
|
|
74
|
+
* wed (set to the Wednesday of the same week, week starts on Sunday)
|
|
75
|
+
* -3wed (move to the third Wednesday before the current datetime)
|
|
76
|
+
|
|
77
|
+
They are represented by either a Weekday object or a PartialDate
|
|
78
|
+
object with a count. A count of 0 means setting instead of shifting.
|
|
79
|
+
Only integer counts are acceptable.
|
|
80
|
+
|
|
81
|
+
It is an error to set to an invalid date (e.g., --31 applied on
|
|
82
|
+
2019-06-25 is an error). The datetime parts which are specified must
|
|
83
|
+
be consecutive (it is an error to specify 12::05). It is also an
|
|
84
|
+
error to shift for occurrence of a partial date with year specified
|
|
85
|
+
(e.g., "+2x2019--").
|
|
86
|
+
|
|
87
|
+
On the other hand, shifting to an invalid date with day number
|
|
88
|
+
specified will shift more until a specified date is valid. For
|
|
89
|
+
example, if you add -2-29 with count 1 to 2019-01-01, you end up with
|
|
90
|
+
2020-02-29, because 2019-02-29 is not a valid date. If the count is 2
|
|
91
|
+
you get 2024-02-29 instead.
|
|
92
|
+
|
|
93
|
+
Shifting to an invalid date by a partial date with just a month number
|
|
94
|
+
will cause the date to moved backwards until the date is valid. E.g.,
|
|
95
|
+
if you shift by -6- with count 1 (next June) from 2019-05-31, you get
|
|
96
|
+
2019-06-30. With count 2 you get 2020-06-30.
|
|
97
|
+
|
|
98
|
+
This library is grown out of frustration that it is tedious to have a
|
|
99
|
+
shell script or program to get a datetime like "the next 6pm from now"
|
|
100
|
+
or "the next 3rd of any month from two days ago". With this module
|
|
101
|
+
they can be specified like "+1x18:00:00.0" and "-2day +1x--3"
|
|
102
|
+
respectively. In the expected use cases, counts are small numbers.
|
|
103
|
+
So the library is not always efficient (at times we just loop "count"
|
|
104
|
+
times to step forward or backward). Whenever it is simple to do so,
|
|
105
|
+
the implementation just forward to relativedelta, in which case they
|
|
106
|
+
are more efficient.
|
|
107
|
+
|
|
108
|
+
At present the program does not handle timezone and daylight saving.
|
|
109
|
+
This is bacause the author lives at a place where no daylight saving
|
|
110
|
+
is observed. Contributions are welcome.
|
datec-0.2/README.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Date command: A command-based date computation engine
|
|
2
|
+
|
|
3
|
+
## Installation
|
|
4
|
+
|
|
5
|
+
You can install the package simply by
|
|
6
|
+
|
|
7
|
+
pip install datec
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
datec allows you to use "date commands" to modify datetime's by adding
|
|
12
|
+
to them, like this:
|
|
13
|
+
|
|
14
|
+
datetime.datetime.now() + datec.Period(2, 'week')
|
|
15
|
+
|
|
16
|
+
A date command can be parsed from strings using the parse() function,
|
|
17
|
+
which create a command from a string representation. This forms the
|
|
18
|
+
basis of the datec command, which is a command-line program to output
|
|
19
|
+
datetime after applying date commands. In general the date
|
|
20
|
+
representation is NxYYYY-mm-ddTHH:MM:SS.ffffff, where unspecified
|
|
21
|
+
parts are omitted leaving the symbols intact, like "2x-2-29T3::." (see
|
|
22
|
+
the following for the meaning). If the fractional part is not
|
|
23
|
+
specified the "." may be omitted, if all time parts are not specified
|
|
24
|
+
the "T::." can be omitted, if all date parts are not specified the
|
|
25
|
+
"--T" can be omitted, and if Nx may be omitted in some cases for
|
|
26
|
+
setting a partial datetime or weekday. There are a couple other more
|
|
27
|
+
formats like +3week and -2wed for shifting by period and weekday.
|
|
28
|
+
|
|
29
|
+
Date commands are in two forms: period shifting commands and partial
|
|
30
|
+
datetime shifting commands. The first type is more familiar: they
|
|
31
|
+
look like
|
|
32
|
+
|
|
33
|
+
* +2week (shift the datetime forward by 2 week)
|
|
34
|
+
* -1month (shift the datetime backward by 1 month)
|
|
35
|
+
|
|
36
|
+
Period is one of year, month, week, day, hour, minute and second,
|
|
37
|
+
represented by an object of the Period class. Fractional numbers are
|
|
38
|
+
acceptable except for year and month. If shifting a period leads to
|
|
39
|
+
an invalid date (e.g., shift backward 1 month from 2019-07-31), it
|
|
40
|
+
moves backwards the closest valid date (here, 2019-06-30). In general
|
|
41
|
+
the parts finer than the shifted part is unaffected (e.g., shifting 1
|
|
42
|
+
month from 2019-07-31 02:00 gives you 2019-06-30 02:00).
|
|
43
|
+
|
|
44
|
+
Partial datetime shifting is less familiar. It looks like:
|
|
45
|
+
|
|
46
|
+
* 12:: (set the hour number to 12)
|
|
47
|
+
* +2x12:: (move forward to the second hour 12)
|
|
48
|
+
* +4x--31 (move forward to the fourth occurrence of day 31 of a month)
|
|
49
|
+
* -3x-02-29 (move backward to the third occurrence of February 29)
|
|
50
|
+
* wed (set to the Wednesday of the same week, week starts on Sunday)
|
|
51
|
+
* -3wed (move to the third Wednesday before the current datetime)
|
|
52
|
+
|
|
53
|
+
They are represented by either a Weekday object or a PartialDate
|
|
54
|
+
object with a count. A count of 0 means setting instead of shifting.
|
|
55
|
+
Only integer counts are acceptable.
|
|
56
|
+
|
|
57
|
+
It is an error to set to an invalid date (e.g., --31 applied on
|
|
58
|
+
2019-06-25 is an error). The datetime parts which are specified must
|
|
59
|
+
be consecutive (it is an error to specify 12::05). It is also an
|
|
60
|
+
error to shift for occurrence of a partial date with year specified
|
|
61
|
+
(e.g., "+2x2019--").
|
|
62
|
+
|
|
63
|
+
On the other hand, shifting to an invalid date with day number
|
|
64
|
+
specified will shift more until a specified date is valid. For
|
|
65
|
+
example, if you add -2-29 with count 1 to 2019-01-01, you end up with
|
|
66
|
+
2020-02-29, because 2019-02-29 is not a valid date. If the count is 2
|
|
67
|
+
you get 2024-02-29 instead.
|
|
68
|
+
|
|
69
|
+
Shifting to an invalid date by a partial date with just a month number
|
|
70
|
+
will cause the date to moved backwards until the date is valid. E.g.,
|
|
71
|
+
if you shift by -6- with count 1 (next June) from 2019-05-31, you get
|
|
72
|
+
2019-06-30. With count 2 you get 2020-06-30.
|
|
73
|
+
|
|
74
|
+
This library is grown out of frustration that it is tedious to have a
|
|
75
|
+
shell script or program to get a datetime like "the next 6pm from now"
|
|
76
|
+
or "the next 3rd of any month from two days ago". With this module
|
|
77
|
+
they can be specified like "+1x18:00:00.0" and "-2day +1x--3"
|
|
78
|
+
respectively. In the expected use cases, counts are small numbers.
|
|
79
|
+
So the library is not always efficient (at times we just loop "count"
|
|
80
|
+
times to step forward or backward). Whenever it is simple to do so,
|
|
81
|
+
the implementation just forward to relativedelta, in which case they
|
|
82
|
+
are more efficient.
|
|
83
|
+
|
|
84
|
+
At present the program does not handle timezone and daylight saving.
|
|
85
|
+
This is bacause the author lives at a place where no daylight saving
|
|
86
|
+
is observed. Contributions are welcome.
|
|
@@ -1,27 +1,29 @@
|
|
|
1
1
|
"""Date command: A command-based date computation engine
|
|
2
2
|
|
|
3
3
|
datec allows you to use "date commands" to modify datetime's by adding
|
|
4
|
-
to them, like
|
|
4
|
+
to them, like this:
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
datetime.datetime.now() + datec.Period(2, 'week')
|
|
7
|
+
|
|
8
|
+
A date command can be parsed from strings using the parse() function,
|
|
7
9
|
which create a command from a string representation. This forms the
|
|
8
10
|
basis of the datec command, which is a command-line program to output
|
|
9
11
|
datetime after applying date commands. In general the date
|
|
10
12
|
representation is NxYYYY-mm-ddTHH:MM:SS.ffffff, where unspecified
|
|
11
13
|
parts are omitted leaving the symbols intact, like "2x-2-29T3::." (see
|
|
12
|
-
the following for the meaning). If fractional part is not
|
|
13
|
-
the "." may be omitted, if all time parts are not specified
|
|
14
|
-
can be omitted, if all date parts are not specified the
|
|
15
|
-
omitted, and if
|
|
16
|
-
|
|
17
|
-
weekday.
|
|
14
|
+
the following for the meaning). If the fractional part is not
|
|
15
|
+
specified the "." may be omitted, if all time parts are not specified
|
|
16
|
+
the "T::." can be omitted, if all date parts are not specified the
|
|
17
|
+
"--T" can be omitted, and if Nx may be omitted in some cases for
|
|
18
|
+
setting a partial datetime or weekday. There are a couple other more
|
|
19
|
+
formats like +3week and -2wed for shifting by period and weekday.
|
|
18
20
|
|
|
19
21
|
Date commands are in two forms: period shifting commands and partial
|
|
20
22
|
datetime shifting commands. The first type is more familiar: they
|
|
21
23
|
look like
|
|
22
24
|
|
|
23
|
-
+2week (shift the datetime forward by 2 week)
|
|
24
|
-
-1month (shift the datetime backward by 1 month)
|
|
25
|
+
* +2week (shift the datetime forward by 2 week)
|
|
26
|
+
* -1month (shift the datetime backward by 1 month)
|
|
25
27
|
|
|
26
28
|
Period is one of year, month, week, day, hour, minute and second,
|
|
27
29
|
represented by an object of the Period class. Fractional numbers are
|
|
@@ -33,12 +35,12 @@ month from 2019-07-31 02:00 gives you 2019-06-30 02:00).
|
|
|
33
35
|
|
|
34
36
|
Partial datetime shifting is less familiar. It looks like:
|
|
35
37
|
|
|
36
|
-
12:: (set the hour number to 12)
|
|
37
|
-
+2x12:: (move forward to the second hour 12)
|
|
38
|
-
+4x--31 (move forward to the fourth occurrence of day 31 of a month)
|
|
39
|
-
-3x-02-29 (move backward to the third occurrence of February 29)
|
|
40
|
-
wed (set to the Wednesday of the same week, week starts on Sunday)
|
|
41
|
-
-3wed (move to the third Wednesday before the current datetime)
|
|
38
|
+
* 12:: (set the hour number to 12)
|
|
39
|
+
* +2x12:: (move forward to the second hour 12)
|
|
40
|
+
* +4x--31 (move forward to the fourth occurrence of day 31 of a month)
|
|
41
|
+
* -3x-02-29 (move backward to the third occurrence of February 29)
|
|
42
|
+
* wed (set to the Wednesday of the same week, week starts on Sunday)
|
|
43
|
+
* -3wed (move to the third Wednesday before the current datetime)
|
|
42
44
|
|
|
43
45
|
They are represented by either a Weekday object or a PartialDate
|
|
44
46
|
object with a count. A count of 0 means setting instead of shifting.
|
|
@@ -47,7 +49,7 @@ Only integer counts are acceptable.
|
|
|
47
49
|
It is an error to set to an invalid date (e.g., --31 applied on
|
|
48
50
|
2019-06-25 is an error). The datetime parts which are specified must
|
|
49
51
|
be consecutive (it is an error to specify 12::05). It is also an
|
|
50
|
-
error to shift for occurrence of partial date with year specified
|
|
52
|
+
error to shift for occurrence of a partial date with year specified
|
|
51
53
|
(e.g., "+2x2019--").
|
|
52
54
|
|
|
53
55
|
On the other hand, shifting to an invalid date with day number
|
|
@@ -56,10 +58,10 @@ example, if you add -2-29 with count 1 to 2019-01-01, you end up with
|
|
|
56
58
|
2020-02-29, because 2019-02-29 is not a valid date. If the count is 2
|
|
57
59
|
you get 2024-02-29 instead.
|
|
58
60
|
|
|
59
|
-
Shifting to invalid date by
|
|
60
|
-
backwards until the date is valid. E.g.,
|
|
61
|
-
count 1 (next June) from 2019-05-31, you get
|
|
62
|
-
you get 2020-06-30.
|
|
61
|
+
Shifting to an invalid date by a partial date with just a month number
|
|
62
|
+
will cause the date to moved backwards until the date is valid. E.g.,
|
|
63
|
+
if you shift by -6- with count 1 (next June) from 2019-05-31, you get
|
|
64
|
+
2019-06-30. With count 2 you get 2020-06-30.
|
|
63
65
|
|
|
64
66
|
This library is grown out of frustration that it is tedious to have a
|
|
65
67
|
shell script or program to get a datetime like "the next 6pm from now"
|
|
@@ -77,16 +79,19 @@ is observed. Contributions are welcome.
|
|
|
77
79
|
|
|
78
80
|
"""
|
|
79
81
|
|
|
82
|
+
import datetime
|
|
80
83
|
import re
|
|
84
|
+
import typing
|
|
81
85
|
|
|
82
86
|
import dateutil.relativedelta as dr
|
|
83
87
|
|
|
84
88
|
|
|
85
89
|
__metaclass__ = type
|
|
86
90
|
|
|
91
|
+
__version__ = '0.2'
|
|
87
92
|
|
|
88
93
|
class ParseError(ValueError):
|
|
89
|
-
|
|
94
|
+
"""Represent an error in parsing."""
|
|
90
95
|
|
|
91
96
|
|
|
92
97
|
class Period:
|
|
@@ -105,14 +110,15 @@ class Period:
|
|
|
105
110
|
period (str): The period
|
|
106
111
|
|
|
107
112
|
"""
|
|
108
|
-
def __init__(self, count, period):
|
|
113
|
+
def __init__(self, count: float, period: str):
|
|
109
114
|
assert period in ('year', 'month', 'week', 'day',
|
|
110
115
|
'hour', 'minute', 'second')
|
|
111
116
|
self._count = count
|
|
112
117
|
self._period = period
|
|
113
118
|
|
|
114
|
-
def __radd__(self, dt):
|
|
115
|
-
return dt + dr.relativedelta(
|
|
119
|
+
def __radd__(self, dt: datetime.datetime) -> datetime.datetime:
|
|
120
|
+
return dt + dr.relativedelta(
|
|
121
|
+
**{self._period + 's': self._count}) # type: ignore
|
|
116
122
|
|
|
117
123
|
PARSE_RE = re.compile(r'''
|
|
118
124
|
^
|
|
@@ -122,7 +128,7 @@ class Period:
|
|
|
122
128
|
''', re.X)
|
|
123
129
|
|
|
124
130
|
@classmethod
|
|
125
|
-
def parse(cls, cmdstr):
|
|
131
|
+
def parse(cls, cmdstr: str) -> 'Period':
|
|
126
132
|
"""Parse a command string to a Period object
|
|
127
133
|
|
|
128
134
|
The command string should be of the form "<N><period>", where
|
|
@@ -138,6 +144,7 @@ class Period:
|
|
|
138
144
|
if not match:
|
|
139
145
|
raise ParseError('Cannot parse string %s' % cmdstr)
|
|
140
146
|
gdt = match.groupdict()
|
|
147
|
+
cnt: float
|
|
141
148
|
try:
|
|
142
149
|
cnt = int(gdt['count'])
|
|
143
150
|
except Exception:
|
|
@@ -169,13 +176,13 @@ class Weekday:
|
|
|
169
176
|
day (int): The weekday
|
|
170
177
|
|
|
171
178
|
"""
|
|
172
|
-
def __init__(self, count, day):
|
|
179
|
+
def __init__(self, count: int, day: int):
|
|
173
180
|
self._count = count
|
|
174
181
|
assert day in range(7)
|
|
175
182
|
self._day = day
|
|
176
183
|
self._drcls = _WEEKDAY_CLS[day]
|
|
177
184
|
|
|
178
|
-
def __radd__(self, dt):
|
|
185
|
+
def __radd__(self, dt: datetime.datetime) -> datetime.datetime:
|
|
179
186
|
if self._count > 0:
|
|
180
187
|
return dt + dr.relativedelta(
|
|
181
188
|
days=1, weekday=self._drcls(self._count))
|
|
@@ -195,7 +202,7 @@ class Weekday:
|
|
|
195
202
|
''', re.X)
|
|
196
203
|
|
|
197
204
|
@classmethod
|
|
198
|
-
def parse(cls, cmdstr):
|
|
205
|
+
def parse(cls, cmdstr: str) -> 'Weekday':
|
|
199
206
|
"""Parse a command string to a Weekday object
|
|
200
207
|
|
|
201
208
|
The command string should be of the form "<N><weekday>", where
|
|
@@ -248,8 +255,13 @@ class PartialDate:
|
|
|
248
255
|
|
|
249
256
|
_INVALID_SIG_RE = re.compile('10+1')
|
|
250
257
|
|
|
251
|
-
def __init__(
|
|
252
|
-
|
|
258
|
+
def __init__(
|
|
259
|
+
self, count: int = 0, year: typing.Optional[int] = None,
|
|
260
|
+
month: typing.Optional[int] = None, day: typing.Optional[int] = None,
|
|
261
|
+
hour: typing.Optional[int] = None, minute: typing.Optional[int] = None,
|
|
262
|
+
second: typing.Optional[int] = None,
|
|
263
|
+
microsecond: typing.Optional[int] = None
|
|
264
|
+
):
|
|
253
265
|
assert not count or not year, 'Absolute date with non-zero count'
|
|
254
266
|
assert not isinstance(second, float) or \
|
|
255
267
|
microsecond is None, 'Doubly specified microsecond'
|
|
@@ -274,7 +286,7 @@ class PartialDate:
|
|
|
274
286
|
'', 'years', 'months', 'days', 'hours', 'minutes', 'seconds'
|
|
275
287
|
]
|
|
276
288
|
|
|
277
|
-
def __radd__(self, dt):
|
|
289
|
+
def __radd__(self, dt: datetime.datetime) -> datetime.datetime:
|
|
278
290
|
if self._firstset == -1:
|
|
279
291
|
return dt
|
|
280
292
|
if not(self._count):
|
|
@@ -288,7 +300,7 @@ class PartialDate:
|
|
|
288
300
|
return self._monthshift(dt)
|
|
289
301
|
return self._dayshift(dt)
|
|
290
302
|
|
|
291
|
-
def _rset(self, dt):
|
|
303
|
+
def _rset(self, dt: datetime.datetime) -> datetime.datetime:
|
|
292
304
|
updater = {'year': self._year,
|
|
293
305
|
'month': self._month,
|
|
294
306
|
'day': self._day,
|
|
@@ -297,9 +309,9 @@ class PartialDate:
|
|
|
297
309
|
'second': self._second,
|
|
298
310
|
'microsecond': self._microsecond}
|
|
299
311
|
updater = {k: v for k, v in updater.items() if v is not None}
|
|
300
|
-
return dt.replace(**updater)
|
|
312
|
+
return dt.replace(**updater) # type: ignore
|
|
301
313
|
|
|
302
|
-
def _simpleshift(self, dt):
|
|
314
|
+
def _simpleshift(self, dt: datetime.datetime) -> datetime.datetime:
|
|
303
315
|
remain = self._count
|
|
304
316
|
ret = self._rset(dt)
|
|
305
317
|
if self._count < 0:
|
|
@@ -309,9 +321,9 @@ class PartialDate:
|
|
|
309
321
|
if ret > dt:
|
|
310
322
|
remain -= 1
|
|
311
323
|
mod_field = self._FIRSTSET_MOD[self._firstset]
|
|
312
|
-
return ret + dr.relativedelta(**{mod_field: remain})
|
|
324
|
+
return ret + dr.relativedelta(**{mod_field: remain}) # type: ignore
|
|
313
325
|
|
|
314
|
-
def _dayshift(self, dt):
|
|
326
|
+
def _dayshift(self, dt: datetime.datetime) -> datetime.datetime:
|
|
315
327
|
# Day specified
|
|
316
328
|
if self._firstset == 2: # modify month
|
|
317
329
|
shift = dr.relativedelta(months=1 if self._count > 0 else -1)
|
|
@@ -344,8 +356,9 @@ class PartialDate:
|
|
|
344
356
|
continue
|
|
345
357
|
count -= 1
|
|
346
358
|
|
|
347
|
-
def _monthshift(self, dt):
|
|
359
|
+
def _monthshift(self, dt: datetime.datetime) -> datetime.datetime:
|
|
348
360
|
# Only month specified, shift by month rather than by year
|
|
361
|
+
assert self._month is not None
|
|
349
362
|
if self._count > 0:
|
|
350
363
|
num_months = self._month - dt.month
|
|
351
364
|
sign = 1
|
|
@@ -389,7 +402,7 @@ class PartialDate:
|
|
|
389
402
|
''', re.X)
|
|
390
403
|
|
|
391
404
|
@classmethod
|
|
392
|
-
def parse(cls, cmdstr):
|
|
405
|
+
def parse(cls, cmdstr: str) -> 'PartialDate':
|
|
393
406
|
"""Parse a command string to a PartialDate object
|
|
394
407
|
|
|
395
408
|
The command string should be of the form
|
|
@@ -413,7 +426,7 @@ class PartialDate:
|
|
|
413
426
|
raise ParseError('Cannot parse string %s' % cmdstr)
|
|
414
427
|
gdt = match.groupdict()
|
|
415
428
|
|
|
416
|
-
def _matchval(key):
|
|
429
|
+
def _matchval(key: str) -> typing.Optional[int]:
|
|
417
430
|
val = gdt.get(key)
|
|
418
431
|
if not val:
|
|
419
432
|
return None
|
|
@@ -433,7 +446,7 @@ class PartialDate:
|
|
|
433
446
|
microsecond)
|
|
434
447
|
|
|
435
448
|
|
|
436
|
-
def parse(cmdstr):
|
|
449
|
+
def parse(cmdstr: str) -> typing.Union[Period, Weekday, PartialDate]:
|
|
437
450
|
"""Attempt to parse one of the possible date command
|
|
438
451
|
|
|
439
452
|
Args:
|
datec-0.2/pyproject.toml
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
dynamic = ["version"]
|
|
7
|
+
name = "datec"
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
requires-python = ">= 3.9"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"python-dateutil"
|
|
12
|
+
]
|
|
13
|
+
authors = [
|
|
14
|
+
{ name="Isaac To", email="isaac.to@gmail.com" },
|
|
15
|
+
]
|
|
16
|
+
description = "Date Command"
|
|
17
|
+
license = "MIT"
|
|
18
|
+
license-files = ["LICENSE"]
|
|
19
|
+
classifiers=[
|
|
20
|
+
"Development Status :: 4 - Beta",
|
|
21
|
+
"Intended Audience :: Developers",
|
|
22
|
+
"Topic :: Software Development :: Libraries",
|
|
23
|
+
"Programming Language :: Python :: 3.9",
|
|
24
|
+
"Programming Language :: Python :: 3.10",
|
|
25
|
+
"Programming Language :: Python :: 3.11",
|
|
26
|
+
"Programming Language :: Python :: 3.12",
|
|
27
|
+
"Programming Language :: Python :: 3.13",
|
|
28
|
+
"Programming Language :: Python :: 3.14",
|
|
29
|
+
"Operating System :: OS Independent",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.scripts]
|
|
33
|
+
datec = "datec:__main__.main"
|
|
34
|
+
|
|
35
|
+
[project.urls]
|
|
36
|
+
Homepage = "https://github.com/isaacto/datec"
|
|
37
|
+
Repository = "https://github.com/isaacto/datec.git"
|
|
38
|
+
Issues = "https://github.com/isaacto/datec/issues"
|
|
39
|
+
|
|
40
|
+
[tool.hatch.version]
|
|
41
|
+
source = "regex"
|
|
42
|
+
path = "datec/__init__.py"
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import unittest
|
|
3
|
+
|
|
4
|
+
import datec
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class DatecTest(unittest.TestCase):
|
|
8
|
+
def test_period(self):
|
|
9
|
+
dt = datetime.datetime(2019, 5, 15)
|
|
10
|
+
self.assertEqual(dt + datec.Period(1, 'month'),
|
|
11
|
+
datetime.datetime(2019, 6, 15))
|
|
12
|
+
self.assertEqual(dt + datec.parse('+1.5second'),
|
|
13
|
+
datetime.datetime(2019, 5, 15, 0, 0, 1, 500000))
|
|
14
|
+
with self.assertRaises(datec.ParseError):
|
|
15
|
+
datec.Period.parse('+1mon')
|
|
16
|
+
|
|
17
|
+
def test_weekday(self):
|
|
18
|
+
dt = datetime.datetime(2019, 5, 15)
|
|
19
|
+
self.assertEqual(dt + datec.Weekday(1, datec.MON),
|
|
20
|
+
datetime.datetime(2019, 5, 20))
|
|
21
|
+
self.assertEqual(dt + datec.Weekday.parse('+2mon'),
|
|
22
|
+
datetime.datetime(2019, 5, 27))
|
|
23
|
+
self.assertEqual(dt + datec.Weekday.parse('-2WED'),
|
|
24
|
+
datetime.datetime(2019, 5, 1))
|
|
25
|
+
self.assertEqual(dt + datec.parse('MON'),
|
|
26
|
+
datetime.datetime(2019, 5, 13))
|
|
27
|
+
self.assertEqual(dt + datec.Weekday(0, datec.FRI),
|
|
28
|
+
datetime.datetime(2019, 5, 17))
|
|
29
|
+
with self.assertRaises(datec.ParseError):
|
|
30
|
+
datec.Weekday.parse('MO')
|
|
31
|
+
|
|
32
|
+
def test_partialdate_simple(self):
|
|
33
|
+
dt = datetime.datetime(2019, 5, 15, 8, 15)
|
|
34
|
+
self.assertEqual(dt + datec.PartialDate(1),
|
|
35
|
+
datetime.datetime(2019, 5, 15, 8, 15))
|
|
36
|
+
self.assertEqual(dt + datec.PartialDate(0, minute=7, second=16),
|
|
37
|
+
datetime.datetime(2019, 5, 15, 8, 7, 16))
|
|
38
|
+
self.assertEqual(dt + datec.PartialDate(1, minute=7, second=16),
|
|
39
|
+
datetime.datetime(2019, 5, 15, 9, 7, 16))
|
|
40
|
+
self.assertEqual(dt + datec.PartialDate(1, minute=17, second=16),
|
|
41
|
+
datetime.datetime(2019, 5, 15, 8, 17, 16))
|
|
42
|
+
self.assertEqual(dt + datec.PartialDate(2, minute=7, second=16),
|
|
43
|
+
datetime.datetime(2019, 5, 15, 10, 7, 16))
|
|
44
|
+
self.assertEqual(dt + datec.PartialDate.parse(':7:16'),
|
|
45
|
+
datetime.datetime(2019, 5, 15, 8, 7, 16))
|
|
46
|
+
self.assertEqual(dt + datec.PartialDate.parse('-1x:7:16'),
|
|
47
|
+
datetime.datetime(2019, 5, 15, 8, 7, 16))
|
|
48
|
+
self.assertEqual(dt + datec.PartialDate.parse('-1x:17:16'),
|
|
49
|
+
datetime.datetime(2019, 5, 15, 7, 17, 16))
|
|
50
|
+
self.assertEqual(dt + datec.parse('-1x:17:16.2'),
|
|
51
|
+
datetime.datetime(2019, 5, 15, 7, 17, 16, 200000))
|
|
52
|
+
with self.assertRaises(datec.ParseError):
|
|
53
|
+
datec.PartialDate.parse('---')
|
|
54
|
+
|
|
55
|
+
def test_partialdate_monthshift(self):
|
|
56
|
+
dt = datetime.datetime(2019, 5, 15)
|
|
57
|
+
self.assertEqual(dt + datec.PartialDate(1, month=2),
|
|
58
|
+
datetime.datetime(2020, 2, 15))
|
|
59
|
+
self.assertEqual(dt + datec.PartialDate(-1, month=2),
|
|
60
|
+
datetime.datetime(2019, 2, 15))
|
|
61
|
+
dt = datetime.datetime(2019, 5, 15)
|
|
62
|
+
self.assertEqual(dt + datec.PartialDate.parse('+1x-2-'),
|
|
63
|
+
datetime.datetime(2020, 2, 15))
|
|
64
|
+
dt = datetime.datetime(2019, 5, 30)
|
|
65
|
+
self.assertEqual(dt + datec.PartialDate(1, month=2),
|
|
66
|
+
datetime.datetime(2020, 2, 29))
|
|
67
|
+
|
|
68
|
+
def test_partialdate_dayshift(self):
|
|
69
|
+
dt = datetime.datetime(2019, 5, 30)
|
|
70
|
+
self.assertEqual(dt + datec.PartialDate(1, month=5, day=30),
|
|
71
|
+
datetime.datetime(2020, 5, 30))
|
|
72
|
+
self.assertEqual(dt + datec.PartialDate(1, day=31),
|
|
73
|
+
datetime.datetime(2019, 5, 31))
|
|
74
|
+
self.assertEqual(dt + datec.PartialDate(2, day=31),
|
|
75
|
+
datetime.datetime(2019, 7, 31))
|
|
76
|
+
self.assertEqual(dt + datec.PartialDate(-1, day=31),
|
|
77
|
+
datetime.datetime(2019, 3, 31))
|
|
78
|
+
dt = datetime.datetime(2019, 2, 15)
|
|
79
|
+
self.assertEqual(dt + datec.PartialDate(1, day=30),
|
|
80
|
+
datetime.datetime(2019, 3, 30))
|
|
81
|
+
with self.assertRaises(ValueError):
|
|
82
|
+
dt + datec.PartialDate(1, month=2, day=30)
|
datec-0.1/PKG-INFO
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 1.1
|
|
2
|
-
Name: datec
|
|
3
|
-
Version: 0.1
|
|
4
|
-
Summary: Date Command
|
|
5
|
-
Home-page: https://github.com/isaacto/datec
|
|
6
|
-
Author: Isaac To
|
|
7
|
-
Author-email: isaac.to@gmail.com
|
|
8
|
-
License: UNKNOWN
|
|
9
|
-
Description: # Date command: A command-based date computation engine
|
|
10
|
-
|
|
11
|
-
datec allows you to use "date commands" to modify datetime's by adding
|
|
12
|
-
to them, like datetime.datetime.now() + Period(2, 'week').
|
|
13
|
-
|
|
14
|
-
A date command can be parsed from string using the parse() function,
|
|
15
|
-
which create a command from a string representation. This forms the
|
|
16
|
-
basis of the datec command, which is a command-line program to output
|
|
17
|
-
datetime after applying date commands. In general the date
|
|
18
|
-
representation is NxYYYY-mm-ddTHH:MM:SS.ffffff, where unspecified
|
|
19
|
-
parts are omitted leaving the symbols intact, like "2x-2-29T3::." (see
|
|
20
|
-
the following for the meaning). If fractional part is not specified
|
|
21
|
-
the "." may be omitted, if all time parts are not specified the "T::."
|
|
22
|
-
can be omitted, if all date parts are not specified the "--T" can be
|
|
23
|
-
omitted, and if N is 0 the x may be omitted. By there are a couple
|
|
24
|
-
other more formats like +3week and -2wed for shifting by period and
|
|
25
|
-
weekday.
|
|
26
|
-
|
|
27
|
-
Date commands are in two forms: period shifting commands and partial
|
|
28
|
-
datetime shifting commands. The first type is more familiar: they
|
|
29
|
-
look like
|
|
30
|
-
|
|
31
|
-
* +2week (shift the datetime forward by 2 week)
|
|
32
|
-
* -1month (shift the datetime backward by 1 month)
|
|
33
|
-
|
|
34
|
-
Period is one of year, month, week, day, hour, minute and second,
|
|
35
|
-
represented by an object of the Period class. Fractional numbers are
|
|
36
|
-
acceptable except for year and month. If shifting a period leads to
|
|
37
|
-
an invalid date (e.g., shift backward 1 month from 2019-07-31), it
|
|
38
|
-
moves backwards the closest valid date (here, 2019-06-30). In general
|
|
39
|
-
the parts finer than the shifted part is unaffected (e.g., shifting 1
|
|
40
|
-
month from 2019-07-31 02:00 gives you 2019-06-30 02:00).
|
|
41
|
-
|
|
42
|
-
Partial datetime shifting is less familiar. It looks like:
|
|
43
|
-
|
|
44
|
-
* 12:: (set the hour number to 12)
|
|
45
|
-
* +2x12:: (move forward to the second hour 12)
|
|
46
|
-
* +4x--31 (move forward to the fourth occurrence of day 31 of a month)
|
|
47
|
-
* -3x-02-29 (move backward to the third occurrence of February 29)
|
|
48
|
-
* wed (set to the Wednesday of the same week, week starts on Sunday)
|
|
49
|
-
* -3wed (move to the third Wednesday before the current datetime)
|
|
50
|
-
|
|
51
|
-
They are represented by either a Weekday object or a PartialDate
|
|
52
|
-
object with a count. A count of 0 means setting instead of shifting.
|
|
53
|
-
Only integer counts are acceptable.
|
|
54
|
-
|
|
55
|
-
It is an error to set to an invalid date (e.g., --31 applied on
|
|
56
|
-
2019-06-25 is an error). The datetime parts which are specified must
|
|
57
|
-
be consecutive (it is an error to specify 12::05). It is also an
|
|
58
|
-
error to shift for occurrence of partial date with year specified
|
|
59
|
-
(e.g., "+2x2019--").
|
|
60
|
-
|
|
61
|
-
On the other hand, shifting to an invalid date with day number
|
|
62
|
-
specified will shift more until a specified date is valid. For
|
|
63
|
-
example, if you add -2-29 with count 1 to 2019-01-01, you end up with
|
|
64
|
-
2020-02-29, because 2019-02-29 is not a valid date. If the count is 2
|
|
65
|
-
you get 2024-02-29 instead.
|
|
66
|
-
|
|
67
|
-
Shifting to invalid date by month will cause the date to moved
|
|
68
|
-
backwards until the date is valid. E.g., if you shift by -6- with
|
|
69
|
-
count 1 (next June) from 2019-05-31, you get 2019-06-30. With count 2
|
|
70
|
-
you get 2020-06-30.
|
|
71
|
-
|
|
72
|
-
This library is grown out of frustration that it is tedious to have a
|
|
73
|
-
shell script or program to get a datetime like "the next 6pm from now"
|
|
74
|
-
or "the next 3rd of any month from two days ago". With this module
|
|
75
|
-
they can be specified like "+1x18:00:00.0" and "-2day +1x--3"
|
|
76
|
-
respectively. In the expected use cases, counts are small numbers.
|
|
77
|
-
So the library is not always efficient (at times we just loop "count"
|
|
78
|
-
times to step forward or backward). Whenever it is simple to do so,
|
|
79
|
-
the implementation just forward to relativedelta, in which case they
|
|
80
|
-
are more efficient.
|
|
81
|
-
|
|
82
|
-
At present the program does not handle timezone and daylight saving.
|
|
83
|
-
This is bacause the author lives at a place where no daylight saving
|
|
84
|
-
is observed. Contributions are welcome.
|
|
85
|
-
|
|
86
|
-
Platform: UNKNOWN
|
|
87
|
-
Classifier: Development Status :: 4 - Beta
|
|
88
|
-
Classifier: Programming Language :: Python :: 2.7
|
|
89
|
-
Classifier: Programming Language :: Python :: 3
|
|
90
|
-
Classifier: Topic :: Software Development :: Libraries
|
|
91
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
92
|
-
Classifier: Operating System :: OS Independent
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 1.1
|
|
2
|
-
Name: datec
|
|
3
|
-
Version: 0.1
|
|
4
|
-
Summary: Date Command
|
|
5
|
-
Home-page: https://github.com/isaacto/datec
|
|
6
|
-
Author: Isaac To
|
|
7
|
-
Author-email: isaac.to@gmail.com
|
|
8
|
-
License: UNKNOWN
|
|
9
|
-
Description: # Date command: A command-based date computation engine
|
|
10
|
-
|
|
11
|
-
datec allows you to use "date commands" to modify datetime's by adding
|
|
12
|
-
to them, like datetime.datetime.now() + Period(2, 'week').
|
|
13
|
-
|
|
14
|
-
A date command can be parsed from string using the parse() function,
|
|
15
|
-
which create a command from a string representation. This forms the
|
|
16
|
-
basis of the datec command, which is a command-line program to output
|
|
17
|
-
datetime after applying date commands. In general the date
|
|
18
|
-
representation is NxYYYY-mm-ddTHH:MM:SS.ffffff, where unspecified
|
|
19
|
-
parts are omitted leaving the symbols intact, like "2x-2-29T3::." (see
|
|
20
|
-
the following for the meaning). If fractional part is not specified
|
|
21
|
-
the "." may be omitted, if all time parts are not specified the "T::."
|
|
22
|
-
can be omitted, if all date parts are not specified the "--T" can be
|
|
23
|
-
omitted, and if N is 0 the x may be omitted. By there are a couple
|
|
24
|
-
other more formats like +3week and -2wed for shifting by period and
|
|
25
|
-
weekday.
|
|
26
|
-
|
|
27
|
-
Date commands are in two forms: period shifting commands and partial
|
|
28
|
-
datetime shifting commands. The first type is more familiar: they
|
|
29
|
-
look like
|
|
30
|
-
|
|
31
|
-
* +2week (shift the datetime forward by 2 week)
|
|
32
|
-
* -1month (shift the datetime backward by 1 month)
|
|
33
|
-
|
|
34
|
-
Period is one of year, month, week, day, hour, minute and second,
|
|
35
|
-
represented by an object of the Period class. Fractional numbers are
|
|
36
|
-
acceptable except for year and month. If shifting a period leads to
|
|
37
|
-
an invalid date (e.g., shift backward 1 month from 2019-07-31), it
|
|
38
|
-
moves backwards the closest valid date (here, 2019-06-30). In general
|
|
39
|
-
the parts finer than the shifted part is unaffected (e.g., shifting 1
|
|
40
|
-
month from 2019-07-31 02:00 gives you 2019-06-30 02:00).
|
|
41
|
-
|
|
42
|
-
Partial datetime shifting is less familiar. It looks like:
|
|
43
|
-
|
|
44
|
-
* 12:: (set the hour number to 12)
|
|
45
|
-
* +2x12:: (move forward to the second hour 12)
|
|
46
|
-
* +4x--31 (move forward to the fourth occurrence of day 31 of a month)
|
|
47
|
-
* -3x-02-29 (move backward to the third occurrence of February 29)
|
|
48
|
-
* wed (set to the Wednesday of the same week, week starts on Sunday)
|
|
49
|
-
* -3wed (move to the third Wednesday before the current datetime)
|
|
50
|
-
|
|
51
|
-
They are represented by either a Weekday object or a PartialDate
|
|
52
|
-
object with a count. A count of 0 means setting instead of shifting.
|
|
53
|
-
Only integer counts are acceptable.
|
|
54
|
-
|
|
55
|
-
It is an error to set to an invalid date (e.g., --31 applied on
|
|
56
|
-
2019-06-25 is an error). The datetime parts which are specified must
|
|
57
|
-
be consecutive (it is an error to specify 12::05). It is also an
|
|
58
|
-
error to shift for occurrence of partial date with year specified
|
|
59
|
-
(e.g., "+2x2019--").
|
|
60
|
-
|
|
61
|
-
On the other hand, shifting to an invalid date with day number
|
|
62
|
-
specified will shift more until a specified date is valid. For
|
|
63
|
-
example, if you add -2-29 with count 1 to 2019-01-01, you end up with
|
|
64
|
-
2020-02-29, because 2019-02-29 is not a valid date. If the count is 2
|
|
65
|
-
you get 2024-02-29 instead.
|
|
66
|
-
|
|
67
|
-
Shifting to invalid date by month will cause the date to moved
|
|
68
|
-
backwards until the date is valid. E.g., if you shift by -6- with
|
|
69
|
-
count 1 (next June) from 2019-05-31, you get 2019-06-30. With count 2
|
|
70
|
-
you get 2020-06-30.
|
|
71
|
-
|
|
72
|
-
This library is grown out of frustration that it is tedious to have a
|
|
73
|
-
shell script or program to get a datetime like "the next 6pm from now"
|
|
74
|
-
or "the next 3rd of any month from two days ago". With this module
|
|
75
|
-
they can be specified like "+1x18:00:00.0" and "-2day +1x--3"
|
|
76
|
-
respectively. In the expected use cases, counts are small numbers.
|
|
77
|
-
So the library is not always efficient (at times we just loop "count"
|
|
78
|
-
times to step forward or backward). Whenever it is simple to do so,
|
|
79
|
-
the implementation just forward to relativedelta, in which case they
|
|
80
|
-
are more efficient.
|
|
81
|
-
|
|
82
|
-
At present the program does not handle timezone and daylight saving.
|
|
83
|
-
This is bacause the author lives at a place where no daylight saving
|
|
84
|
-
is observed. Contributions are welcome.
|
|
85
|
-
|
|
86
|
-
Platform: UNKNOWN
|
|
87
|
-
Classifier: Development Status :: 4 - Beta
|
|
88
|
-
Classifier: Programming Language :: Python :: 2.7
|
|
89
|
-
Classifier: Programming Language :: Python :: 3
|
|
90
|
-
Classifier: Topic :: Software Development :: Libraries
|
|
91
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
92
|
-
Classifier: Operating System :: OS Independent
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
python-dateutil
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
datec
|
datec-0.1/setup.cfg
DELETED
datec-0.1/setup.py
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import setuptools
|
|
2
|
-
|
|
3
|
-
with open('README.md', 'r') as fh:
|
|
4
|
-
long_description = fh.read()
|
|
5
|
-
|
|
6
|
-
setuptools.setup(
|
|
7
|
-
name='datec',
|
|
8
|
-
version='0.1',
|
|
9
|
-
author='Isaac To',
|
|
10
|
-
author_email='isaac.to@gmail.com',
|
|
11
|
-
description='Date Command',
|
|
12
|
-
long_description=long_description,
|
|
13
|
-
long_description_content_type='text/markdown',
|
|
14
|
-
url='https://github.com/isaacto/datec',
|
|
15
|
-
packages=setuptools.find_packages(),
|
|
16
|
-
install_requires=[
|
|
17
|
-
'python-dateutil'
|
|
18
|
-
],
|
|
19
|
-
entry_points={
|
|
20
|
-
"console_scripts": [
|
|
21
|
-
"datec=datec.__main__:main",
|
|
22
|
-
]
|
|
23
|
-
},
|
|
24
|
-
classifiers=[
|
|
25
|
-
'Development Status :: 4 - Beta',
|
|
26
|
-
'Programming Language :: Python :: 2.7',
|
|
27
|
-
'Programming Language :: Python :: 3',
|
|
28
|
-
'Topic :: Software Development :: Libraries',
|
|
29
|
-
'License :: OSI Approved :: MIT License',
|
|
30
|
-
'Operating System :: OS Independent',
|
|
31
|
-
],
|
|
32
|
-
)
|