Skip to content

Commit

Permalink
Enhance datetime package
Browse files Browse the repository at this point in the history
revise CoreTimeStamp and CoreDateTime types
to consider sqlite timezone as UTC independent
from server timezone.
  • Loading branch information
mononobi committed Mar 8, 2021
1 parent 99ce093 commit 50c3371
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 41 deletions.
2 changes: 2 additions & 0 deletions src/pyrin/database/orm/types/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ class CoreTimeStamp(DateTimeMixin):
core timestamp class.
this is a helper type that will handle datetime values correctly on sqlite backend.
on sqlite backend, the timezone of database will always considered as utc.
it works as default on other backends.
"""

Expand All @@ -135,6 +136,7 @@ class CoreDateTime(DateTimeMixin):
core datetime class.
this is a helper type that will handle datetime values correctly on sqlite backend.
on sqlite backend, the timezone of database will always considered as utc.
it works as default on other backends.
"""

Expand Down
4 changes: 2 additions & 2 deletions src/pyrin/database/orm/types/mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def _to_database(self, value, dialect):
if not isinstance(value, datetime):
value = datetime_utils.to_datetime_from_date(value)

return datetime_services.convert(value, to_server=True, from_server=True)
return datetime_services.convert_to_utc(value, from_server=True)

return value

Expand All @@ -51,7 +51,7 @@ def _from_database(self, value, dialect):
"""

if dialect.name == DialectEnum.SQLITE:
return datetime_services.convert(value, to_server=True, from_server=True)
return datetime_services.convert_from_utc(value, to_server=True)

return value

Expand Down
14 changes: 14 additions & 0 deletions src/pyrin/globalization/datetime/enumerations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
"""
datetime enumerations module.
"""

from pyrin.core.enumerations import CoreEnum


class TimezoneEnum(CoreEnum):
"""
timezone enum.
"""

UTC = 'UTC'
129 changes: 99 additions & 30 deletions src/pyrin/globalization/datetime/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

from pyrin.core.structs import Manager
from pyrin.globalization.datetime import DateTimePackage
from pyrin.globalization.datetime.enumerations import TimezoneEnum


class DateTimeManager(Manager):
Expand All @@ -32,28 +33,69 @@ def __init__(self, **options):
server_timezone = config_services.get('globalization',
'locale', 'babel_default_timezone')

client_timezone = config_services.get('globalization',
'locale', 'client_timezone')
client_timezone = config_services.get('globalization', 'locale', 'client_timezone')

self._server_timezone = pytz.timezone(server_timezone)
self._client_timezone = pytz.timezone(client_timezone)
self._server_timezone = self.get_timezone(server_timezone)
self._client_timezone = self.get_timezone(client_timezone)

def _add_timezone(self, value, server):
def _try_add_timezone(self, value, timezone):
"""
adds the current timezone info into input value if it has no timezone info.
adds the given timezone info into input value if it has no timezone info.
:param datetime value: value to add timezone info into it.
:param bool server: specifies that server or client timezone must be added.
:param str timezone: timezone name to get datetime based on it.
:rtype: datetime
"""

localized_value = value
if value.tzinfo is None:
localized_value = self.localize(value, server)
localized_value = self._localize(value, timezone)

return localized_value

def _localize(self, value, timezone):
"""
localizes input datetime with given timezone.
input value should not have a timezone info.
:param datetime value: value to be localized.
:param str timezone: timezone name to get datetime based on it.
:rtype: datetime
"""

timezone = self.get_timezone(timezone)
return timezone.localize(value)

def _as_timezone(self, value, timezone):
"""
gets the result of `astimezone` on the given value.
:param datetime value: value to get normalized.
:param str timezone: timezone name to get datetime based on it.
:rtype: datetime
"""

timezone = self.get_timezone(timezone)
return value.astimezone(timezone)

def _normalize(self, value, timezone):
"""
normalizes input value using given timezone and returns it.
:param datetime value: value to get normalized.
:param str timezone: timezone name to get datetime based on it.
:rtype: datetime
"""

value = self._as_timezone(value, timezone)
timezone = self.get_timezone(timezone)
return timezone.normalize(value)

def now(self, server=True, timezone=None):
"""
gets the current datetime based on requested timezone.
Expand Down Expand Up @@ -222,9 +264,41 @@ def convert(self, value, to_server, from_server=None):
if from_server is None:
from_server = not to_server

localized_value = self._add_timezone(value, server=from_server)
localized_value = self.try_add_timezone(value, server=from_server)
return self.normalize(localized_value, server=to_server)

def convert_to_utc(self, value, from_server):
"""
converts the given datetime to utc.
:param datetime value: value to be converted.
:param bool from_server: specifies that value must be normalized
from server timezone. if set to False, it
will be normalized from client timezone.
:rtype: datetime
"""

localized_value = self.try_add_timezone(value, server=from_server)
return self._normalize(localized_value, TimezoneEnum.UTC)

def convert_from_utc(self, value, to_server):
"""
converts the given datetime from utc.
:param datetime value: value to be converted.
:param bool to_server: specifies that value must be normalized
to server timezone. if set to False, it
will be normalized to client timezone.
:rtype: datetime
"""

localized_value = self._try_add_timezone(value, TimezoneEnum.UTC)
return self.normalize(localized_value, to_server)

def as_timezone(self, value, server):
"""
gets the result of `astimezone` on the given value.
Expand All @@ -237,8 +311,8 @@ def as_timezone(self, value, server):
:rtype: datetime
"""

timezone = self.get_current_timezone(server=server)
return value.astimezone(timezone)
timezone = self.get_timezone_name(server=server)
return self._as_timezone(value, timezone)

def normalize(self, value, server):
"""
Expand All @@ -250,8 +324,8 @@ def normalize(self, value, server):
:rtype: datetime
"""

value = self.as_timezone(value, server=server)
return self.get_current_timezone(server).normalize(value)
timezone = self.get_timezone_name(server)
return self._normalize(value, timezone)

def localize(self, value, server):
"""
Expand All @@ -265,24 +339,21 @@ def localize(self, value, server):
:rtype: datetime
"""

return self.get_current_timezone(server).localize(value)
timezone = self.get_timezone_name(server)
return self._localize(value, timezone)

def replace_timezone(self, value, server):
def try_add_timezone(self, value, server):
"""
replaces given value's timezone with server or client timezone.
it returns a new object.
note that this method does not normalize the value, it just replaces the
timezone and localizes the value.
adds the current timezone info into input value if it has no timezone info.
:param datetime value: value to replace its timezone.
:param bool server: specifies that server or client timezone must used.
:param datetime value: value to add timezone info into it.
:param bool server: specifies that server or client timezone must be added.
:rtype: datetime
"""

value = value.replace(tzinfo=None)
return self._add_timezone(value, server)
timezone = self.get_timezone_name(server)
return self._try_add_timezone(value, timezone)

def to_datetime_string(self, value, to_server, from_server=None):
"""
Expand Down Expand Up @@ -522,12 +593,10 @@ def datetime(self, year, month, day, hour=0, minute=0,
:rtype: datetime
"""

if timezone not in (None, ''):
timezone = self.get_timezone(timezone)
else:
timezone = self.get_current_timezone(server=server)

result = datetime(year=year, month=month, day=day, hour=hour, minute=minute,
second=second, microsecond=microsecond, fold=fold)

return timezone.localize(result)
if timezone not in (None, ''):
return self._try_add_timezone(result, timezone)

return self.try_add_timezone(result, server)
46 changes: 37 additions & 9 deletions src/pyrin/globalization/datetime/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,38 @@ def convert(value, to_server, from_server=None):
from_server=from_server)


def convert_to_utc(value, from_server):
"""
converts the given datetime to utc.
:param datetime value: value to be converted.
:param bool from_server: specifies that value must be normalized
from server timezone. if set to False, it
will be normalized from client timezone.
:rtype: datetime
"""

return get_component(DateTimePackage.COMPONENT_NAME).convert_to_utc(value, from_server)


def convert_from_utc(value, to_server):
"""
converts the given datetime from utc.
:param datetime value: value to be converted.
:param bool to_server: specifies that value must be normalized
to server timezone. if set to False, it
will be normalized to client timezone.
:rtype: datetime
"""

return get_component(DateTimePackage.COMPONENT_NAME).convert_from_utc(value, to_server)


def as_timezone(value, server):
"""
gets the result of `astimezone` on the given value.
Expand Down Expand Up @@ -213,21 +245,17 @@ def localize(value, server):
return get_component(DateTimePackage.COMPONENT_NAME).localize(value, server)


def replace_timezone(value, server):
def try_add_timezone(value, server):
"""
replaces given value's timezone with server or client timezone.
it returns a new object.
note that this method does not normalize the value, it just replaces the
timezone and localizes the value.
adds the current timezone info into input value if it has no timezone info.
:param datetime value: value to replace its timezone.
:param bool server: specifies that server or client timezone must used.
:param datetime value: value to add timezone info into it.
:param bool server: specifies that server or client timezone must be added.
:rtype: datetime
"""

return get_component(DateTimePackage.COMPONENT_NAME).replace_timezone(value, server)
return get_component(DateTimePackage.COMPONENT_NAME).try_add_timezone(value, server)


def to_datetime_string(value, to_server, from_server=None):
Expand Down

0 comments on commit 50c3371

Please sign in to comment.