Source code for biryani.datetimeconv

# -*- coding: utf-8 -*-


# Biryani -- A conversion and validation toolbox
# By: Emmanuel Raviart <emmanuel@raviart.com>
#
# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Emmanuel Raviart
# http://packages.python.org/Biryani/
#
# This file is part of Biryani.
#
# 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.


"""Date and Time Related Converters

.. note:: Date & time converters are not in :mod:`biryani.baseconv`, because some of them depend from external
   libraries (``isodate`` & ``pytz``).
"""


import calendar
import datetime

import isodate
import pytz

from .baseconv import cleanup_line, function, pipe
from . import states


__all__ = [
    'date_to_datetime',
    'date_to_iso8601_str',
    'date_to_timestamp',
    'datetime_to_date',
    'datetime_to_iso8601_str',
    'datetime_to_timestamp',
    'iso8601_input_to_date',
    'iso8601_input_to_datetime',
    'iso8601_input_to_time',
    'iso8601_str_to_date',
    'iso8601_str_to_datetime',
    'iso8601_str_to_time',
    'set_datetime_tzinfo',
    'time_to_iso8601_str',
    'timestamp_to_date',
    'timestamp_to_datetime',
    ]


# Level-1 Converters


[docs]def date_to_datetime(value, state = None): """Convert a date object to a datetime. >>> import datetime >>> date_to_datetime(datetime.date(2012, 3, 4)) (datetime.datetime(2012, 3, 4, 0, 0), None) >>> date_to_datetime(datetime.datetime(2012, 3, 4, 5, 6, 7)) (datetime.datetime(2012, 3, 4, 0, 0), None) >>> date_to_datetime(None) (None, None) """ if value is None: return value, None return datetime.datetime(value.year, value.month, value.day), None
[docs]def date_to_iso8601_str(value, state = None): """Convert a date to a string using ISO 8601 format. >>> date_to_iso8601_str(datetime.date(2012, 3, 4)) (u'2012-03-04', None) >>> date_to_iso8601_str(datetime.datetime(2012, 3, 4, 5, 6, 7)) (u'2012-03-04', None) >>> date_to_iso8601_str(None) (None, None) """ if value is None: return value, None # the datetime strftime() methods require year >= 1900 # return unicode(value.strftime('%Y-%m-%d')), None return u'{:04d}-{:02d}-{:02d}'.format(value.year, value.month, value.day), None
[docs]def date_to_timestamp(value, state = None): """Convert a datetime to a JavaScript timestamp. >>> date_to_timestamp(datetime.date(2012, 3, 4)) (1330819200000, None) >>> date_to_timestamp(datetime.datetime(2012, 3, 4, 5, 6, 7)) (1330837567000, None) >>> date_to_timestamp(None) (None, None) """ if value is None: return value, None return int(calendar.timegm(value.timetuple()) * 1000), None
[docs]def datetime_to_date(value, state = None): """Convert a datetime object to a date. >>> datetime_to_date(datetime.datetime(2012, 3, 4, 5, 6, 7)) (datetime.date(2012, 3, 4), None) >>> datetime_to_date(datetime.date(2012, 3, 4)) Traceback (most recent call last): AttributeError: >>> datetime_to_date(None) (None, None) """ if value is None: return value, None return value.date(), None
[docs]def datetime_to_iso8601_str(value, state = None): """Convert a datetime to a string using ISO 8601 format. >>> datetime_to_iso8601_str(datetime.datetime(2012, 3, 4, 5, 6, 7)) (u'2012-03-04 05:06:07', None) >>> datetime_to_iso8601_str(datetime.date(2012, 3, 4)) (u'2012-03-04 00:00:00', None) >>> datetime_to_iso8601_str(None) (None, None) """ if value is None: return value, None return unicode(value.strftime('%Y-%m-%d %H:%M:%S')), None
[docs]def datetime_to_timestamp(value, state = None): """Convert a datetime to a JavaScript timestamp. >>> datetime_to_timestamp(datetime.datetime(2012, 3, 4, 5, 6, 7)) (1330837567000, None) >>> datetime_to_timestamp(datetime.date(2012, 3, 4)) (1330819200000, None) >>> datetime_to_timestamp(None) (None, None) """ if state is None: state = states.default_state if not isinstance(value, datetime.datetime): return date_to_timestamp(value, state = state) utcoffset = value.utcoffset() if utcoffset is not None: value -= utcoffset return int(calendar.timegm(value.timetuple()) * 1000 + value.microsecond / 1000.0), None
[docs]def iso8601_str_to_date(value, state = None): """Convert a clean string in ISO 8601 format to a date. .. note:: For a converter that doesn't require a clean string, see :func:`iso8601_input_to_date`. >>> iso8601_str_to_date(u'2012-03-04') (datetime.date(2012, 3, 4), None) >>> iso8601_str_to_date(u'20120304') (datetime.date(2012, 3, 4), None) >>> iso8601_str_to_date(u'2012-03-04 05:06:07') (datetime.date(2012, 3, 4), None) >>> iso8601_str_to_date(u'2012-03-04T05:06:07') (datetime.date(2012, 3, 4), None) >>> iso8601_str_to_date(u'2012-03-04 05:06:07+01:00') (datetime.date(2012, 3, 4), None) >>> iso8601_str_to_date(u'2012-03-04 05:06:07-02:00') (datetime.date(2012, 3, 4), None) >>> iso8601_str_to_date(u'2012-03-04 05:06:07 +01:00') (datetime.date(2012, 3, 4), None) >>> iso8601_str_to_date(u'2012-03-04 05:06:07 -02:00') (datetime.date(2012, 3, 4), None) >>> iso8601_str_to_date(u'20120304 05:06:07') (datetime.date(2012, 3, 4), None) >>> iso8601_str_to_date(u'today') (u'today', u'Value must be a date in ISO 8601 format') >>> iso8601_str_to_date(u'') (u'', u'Value must be a date in ISO 8601 format') >>> iso8601_str_to_date(None) (None, None) """ if value is None: return value, None if state is None: state = states.default_state try: return isodate.parse_date(value), None except (isodate.ISO8601Error, ValueError): return value, state._(u'Value must be a date in ISO 8601 format')
[docs]def iso8601_str_to_datetime(value, state = None): """Convert a clean string in ISO 8601 format to a datetime. .. note:: For a converter that doesn't require a clean string, see :func:`iso8601_input_to_datetime`. >>> iso8601_str_to_datetime(u'2012-03-04') (datetime.datetime(2012, 3, 4, 0, 0), None) >>> iso8601_str_to_datetime(u'20120304') (datetime.datetime(2012, 3, 4, 0, 0), None) >>> iso8601_str_to_datetime(u'2012-03-04 05:06:07') (datetime.datetime(2012, 3, 4, 5, 6, 7), None) >>> iso8601_str_to_datetime(u'2012-03-04T05:06:07') (datetime.datetime(2012, 3, 4, 5, 6, 7), None) >>> iso8601_str_to_datetime(u'2012-03-04 05:06:07+01:00') (datetime.datetime(2012, 3, 4, 4, 6, 7), None) >>> iso8601_str_to_datetime(u'2012-03-04 05:06:07-02:00') (datetime.datetime(2012, 3, 4, 7, 6, 7), None) >>> iso8601_str_to_datetime(u'2012-03-04 05:06:07 +01:00') (datetime.datetime(2012, 3, 4, 4, 6, 7), None) >>> iso8601_str_to_datetime(u'2012-03-04 05:06:07 -02:00') (datetime.datetime(2012, 3, 4, 7, 6, 7), None) >>> iso8601_str_to_datetime(u'20120304 05:06:07') (datetime.datetime(2012, 3, 4, 5, 6, 7), None) >>> iso8601_str_to_datetime(u'now') (u'now', u'Value must be a date-time in ISO 8601 format') >>> iso8601_str_to_datetime(u'') (u'', u'Value must be a date-time in ISO 8601 format') >>> iso8601_str_to_datetime(None) (None, None) """ if value is None: return value, None if state is None: state = states.default_state original_value = value if u'T' not in value: if u' ' in value: # Accept a " " instead of a "T" for time separator. value = value.replace(u' ', u'T', 1) else: # Time seems to be missing. Add a zero time. value += u'T00:00:00' # Parsing fails when time zone is preceded with a space. So we remove space before "+" and "-". while u' +' in value: value = value.replace(u' +', '+') while u' -' in value: value = value.replace(u' -', '-') try: value = isodate.parse_datetime(value) except (isodate.ISO8601Error, ValueError): return original_value, state._(u'Value must be a date-time in ISO 8601 format') if value.tzinfo is not None: # Convert datetime to UTC. value = value.astimezone(pytz.utc).replace(tzinfo = None) return value, None
[docs]def iso8601_str_to_time(value, state = None): """Convert a clean string in ISO 8601 format to a time. .. note:: For a converter that doesn't require a clean string, see :func:`iso8601_input_to_time`. >>> iso8601_str_to_time(u'05:06:07') (datetime.time(5, 6, 7), None) >>> iso8601_str_to_time(u'T05:06:07') (datetime.time(5, 6, 7), None) >>> iso8601_str_to_time(u'05:06:07+01:00') (datetime.time(4, 6, 7), None) >>> iso8601_str_to_time(u'05:06:07-02:00') (datetime.time(7, 6, 7), None) >>> iso8601_str_to_time(u'05:06:07 +01:00') (datetime.time(4, 6, 7), None) >>> iso8601_str_to_time(u'05:06:07 -02:00') (datetime.time(7, 6, 7), None) >>> iso8601_str_to_time(u'05:06:07') (datetime.time(5, 6, 7), None) >>> iso8601_str_to_time(u'now') (u'now', u'Value must be a time in ISO 8601 format') >>> iso8601_str_to_time(u'') (u'', u'Value must be a time in ISO 8601 format') >>> iso8601_str_to_time(None) (None, None) """ if value is None: return value, None if state is None: state = states.default_state # Parsing fails when time zone is preceded with a space. So we remove space before "+" and "-". while u' +' in value: value = value.replace(u' +', '+') while u' -' in value: value = value.replace(u' -', '-') try: value = isodate.parse_time(value) except isodate.ISO8601Error: return value, state._(u'Value must be a time in ISO 8601 format') if value.tzinfo is not None: # Convert time to UTC (using a temporary datetime). datetime_value = datetime.datetime.combine(datetime.date(2, 2, 2), value) datetime_value = datetime_value.astimezone(pytz.utc).replace(tzinfo = None) value = datetime_value.time() return value, None
[docs]def set_datetime_tzinfo(tzinfo = None): """Return a converter that sets or clears the field tzinfo of a datetime. >>> import datetime, pytz >>> set_datetime_tzinfo()(datetime.datetime(2011, 1, 2, 3, 4, 5)) (datetime.datetime(2011, 1, 2, 3, 4, 5), None) >>> datetime.datetime(2011, 1, 2, 3, 4, 5, tzinfo = pytz.utc) datetime.datetime(2011, 1, 2, 3, 4, 5, tzinfo=<UTC>) >>> set_datetime_tzinfo()(datetime.datetime(2011, 1, 2, 3, 4, 5, tzinfo = pytz.utc)) (datetime.datetime(2011, 1, 2, 3, 4, 5), None) >>> set_datetime_tzinfo(pytz.utc)(datetime.datetime(2011, 1, 2, 3, 4, 5)) (datetime.datetime(2011, 1, 2, 3, 4, 5, tzinfo=<UTC>), None) """ return function(lambda value: value.replace(tzinfo = tzinfo))
[docs]def time_to_iso8601_str(value, state = None): """Convert a time to a string using ISO 8601 format. >>> time_to_iso8601_str(datetime.time(5, 6, 7)) (u'05:06:07', None) >>> time_to_iso8601_str(None) (None, None) """ if value is None: return value, None return unicode(value.strftime('%H:%M:%S')), None
[docs]def timestamp_to_date(value, state = None): """Convert a JavaScript timestamp to a date. >>> timestamp_to_date(123456789.123) (datetime.date(1970, 1, 2), None) >>> timestamp_to_date(u'123456789.123') Traceback (most recent call last): TypeError: >>> pipe(input_to_float, timestamp_to_date)(u'123456789.123') (datetime.date(1970, 1, 2), None) >>> timestamp_to_date(None) (None, None) """ if value is None: return value, None if state is None: state = states.default_state try: return datetime.date.fromtimestamp(value / 1000.0), None except ValueError: return value, state._(u'Value must be a timestamp')
[docs]def timestamp_to_datetime(value, state = None): """Convert a JavaScript timestamp to a datetime. .. note:: Since a timestamp has no timezone, the generated datetime has no *tzinfo* attribute. Use :func:`set_datetime_tzinfo` to add one. >>> timestamp_to_datetime(123456789.123) (datetime.datetime(1970, 1, 2, 11, 17, 36, 789123), None) >>> import pytz >>> pipe(timestamp_to_datetime, set_datetime_tzinfo(pytz.utc))(123456789.123) (datetime.datetime(1970, 1, 2, 11, 17, 36, 789123, tzinfo=<UTC>), None) >>> timestamp_to_datetime(u'123456789.123') Traceback (most recent call last): TypeError: >>> pipe(input_to_float, timestamp_to_datetime)(u'123456789.123') (datetime.datetime(1970, 1, 2, 11, 17, 36, 789123), None) >>> timestamp_to_datetime(None) (None, None) """ if value is None: return value, None if state is None: state = states.default_state try: # Since a timestamp doesn't containe timezone information, the generated datetime has no timezone (ie naive # datetime), so we don't use pytz.utc. # return datetime.datetime.fromtimestamp(value / 1000.0, pytz.utc), None return datetime.datetime.fromtimestamp(value / 1000.0), None except ValueError: return value, state._(u'Value must be a timestamp') # Level-2 Converters
iso8601_input_to_date = pipe(cleanup_line, iso8601_str_to_date) """Convert a string in ISO 8601 format to a date. >>> iso8601_input_to_date(u'2012-03-04') (datetime.date(2012, 3, 4), None) >>> iso8601_input_to_date(u'20120304') (datetime.date(2012, 3, 4), None) >>> iso8601_input_to_date(u'2012-03-04 05:06:07') (datetime.date(2012, 3, 4), None) >>> iso8601_input_to_date(u'2012-03-04T05:06:07') (datetime.date(2012, 3, 4), None) >>> iso8601_input_to_date(u'2012-03-04 05:06:07+01:00') (datetime.date(2012, 3, 4), None) >>> iso8601_input_to_date(u'2012-03-04 05:06:07-02:00') (datetime.date(2012, 3, 4), None) >>> iso8601_input_to_date(u'2012-03-04 05:06:07 +01:00') (datetime.date(2012, 3, 4), None) >>> iso8601_input_to_date(u'2012-03-04 05:06:07 -02:00') (datetime.date(2012, 3, 4), None) >>> iso8601_input_to_date(u'20120304 05:06:07') (datetime.date(2012, 3, 4), None) >>> iso8601_input_to_date(u' 2012-03-04 ') (datetime.date(2012, 3, 4), None) >>> iso8601_input_to_date(u'today') (u'today', u'Value must be a date in ISO 8601 format') >>> iso8601_input_to_date(u' ') (None, None) >>> iso8601_input_to_date(None) (None, None) """ iso8601_input_to_datetime = pipe(cleanup_line, iso8601_str_to_datetime) """Convert a string in ISO 8601 format to a datetime. >>> iso8601_input_to_datetime(u'2012-03-04') (datetime.datetime(2012, 3, 4, 0, 0), None) >>> iso8601_input_to_datetime(u'20120304') (datetime.datetime(2012, 3, 4, 0, 0), None) >>> iso8601_input_to_datetime(u'2012-03-04 05:06:07') (datetime.datetime(2012, 3, 4, 5, 6, 7), None) >>> iso8601_input_to_datetime(u'2012-03-04T05:06:07') (datetime.datetime(2012, 3, 4, 5, 6, 7), None) >>> iso8601_input_to_datetime(u'2012-03-04 05:06:07+01:00') (datetime.datetime(2012, 3, 4, 4, 6, 7), None) >>> iso8601_input_to_datetime(u'2012-03-04 05:06:07-02:00') (datetime.datetime(2012, 3, 4, 7, 6, 7), None) >>> iso8601_input_to_datetime(u'2012-03-04 05:06:07 +01:00') (datetime.datetime(2012, 3, 4, 4, 6, 7), None) >>> iso8601_input_to_datetime(u'2012-03-04 05:06:07 -02:00') (datetime.datetime(2012, 3, 4, 7, 6, 7), None) >>> iso8601_input_to_datetime(u'20120304 05:06:07') (datetime.datetime(2012, 3, 4, 5, 6, 7), None) >>> iso8601_input_to_datetime(u' 2012-03-04 05:06:07 ') (datetime.datetime(2012, 3, 4, 5, 6, 7), None) >>> iso8601_input_to_datetime(u'now') (u'now', u'Value must be a date-time in ISO 8601 format') >>> iso8601_input_to_datetime(u' ') (None, None) >>> iso8601_input_to_datetime(None) (None, None) """ iso8601_input_to_time = pipe(cleanup_line, iso8601_str_to_time) """Convert a string in ISO 8601 format to a time. >>> iso8601_input_to_time(u'05:06:07') (datetime.time(5, 6, 7), None) >>> iso8601_input_to_time(u'T05:06:07') (datetime.time(5, 6, 7), None) >>> iso8601_input_to_time(u'05:06:07+01:00') (datetime.time(4, 6, 7), None) >>> iso8601_input_to_time(u'05:06:07-02:00') (datetime.time(7, 6, 7), None) >>> iso8601_input_to_time(u'05:06:07 +01:00') (datetime.time(4, 6, 7), None) >>> iso8601_input_to_time(u'05:06:07 -02:00') (datetime.time(7, 6, 7), None) >>> iso8601_input_to_time(u'05:06:07') (datetime.time(5, 6, 7), None) >>> iso8601_input_to_time(u' 05:06:07 ') (datetime.time(5, 6, 7), None) >>> iso8601_input_to_time(u'now') (u'now', u'Value must be a time in ISO 8601 format') >>> iso8601_input_to_time(u' ') (None, None) >>> iso8601_input_to_time(None) (None, None) """