datec 0.1__tar.gz → 0.3__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.3/.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.3/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.3/PKG-INFO ADDED
@@ -0,0 +1,119 @@
1
+ Metadata-Version: 2.4
2
+ Name: datec
3
+ Version: 0.3
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, or sleep until that time if
44
+ "-w" is given. In general the date representation is
45
+ NxYYYY-mm-ddTHH:MM:SS.ffffff, where unspecified parts are omitted
46
+ leaving the symbols intact, like "2x-2-29T3::." (see the following for
47
+ the meaning). If the fractional part is not specified the "." may be
48
+ omitted, if all time parts are not specified the "T::." can be
49
+ omitted, if all date parts are not specified the "--T" can be omitted,
50
+ and if Nx may be omitted in some cases for setting a partial datetime
51
+ or weekday. There are a couple other more formats like +3week and
52
+ -2wed for shifting by period and weekday.
53
+
54
+ Date commands are in two forms: period shifting commands and partial
55
+ datetime shifting commands. The first type is more familiar: they
56
+ look like
57
+
58
+ * +2week (shift the datetime forward by 2 week)
59
+ * -1month (shift the datetime backward by 1 month)
60
+
61
+ Period is one of year, month, week, day, hour, minute and second,
62
+ represented by an object of the Period class. Fractional numbers are
63
+ acceptable except for year and month. If shifting a period leads to
64
+ an invalid date (e.g., shift backward 1 month from 2019-07-31), it
65
+ moves backwards the closest valid date (here, 2019-06-30). In general
66
+ the parts finer than the shifted part is unaffected (e.g., shifting 1
67
+ month from 2019-07-31 02:00 gives you 2019-06-30 02:00).
68
+
69
+ Partial datetime shifting is less familiar. It looks like:
70
+
71
+ * 12:: (set the hour number to 12)
72
+ * +2x12:: (move forward to the second hour 12)
73
+ * +4x--31 (move forward to the fourth occurrence of day 31 of a month)
74
+ * -3x-02-29 (move backward to the third occurrence of February 29)
75
+ * wed (set to the Wednesday of the same week, week starts on Sunday)
76
+ * -3wed (move to the third Wednesday before the current datetime)
77
+
78
+ They are represented by either a Weekday object or a PartialDate
79
+ object with a count. A count of 0 means setting instead of shifting.
80
+ Only integer counts are acceptable.
81
+
82
+ A trailing "/" on a partial date command sets all fields after the
83
+ last specified field to zero. For example, `12::/` sets the hour to
84
+ 12 and the minute, second and microsecond to 0, whereas `12::` would
85
+ leave those unchanged.
86
+
87
+ It is an error to set to an invalid date (e.g., --31 applied on
88
+ 2019-06-25 is an error). The datetime parts which are specified must
89
+ be consecutive (it is an error to specify 12::05). It is also an
90
+ error to shift for occurrence of a partial date with year specified
91
+ (e.g., "+2x2019--").
92
+
93
+ On the other hand, shifting to an invalid date with day number
94
+ specified will shift more until a specified date is valid. For
95
+ example, if you add -2-29 with count 1 to 2019-01-01, you end up with
96
+ 2020-02-29, because 2019-02-29 is not a valid date. If the count is 2
97
+ you get 2024-02-29 instead.
98
+
99
+ Shifting to an invalid date by a partial date with just a month number
100
+ will cause the date to moved backwards until the date is valid. E.g.,
101
+ if you shift by -6- with count 1 (next June) from 2019-05-31, you get
102
+ 2019-06-30. With count 2 you get 2020-06-30.
103
+
104
+ All these functionalities are available in the constructors too. Read
105
+ their docstring to find how to use them.
106
+
107
+ This library is grown out of frustration that it is tedious to have a
108
+ shell script or program to get a datetime like "the next 6pm from now"
109
+ or "the next 3rd of any month from two days ago". With this module
110
+ they can be specified like "+1x18:00:00.0" and "-2day +1x--3"
111
+ respectively. In the expected use cases, counts are small numbers.
112
+ So the library is not always efficient (at times we just loop "count"
113
+ times to step forward or backward). Whenever it is simple to do so,
114
+ the implementation just forward to relativedelta, in which case they
115
+ are more efficient.
116
+
117
+ At present the program does not handle timezone and daylight saving.
118
+ This is bacause the author lives at a place where no daylight saving
119
+ is observed. Contributions are welcome.
datec-0.3/README.md ADDED
@@ -0,0 +1,95 @@
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, or sleep until that time if
20
+ "-w" is given. In general the date representation is
21
+ NxYYYY-mm-ddTHH:MM:SS.ffffff, where unspecified parts are omitted
22
+ leaving the symbols intact, like "2x-2-29T3::." (see the following for
23
+ the meaning). If the fractional part is not specified the "." may be
24
+ omitted, if all time parts are not specified the "T::." can be
25
+ omitted, if all date parts are not specified the "--T" can be omitted,
26
+ and if Nx may be omitted in some cases for setting a partial datetime
27
+ or weekday. There are a couple other more formats like +3week and
28
+ -2wed for shifting by period and weekday.
29
+
30
+ Date commands are in two forms: period shifting commands and partial
31
+ datetime shifting commands. The first type is more familiar: they
32
+ look like
33
+
34
+ * +2week (shift the datetime forward by 2 week)
35
+ * -1month (shift the datetime backward by 1 month)
36
+
37
+ Period is one of year, month, week, day, hour, minute and second,
38
+ represented by an object of the Period class. Fractional numbers are
39
+ acceptable except for year and month. If shifting a period leads to
40
+ an invalid date (e.g., shift backward 1 month from 2019-07-31), it
41
+ moves backwards the closest valid date (here, 2019-06-30). In general
42
+ the parts finer than the shifted part is unaffected (e.g., shifting 1
43
+ month from 2019-07-31 02:00 gives you 2019-06-30 02:00).
44
+
45
+ Partial datetime shifting is less familiar. It looks like:
46
+
47
+ * 12:: (set the hour number to 12)
48
+ * +2x12:: (move forward to the second hour 12)
49
+ * +4x--31 (move forward to the fourth occurrence of day 31 of a month)
50
+ * -3x-02-29 (move backward to the third occurrence of February 29)
51
+ * wed (set to the Wednesday of the same week, week starts on Sunday)
52
+ * -3wed (move to the third Wednesday before the current datetime)
53
+
54
+ They are represented by either a Weekday object or a PartialDate
55
+ object with a count. A count of 0 means setting instead of shifting.
56
+ Only integer counts are acceptable.
57
+
58
+ A trailing "/" on a partial date command sets all fields after the
59
+ last specified field to zero. For example, `12::/` sets the hour to
60
+ 12 and the minute, second and microsecond to 0, whereas `12::` would
61
+ leave those unchanged.
62
+
63
+ It is an error to set to an invalid date (e.g., --31 applied on
64
+ 2019-06-25 is an error). The datetime parts which are specified must
65
+ be consecutive (it is an error to specify 12::05). It is also an
66
+ error to shift for occurrence of a partial date with year specified
67
+ (e.g., "+2x2019--").
68
+
69
+ On the other hand, shifting to an invalid date with day number
70
+ specified will shift more until a specified date is valid. For
71
+ example, if you add -2-29 with count 1 to 2019-01-01, you end up with
72
+ 2020-02-29, because 2019-02-29 is not a valid date. If the count is 2
73
+ you get 2024-02-29 instead.
74
+
75
+ Shifting to an invalid date by a partial date with just a month number
76
+ will cause the date to moved backwards until the date is valid. E.g.,
77
+ if you shift by -6- with count 1 (next June) from 2019-05-31, you get
78
+ 2019-06-30. With count 2 you get 2020-06-30.
79
+
80
+ All these functionalities are available in the constructors too. Read
81
+ their docstring to find how to use them.
82
+
83
+ This library is grown out of frustration that it is tedious to have a
84
+ shell script or program to get a datetime like "the next 6pm from now"
85
+ or "the next 3rd of any month from two days ago". With this module
86
+ they can be specified like "+1x18:00:00.0" and "-2day +1x--3"
87
+ respectively. In the expected use cases, counts are small numbers.
88
+ So the library is not always efficient (at times we just loop "count"
89
+ times to step forward or backward). Whenever it is simple to do so,
90
+ the implementation just forward to relativedelta, in which case they
91
+ are more efficient.
92
+
93
+ At present the program does not handle timezone and daylight saving.
94
+ This is bacause the author lives at a place where no daylight saving
95
+ 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 datetime.datetime.now() + Period(2, 'week').
4
+ to them, like this:
5
5
 
6
- A date command can be parsed from string using the parse() function,
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 specified
13
- the "." may be omitted, if all time parts are not specified the "T::."
14
- can be omitted, if all date parts are not specified the "--T" can be
15
- omitted, and if N is 0 the x may be omitted. By there are a couple
16
- other more formats like +3week and -2wed for shifting by period and
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,21 +35,26 @@ 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.
45
47
  Only integer counts are acceptable.
46
48
 
49
+ A trailing "/" on a partial date command sets all fields after the
50
+ last specified field to zero. For example, `12::/` sets the hour to
51
+ 12 and the minute, second and microsecond to 0, whereas `12::` would
52
+ leave those unchanged.
53
+
47
54
  It is an error to set to an invalid date (e.g., --31 applied on
48
55
  2019-06-25 is an error). The datetime parts which are specified must
49
56
  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
57
+ error to shift for occurrence of a partial date with year specified
51
58
  (e.g., "+2x2019--").
52
59
 
53
60
  On the other hand, shifting to an invalid date with day number
@@ -56,10 +63,10 @@ example, if you add -2-29 with count 1 to 2019-01-01, you end up with
56
63
  2020-02-29, because 2019-02-29 is not a valid date. If the count is 2
57
64
  you get 2024-02-29 instead.
58
65
 
59
- Shifting to invalid date by month will cause the date to moved
60
- backwards until the date is valid. E.g., if you shift by -6- with
61
- count 1 (next June) from 2019-05-31, you get 2019-06-30. With count 2
62
- you get 2020-06-30.
66
+ Shifting to an invalid date by a partial date with just a month number
67
+ will cause the date to moved backwards until the date is valid. E.g.,
68
+ if you shift by -6- with count 1 (next June) from 2019-05-31, you get
69
+ 2019-06-30. With count 2 you get 2020-06-30.
63
70
 
64
71
  This library is grown out of frustration that it is tedious to have a
65
72
  shell script or program to get a datetime like "the next 6pm from now"
@@ -77,16 +84,18 @@ is observed. Contributions are welcome.
77
84
 
78
85
  """
79
86
 
87
+ import contextlib
88
+ import datetime
80
89
  import re
90
+ import typing
81
91
 
82
92
  import dateutil.relativedelta as dr
83
93
 
84
94
 
85
- __metaclass__ = type
86
-
95
+ __version__ = '0.3'
87
96
 
88
97
  class ParseError(ValueError):
89
- pass
98
+ """Represent an error in parsing."""
90
99
 
91
100
 
92
101
  class Period:
@@ -105,14 +114,18 @@ class Period:
105
114
  period (str): The period
106
115
 
107
116
  """
108
- def __init__(self, count, period):
109
- assert period in ('year', 'month', 'week', 'day',
110
- 'hour', 'minute', 'second')
117
+ def __init__(self, count: float, period: str):
118
+ if period not in ('year', 'month', 'week', 'day',
119
+ 'hour', 'minute', 'second'):
120
+ raise ValueError(f'Invalid period: {period}')
121
+ if period in ('year', 'month') and count != int(count):
122
+ raise ValueError(f'Invalid count {count} for period {period}')
111
123
  self._count = count
112
124
  self._period = period
113
125
 
114
- def __radd__(self, dt):
115
- return dt + dr.relativedelta(**{self._period + 's': self._count})
126
+ def __radd__(self, dt: datetime.datetime) -> datetime.datetime:
127
+ return dt + dr.relativedelta(
128
+ **{self._period + 's': self._count}) # type: ignore
116
129
 
117
130
  PARSE_RE = re.compile(r'''
118
131
  ^
@@ -122,7 +135,7 @@ class Period:
122
135
  ''', re.X)
123
136
 
124
137
  @classmethod
125
- def parse(cls, cmdstr):
138
+ def parse(cls, cmdstr: str) -> 'Period':
126
139
  """Parse a command string to a Period object
127
140
 
128
141
  The command string should be of the form "<N><period>", where
@@ -138,11 +151,13 @@ class Period:
138
151
  if not match:
139
152
  raise ParseError('Cannot parse string %s' % cmdstr)
140
153
  gdt = match.groupdict()
154
+ cnt: float
155
+ with contextlib.suppress(ValueError):
156
+ return cls(int(gdt['count']), gdt['period'])
141
157
  try:
142
- cnt = int(gdt['count'])
143
- except Exception:
144
- cnt = float(gdt['count'])
145
- return cls(cnt, gdt['period'])
158
+ return cls(float(gdt['count']), gdt['period'])
159
+ except ValueError as err:
160
+ raise ParseError('Cannot parse string %s' % cmdstr)
146
161
 
147
162
 
148
163
  _WEEKDAY_CLS = [dr.SU, dr.MO, dr.TU, dr.WE, dr.TH, dr.FR, dr.SA]
@@ -169,13 +184,14 @@ class Weekday:
169
184
  day (int): The weekday
170
185
 
171
186
  """
172
- def __init__(self, count, day):
187
+ def __init__(self, count: int, day: int):
173
188
  self._count = count
174
- assert day in range(7)
189
+ if day not in range(7):
190
+ raise ValueError('Invalid weekday: %d' % day)
175
191
  self._day = day
176
192
  self._drcls = _WEEKDAY_CLS[day]
177
193
 
178
- def __radd__(self, dt):
194
+ def __radd__(self, dt: datetime.datetime) -> datetime.datetime:
179
195
  if self._count > 0:
180
196
  return dt + dr.relativedelta(
181
197
  days=1, weekday=self._drcls(self._count))
@@ -189,13 +205,13 @@ class Weekday:
189
205
 
190
206
  PARSE_RE = re.compile(r'''
191
207
  ^
192
- (?P<count> [+-] (?: [0-9]+ | [0-9]*\.[0-9]*) )?
208
+ (?P<count> [+-] (?: [0-9]+) )?
193
209
  (?P<weekday> sun|mon|tue|wed|thu|fri|sat)
194
210
  $
195
211
  ''', re.X)
196
212
 
197
213
  @classmethod
198
- def parse(cls, cmdstr):
214
+ def parse(cls, cmdstr: str) -> 'Weekday':
199
215
  """Parse a command string to a Weekday object
200
216
 
201
217
  The command string should be of the form "<N><weekday>", where
@@ -235,31 +251,44 @@ class PartialDate:
235
251
 
236
252
  Args:
237
253
 
238
- count (int): The number of periods to shift
239
- year (int): The year number
240
- month (int): The month number (1 to 12)
241
- day (int): The day number (1 to 31)
242
- hour (int): The hour number (0 to 23)
243
- minute (int): The minute number (0 to 59)
244
- second (int or float): The second number (0 to smaller than 60)
245
- microsecond (int): The microsecond number (0 to 999999)
254
+ count: The number of periods to shift
255
+ year: The year number
256
+ month: The month number (1 to 12)
257
+ day: The day number (1 to 31)
258
+ hour: The hour number (0 to 23)
259
+ minute: The minute number (0 to 59)
260
+ second: The second number (0 to smaller than 60)
261
+ microsecond: The microsecond number (0 to 999999)
262
+ zero: Whether to zero out the fields after the last specified one
246
263
 
247
264
  """
248
265
 
249
266
  _INVALID_SIG_RE = re.compile('10+1')
250
267
 
251
- def __init__(self, count=0, year=None, month=None, day=None,
252
- hour=None, minute=None, second=None, microsecond=None):
253
- assert not count or not year, 'Absolute date with non-zero count'
254
- assert not isinstance(second, float) or \
255
- microsecond is None, 'Doubly specified microsecond'
256
- vals = [year, month, day, hour, minute, second, microsecond]
257
- sig = ''.join([("0" if v is None else "1") for v in vals])
258
- assert not self._INVALID_SIG_RE.search(sig), \
259
- 'Non-consecutive components'
268
+ def __init__(
269
+ self, count: int = 0, year: typing.Optional[int] = None,
270
+ month: typing.Optional[int] = None, day: typing.Optional[int] = None,
271
+ hour: typing.Optional[int] = None, minute: typing.Optional[int] = None,
272
+ second: typing.Optional[typing.Union[int, float]] = None,
273
+ microsecond: typing.Optional[int] = None,
274
+ zero: bool = False
275
+ ):
276
+ if count and year:
277
+ raise ValueError('Absolute date with non-zero count')
278
+ if isinstance(second, float) and microsecond is not None:
279
+ raise ValueError('Doubly specified microsecond')
260
280
  if isinstance(second, float):
261
281
  second, orig_second = int(second), second
262
282
  microsecond = int((orig_second - second) * 1000000 + 0.5)
283
+ vals = [year, month, day, hour, minute, second, microsecond]
284
+ sig = ''.join([("0" if v is None else "1") for v in vals])
285
+ if self._INVALID_SIG_RE.search(sig):
286
+ raise ValueError('Non-consecutive components')
287
+ if zero:
288
+ lastset = sig.rfind('1')
289
+ for i in range(lastset + 1, 7):
290
+ vals[i] = 0
291
+ year, month, day, hour, minute, second, microsecond = vals
263
292
  self._count = count
264
293
  self._year = year
265
294
  self._month = month
@@ -274,7 +303,7 @@ class PartialDate:
274
303
  '', 'years', 'months', 'days', 'hours', 'minutes', 'seconds'
275
304
  ]
276
305
 
277
- def __radd__(self, dt):
306
+ def __radd__(self, dt: datetime.datetime) -> datetime.datetime:
278
307
  if self._firstset == -1:
279
308
  return dt
280
309
  if not(self._count):
@@ -288,7 +317,7 @@ class PartialDate:
288
317
  return self._monthshift(dt)
289
318
  return self._dayshift(dt)
290
319
 
291
- def _rset(self, dt):
320
+ def _rset(self, dt: datetime.datetime) -> datetime.datetime:
292
321
  updater = {'year': self._year,
293
322
  'month': self._month,
294
323
  'day': self._day,
@@ -297,9 +326,9 @@ class PartialDate:
297
326
  'second': self._second,
298
327
  'microsecond': self._microsecond}
299
328
  updater = {k: v for k, v in updater.items() if v is not None}
300
- return dt.replace(**updater)
329
+ return dt.replace(**updater) # type: ignore
301
330
 
302
- def _simpleshift(self, dt):
331
+ def _simpleshift(self, dt: datetime.datetime) -> datetime.datetime:
303
332
  remain = self._count
304
333
  ret = self._rset(dt)
305
334
  if self._count < 0:
@@ -309,9 +338,9 @@ class PartialDate:
309
338
  if ret > dt:
310
339
  remain -= 1
311
340
  mod_field = self._FIRSTSET_MOD[self._firstset]
312
- return ret + dr.relativedelta(**{mod_field: remain})
341
+ return ret + dr.relativedelta(**{mod_field: remain}) # type: ignore
313
342
 
314
- def _dayshift(self, dt):
343
+ def _dayshift(self, dt: datetime.datetime) -> datetime.datetime:
315
344
  # Day specified
316
345
  if self._firstset == 2: # modify month
317
346
  shift = dr.relativedelta(months=1 if self._count > 0 else -1)
@@ -344,8 +373,9 @@ class PartialDate:
344
373
  continue
345
374
  count -= 1
346
375
 
347
- def _monthshift(self, dt):
376
+ def _monthshift(self, dt: datetime.datetime) -> datetime.datetime:
348
377
  # Only month specified, shift by month rather than by year
378
+ assert self._month is not None
349
379
  if self._count > 0:
350
380
  num_months = self._month - dt.month
351
381
  sign = 1
@@ -389,7 +419,7 @@ class PartialDate:
389
419
  ''', re.X)
390
420
 
391
421
  @classmethod
392
- def parse(cls, cmdstr):
422
+ def parse(cls, cmdstr: str) -> 'PartialDate':
393
423
  """Parse a command string to a PartialDate object
394
424
 
395
425
  The command string should be of the form
@@ -399,13 +429,17 @@ class PartialDate:
399
429
  empty string. If all date parts are not specified the "--T"
400
430
  may be omitted. If all the time parts are not specified the
401
431
  "T::." may be omitted. If the microsecond part is not
402
- specified the "." part may be omitted.
432
+ specified the "." part may be omitted. A trailing "/" causes
433
+ all fields after the last specified field to be set to 0.
403
434
 
404
435
  Args:
405
436
 
406
437
  cmdstr (str): The command string
407
438
 
408
439
  """
440
+ zero = cmdstr.endswith('/')
441
+ if zero:
442
+ cmdstr = cmdstr[:-1]
409
443
  match = cls.PARSE_RE1.match(cmdstr.lower())
410
444
  if not match:
411
445
  match = cls.PARSE_RE2.match(cmdstr)
@@ -413,7 +447,7 @@ class PartialDate:
413
447
  raise ParseError('Cannot parse string %s' % cmdstr)
414
448
  gdt = match.groupdict()
415
449
 
416
- def _matchval(key):
450
+ def _matchval(key: str) -> typing.Optional[int]:
417
451
  val = gdt.get(key)
418
452
  if not val:
419
453
  return None
@@ -430,10 +464,11 @@ class PartialDate:
430
464
  _matchval('hour'),
431
465
  _matchval('minute'),
432
466
  _matchval('second'),
433
- microsecond)
467
+ microsecond,
468
+ zero)
434
469
 
435
470
 
436
- def parse(cmdstr):
471
+ def parse(cmdstr: str) -> typing.Union[Period, Weekday, PartialDate]:
437
472
  """Attempt to parse one of the possible date command
438
473
 
439
474
  Args:
@@ -1,21 +1,25 @@
1
1
  from __future__ import print_function
2
2
 
3
- import sys
4
3
  import datetime
4
+ import sys
5
+ import time
5
6
 
6
7
  import datec
7
8
 
8
9
 
9
- def main():
10
+ def main() -> None:
10
11
  dt = datetime.datetime.now()
12
+ wait = False
11
13
  for cmd in sys.argv[1:]:
12
14
  if cmd == '-h':
13
15
  print('''Date command
14
16
 
15
- Usage: datec [<command>] ...
17
+ Usage: datec [-w] [<command>] ...
16
18
 
17
- Datec starts from the current time and apply commands. The ending
18
- datetime is printed in ISO YYYY-MM-DDTHH:MM:SS.ffffff format.
19
+ Datec starts from the current time and apply commands. Normally the
20
+ ending datetime is printed in ISO YYYY-MM-DDTHH:MM:SS.ffffff format.
21
+ But if -w is given, no output is printed; instead the program sleeps
22
+ until the ending datetime.
19
23
 
20
24
  <command> may be of the following forms:
21
25
 
@@ -28,8 +32,16 @@ datetime is printed in ISO YYYY-MM-DDTHH:MM:SS.ffffff format.
28
32
 
29
33
  ''', file=sys.stderr)
30
34
  sys.exit(0)
35
+ if cmd == '-w':
36
+ wait = True
37
+ continue
31
38
  dt = dt + datec.parse(cmd.lower())
32
- print(dt.isoformat())
39
+ if wait:
40
+ delta = (dt - datetime.datetime.now()).total_seconds()
41
+ if delta > 0:
42
+ time.sleep(delta)
43
+ else:
44
+ print(dt.isoformat())
33
45
 
34
46
 
35
47
  if __name__ == '__main__':
File without changes
@@ -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,9 @@
1
+ mypy
2
+ pylint
3
+ pytest
4
+ pytest-cov
5
+ python-dateutil
6
+ twine
7
+ types-python-dateutil
8
+ wheel
9
+ makefilemenu
@@ -0,0 +1,118 @@
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
+ with self.assertRaises(datec.ParseError):
17
+ datec.Period.parse('+.day')
18
+
19
+ def test_weekday(self):
20
+ dt = datetime.datetime(2019, 5, 15)
21
+ self.assertEqual(dt + datec.Weekday(1, datec.MON),
22
+ datetime.datetime(2019, 5, 20))
23
+ self.assertEqual(dt + datec.Weekday.parse('+2mon'),
24
+ datetime.datetime(2019, 5, 27))
25
+ self.assertEqual(dt + datec.Weekday.parse('-2WED'),
26
+ datetime.datetime(2019, 5, 1))
27
+ self.assertEqual(dt + datec.parse('MON'),
28
+ datetime.datetime(2019, 5, 13))
29
+ self.assertEqual(dt + datec.Weekday(0, datec.FRI),
30
+ datetime.datetime(2019, 5, 17))
31
+ with self.assertRaises(datec.ParseError):
32
+ datec.Weekday.parse('MO')
33
+
34
+ def test_partialdate_simple(self):
35
+ dt = datetime.datetime(2019, 5, 15, 8, 15)
36
+ self.assertEqual(dt + datec.PartialDate(1),
37
+ datetime.datetime(2019, 5, 15, 8, 15))
38
+ self.assertEqual(dt + datec.PartialDate(0, minute=7, second=16),
39
+ datetime.datetime(2019, 5, 15, 8, 7, 16))
40
+ self.assertEqual(dt + datec.PartialDate(1, minute=7, second=16),
41
+ datetime.datetime(2019, 5, 15, 9, 7, 16))
42
+ self.assertEqual(dt + datec.PartialDate(1, minute=17, second=16),
43
+ datetime.datetime(2019, 5, 15, 8, 17, 16))
44
+ self.assertEqual(dt + datec.PartialDate(2, minute=7, second=16),
45
+ datetime.datetime(2019, 5, 15, 10, 7, 16))
46
+ self.assertEqual(dt + datec.PartialDate(2, minute=7, second=16.5),
47
+ datetime.datetime(2019, 5, 15, 10, 7, 16, 500000))
48
+ self.assertEqual(dt + datec.PartialDate.parse(':7:16'),
49
+ datetime.datetime(2019, 5, 15, 8, 7, 16))
50
+ self.assertEqual(dt + datec.PartialDate.parse('-1x:7:16'),
51
+ datetime.datetime(2019, 5, 15, 8, 7, 16))
52
+ self.assertEqual(dt + datec.PartialDate.parse('-1x:17:16'),
53
+ datetime.datetime(2019, 5, 15, 7, 17, 16))
54
+ self.assertEqual(dt + datec.parse('-1x:17:16.2'),
55
+ datetime.datetime(2019, 5, 15, 7, 17, 16, 200000))
56
+ with self.assertRaises(datec.ParseError):
57
+ datec.PartialDate.parse('---')
58
+
59
+ def test_partialdate_monthshift(self):
60
+ dt = datetime.datetime(2019, 5, 15)
61
+ self.assertEqual(dt + datec.PartialDate(1, month=2),
62
+ datetime.datetime(2020, 2, 15))
63
+ self.assertEqual(dt + datec.PartialDate(-1, month=2),
64
+ datetime.datetime(2019, 2, 15))
65
+ dt = datetime.datetime(2019, 5, 15)
66
+ self.assertEqual(dt + datec.PartialDate.parse('+1x-2-'),
67
+ datetime.datetime(2020, 2, 15))
68
+ dt = datetime.datetime(2019, 5, 30)
69
+ self.assertEqual(dt + datec.PartialDate(1, month=2),
70
+ datetime.datetime(2020, 2, 29))
71
+
72
+ def test_partialdate_dayshift(self):
73
+ dt = datetime.datetime(2019, 5, 30)
74
+ self.assertEqual(dt + datec.PartialDate(1, month=5, day=30),
75
+ datetime.datetime(2020, 5, 30))
76
+ self.assertEqual(dt + datec.PartialDate(1, day=31),
77
+ datetime.datetime(2019, 5, 31))
78
+ self.assertEqual(dt + datec.PartialDate(2, day=31),
79
+ datetime.datetime(2019, 7, 31))
80
+ self.assertEqual(dt + datec.PartialDate(-1, day=31),
81
+ datetime.datetime(2019, 3, 31))
82
+ dt = datetime.datetime(2019, 2, 15)
83
+ self.assertEqual(dt + datec.PartialDate(1, day=30),
84
+ datetime.datetime(2019, 3, 30))
85
+ with self.assertRaises(ValueError):
86
+ dt + datec.PartialDate(1, month=2, day=30)
87
+
88
+ def test_partialdate_zero(self):
89
+ dt = datetime.datetime(2019, 5, 15, 8, 15)
90
+ self.assertEqual(dt + datec.PartialDate(0, hour=12, zero=True),
91
+ datetime.datetime(2019, 5, 15, 12, 0, 0))
92
+ self.assertEqual(dt + datec.PartialDate(0, minute=30, zero=True),
93
+ datetime.datetime(2019, 5, 15, 8, 30, 0))
94
+ self.assertEqual(dt + datec.parse('12::/'),
95
+ datetime.datetime(2019, 5, 15, 12, 0, 0))
96
+ self.assertEqual(dt + datec.parse('12:30:/'),
97
+ datetime.datetime(2019, 5, 15, 12, 30, 0))
98
+ self.assertEqual(dt + datec.parse('2020-03-15/'),
99
+ datetime.datetime(2020, 3, 15, 0, 0, 0))
100
+ # without zero, minute/second are unchanged
101
+ self.assertEqual(dt + datec.PartialDate(0, hour=12),
102
+ datetime.datetime(2019, 5, 15, 12, 15))
103
+ self.assertEqual(dt + datec.parse('12::'),
104
+ datetime.datetime(2019, 5, 15, 12, 15))
105
+
106
+ def test_validation(self):
107
+ with self.assertRaises(ValueError):
108
+ datec.Period(1, 'invalid')
109
+ with self.assertRaises(ValueError):
110
+ datec.Period(1.5, 'month')
111
+ with self.assertRaises(ValueError):
112
+ datec.Weekday(1, 7)
113
+ with self.assertRaises(ValueError):
114
+ datec.PartialDate(1, year=2020)
115
+ with self.assertRaises(ValueError):
116
+ datec.PartialDate(second=1.5, microsecond=500)
117
+ with self.assertRaises(ValueError):
118
+ datec.PartialDate(hour=12, second=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,9 +0,0 @@
1
- setup.py
2
- datec/__init__.py
3
- datec/__main__.py
4
- datec.egg-info/PKG-INFO
5
- datec.egg-info/SOURCES.txt
6
- datec.egg-info/dependency_links.txt
7
- datec.egg-info/entry_points.txt
8
- datec.egg-info/requires.txt
9
- datec.egg-info/top_level.txt
@@ -1 +0,0 @@
1
-
@@ -1,3 +0,0 @@
1
- [console_scripts]
2
- datec = datec.__main__:main
3
-
@@ -1 +0,0 @@
1
- python-dateutil
@@ -1 +0,0 @@
1
- datec
datec-0.1/setup.cfg DELETED
@@ -1,4 +0,0 @@
1
- [egg_info]
2
- tag_build =
3
- tag_date = 0
4
-
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
- )