Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FutureDatetime and PastDatetime providers #779

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions mimesis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
Development,
File,
Food,
FutureDatetime,
Generic,
Hardware,
Internet,
Expand All @@ -46,6 +47,7 @@
'Development',
'File',
'Food',
'FutureDatetime',
'Hardware',
'Internet',
'Numbers',
Expand Down
3 changes: 2 additions & 1 deletion mimesis/providers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from mimesis.providers.clothing import Clothing
from mimesis.providers.code import Code
from mimesis.providers.cryptographic import Cryptographic
from mimesis.providers.date import Datetime
from mimesis.providers.date import Datetime, FutureDatetime
from mimesis.providers.development import Development
from mimesis.providers.file import File
from mimesis.providers.food import Food
Expand Down Expand Up @@ -44,6 +44,7 @@
'Development',
'File',
'Food',
'FutureDatetime',
'Hardware',
'Internet',
'Numbers',
Expand Down
128 changes: 125 additions & 3 deletions mimesis/providers/date.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from mimesis.providers.base import BaseDataProvider
from mimesis.typing import Date, DateTime, Time

__all__ = ['Datetime']
__all__ = ['Datetime', 'FutureDatetime']


class Datetime(BaseDataProvider):
Expand Down Expand Up @@ -50,7 +50,6 @@ def bulk_create_datetimes(date_start: DateTime,
See datetime module documentation for more:
https://docs.python.org/3.7/library/datetime.html#timedelta-objects


:param date_start: Begin of the range.
:param date_end: End of the range.
:param kwargs: Keyword arguments for datetime.timedelta
Expand Down Expand Up @@ -135,7 +134,7 @@ def date(self, start: int = 2000, end: int = 2019) -> Date:

:param start: Minimum value of year.
:param end: Maximum value of year.
:return: Formatted date.
:return: Date.
"""
year = self.random.randint(start, end)
month = self.random.randint(1, 12)
Expand Down Expand Up @@ -255,3 +254,126 @@ def timestamp(self, posix: bool = True, **kwargs) -> Union[str, int]:
return timegm(stamp.utctimetuple())

return stamp.strftime('%Y-%m-%dT%H:%M:%SZ')


class FutureDatetime(BaseDataProvider):
"""Class for generating data related to the date and time in the future."""

def __init__(self, days: int = 1, weeks: int = 0, hours: int = 0,
minutes: int = 0, seconds: int = 0, *args, **kwargs):
"""Initialize attributes.

:param days: Number of days to add to the current date.
:param weeks: Number of weeks to add to the current date.
:param hours: Number of hours to add to the current date.
:param minutes: Number of minutes to add to the current date.
:param seconds: Number of seconds to add to the current date.
"""
super().__init__(*args, **kwargs)
self.future = datetime.now() + timedelta(
days=days, weeks=weeks, hours=hours, minutes=minutes,
seconds=seconds)
self._dt = Datetime(*args, **kwargs)

class Meta:
"""Class for metadata."""

name = 'future_datetime'

def _validate_future_year(self, year: int) -> int:
"""Check if the year is after the year of the current future.

:param year: Year to validate.
:raise ValueError: If ``year`` is before the year of the
current future
"""
if year < self.future.year:
raise ValueError(f'Year {year} is before the current future')

def week_date(self, end: int = None) -> str:
"""Get week number with year from the year of the current future.

:param end: To end.
:raises ValueError: When ``end`` is before the year of the
current future.
:return: Week number.
"""
if not end:
end = self.future.year + 1
else:
self._validate_future_year(end)
return self._dt.week_date(start=self.future.year, end=end)

def year(self, maximum: int = None) -> int:
"""Generate a random year from the year of the current future.

:param maximum: Maximum value.
:raises ValueError: When ``maximum`` is before the year of the
current future.
:return: Year.
"""
if not maximum:
maximum = self.future.year + 65
else:
self._validate_future_year(maximum)
return self._dt.year(minimum=self.future.year, maximum=maximum)

def date(self, end: int = None) -> Date:
"""Generate random date object from the year of the current future.

:param end: Maximum value of year.
:raises ValueError: When ``end`` is before the year of the
current future.
:return: Formatted date.
"""
if not end:
end = self.future.year + 19
else:
self._validate_future_year(end)
return self._dt.date(start=self.future.year, end=end)

def formatted_date(self, fmt: str = '', **kwargs) -> str:
"""Generate random date as string.

:param fmt: The format of date, if None then use standard
accepted in the current locale.
:param kwargs: Keyword arguments for :meth:`~Datetime.date()`
:return: Formatted date.
"""
return self._dt.formatted_date(fmt=fmt, start=self.future.year)

def datetime(self, end: int = None,
timezone: Optional[str] = None) -> DateTime:
"""Generate random datetime from the year of the current future.

:param end: Maximum value of year.
:raises ValueError: When ``end`` is before the year of the
current future.
:param timezone: Set custom timezone (pytz required).
:return: Datetime
"""
if not end:
end = self.future.year + 19
else:
self._validate_future_year(end)
return self._dt.datetime(
start=self.future.year, end=end, timezone=timezone)

def formatted_datetime(self, fmt: str = '', **kwargs) -> str:
"""Generate datetime string in human readable format.

:param fmt: Custom format (default is format for current locale)
:param kwargs: Keyword arguments for :meth:`~Datetime.datetime()`
:return: Formatted datetime string.
"""
return self._dt.formatted_datetime(fmt=fmt, start=self.future.year)

def timestamp(self, posix: bool = True, **kwargs) -> Union[str, int]:
"""Generate random timestamp.

:param posix: POSIX time.
:param kwargs: Kwargs for :meth:`~FutureDatetime.datetime()`.
:return: Timestamp.
"""
return self._dt.timestamp(
posix=posix, start=self.future.year, end=self.future.year + 19)
4 changes: 2 additions & 2 deletions tests/test_providers/test_datetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ def test_datetime(self, _datetime, start, end, timezone):
],
)
def test_formatted_datetime(self, _datetime, start, end):
dt_str = _datetime.formatted_date(fmt='%Y', start=start, end=end)
dt_str = _datetime.formatted_datetime(fmt='%Y', start=start, end=end)
assert isinstance(dt_str, str)
assert start <= int(dt_str) <= end

Expand Down Expand Up @@ -213,7 +213,7 @@ def test_timestamp(self, d1, d2):
assert d1.timestamp(posix=False) == d2.timestamp(posix=False)

def test_formatted_datetime(self, d1, d2):
assert d1.formatted_date() == d2.formatted_date()
assert d1.formatted_datetime() == d2.formatted_datetime()

def test_week_date(self, d1, d2):
assert d1.week_date() == d2.week_date()
Expand Down
123 changes: 123 additions & 0 deletions tests/test_providers/test_future_datetime.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# -*- coding: utf-8 -*-
import datetime
import re

import pytest

from mimesis import FutureDatetime
from mimesis.data import GMT_OFFSETS, TIMEZONES

from . import patterns


class TestFutureDatetime(object):

@pytest.fixture
def future_dt(self):
return FutureDatetime()

def test_str(self, dt):
assert re.match(patterns.DATA_PROVIDER_STR_REGEX, str(dt))

def test_week_date(self, future_dt):
result = future_dt.week_date()
result = result.replace('-', ' ').replace('W', '')
year, week = result.split(' ')
assert int(year) >= future_dt.future.year
assert int(year) <= future_dt.future.year + 1
assert int(week) <= 52

with pytest.raises(ValueError):
future_dt.week_date(end=datetime.MINYEAR)

def test_year(self, future_dt):
result = future_dt.year()
assert result >= future_dt.future.year
assert result <= future_dt.future.year + 65

with pytest.raises(ValueError):
future_dt.year(maximum=datetime.MINYEAR)

def test_date(self, future_dt):
date_object = future_dt.date()
assert isinstance(date_object, datetime.date)
assert date_object.year >= future_dt.future.year
assert date_object.year <= future_dt.future.year + 19

with pytest.raises(ValueError):
future_dt.date(end=datetime.MINYEAR)

def test_formatted_date(self, future_dt):
fmt_date = future_dt.formatted_date('%Y', end=datetime.MAXYEAR)
assert int(fmt_date) >= future_dt.future.year
assert isinstance(fmt_date, str)

@pytest.mark.parametrize(
'end, timezone', [
(datetime.MAXYEAR, 'Europe/Paris'),
(datetime.MAXYEAR, None),
],
)
def test_datetime(self, future_dt, end, timezone):
dt_obj = future_dt.datetime(end=end, timezone=timezone)

assert future_dt.future.year <= dt_obj.year <= datetime.MAXYEAR
assert isinstance(dt_obj, datetime.datetime)

with pytest.raises(ValueError):
future_dt.datetime(end=datetime.MINYEAR)

def test_formatted_datetime(self, future_dt):
dt_obj = future_dt.formatted_datetime('%Y', end=datetime.MAXYEAR)
assert int(dt_obj) >= future_dt.future.year
assert isinstance(dt_obj, str)

def test_timestamp(self, future_dt):
result = future_dt.timestamp(end=datetime.MAXYEAR)
assert result is not None
assert isinstance(result, int)
year = datetime.datetime.fromtimestamp(int(result)).year
assert year >= future_dt.future.year

result = future_dt.timestamp(posix=False, end=datetime.MAXYEAR)
assert isinstance(result, str)


class TestSeededFutureDatetime(object):

@pytest.fixture
def d1(self, seed):
return FutureDatetime(seed=seed)

@pytest.fixture
def d2(self, seed):
return FutureDatetime(seed=seed)

def test_week_date(self, d1, d2):
assert d1.week_date() == d2.week_date()
assert d1.week_date(end=datetime.MAXYEAR) == \
d2.week_date(end=datetime.MAXYEAR)

def test_year(self, d1, d2):
assert d1.year() == d2.year()
assert d1.year(maximum=datetime.MAXYEAR) == \
d2.year(maximum=datetime.MAXYEAR)

def test_date(self, d1, d2):
assert d1.date() == d2.date()
assert d1.date(end=datetime.MAXYEAR) == d2.date(end=datetime.MAXYEAR)

def test_formatted_date(self, d1, d2):
assert d1.formatted_date() == d2.formatted_date()

def test_datetime(self, d1, d2):
assert d1.datetime() == d2.datetime()
assert d1.datetime(end=datetime.MAXYEAR) == \
d2.datetime(end=datetime.MAXYEAR)

def test_formatted_datetime(self, d1, d2):
assert d1.formatted_datetime() == d2.formatted_datetime()

def test_timestamp(self, d1, d2):
assert d1.timestamp() == d2.timestamp()
assert d1.timestamp(posix=False) == d2.timestamp(posix=False)