mirror of
https://github.com/kennethreitz/maya.git
synced 2026-06-05 23:00:18 +00:00
Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9766619d00 | |||
| bcc4885f99 | |||
| 564fc5a49e | |||
| 722eb258b0 | |||
| 66ec91e6ce | |||
| 46ab6ac552 | |||
| cd4b4e7e8b | |||
| cde6097142 | |||
| d9cd563d1b | |||
| 87776b19ae | |||
| 6da33e920d | |||
| b0b5bb7ff6 | |||
| 2a69de0a47 | |||
| b127699de3 | |||
| f3f95a92ee | |||
| bf16792875 | |||
| be42580883 | |||
| 03540c48c1 | |||
| 2845e5dee0 | |||
| 882a6aead7 | |||
| c0bf037d2e | |||
| b12a8dad11 | |||
| 0a7061c125 | |||
| d0d3b0136e | |||
| ff2a417198 | |||
| 669ef888e8 | |||
| 11c0d683d7 | |||
| 8493dd7e84 | |||
| d900ed2d2c |
+10
-13
@@ -1,6 +1,6 @@
|
||||
name: Continuous Integration and Deployment
|
||||
|
||||
on: [push]
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -8,15 +8,14 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
max-parallel: 4
|
||||
matrix:
|
||||
python-version: [2.7, 3.5, 3.6, 3.7]
|
||||
os: [ubuntu-latest, macOS-latest]
|
||||
python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"]
|
||||
os: [ubuntu-latest, macOS-latest, windows-latest]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v1
|
||||
uses: actions/setup-python@v4.2.0
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Setup build and test environment
|
||||
@@ -46,9 +45,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python 3.7
|
||||
uses: actions/setup-python@v1
|
||||
uses: actions/setup-python@v4.2.0
|
||||
with:
|
||||
python-version: 3.7
|
||||
- name: Setup docs environment
|
||||
@@ -62,20 +61,18 @@ jobs:
|
||||
needs: [build, docs]
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
if: startsWith(github.event.ref, 'refs/tags') && github.ref == 'refs/heads/master'
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python 3.7
|
||||
if: startsWith(github.event.ref, 'refs/tags')
|
||||
uses: actions/setup-python@v1
|
||||
uses: actions/setup-python@v4.2.0
|
||||
with:
|
||||
python-version: 3.7
|
||||
- name: Build Package
|
||||
if: startsWith(github.event.ref, 'refs/tags')
|
||||
run: |
|
||||
python -m pip install --upgrade pip setuptools wheel
|
||||
python setup.py sdist bdist_wheel --universal
|
||||
- name: Publish Package on PyPI
|
||||
if: startsWith(github.event.ref, 'refs/tags')
|
||||
uses: pypa/gh-action-pypi-publish@master
|
||||
with:
|
||||
user: __token__
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
name: "Close Stale Issues and Pull Requests"
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 * * * *"
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 60
|
||||
days-before-close: 7
|
||||
stale-issue-label: stale
|
||||
stale-pr-label: state
|
||||
stale-issue-message: 'This Issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days'
|
||||
stale-pr-message: 'This Pull Request is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days'
|
||||
+6
-11
@@ -92,6 +92,11 @@ Behold, datetimes for humans!
|
||||
>>> dt.snap('@d+3h').rfc2822()
|
||||
'Mon, 21 Feb 1994 03:00:00 GMT'
|
||||
|
||||
# snap modifiers within a timezone
|
||||
>>> dt = maya.when('Mon, 21 Feb 1994 21:21:42 GMT')
|
||||
>>> dt.snap_tz('+3h@d', 'Australia/Perth').rfc2822()
|
||||
'Mon, 21 Feb 1994 16:00:00 GMT'
|
||||
|
||||
☤ Advanced Usage of Maya
|
||||
------------------------
|
||||
|
||||
@@ -126,7 +131,7 @@ From here, there are a number of methods available to you, which you can use to
|
||||
|
||||
|
||||
☤ What about Delorean_, Arrow_, & Pendulum_?
|
||||
-----------------------------------------
|
||||
--------------------------------------------
|
||||
|
||||
All these projects complement each other, and are friends. Pendulum, for example, helps power Maya's parsing.
|
||||
|
||||
@@ -149,16 +154,6 @@ Installation is easy, with:
|
||||
$ pip install maya
|
||||
|
||||
|
||||
☤ Demo
|
||||
------
|
||||
|
||||
Try ``maya`` interactively using this online demo:
|
||||
|
||||
.. image:: https://user-images.githubusercontent.com/1155573/49400125-16e73500-f722-11e8-9275-e1d7eb3bbf99.png
|
||||
:target: https://notebooks.ai/demo/gh/kennethreitz/maya
|
||||
:alt: Open Live Demo
|
||||
|
||||
|
||||
How to Contribute
|
||||
-----------------
|
||||
|
||||
|
||||
+1
-1
@@ -73,7 +73,7 @@ release = maya.__version__
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = None
|
||||
language = "en"
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
|
||||
+44
-4
@@ -153,6 +153,17 @@ class MayaDT(object):
|
||||
"""
|
||||
return self.from_datetime(snaptime.snap(self.datetime(), instruction))
|
||||
|
||||
def snap_tz(self, instruction, in_timezone):
|
||||
"""
|
||||
Returns a new MayaDT object modified by the given instruction.
|
||||
The modifications happen in the given timezone.
|
||||
|
||||
Powered by snaptime. See https://github.com/zartstrom/snaptime
|
||||
for a complete documentation about the snaptime instructions.
|
||||
"""
|
||||
dt_tz = self.datetime(to_timezone=in_timezone)
|
||||
return self.from_datetime(snaptime.snap_tz(dt_tz, instruction, dt_tz.tzinfo))
|
||||
|
||||
# Timezone Crap
|
||||
# -------------
|
||||
@property
|
||||
@@ -212,6 +223,16 @@ class MayaDT(object):
|
||||
"""Returns MayaDT instance from iso8601 string."""
|
||||
return parse(iso8601_string)
|
||||
|
||||
@classmethod
|
||||
def from_long_count(klass, long_count_string):
|
||||
"""Returns MayaDT instance from Maya Long Count string."""
|
||||
days_since_creation = -1856305
|
||||
factors = (144000, 7200, 360, 20, 1)
|
||||
for i, value in enumerate(long_count_string.split('.')):
|
||||
days_since_creation += int(value) * factors[i]
|
||||
days_since_creation *= 3600 * 24
|
||||
return klass(epoch=days_since_creation)
|
||||
|
||||
@staticmethod
|
||||
def from_rfc2822(rfc2822_string):
|
||||
"""Returns MayaDT instance from rfc2822 string."""
|
||||
@@ -236,7 +257,10 @@ class MayaDT(object):
|
||||
if to_timezone:
|
||||
dt = self.datetime().astimezone(pytz.timezone(to_timezone))
|
||||
else:
|
||||
dt = Datetime.utcfromtimestamp(self._epoch)
|
||||
try:
|
||||
dt = Datetime.utcfromtimestamp(self._epoch)
|
||||
except: # Fallback for before year 1970 issue
|
||||
dt = Datetime.utcfromtimestamp(0) + timedelta(microseconds=self._epoch*1000000)
|
||||
dt.replace(tzinfo=self._tz)
|
||||
# Strip the timezone info if requested to do so.
|
||||
if naive:
|
||||
@@ -269,6 +293,22 @@ class MayaDT(object):
|
||||
"""Returns an RFC 3339 representation of the MayaDT."""
|
||||
return self.datetime().strftime("%Y-%m-%dT%H:%M:%S.%f")[:-5] + "Z"
|
||||
|
||||
def long_count(self):
|
||||
"""Returns a Mayan Long Count representation of the Maya DT."""
|
||||
# Creation (0.0.0.0.0) occurred on -3114-08-11
|
||||
# 1856305 is distance (in days) between Creation and UNIX epoch
|
||||
days_since_creation = int(1856305 + self._epoch / (3600 * 24))
|
||||
caps = (0, 20, 20, 18, 20)
|
||||
lc_date = [0, 0, 0, 0, days_since_creation]
|
||||
for i in range(4, 0, -1):
|
||||
if lc_date[i] >= caps[i]:
|
||||
lc_date[i - 1] += int(lc_date[i] / caps[i])
|
||||
lc_date[i] %= caps[i]
|
||||
elif lc_date[i] < 0:
|
||||
lc_date[i - 1] += int(lc_date[i] / caps[i])
|
||||
lc_date[i] = 0
|
||||
return '.'.join(str(i) for i in lc_date)
|
||||
|
||||
# Properties
|
||||
# ----------
|
||||
@property
|
||||
@@ -335,7 +375,7 @@ class MayaDT(object):
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
delta = humanize.time.abs_timedelta(
|
||||
delta = humanize.time._abs_timedelta(
|
||||
timedelta(seconds=(self.epoch - now().epoch))
|
||||
)
|
||||
|
||||
@@ -576,7 +616,7 @@ class MayaInterval(object):
|
||||
start += duration
|
||||
|
||||
def quantize(self, duration, snap_out=False, timezone="UTC"):
|
||||
"""Returns a quanitzed interval."""
|
||||
"""Returns a quantized interval."""
|
||||
# Convert seconds to timedelta, if appropriate.
|
||||
duration = _seconds_or_timedelta(duration)
|
||||
timezone = pytz.timezone(timezone)
|
||||
@@ -684,7 +724,7 @@ def when(string, timezone="UTC", prefer_dates_from="current_period"):
|
||||
Keyword Arguments:
|
||||
string -- string to be parsed
|
||||
timezone -- timezone referenced from (default: 'UTC')
|
||||
prefer_dates_from -- what dates are prefered when `string` is ambigous.
|
||||
prefer_dates_from -- what dates are preferred when `string` is ambiguous.
|
||||
options are 'past', 'future', and 'current_period'
|
||||
(default: 'current_period'). see: [1]
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import datetime
|
||||
|
||||
import pytz
|
||||
from freezegun import freeze_time
|
||||
import pytest
|
||||
|
||||
@@ -23,3 +26,73 @@ def frozen_now(request):
|
||||
now_string, tz_offset = request.param
|
||||
with freeze_time(now_string, tz_offset=tz_offset):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture(params=[
|
||||
datetime.datetime(2020, 8, 10, 22, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 8, 10, 23, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 8, 11, 0, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 8, 11, 1, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 8, 11, 2, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 8, 11, 3, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 8, 11, 4, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 8, 11, 5, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 8, 11, 6, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 8, 11, 7, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 8, 11, 8, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 8, 11, 9, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 8, 11, 10, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 8, 11, 11, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 8, 11, 12, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 8, 11, 13, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 8, 11, 14, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 8, 11, 15, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 8, 11, 16, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 8, 11, 17, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 8, 11, 18, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 8, 11, 19, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 8, 11, 20, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 8, 11, 21, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
], ids=str)
|
||||
def frozen_2020_08_11_in_paris(request):
|
||||
"""
|
||||
fixture setting datetime.now() to every hour of the 11th of august 2020 in Paris
|
||||
(summer time, GMT+2)
|
||||
"""
|
||||
with freeze_time(request.param):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture(params=[
|
||||
datetime.datetime(2020, 2, 10, 23, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 2, 11, 0, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 2, 11, 1, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 2, 11, 2, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 2, 11, 3, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 2, 11, 4, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 2, 11, 5, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 2, 11, 6, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 2, 11, 7, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 2, 11, 8, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 2, 11, 9, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 2, 11, 10, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 2, 11, 11, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 2, 11, 12, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 2, 11, 13, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 2, 11, 14, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 2, 11, 15, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 2, 11, 16, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 2, 11, 17, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 2, 11, 18, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 2, 11, 19, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 2, 11, 20, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 2, 11, 21, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
datetime.datetime(2020, 2, 11, 22, 2, 0, tzinfo=pytz.timezone('UTC')),
|
||||
], ids=str)
|
||||
def frozen_2020_02_11_in_paris(request):
|
||||
"""
|
||||
fixture setting datetime.now() to every hour of the 11th of february 2020 in Paris
|
||||
(winter time, GMT+1)
|
||||
"""
|
||||
with freeze_time(request.param):
|
||||
yield
|
||||
|
||||
@@ -30,6 +30,21 @@ def test_iso8601(string, expected):
|
||||
assert r == d.iso8601()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"string,expected",
|
||||
[
|
||||
('January 1, 1970', "12.17.16.7.5"),
|
||||
('December 21, 2012', "13.0.0.0.0"),
|
||||
('March 4, 1900', "12.14.5.10.0"),
|
||||
],
|
||||
)
|
||||
def test_long_count(string, expected):
|
||||
r = maya.parse(string).long_count()
|
||||
d = maya.MayaDT.from_long_count(r)
|
||||
assert r == expected
|
||||
assert r == d.long_count()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"string,expected",
|
||||
[
|
||||
@@ -71,6 +86,28 @@ def test_issue_104():
|
||||
t = maya.MayaDT.from_struct(t)
|
||||
assert str(t) == "Wed, 11 Oct 2017 21:12:11 GMT"
|
||||
|
||||
def test_before_1970():
|
||||
d1 = maya.when("1899-17-11 08:09:10")
|
||||
assert d1.year == 1899
|
||||
assert d1.month == 11
|
||||
assert d1.day == 17
|
||||
assert d1.week == 46
|
||||
assert d1.weekday == 5
|
||||
assert d1.hour == 8
|
||||
assert d1.minute == 9
|
||||
assert d1.second == 10
|
||||
assert d1.microsecond == 0
|
||||
# Test properties for maya.parse()
|
||||
d2 = maya.parse("February 29, 1904 13:12:34")
|
||||
assert d2.year == 1904
|
||||
assert d2.month == 2
|
||||
assert d2.day == 29
|
||||
assert d2.week == 9
|
||||
assert d2.weekday == 1
|
||||
assert d2.hour == 13
|
||||
assert d2.minute == 12
|
||||
assert d2.second == 34
|
||||
assert d2.microsecond == 0
|
||||
|
||||
def test_human_when():
|
||||
r1 = maya.when("yesterday")
|
||||
@@ -78,6 +115,42 @@ def test_human_when():
|
||||
assert (r2.day - r1.day) in (1, -30, -29, -28, -27)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("frozen_2020_08_11_in_paris")
|
||||
def test_human_when_today_with_timezone_summer_time():
|
||||
d = maya.when("today", timezone='Europe/Paris')
|
||||
assert str(d.datetime(to_timezone="Europe/Paris").date()) == '2020-08-11'
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("frozen_2020_02_11_in_paris")
|
||||
def test_human_when_today_with_timezone_winter_time():
|
||||
d = maya.when("today", timezone='Europe/Paris')
|
||||
assert str(d.datetime(to_timezone="Europe/Paris").date()) == '2020-02-11'
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("frozen_2020_08_11_in_paris")
|
||||
def test_human_when_yesterday_with_timezone_summer_time():
|
||||
d = maya.when("yesterday", timezone='Europe/Paris')
|
||||
assert str(d.datetime(to_timezone="Europe/Paris").date()) == '2020-08-10'
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("frozen_2020_02_11_in_paris")
|
||||
def test_human_when_yesterday_with_timezone_winter_time():
|
||||
d = maya.when("yesterday", timezone='Europe/Paris')
|
||||
assert str(d.datetime(to_timezone="Europe/Paris").date()) == '2020-02-10'
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("frozen_2020_08_11_in_paris")
|
||||
def test_human_when_midnight_with_timezone_summer_time():
|
||||
d = maya.when("midnight", timezone='Europe/Paris')
|
||||
assert str(d.datetime(to_timezone="Europe/Paris")) == '2020-08-11 00:00:00+02:00'
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("frozen_2020_02_11_in_paris")
|
||||
def test_human_when_midnight_with_timezone_winter_time():
|
||||
d = maya.when("midnight", timezone='Europe/Paris')
|
||||
assert str(d.datetime(to_timezone="Europe/Paris")) == '2020-02-11 00:00:00+01:00'
|
||||
|
||||
|
||||
def test_machine_parse():
|
||||
r1 = maya.parse("August 14, 2015")
|
||||
assert r1.day == 14
|
||||
@@ -368,3 +441,23 @@ def test_snaptime(when_str, snap_str, expected_when):
|
||||
dt = dt.snap(snap_str)
|
||||
# then
|
||||
assert dt == maya.when(expected_when)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"when_str,snap_str,timezone,expected_when",
|
||||
[
|
||||
(
|
||||
"Mon, 21 Feb 1994 21:21:42 GMT",
|
||||
"@d",
|
||||
"Australia/Perth",
|
||||
"Mon, 21 Feb 1994 16:00:00 GMT",
|
||||
)
|
||||
],
|
||||
)
|
||||
def test_snaptime_tz(when_str, snap_str, timezone, expected_when):
|
||||
# given
|
||||
dt = maya.when(when_str)
|
||||
# when
|
||||
dt = dt.snap_tz(snap_str, timezone)
|
||||
# then
|
||||
assert dt == maya.when(expected_when)
|
||||
|
||||
Reference in New Issue
Block a user