DateTime and TimeSpan functions
This commit introduces set of functions/methods/properties to work with date-times and time intervals. Also adds Integer and DateTime helper smart-types Change-Id: I3b0571ff522a7c6624afab0d3c008830098b3080
This commit is contained in:
parent
86ca4124a5
commit
50c6cbd2e8
@ -1,5 +1,5 @@
|
|||||||
pbr>=0.11,<2.0
|
pbr>=0.11,<2.0
|
||||||
Babel>=1.3
|
Babel>=1.3
|
||||||
|
python-dateutil>=2.4.2
|
||||||
ply<=3.6
|
ply<=3.6
|
||||||
six>=1.9.0
|
six>=1.9.0
|
||||||
|
@ -25,6 +25,7 @@ from yaql.standard_library import boolean as std_boolean
|
|||||||
from yaql.standard_library import branching as std_branching
|
from yaql.standard_library import branching as std_branching
|
||||||
from yaql.standard_library import collections as std_collections
|
from yaql.standard_library import collections as std_collections
|
||||||
from yaql.standard_library import common as std_common
|
from yaql.standard_library import common as std_common
|
||||||
|
from yaql.standard_library import date_time as std_datetime
|
||||||
from yaql.standard_library import math as std_math
|
from yaql.standard_library import math as std_math
|
||||||
from yaql.standard_library import queries as std_queries
|
from yaql.standard_library import queries as std_queries
|
||||||
from yaql.standard_library import regex as std_regex
|
from yaql.standard_library import regex as std_regex
|
||||||
@ -68,7 +69,7 @@ def create_context(data=utils.NO_VALUE, context=None, system=True,
|
|||||||
math=True, collections=True, queries=True,
|
math=True, collections=True, queries=True,
|
||||||
regex=True, branching=True,
|
regex=True, branching=True,
|
||||||
no_sets=False, finalizer=None, delegates=False,
|
no_sets=False, finalizer=None, delegates=False,
|
||||||
convention=None):
|
convention=None, datetime=True):
|
||||||
|
|
||||||
context = _setup_context(data, context, finalizer, convention)
|
context = _setup_context(data, context, finalizer, convention)
|
||||||
if system:
|
if system:
|
||||||
@ -91,6 +92,9 @@ def create_context(data=utils.NO_VALUE, context=None, system=True,
|
|||||||
std_regex.register(context)
|
std_regex.register(context)
|
||||||
if branching:
|
if branching:
|
||||||
std_branching.register(context)
|
std_branching.register(context)
|
||||||
|
if datetime:
|
||||||
|
std_datetime.register(context)
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
YaqlFactory = factory.YaqlFactory
|
YaqlFactory = factory.YaqlFactory
|
||||||
|
@ -495,10 +495,10 @@ def meta(name, value):
|
|||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
def yaql_property(python_type):
|
def yaql_property(source_type):
|
||||||
def decorator(func):
|
def decorator(func):
|
||||||
@name('#property#{0}'.format(get_function_definition(func).name))
|
@name('#property#{0}'.format(get_function_definition(func).name))
|
||||||
@parameter('obj', yaqltypes.PythonType(python_type, False))
|
@parameter('obj', source_type)
|
||||||
def wrapper(obj):
|
def wrapper(obj):
|
||||||
return func(obj)
|
return func(obj)
|
||||||
return wrapper
|
return wrapper
|
||||||
|
@ -13,7 +13,9 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from dateutil import tz
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from yaql.language import exceptions
|
from yaql.language import exceptions
|
||||||
@ -138,6 +140,26 @@ class String(PythonType):
|
|||||||
return None if value is None else six.text_type(value)
|
return None if value is None else six.text_type(value)
|
||||||
|
|
||||||
|
|
||||||
|
class Integer(PythonType):
|
||||||
|
def __init__(self, nullable=False):
|
||||||
|
super(Integer, self).__init__(six.integer_types, nullable=nullable)
|
||||||
|
|
||||||
|
|
||||||
|
class DateTime(PythonType):
|
||||||
|
utctz = tz.tzutc()
|
||||||
|
|
||||||
|
def __init__(self, nullable=False):
|
||||||
|
super(DateTime, self).__init__(datetime.datetime, nullable=nullable)
|
||||||
|
|
||||||
|
def convert(self, value, *args, **kwargs):
|
||||||
|
if isinstance(value, datetime.datetime):
|
||||||
|
if value.tzinfo is None:
|
||||||
|
return value.replace(tzinfo=self.utctz)
|
||||||
|
else:
|
||||||
|
return value
|
||||||
|
return super(DateTime, self).convert(value, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class Iterable(PythonType):
|
class Iterable(PythonType):
|
||||||
def __init__(self, validators=None):
|
def __init__(self, validators=None):
|
||||||
super(Iterable, self).__init__(
|
super(Iterable, self).__init__(
|
||||||
|
431
yaql/standard_library/date_time.py
Normal file
431
yaql/standard_library/date_time.py
Normal file
@ -0,0 +1,431 @@
|
|||||||
|
# Copyright (c) 2015 Mirantis, Inc.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import time as python_time
|
||||||
|
|
||||||
|
from yaql.language import specs
|
||||||
|
from yaql.language import yaqltypes
|
||||||
|
|
||||||
|
from dateutil import parser
|
||||||
|
from dateutil import tz
|
||||||
|
|
||||||
|
|
||||||
|
DATETIME_TYPE = datetime.datetime
|
||||||
|
TIMESPAN_TYPE = datetime.timedelta
|
||||||
|
ZERO_TIMESPAN = datetime.timedelta()
|
||||||
|
UTCTZ = yaqltypes.DateTime.utctz
|
||||||
|
|
||||||
|
|
||||||
|
def _get_tz(offset):
|
||||||
|
if offset is None:
|
||||||
|
return None
|
||||||
|
if offset == ZERO_TIMESPAN:
|
||||||
|
return UTCTZ
|
||||||
|
return tz.tzoffset(None, seconds(offset))
|
||||||
|
|
||||||
|
|
||||||
|
@specs.name('datetime')
|
||||||
|
@specs.parameter('year', int)
|
||||||
|
@specs.parameter('month', int)
|
||||||
|
@specs.parameter('day', int)
|
||||||
|
@specs.parameter('hour', int)
|
||||||
|
@specs.parameter('minute', int)
|
||||||
|
@specs.parameter('second', int)
|
||||||
|
@specs.parameter('microsecond', int)
|
||||||
|
@specs.parameter('offset', TIMESPAN_TYPE)
|
||||||
|
def build_datetime(year, month, day, hour=0, minute=0, second=0,
|
||||||
|
microsecond=0, offset=ZERO_TIMESPAN):
|
||||||
|
zone = _get_tz(offset)
|
||||||
|
return DATETIME_TYPE(year, month, day, hour, minute, second,
|
||||||
|
microsecond, zone)
|
||||||
|
|
||||||
|
|
||||||
|
@specs.name('datetime')
|
||||||
|
@specs.parameter('timestamp', yaqltypes.Number())
|
||||||
|
@specs.parameter('offset', TIMESPAN_TYPE)
|
||||||
|
def datetime_from_timestamp(timestamp, offset=ZERO_TIMESPAN):
|
||||||
|
zone = _get_tz(offset)
|
||||||
|
return datetime.datetime.fromtimestamp(timestamp, tz=zone)
|
||||||
|
|
||||||
|
|
||||||
|
@specs.name('datetime')
|
||||||
|
@specs.parameter('string', yaqltypes.String())
|
||||||
|
@specs.parameter('format__', yaqltypes.String(True))
|
||||||
|
def datetime_from_string(string, format__=None):
|
||||||
|
if not format__:
|
||||||
|
result = parser.parse(string)
|
||||||
|
else:
|
||||||
|
result = DATETIME_TYPE.strptime(string, format__)
|
||||||
|
if not result.tzinfo:
|
||||||
|
return result.replace(tzinfo=UTCTZ)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
@specs.name('timespan')
|
||||||
|
@specs.parameter('days', int)
|
||||||
|
@specs.parameter('hours', int)
|
||||||
|
@specs.parameter('minutes', int)
|
||||||
|
@specs.parameter('seconds', yaqltypes.Integer())
|
||||||
|
@specs.parameter('milliseconds', yaqltypes.Integer())
|
||||||
|
@specs.parameter('microseconds', yaqltypes.Integer())
|
||||||
|
def build_timespan(days=0, hours=0, minutes=0, seconds=0,
|
||||||
|
milliseconds=0, microseconds=0):
|
||||||
|
return TIMESPAN_TYPE(
|
||||||
|
days=days, hours=hours, minutes=minutes, seconds=seconds,
|
||||||
|
milliseconds=milliseconds, microseconds=microseconds)
|
||||||
|
|
||||||
|
|
||||||
|
@specs.yaql_property(TIMESPAN_TYPE)
|
||||||
|
def microseconds(timespan):
|
||||||
|
return (86400000000 * timespan.days +
|
||||||
|
1000000 * timespan.seconds +
|
||||||
|
timespan.microseconds)
|
||||||
|
|
||||||
|
|
||||||
|
@specs.yaql_property(TIMESPAN_TYPE)
|
||||||
|
def milliseconds(timespan):
|
||||||
|
return microseconds(timespan) / 1000.0
|
||||||
|
|
||||||
|
|
||||||
|
@specs.yaql_property(TIMESPAN_TYPE)
|
||||||
|
def seconds(timespan):
|
||||||
|
return microseconds(timespan) / 1000000.0
|
||||||
|
|
||||||
|
|
||||||
|
@specs.yaql_property(TIMESPAN_TYPE)
|
||||||
|
def minutes(timespan):
|
||||||
|
return microseconds(timespan) / 60000000.0
|
||||||
|
|
||||||
|
|
||||||
|
@specs.yaql_property(TIMESPAN_TYPE)
|
||||||
|
def hours(timespan):
|
||||||
|
return microseconds(timespan) / 3600000000.0
|
||||||
|
|
||||||
|
|
||||||
|
@specs.yaql_property(TIMESPAN_TYPE)
|
||||||
|
def days(timespan):
|
||||||
|
return microseconds(timespan) / 86400000000.0
|
||||||
|
|
||||||
|
|
||||||
|
def now(offset=ZERO_TIMESPAN):
|
||||||
|
zone = _get_tz(offset)
|
||||||
|
return DATETIME_TYPE.now(tz=zone)
|
||||||
|
|
||||||
|
|
||||||
|
def localtz():
|
||||||
|
if python_time.daylight:
|
||||||
|
return TIMESPAN_TYPE(seconds=-python_time.altzone)
|
||||||
|
else:
|
||||||
|
return TIMESPAN_TYPE(seconds=-python_time.timezone)
|
||||||
|
|
||||||
|
|
||||||
|
def utctz():
|
||||||
|
return ZERO_TIMESPAN
|
||||||
|
|
||||||
|
|
||||||
|
@specs.name('#operator_+')
|
||||||
|
@specs.parameter('dt', yaqltypes.DateTime())
|
||||||
|
@specs.parameter('ts', TIMESPAN_TYPE)
|
||||||
|
def datetime_plus_timespan(dt, ts):
|
||||||
|
return dt + ts
|
||||||
|
|
||||||
|
|
||||||
|
@specs.name('#operator_+')
|
||||||
|
@specs.parameter('ts', TIMESPAN_TYPE)
|
||||||
|
@specs.parameter('dt', yaqltypes.DateTime())
|
||||||
|
def timespan_plus_datetime(ts, dt):
|
||||||
|
return ts + dt
|
||||||
|
|
||||||
|
|
||||||
|
@specs.name('#operator_-')
|
||||||
|
@specs.parameter('dt', yaqltypes.DateTime())
|
||||||
|
@specs.parameter('ts', TIMESPAN_TYPE)
|
||||||
|
def datetime_minus_timespan(dt, ts):
|
||||||
|
return dt - ts
|
||||||
|
|
||||||
|
|
||||||
|
@specs.name('#operator_-')
|
||||||
|
@specs.parameter('dt1', yaqltypes.DateTime())
|
||||||
|
@specs.parameter('dt2', yaqltypes.DateTime())
|
||||||
|
def datetime_minus_datetime(dt1, dt2):
|
||||||
|
return dt1 - dt2
|
||||||
|
|
||||||
|
|
||||||
|
@specs.name('#operator_+')
|
||||||
|
@specs.parameter('ts1', TIMESPAN_TYPE)
|
||||||
|
@specs.parameter('ts2', TIMESPAN_TYPE)
|
||||||
|
def timespan_plus_timespan(ts1, ts2):
|
||||||
|
return ts1 + ts2
|
||||||
|
|
||||||
|
|
||||||
|
@specs.name('#operator_-')
|
||||||
|
@specs.parameter('ts1', TIMESPAN_TYPE)
|
||||||
|
@specs.parameter('ts2', TIMESPAN_TYPE)
|
||||||
|
def timespan_minus_timespan(ts1, ts2):
|
||||||
|
return ts1 - ts2
|
||||||
|
|
||||||
|
|
||||||
|
@specs.name('#operator_>')
|
||||||
|
@specs.parameter('dt1', yaqltypes.DateTime())
|
||||||
|
@specs.parameter('dt2', yaqltypes.DateTime())
|
||||||
|
def datetime_gt_datetime(dt1, dt2):
|
||||||
|
return dt1 > dt2
|
||||||
|
|
||||||
|
|
||||||
|
@specs.name('#operator_>=')
|
||||||
|
@specs.parameter('dt1', yaqltypes.DateTime())
|
||||||
|
@specs.parameter('dt2', yaqltypes.DateTime())
|
||||||
|
def datetime_gte_datetime(dt1, dt2):
|
||||||
|
return dt1 >= dt2
|
||||||
|
|
||||||
|
|
||||||
|
@specs.name('#operator_<')
|
||||||
|
@specs.parameter('dt1', yaqltypes.DateTime())
|
||||||
|
@specs.parameter('dt2', yaqltypes.DateTime())
|
||||||
|
def datetime_lt_datetime(dt1, dt2):
|
||||||
|
return dt1 < dt2
|
||||||
|
|
||||||
|
|
||||||
|
@specs.name('#operator_<=')
|
||||||
|
@specs.parameter('dt1', yaqltypes.DateTime())
|
||||||
|
@specs.parameter('dt2', yaqltypes.DateTime())
|
||||||
|
def datetime_lte_datetime(dt1, dt2):
|
||||||
|
return dt1 <= dt2
|
||||||
|
|
||||||
|
|
||||||
|
@specs.name('*equal')
|
||||||
|
@specs.parameter('dt1', yaqltypes.DateTime())
|
||||||
|
@specs.parameter('dt2', yaqltypes.DateTime())
|
||||||
|
def datetime_eq_datetime(dt1, dt2):
|
||||||
|
return dt1 == dt2
|
||||||
|
|
||||||
|
|
||||||
|
@specs.name('*not_equal')
|
||||||
|
@specs.parameter('dt1', yaqltypes.DateTime())
|
||||||
|
@specs.parameter('dt2', yaqltypes.DateTime())
|
||||||
|
def datetime_neq_datetime(dt1, dt2):
|
||||||
|
return dt1 != dt2
|
||||||
|
|
||||||
|
|
||||||
|
@specs.name('#operator_>')
|
||||||
|
@specs.parameter('ts1', TIMESPAN_TYPE)
|
||||||
|
@specs.parameter('ts2', TIMESPAN_TYPE)
|
||||||
|
def timespan_gt_timespan(ts1, ts2):
|
||||||
|
return ts1 > ts2
|
||||||
|
|
||||||
|
|
||||||
|
@specs.name('#operator_>=')
|
||||||
|
@specs.parameter('ts1', TIMESPAN_TYPE)
|
||||||
|
@specs.parameter('ts2', TIMESPAN_TYPE)
|
||||||
|
def timespan_gte_timespan(ts1, ts2):
|
||||||
|
return ts1 >= ts2
|
||||||
|
|
||||||
|
|
||||||
|
@specs.name('#operator_<')
|
||||||
|
@specs.parameter('ts1', TIMESPAN_TYPE)
|
||||||
|
@specs.parameter('ts2', TIMESPAN_TYPE)
|
||||||
|
def timespan_lt_timespan(ts1, ts2):
|
||||||
|
return ts1 < ts2
|
||||||
|
|
||||||
|
|
||||||
|
@specs.name('#operator_<=')
|
||||||
|
@specs.parameter('ts1', TIMESPAN_TYPE)
|
||||||
|
@specs.parameter('ts2', TIMESPAN_TYPE)
|
||||||
|
def timespan_lte_timespan(ts1, ts2):
|
||||||
|
return ts1 <= ts2
|
||||||
|
|
||||||
|
|
||||||
|
@specs.name('*equal')
|
||||||
|
@specs.parameter('ts1', TIMESPAN_TYPE)
|
||||||
|
@specs.parameter('ts2', TIMESPAN_TYPE)
|
||||||
|
def timespan_eq_timespan(ts1, ts2):
|
||||||
|
return ts1 == ts2
|
||||||
|
|
||||||
|
|
||||||
|
@specs.name('*not_equal')
|
||||||
|
@specs.parameter('ts1', TIMESPAN_TYPE)
|
||||||
|
@specs.parameter('ts2', TIMESPAN_TYPE)
|
||||||
|
def timespan_neq_timespan(ts1, ts2):
|
||||||
|
return ts1 != ts2
|
||||||
|
|
||||||
|
|
||||||
|
@specs.name('#operator_*')
|
||||||
|
@specs.parameter('ts', TIMESPAN_TYPE)
|
||||||
|
@specs.parameter('n', yaqltypes.Number())
|
||||||
|
def timespan_by_num(ts, n):
|
||||||
|
return TIMESPAN_TYPE(microseconds=(microseconds(ts) * n))
|
||||||
|
|
||||||
|
|
||||||
|
@specs.name('#operator_*')
|
||||||
|
@specs.parameter('n', yaqltypes.Number())
|
||||||
|
@specs.parameter('ts', TIMESPAN_TYPE)
|
||||||
|
def num_by_timespan(n, ts):
|
||||||
|
return TIMESPAN_TYPE(microseconds=(microseconds(ts) * n))
|
||||||
|
|
||||||
|
|
||||||
|
@specs.name('#operator_/')
|
||||||
|
@specs.parameter('ts1', TIMESPAN_TYPE)
|
||||||
|
@specs.parameter('ts2', TIMESPAN_TYPE)
|
||||||
|
def div_timespans(ts1, ts2):
|
||||||
|
return (0.0 + microseconds(ts1)) / microseconds(ts2)
|
||||||
|
|
||||||
|
|
||||||
|
@specs.name('#operator_/')
|
||||||
|
@specs.parameter('ts', TIMESPAN_TYPE)
|
||||||
|
@specs.parameter('n', yaqltypes.Number())
|
||||||
|
def div_timespan_by_num(ts, n):
|
||||||
|
return TIMESPAN_TYPE(microseconds=(microseconds(ts) / n))
|
||||||
|
|
||||||
|
|
||||||
|
@specs.name('#unary_operator_-')
|
||||||
|
@specs.parameter('ts', TIMESPAN_TYPE)
|
||||||
|
def negative_timespan(ts):
|
||||||
|
return -ts
|
||||||
|
|
||||||
|
|
||||||
|
@specs.name('#unary_operator_+')
|
||||||
|
@specs.parameter('ts', TIMESPAN_TYPE)
|
||||||
|
def positive_timespan(ts):
|
||||||
|
return ts
|
||||||
|
|
||||||
|
|
||||||
|
@specs.yaql_property(DATETIME_TYPE)
|
||||||
|
def year(dt):
|
||||||
|
return dt.year
|
||||||
|
|
||||||
|
|
||||||
|
@specs.yaql_property(DATETIME_TYPE)
|
||||||
|
def month(dt):
|
||||||
|
return dt.month
|
||||||
|
|
||||||
|
|
||||||
|
@specs.yaql_property(DATETIME_TYPE)
|
||||||
|
def day(dt):
|
||||||
|
return dt.day
|
||||||
|
|
||||||
|
|
||||||
|
@specs.yaql_property(DATETIME_TYPE)
|
||||||
|
def hour(dt):
|
||||||
|
return dt.hour
|
||||||
|
|
||||||
|
|
||||||
|
@specs.yaql_property(DATETIME_TYPE)
|
||||||
|
def minute(dt):
|
||||||
|
return dt.minute
|
||||||
|
|
||||||
|
|
||||||
|
@specs.yaql_property(DATETIME_TYPE)
|
||||||
|
def second(dt):
|
||||||
|
return dt.second
|
||||||
|
|
||||||
|
|
||||||
|
@specs.yaql_property(DATETIME_TYPE)
|
||||||
|
def microsecond(dt):
|
||||||
|
return dt.microsecond
|
||||||
|
|
||||||
|
|
||||||
|
@specs.yaql_property(yaqltypes.DateTime())
|
||||||
|
def date(dt):
|
||||||
|
return DATETIME_TYPE(
|
||||||
|
year=dt.year, month=dt.month, day=dt.day, tzinfo=dt.tzinfo)
|
||||||
|
|
||||||
|
|
||||||
|
@specs.yaql_property(yaqltypes.DateTime())
|
||||||
|
def time(dt):
|
||||||
|
return dt - date(dt)
|
||||||
|
|
||||||
|
|
||||||
|
@specs.yaql_property(DATETIME_TYPE)
|
||||||
|
def weekday(dt):
|
||||||
|
return dt.weekday()
|
||||||
|
|
||||||
|
|
||||||
|
@specs.yaql_property(yaqltypes.DateTime())
|
||||||
|
def utc(dt):
|
||||||
|
return dt - dt.utcoffset()
|
||||||
|
|
||||||
|
|
||||||
|
@specs.yaql_property(DATETIME_TYPE)
|
||||||
|
def offset(dt):
|
||||||
|
return dt.utcoffset() or ZERO_TIMESPAN
|
||||||
|
|
||||||
|
|
||||||
|
@specs.yaql_property(DATETIME_TYPE)
|
||||||
|
def timestamp(dt):
|
||||||
|
return (utc(dt) - DATETIME_TYPE(1970, 1, 1, tzinfo=UTCTZ)).total_seconds()
|
||||||
|
|
||||||
|
|
||||||
|
@specs.method
|
||||||
|
@specs.parameter('dt', yaqltypes.DateTime())
|
||||||
|
@specs.parameter('year', int)
|
||||||
|
@specs.parameter('month', int)
|
||||||
|
@specs.parameter('day', int)
|
||||||
|
@specs.parameter('hour', int)
|
||||||
|
@specs.parameter('minute', int)
|
||||||
|
@specs.parameter('second', int)
|
||||||
|
@specs.parameter('microsecond', int)
|
||||||
|
@specs.parameter('offset', TIMESPAN_TYPE)
|
||||||
|
def replace(dt, year=None, month=None, day=None, hour=None, minute=None,
|
||||||
|
second=None, microsecond=None, offset=None):
|
||||||
|
replacements = {}
|
||||||
|
if year is not None:
|
||||||
|
replacements['year'] = year
|
||||||
|
if month is not None:
|
||||||
|
replacements['month'] = month
|
||||||
|
if day is not None:
|
||||||
|
replacements['day'] = day
|
||||||
|
if hour is not None:
|
||||||
|
replacements['hour'] = hour
|
||||||
|
if minute is not None:
|
||||||
|
replacements['minute'] = minute
|
||||||
|
if second is not None:
|
||||||
|
replacements['second'] = second
|
||||||
|
if microsecond is not None:
|
||||||
|
replacements['microsecond'] = microsecond
|
||||||
|
if offset is not None:
|
||||||
|
replacements['tzinfo'] = _get_tz(offset)
|
||||||
|
|
||||||
|
return dt.replace(**replacements)
|
||||||
|
|
||||||
|
|
||||||
|
@specs.method
|
||||||
|
@specs.parameter('dt', yaqltypes.DateTime())
|
||||||
|
@specs.parameter('format__', yaqltypes.String())
|
||||||
|
def format_(dt, format__):
|
||||||
|
return dt.strftime(format__)
|
||||||
|
|
||||||
|
|
||||||
|
def register(context):
|
||||||
|
functions = (
|
||||||
|
build_datetime, build_timespan, datetime_from_timestamp,
|
||||||
|
datetime_from_string, now, localtz, utctz, utc,
|
||||||
|
days, hours, minutes, seconds, milliseconds, microseconds,
|
||||||
|
datetime_plus_timespan, timespan_plus_datetime,
|
||||||
|
datetime_minus_timespan, datetime_minus_datetime,
|
||||||
|
timespan_plus_timespan, timespan_minus_timespan,
|
||||||
|
datetime_gt_datetime, datetime_gte_datetime,
|
||||||
|
datetime_lt_datetime, datetime_lte_datetime,
|
||||||
|
datetime_eq_datetime, datetime_neq_datetime,
|
||||||
|
timespan_gt_timespan, timespan_gte_timespan,
|
||||||
|
timespan_lt_timespan, timespan_lte_timespan,
|
||||||
|
timespan_eq_timespan, timespan_neq_timespan,
|
||||||
|
negative_timespan, positive_timespan,
|
||||||
|
timespan_by_num, num_by_timespan, div_timespans, div_timespan_by_num,
|
||||||
|
year, month, day, hour, minute, second, microsecond, weekday,
|
||||||
|
offset, timestamp, date, time, replace, format_
|
||||||
|
)
|
||||||
|
|
||||||
|
for func in functions:
|
||||||
|
context.register_function(func)
|
161
yaql/tests/test_datetime.py
Normal file
161
yaql/tests/test_datetime.py
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
# Copyright (c) 2015 Mirantis, Inc.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import time
|
||||||
|
|
||||||
|
from dateutil import tz
|
||||||
|
from testtools import matchers
|
||||||
|
|
||||||
|
import yaql.tests
|
||||||
|
|
||||||
|
DT = datetime.datetime
|
||||||
|
TS = datetime.timedelta
|
||||||
|
|
||||||
|
|
||||||
|
class TestDatetime(yaql.tests.TestCase):
|
||||||
|
def test_build_datetime_components(self):
|
||||||
|
dt = DT(2015, 8, 29, tzinfo=tz.tzutc())
|
||||||
|
self.assertEqual(
|
||||||
|
dt, self.eval('datetime(2015, 8, 29)'))
|
||||||
|
self.assertEqual(
|
||||||
|
dt, self.eval('datetime(year => 2015, month => 8, day => 29,'
|
||||||
|
'hour => 0, minute => 0, second => 0, '
|
||||||
|
'microsecond => 0)'))
|
||||||
|
|
||||||
|
def test_build_datetime_iso(self):
|
||||||
|
self.assertEqual(
|
||||||
|
DT(2015, 8, 29, tzinfo=tz.tzutc()),
|
||||||
|
self.eval('datetime("2015-8-29")')
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
DT(2008, 9, 3, 20, 56, 35, 450686, tzinfo=tz.tzutc()),
|
||||||
|
self.eval('datetime("2008-09-03T20:56:35.450686")')
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
DT(2008, 9, 3, 20, 56, 35, 450686, tzinfo=tz.tzutc()),
|
||||||
|
self.eval('datetime("2008-09-03T20:56:35.450686Z")')
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
DT(2008, 9, 3, 0, 0, tzinfo=tz.tzutc()),
|
||||||
|
self.eval('datetime("20080903")')
|
||||||
|
)
|
||||||
|
dt = self.eval('datetime("2008-09-03T20:56:35.450686+03:00")')
|
||||||
|
self.assertEqual(
|
||||||
|
DT(2008, 9, 3, 20, 56, 35, 450686),
|
||||||
|
dt.replace(tzinfo=None)
|
||||||
|
)
|
||||||
|
self.assertEqual(TS(hours=3), dt.utcoffset())
|
||||||
|
|
||||||
|
def test_build_datetime_string(self):
|
||||||
|
self.assertEqual(
|
||||||
|
DT(2006, 11, 21, 16, 30, tzinfo=tz.tzutc()),
|
||||||
|
self.eval('datetime("Tuesday, 21. November 2006 04:30PM", '
|
||||||
|
'"%A, %d. %B %Y %I:%M%p")')
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_datetime_fields(self):
|
||||||
|
dt = DT(2006, 11, 21, 16, 30, tzinfo=tz.tzutc())
|
||||||
|
self.assertEqual(2006, self.eval('$.year', dt))
|
||||||
|
self.assertEqual(11, self.eval('$.month', dt))
|
||||||
|
self.assertEqual(21, self.eval('$.day', dt))
|
||||||
|
self.assertEqual(16, self.eval('$.hour', dt))
|
||||||
|
self.assertEqual(30, self.eval('$.minute', dt))
|
||||||
|
self.assertEqual(0, self.eval('$.second', dt))
|
||||||
|
self.assertEqual(0, self.eval('$.microsecond', dt))
|
||||||
|
self.assertEqual(1164126600, self.eval('$.timestamp', dt))
|
||||||
|
self.assertEqual(1, self.eval('$.weekday', dt))
|
||||||
|
self.assertEqual(TS(), self.eval('$.offset', dt))
|
||||||
|
self.assertEqual(TS(hours=16, minutes=30), self.eval('$.time', dt))
|
||||||
|
self.assertEqual(dt.replace(hour=0, minute=0), self.eval('$.date', dt))
|
||||||
|
self.assertEqual(dt, self.eval('$.utc', dt))
|
||||||
|
|
||||||
|
def test_build_timespan(self):
|
||||||
|
self.assertEqual(TS(0), self.eval('timespan()'))
|
||||||
|
self.assertEqual(
|
||||||
|
TS(1, 7384, 5006),
|
||||||
|
self.eval('timespan(days => 1, hours => 2, minutes => 3, '
|
||||||
|
'seconds => 4, milliseconds => 5, microseconds => 6)'))
|
||||||
|
self.assertEqual(
|
||||||
|
TS(1, 7384, 4994),
|
||||||
|
self.eval('timespan(days => 1, hours => 2, minutes => 3, '
|
||||||
|
'seconds =>4, milliseconds => 5, microseconds => -6)'))
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
TS(microseconds=-1000), self.eval('timespan(milliseconds => -1)'))
|
||||||
|
|
||||||
|
def test_datetime_from_timestamp(self):
|
||||||
|
dt = DT(2006, 11, 21, 16, 30, tzinfo=tz.tzutc())
|
||||||
|
self.assertEqual(dt, self.eval('datetime(1164126600)'))
|
||||||
|
|
||||||
|
def test_replace(self):
|
||||||
|
dt = DT(2006, 11, 21, 16, 30, tzinfo=tz.tzutc())
|
||||||
|
self.assertEqual(
|
||||||
|
DT(2009, 11, 21, 16, 40, tzinfo=tz.tzutc()),
|
||||||
|
self.eval('$.replace(year => 2009, minute => 40)', dt))
|
||||||
|
|
||||||
|
def test_timespan_fields(self):
|
||||||
|
ts = TS(1, 51945, 5000)
|
||||||
|
self.assertAlmostEqual(1.6, self.eval('$.days', ts), places=2)
|
||||||
|
self.assertAlmostEqual(38.43, self.eval('$.hours', ts), places=2)
|
||||||
|
self.assertAlmostEqual(2305.75, self.eval('$.minutes', ts), places=2)
|
||||||
|
self.assertAlmostEqual(138345, self.eval('$.seconds', ts), places=1)
|
||||||
|
self.assertEqual(138345005, self.eval('$.milliseconds', ts))
|
||||||
|
self.assertEqual(138345005000, self.eval('$.microseconds', ts))
|
||||||
|
|
||||||
|
def test_now(self):
|
||||||
|
self.assertIsInstance(self.eval('now()'), DT)
|
||||||
|
self.assertIsInstance(self.eval('now(utctz())'), DT)
|
||||||
|
self.assertIsInstance(self.eval('now(localtz())'), DT)
|
||||||
|
self.assertThat(
|
||||||
|
self.eval('now(utctz()) - now()'),
|
||||||
|
matchers.LessThan(TS(seconds=1))
|
||||||
|
)
|
||||||
|
self.assertTrue(self.eval('now(localtz()).offset = localtz()'))
|
||||||
|
|
||||||
|
def test_datetime_math(self):
|
||||||
|
self.context['dt1'] = self.eval('now()')
|
||||||
|
time.sleep(0.1)
|
||||||
|
self.context['dt2'] = self.eval('now()')
|
||||||
|
delta = TS(milliseconds=120)
|
||||||
|
self.assertIsInstance(self.eval('$dt2 - $dt1'), TS)
|
||||||
|
self.assertThat(self.eval('$dt2 - $dt1'), matchers.LessThan(delta))
|
||||||
|
self.assertTrue(self.eval('($dt2 - $dt1) + $dt1 = $dt2'))
|
||||||
|
self.assertTrue(self.eval('$dt1 + ($dt2 - $dt1) = $dt2'))
|
||||||
|
self.assertThat(
|
||||||
|
self.eval('($dt2 - $dt1) * 2'), matchers.LessThan(2 * delta))
|
||||||
|
self.assertThat(
|
||||||
|
self.eval('2.1 * ($dt2 - $dt1)'), matchers.LessThan(2 * delta))
|
||||||
|
self.assertTrue(self.eval('-($dt1 - $dt2) = +($dt2 - $dt1)'))
|
||||||
|
self.assertTrue(self.eval('$dt2 > $dt1'))
|
||||||
|
self.assertTrue(self.eval('$dt2 >= $dt1'))
|
||||||
|
self.assertTrue(self.eval('$dt2 != $dt1'))
|
||||||
|
self.assertTrue(self.eval('$dt1 = $dt1'))
|
||||||
|
self.assertTrue(self.eval('$dt1 < $dt2'))
|
||||||
|
self.assertTrue(self.eval('$dt1 <= $dt2'))
|
||||||
|
self.assertEqual(-1, self.eval('($dt2 - $dt1) / ($dt1 - $dt2)'))
|
||||||
|
self.assertTrue(self.eval('$dt2 - ($dt2 - $dt1) = $dt1'))
|
||||||
|
self.assertEqual(
|
||||||
|
0, self.eval('($dt2 - $dt1) - ($dt2 - $dt1)').total_seconds())
|
||||||
|
|
||||||
|
delta2 = self.eval('($dt2 - $dt1) / 2.1')
|
||||||
|
self.assertThat(delta2, matchers.LessThan(delta / 2))
|
||||||
|
self.assertTrue(self.eval('$dt1 + $ < $dt2', delta2))
|
||||||
|
self.assertTrue(self.eval('$ + $dt1 < $dt2', delta2))
|
||||||
|
self.assertTrue(self.eval('$dt2 - $dt1 > $', delta2))
|
||||||
|
self.assertTrue(self.eval('$dt2 - $dt1 >= $', delta2))
|
||||||
|
self.assertTrue(self.eval('$dt2 - $dt1 != $', delta2))
|
||||||
|
self.assertFalse(self.eval('$dt2 - $dt1 < $', delta2))
|
||||||
|
self.assertFalse(self.eval('$dt2 - $dt1 <= $', delta2))
|
||||||
|
self.assertTrue(self.eval('($dt2 - $dt1) + $ > $', delta2))
|
Loading…
Reference in New Issue
Block a user