Use standardised datetime format

This commit is contained in:
Paulus Schoutsen 2016-04-16 00:55:35 -07:00
parent e7520ef401
commit 68d92c3196
29 changed files with 154 additions and 162 deletions

View file

@ -24,7 +24,7 @@ _LOGGER = logging.getLogger(__name__)
def trigger(hass, config, action): def trigger(hass, config, action):
"""Listen for state changes based on configuration.""" """Listen for state changes based on configuration."""
if CONF_AFTER in config: if CONF_AFTER in config:
after = dt_util.parse_time_str(config[CONF_AFTER]) after = dt_util.parse_time(config[CONF_AFTER])
if after is None: if after is None:
_error_time(config[CONF_AFTER], CONF_AFTER) _error_time(config[CONF_AFTER], CONF_AFTER)
return False return False
@ -62,13 +62,13 @@ def if_action(hass, config):
return None return None
if before is not None: if before is not None:
before = dt_util.parse_time_str(before) before = dt_util.parse_time(before)
if before is None: if before is None:
_error_time(before, CONF_BEFORE) _error_time(before, CONF_BEFORE)
return None return None
if after is not None: if after is not None:
after = dt_util.parse_time_str(after) after = dt_util.parse_time(after)
if after is None: if after is None:
_error_time(after, CONF_AFTER) _error_time(after, CONF_AFTER)
return None return None

View file

@ -49,8 +49,7 @@ class VeraBinarySensor(VeraDevice, BinarySensorDevice):
last_tripped = self.vera_device.last_trip last_tripped = self.vera_device.last_trip
if last_tripped is not None: if last_tripped is not None:
utc_time = dt_util.utc_from_timestamp(int(last_tripped)) utc_time = dt_util.utc_from_timestamp(int(last_tripped))
attr[ATTR_LAST_TRIP_TIME] = dt_util.datetime_to_str( attr[ATTR_LAST_TRIP_TIME] = utc_time.isoformat()
utc_time)
else: else:
attr[ATTR_LAST_TRIP_TIME] = None attr[ATTR_LAST_TRIP_TIME] = None
tripped = self.vera_device.is_tripped tripped = self.vera_device.is_tripped

View file

@ -182,7 +182,7 @@ def _api_history_period(handler, path_match, data):
one_day = timedelta(seconds=86400) one_day = timedelta(seconds=86400)
if date_str: if date_str:
start_date = dt_util.date_str_to_date(date_str) start_date = dt_util.parse_date(date_str)
if start_date is None: if start_date is None:
handler.write_json_message("Error parsing JSON", HTTP_BAD_REQUEST) handler.write_json_message("Error parsing JSON", HTTP_BAD_REQUEST)

View file

@ -72,8 +72,7 @@ class VeraLight(VeraDevice, Light):
last_tripped = self.vera_device.last_trip last_tripped = self.vera_device.last_trip
if last_tripped is not None: if last_tripped is not None:
utc_time = dt_util.utc_from_timestamp(int(last_tripped)) utc_time = dt_util.utc_from_timestamp(int(last_tripped))
attr[ATTR_LAST_TRIP_TIME] = dt_util.datetime_to_str( attr[ATTR_LAST_TRIP_TIME] = utc_time.isoformat()
utc_time)
else: else:
attr[ATTR_LAST_TRIP_TIME] = None attr[ATTR_LAST_TRIP_TIME] = None
tripped = self.vera_device.is_tripped tripped = self.vera_device.is_tripped

View file

@ -87,7 +87,7 @@ def _handle_get_logbook(handler, path_match, data):
date_str = path_match.group('date') date_str = path_match.group('date')
if date_str: if date_str:
start_date = dt_util.date_str_to_date(date_str) start_date = dt_util.parse_date(date_str)
if start_date is None: if start_date is None:
handler.write_json_message("Error parsing JSON", HTTP_BAD_REQUEST) handler.write_json_message("Error parsing JSON", HTTP_BAD_REQUEST)
@ -122,7 +122,7 @@ class Entry(object):
def as_dict(self): def as_dict(self):
"""Convert entry to a dict to be used within JSON.""" """Convert entry to a dict to be used within JSON."""
return { return {
'when': dt_util.datetime_to_str(self.when), 'when': self.when,
'name': self.name, 'name': self.name,
'message': self.message, 'message': self.message,
'domain': self.domain, 'domain': self.domain,

View file

@ -44,12 +44,12 @@ class FileNotificationService(BaseNotificationService):
if os.stat(self.filepath).st_size == 0: if os.stat(self.filepath).st_size == 0:
title = '{} notifications (Log started: {})\n{}\n'.format( title = '{} notifications (Log started: {})\n{}\n'.format(
kwargs.get(ATTR_TITLE), kwargs.get(ATTR_TITLE),
dt_util.strip_microseconds(dt_util.utcnow()), dt_util.utcnow().isoformat(),
'-' * 80) '-' * 80)
file.write(title) file.write(title)
if self.add_timestamp == 1: if self.add_timestamp == 1:
text = '{} {}\n'.format(dt_util.utcnow(), message) text = '{} {}\n'.format(dt_util.utcnow().isoformat(), message)
file.write(text) file.write(text)
else: else:
text = '{}\n'.format(message) text = '{}\n'.format(message)

View file

@ -478,7 +478,7 @@ class Recorder(threading.Thread):
def _adapt_datetime(datetimestamp): def _adapt_datetime(datetimestamp):
"""Turn a datetime into an integer for in the DB.""" """Turn a datetime into an integer for in the DB."""
return dt_util.as_utc(datetimestamp.replace(microsecond=0)).timestamp() return dt_util.as_utc(datetimestamp).timestamp()
def _verify_instance(): def _verify_instance():

View file

@ -23,6 +23,8 @@ ATTR_TARGET = 'Destination'
ATTR_REMAINING_TIME = 'Remaining time' ATTR_REMAINING_TIME = 'Remaining time'
ICON = 'mdi:bus' ICON = 'mdi:bus'
TIME_STR_FORMAT = "%H:%M"
# Return cached results if last scan was less then this time ago. # Return cached results if last scan was less then this time ago.
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60) MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60)
@ -126,10 +128,10 @@ class PublicTransportData(object):
try: try:
self.times = [ self.times = [
dt_util.datetime_to_time_str( dt_util.as_local(
dt_util.as_local(dt_util.utc_from_timestamp( dt_util.utc_from_timestamp(
item['from']['departureTimestamp'])) item['from']['departureTimestamp'])).strftime(
) TIME_STR_FORMAT)
for item in connections for item in connections
] ]
self.times.append( self.times.append(

View file

@ -131,9 +131,9 @@ class SystemMonitorSensor(Entity):
elif self.type == 'ipv6_address': elif self.type == 'ipv6_address':
self._state = psutil.net_if_addrs()[self.argument][1][1] self._state = psutil.net_if_addrs()[self.argument][1][1]
elif self.type == 'last_boot': elif self.type == 'last_boot':
self._state = dt_util.datetime_to_date_str( self._state = dt_util.as_local(
dt_util.as_local( dt_util.utc_from_timestamp(psutil.boot_time())
dt_util.utc_from_timestamp(psutil.boot_time()))) ).date().isoformat()
elif self.type == 'since_last_boot': elif self.type == 'since_last_boot':
self._state = dt_util.utcnow() - dt_util.utc_from_timestamp( self._state = dt_util.utcnow() - dt_util.utc_from_timestamp(
psutil.boot_time()) psutil.boot_time())

View file

@ -19,6 +19,8 @@ OPTION_TYPES = {
'time_utc': 'Time (UTC)', 'time_utc': 'Time (UTC)',
} }
TIME_STR_FORMAT = "%H:%M"
def setup_platform(hass, config, add_devices, discovery_info=None): def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Time and Date sensor.""" """Setup the Time and Date sensor."""
@ -70,9 +72,9 @@ class TimeDateSensor(Entity):
def update(self): def update(self):
"""Get the latest data and updates the states.""" """Get the latest data and updates the states."""
time_date = dt_util.utcnow() time_date = dt_util.utcnow()
time = dt_util.datetime_to_time_str(dt_util.as_local(time_date)) time = dt_util.as_local(time_date).strftime(TIME_STR_FORMAT)
time_utc = dt_util.datetime_to_time_str(time_date) time_utc = time_date.strftime(TIME_STR_FORMAT)
date = dt_util.datetime_to_date_str(dt_util.as_local(time_date)) date = dt_util.as_local(time_date).date().isoformat()
# Calculate the beat (Swatch Internet Time) time without date. # Calculate the beat (Swatch Internet Time) time without date.
hours, minutes, seconds = time_date.strftime('%H:%M:%S').split(':') hours, minutes, seconds = time_date.strftime('%H:%M:%S').split(':')

View file

@ -65,8 +65,7 @@ class VeraSensor(VeraDevice, Entity):
last_tripped = self.vera_device.last_trip last_tripped = self.vera_device.last_trip
if last_tripped is not None: if last_tripped is not None:
utc_time = dt_util.utc_from_timestamp(int(last_tripped)) utc_time = dt_util.utc_from_timestamp(int(last_tripped))
attr[ATTR_LAST_TRIP_TIME] = dt_util.datetime_to_str( attr[ATTR_LAST_TRIP_TIME] = utc_time.isoformat()
utc_time)
else: else:
attr[ATTR_LAST_TRIP_TIME] = None attr[ATTR_LAST_TRIP_TIME] = None
tripped = self.vera_device.is_tripped tripped = self.vera_device.is_tripped

View file

@ -12,6 +12,7 @@ from homeassistant.helpers.entity import Entity
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
DEFAULT_NAME = "Worldclock Sensor" DEFAULT_NAME = "Worldclock Sensor"
ICON = 'mdi:clock' ICON = 'mdi:clock'
TIME_STR_FORMAT = "%H:%M"
def setup_platform(hass, config, add_devices, discovery_info=None): def setup_platform(hass, config, add_devices, discovery_info=None):
@ -59,5 +60,5 @@ class WorldClockSensor(Entity):
def update(self): def update(self):
"""Get the time and updates the states.""" """Get the time and updates the states."""
self._state = dt_util.datetime_to_time_str( self._state = dt_util.now(time_zone=self._time_zone).strftime(
dt_util.now(time_zone=self._time_zone)) TIME_STR_FORMAT)

View file

@ -138,10 +138,8 @@ class YrSensor(Entity):
# Find sensor # Find sensor
for time_entry in self._weather.data['product']['time']: for time_entry in self._weather.data['product']['time']:
valid_from = dt_util.str_to_datetime( valid_from = dt_util.parse_datetime(time_entry['@from'])
time_entry['@from'], "%Y-%m-%dT%H:%M:%SZ") valid_to = dt_util.parse_datetime(time_entry['@to'])
valid_to = dt_util.str_to_datetime(
time_entry['@to'], "%Y-%m-%dT%H:%M:%SZ")
loc_data = time_entry['location'] loc_data = time_entry['location']
@ -204,5 +202,4 @@ class YrData(object):
model = self.data['meta']['model'] model = self.data['meta']['model']
if '@nextrun' not in model: if '@nextrun' not in model:
model = model[0] model = model[0]
self._nextrun = dt_util.str_to_datetime(model['@nextrun'], self._nextrun = dt_util.parse_datetime(model['@nextrun'])
"%Y-%m-%dT%H:%M:%SZ")

View file

@ -50,7 +50,7 @@ def next_setting_utc(hass, entity_id=None):
state = hass.states.get(ENTITY_ID) state = hass.states.get(ENTITY_ID)
try: try:
return dt_util.str_to_datetime( return dt_util.parse_datetime(
state.attributes[STATE_ATTR_NEXT_SETTING]) state.attributes[STATE_ATTR_NEXT_SETTING])
except (AttributeError, KeyError): except (AttributeError, KeyError):
# AttributeError if state is None # AttributeError if state is None
@ -72,8 +72,7 @@ def next_rising_utc(hass, entity_id=None):
state = hass.states.get(ENTITY_ID) state = hass.states.get(ENTITY_ID)
try: try:
return dt_util.str_to_datetime( return dt_util.parse_datetime(state.attributes[STATE_ATTR_NEXT_RISING])
state.attributes[STATE_ATTR_NEXT_RISING])
except (AttributeError, KeyError): except (AttributeError, KeyError):
# AttributeError if state is None # AttributeError if state is None
# KeyError if STATE_ATTR_NEXT_RISING does not exist # KeyError if STATE_ATTR_NEXT_RISING does not exist
@ -150,10 +149,8 @@ class Sun(Entity):
def state_attributes(self): def state_attributes(self):
"""Return the state attributes of the sun.""" """Return the state attributes of the sun."""
return { return {
STATE_ATTR_NEXT_RISING: STATE_ATTR_NEXT_RISING: self.next_rising.isoformat(),
dt_util.datetime_to_str(self.next_rising), STATE_ATTR_NEXT_SETTING: self.next_setting.isoformat(),
STATE_ATTR_NEXT_SETTING:
dt_util.datetime_to_str(self.next_setting),
STATE_ATTR_ELEVATION: round(self.solar_elevation, 2) STATE_ATTR_ELEVATION: round(self.solar_elevation, 2)
} }

View file

@ -50,8 +50,7 @@ class VeraSwitch(VeraDevice, SwitchDevice):
last_tripped = self.vera_device.last_trip last_tripped = self.vera_device.last_trip
if last_tripped is not None: if last_tripped is not None:
utc_time = dt_util.utc_from_timestamp(int(last_tripped)) utc_time = dt_util.utc_from_timestamp(int(last_tripped))
attr[ATTR_LAST_TRIP_TIME] = dt_util.datetime_to_str( attr[ATTR_LAST_TRIP_TIME] = utc_time.isoformat()
utc_time)
else: else:
attr[ATTR_LAST_TRIP_TIME] = None attr[ATTR_LAST_TRIP_TIME] = None
tripped = self.vera_device.is_tripped tripped = self.vera_device.is_tripped

View file

@ -156,8 +156,7 @@ class Event(object):
self.event_type = event_type self.event_type = event_type
self.data = data or {} self.data = data or {}
self.origin = origin self.origin = origin
self.time_fired = dt_util.strip_microseconds( self.time_fired = time_fired or dt_util.utcnow()
time_fired or dt_util.utcnow())
def as_dict(self): def as_dict(self):
"""Create a dict representation of this Event.""" """Create a dict representation of this Event."""
@ -165,7 +164,7 @@ class Event(object):
'event_type': self.event_type, 'event_type': self.event_type,
'data': dict(self.data), 'data': dict(self.data),
'origin': str(self.origin), 'origin': str(self.origin),
'time_fired': dt_util.datetime_to_str(self.time_fired), 'time_fired': self.time_fired,
} }
def __repr__(self): def __repr__(self):
@ -310,15 +309,9 @@ class State(object):
self.entity_id = entity_id.lower() self.entity_id = entity_id.lower()
self.state = str(state) self.state = str(state)
self.attributes = MappingProxyType(attributes or {}) self.attributes = MappingProxyType(attributes or {})
self.last_updated = dt_util.strip_microseconds( self.last_updated = last_updated or dt_util.utcnow()
last_updated or dt_util.utcnow())
# Strip microsecond from last_changed else we cannot guarantee self.last_changed = last_changed or self.last_updated
# state == State.from_dict(state.as_dict())
# This behavior occurs because to_dict uses datetime_to_str
# which does not preserve microseconds
self.last_changed = dt_util.strip_microseconds(
last_changed or self.last_updated)
@property @property
def domain(self): def domain(self):
@ -346,8 +339,8 @@ class State(object):
return {'entity_id': self.entity_id, return {'entity_id': self.entity_id,
'state': self.state, 'state': self.state,
'attributes': dict(self.attributes), 'attributes': dict(self.attributes),
'last_changed': dt_util.datetime_to_str(self.last_changed), 'last_changed': self.last_changed,
'last_updated': dt_util.datetime_to_str(self.last_updated)} 'last_updated': self.last_updated}
@classmethod @classmethod
def from_dict(cls, json_dict): def from_dict(cls, json_dict):
@ -361,13 +354,13 @@ class State(object):
last_changed = json_dict.get('last_changed') last_changed = json_dict.get('last_changed')
if last_changed: if isinstance(last_changed, str):
last_changed = dt_util.str_to_datetime(last_changed) last_changed = dt_util.parse_datetime(last_changed)
last_updated = json_dict.get('last_updated') last_updated = json_dict.get('last_updated')
if last_updated: if isinstance(last_updated, str):
last_updated = dt_util.str_to_datetime(last_updated) last_updated = dt_util.parse_datetime(last_updated)
return cls(json_dict['entity_id'], json_dict['state'], return cls(json_dict['entity_id'], json_dict['state'],
json_dict.get('attributes'), last_changed, last_updated) json_dict.get('attributes'), last_changed, last_updated)
@ -386,7 +379,7 @@ class State(object):
return "<state {}={}{} @ {}>".format( return "<state {}={}{} @ {}>".format(
self.entity_id, self.state, attr, self.entity_id, self.state, attr,
dt_util.datetime_to_local_str(self.last_changed)) dt_util.as_local(self.last_changed).isoformat())
class StateMachine(object): class StateMachine(object):
@ -819,6 +812,6 @@ def create_worker_pool(worker_count=None):
for start, job in current_jobs: for start, job in current_jobs:
_LOGGER.warning("WorkerPool:Current job from %s: %s", _LOGGER.warning("WorkerPool:Current job from %s: %s",
dt_util.datetime_to_local_str(start), job) dt_util.as_local(start).isoformat(), job)
return util.ThreadPool(job_handler, worker_count, busy_callback) return util.ThreadPool(job_handler, worker_count, busy_callback)

View file

@ -92,9 +92,8 @@ class TrackStates(object):
def get_changed_since(states, utc_point_in_time): def get_changed_since(states, utc_point_in_time):
"""Return list of states that have been changed since utc_point_in_time.""" """Return list of states that have been changed since utc_point_in_time."""
point_in_time = dt_util.strip_microseconds(utc_point_in_time) return [state for state in states
if state.last_updated >= utc_point_in_time]
return [state for state in states if state.last_updated >= point_in_time]
def reproduce_state(hass, states, blocking=False): def reproduce_state(hass, states, blocking=False):

View file

@ -7,6 +7,7 @@ HomeAssistantError will be raised.
For more details about the Python API, please refer to the documentation at For more details about the Python API, please refer to the documentation at
https://home-assistant.io/developers/python_api/ https://home-assistant.io/developers/python_api/
""" """
from datetime import datetime
import enum import enum
import json import json
import logging import logging
@ -277,7 +278,9 @@ class JSONEncoder(json.JSONEncoder):
Hand other objects to the original method. Hand other objects to the original method.
""" """
if hasattr(obj, 'as_dict'): if isinstance(obj, datetime):
return obj.isoformat()
elif hasattr(obj, 'as_dict'):
return obj.as_dict() return obj.as_dict()
try: try:

View file

@ -12,7 +12,7 @@ import string
from functools import wraps from functools import wraps
from types import MappingProxyType from types import MappingProxyType
from .dt import datetime_to_local_str, utcnow from .dt import as_local, utcnow
RE_SANITIZE_FILENAME = re.compile(r'(~|\.\.|/|\\)') RE_SANITIZE_FILENAME = re.compile(r'(~|\.\.|/|\\)')
RE_SANITIZE_PATH = re.compile(r'(~|\.(\.)+)') RE_SANITIZE_PATH = re.compile(r'(~|\.(\.)+)')
@ -43,7 +43,7 @@ def repr_helper(inp):
repr_helper(key)+"="+repr_helper(item) for key, item repr_helper(key)+"="+repr_helper(item) for key, item
in inp.items()) in inp.items())
elif isinstance(inp, datetime): elif isinstance(inp, datetime):
return datetime_to_local_str(inp) return as_local(inp).isoformat()
else: else:
return str(inp) return str(inp)

View file

@ -1,14 +1,24 @@
"""Provides helper methods to handle the time in HA.""" """Provides helper methods to handle the time in HA."""
import datetime as dt import datetime as dt
import re
import pytz import pytz
DATETIME_STR_FORMAT = "%H:%M:%S %d-%m-%Y"
DATE_STR_FORMAT = "%Y-%m-%d" DATE_STR_FORMAT = "%Y-%m-%d"
TIME_STR_FORMAT = "%H:%M"
UTC = DEFAULT_TIME_ZONE = pytz.utc UTC = DEFAULT_TIME_ZONE = pytz.utc
# Copyright (c) Django Software Foundation and individual contributors.
# All rights reserved.
# https://github.com/django/django/blob/master/LICENSE
DATETIME_RE = re.compile(
r'(?P<year>\d{4})-(?P<month>\d{1,2})-(?P<day>\d{1,2})'
r'[T ](?P<hour>\d{1,2}):(?P<minute>\d{1,2})'
r'(?::(?P<second>\d{1,2})(?:\.(?P<microsecond>\d{1,6})\d{0,6})?)?'
r'(?P<tzinfo>Z|[+-]\d{2}(?::?\d{2})?)?$'
)
def set_default_time_zone(time_zone): def set_default_time_zone(time_zone):
"""Set a default time zone to be used when none is specified.""" """Set a default time zone to be used when none is specified."""
global DEFAULT_TIME_ZONE # pylint: disable=global-statement global DEFAULT_TIME_ZONE # pylint: disable=global-statement
@ -75,48 +85,39 @@ def start_of_local_day(dt_or_d=None):
tzinfo=DEFAULT_TIME_ZONE) tzinfo=DEFAULT_TIME_ZONE)
def datetime_to_local_str(dattim): # Copyright (c) Django Software Foundation and individual contributors.
"""Convert datetime to specified time_zone and returns a string.""" # All rights reserved.
return datetime_to_str(as_local(dattim)) # https://github.com/django/django/blob/master/LICENSE
def parse_datetime(dt_str):
"""Parse a string and return a datetime.datetime.
This function supports time zone offsets. When the input contains one,
def datetime_to_str(dattim): the output uses a timezone with a fixed offset from UTC.
"""Convert datetime to a string format. Raises ValueError if the input is well formatted but not a valid datetime.
Returns None if the input isn't well formatted.
@rtype : str
""" """
return dattim.strftime(DATETIME_STR_FORMAT) match = DATETIME_RE.match(dt_str)
if not match:
def datetime_to_time_str(dattim):
"""Convert datetime to a string containing only the time.
@rtype : str
"""
return dattim.strftime(TIME_STR_FORMAT)
def datetime_to_date_str(dattim):
"""Convert datetime to a string containing only the date.
@rtype : str
"""
return dattim.strftime(DATE_STR_FORMAT)
def str_to_datetime(dt_str, dt_format=DATETIME_STR_FORMAT):
"""Convert a string to a UTC datetime object.
@rtype: datetime
"""
try:
return dt.datetime.strptime(
dt_str, dt_format).replace(tzinfo=pytz.utc)
except ValueError: # If dt_str did not match our format
return None return None
kws = match.groupdict()
if kws['microsecond']:
kws['microsecond'] = kws['microsecond'].ljust(6, '0')
tzinfo = kws.pop('tzinfo')
if tzinfo == 'Z':
tzinfo = UTC
elif tzinfo is not None:
offset_mins = int(tzinfo[-2:]) if len(tzinfo) > 3 else 0
offset_hours = int(tzinfo[1:3])
offset = dt.timedelta(hours=offset_hours, minutes=offset_mins)
if tzinfo[0] == '-':
offset = -offset
tzinfo = dt.timezone(offset)
kws = {k: int(v) for k, v in kws.items() if v is not None}
kws['tzinfo'] = tzinfo
return dt.datetime(**kws)
def date_str_to_date(dt_str): def parse_date(dt_str):
"""Convert a date string to a date object.""" """Convert a date string to a date object."""
try: try:
return dt.datetime.strptime(dt_str, DATE_STR_FORMAT).date() return dt.datetime.strptime(dt_str, DATE_STR_FORMAT).date()
@ -124,12 +125,7 @@ def date_str_to_date(dt_str):
return None return None
def strip_microseconds(dattim): def parse_time(time_str):
"""Return a copy of dattime object but with microsecond set to 0."""
return dattim.replace(microsecond=0)
def parse_time_str(time_str):
"""Parse a time string (00:20:00) into Time object. """Parse a time string (00:20:00) into Time object.
Return None if invalid. Return None if invalid.

View file

@ -34,7 +34,7 @@ class TestAutomationSun(unittest.TestCase):
def test_sunset_trigger(self): def test_sunset_trigger(self):
"""Test the sunset trigger.""" """Test the sunset trigger."""
self.hass.states.set(sun.ENTITY_ID, sun.STATE_ABOVE_HORIZON, { self.hass.states.set(sun.ENTITY_ID, sun.STATE_ABOVE_HORIZON, {
sun.STATE_ATTR_NEXT_SETTING: '02:00:00 16-09-2015', sun.STATE_ATTR_NEXT_SETTING: '2015-09-16T02:00:00Z',
}) })
now = datetime(2015, 9, 15, 23, tzinfo=dt_util.UTC) now = datetime(2015, 9, 15, 23, tzinfo=dt_util.UTC)
@ -61,7 +61,7 @@ class TestAutomationSun(unittest.TestCase):
def test_sunrise_trigger(self): def test_sunrise_trigger(self):
"""Test the sunrise trigger.""" """Test the sunrise trigger."""
self.hass.states.set(sun.ENTITY_ID, sun.STATE_ABOVE_HORIZON, { self.hass.states.set(sun.ENTITY_ID, sun.STATE_ABOVE_HORIZON, {
sun.STATE_ATTR_NEXT_RISING: '14:00:00 16-09-2015', sun.STATE_ATTR_NEXT_RISING: '2015-09-16T14:00:00Z',
}) })
now = datetime(2015, 9, 13, 23, tzinfo=dt_util.UTC) now = datetime(2015, 9, 13, 23, tzinfo=dt_util.UTC)
@ -88,7 +88,7 @@ class TestAutomationSun(unittest.TestCase):
def test_sunset_trigger_with_offset(self): def test_sunset_trigger_with_offset(self):
"""Test the sunset trigger with offset.""" """Test the sunset trigger with offset."""
self.hass.states.set(sun.ENTITY_ID, sun.STATE_ABOVE_HORIZON, { self.hass.states.set(sun.ENTITY_ID, sun.STATE_ABOVE_HORIZON, {
sun.STATE_ATTR_NEXT_SETTING: '02:00:00 16-09-2015', sun.STATE_ATTR_NEXT_SETTING: '2015-09-16T02:00:00Z',
}) })
now = datetime(2015, 9, 15, 23, tzinfo=dt_util.UTC) now = datetime(2015, 9, 15, 23, tzinfo=dt_util.UTC)
@ -116,7 +116,7 @@ class TestAutomationSun(unittest.TestCase):
def test_sunrise_trigger_with_offset(self): def test_sunrise_trigger_with_offset(self):
"""Test the runrise trigger with offset.""" """Test the runrise trigger with offset."""
self.hass.states.set(sun.ENTITY_ID, sun.STATE_ABOVE_HORIZON, { self.hass.states.set(sun.ENTITY_ID, sun.STATE_ABOVE_HORIZON, {
sun.STATE_ATTR_NEXT_RISING: '14:00:00 16-09-2015', sun.STATE_ATTR_NEXT_RISING: '2015-09-16T14:00:00Z',
}) })
now = datetime(2015, 9, 13, 23, tzinfo=dt_util.UTC) now = datetime(2015, 9, 13, 23, tzinfo=dt_util.UTC)
@ -144,7 +144,7 @@ class TestAutomationSun(unittest.TestCase):
def test_if_action_before(self): def test_if_action_before(self):
"""Test if action was before.""" """Test if action was before."""
self.hass.states.set(sun.ENTITY_ID, sun.STATE_ABOVE_HORIZON, { self.hass.states.set(sun.ENTITY_ID, sun.STATE_ABOVE_HORIZON, {
sun.STATE_ATTR_NEXT_RISING: '14:00:00 16-09-2015', sun.STATE_ATTR_NEXT_RISING: '2015-09-16T14:00:00Z',
}) })
_setup_component(self.hass, automation.DOMAIN, { _setup_component(self.hass, automation.DOMAIN, {
@ -180,7 +180,7 @@ class TestAutomationSun(unittest.TestCase):
def test_if_action_after(self): def test_if_action_after(self):
"""Test if action was after.""" """Test if action was after."""
self.hass.states.set(sun.ENTITY_ID, sun.STATE_ABOVE_HORIZON, { self.hass.states.set(sun.ENTITY_ID, sun.STATE_ABOVE_HORIZON, {
sun.STATE_ATTR_NEXT_RISING: '14:00:00 16-09-2015', sun.STATE_ATTR_NEXT_RISING: '2015-09-16T14:00:00Z',
}) })
_setup_component(self.hass, automation.DOMAIN, { _setup_component(self.hass, automation.DOMAIN, {
@ -216,7 +216,7 @@ class TestAutomationSun(unittest.TestCase):
def test_if_action_before_with_offset(self): def test_if_action_before_with_offset(self):
"""Test if action was before offset.""" """Test if action was before offset."""
self.hass.states.set(sun.ENTITY_ID, sun.STATE_ABOVE_HORIZON, { self.hass.states.set(sun.ENTITY_ID, sun.STATE_ABOVE_HORIZON, {
sun.STATE_ATTR_NEXT_RISING: '14:00:00 16-09-2015', sun.STATE_ATTR_NEXT_RISING: '2015-09-16T14:00:00Z',
}) })
_setup_component(self.hass, automation.DOMAIN, { _setup_component(self.hass, automation.DOMAIN, {
@ -253,7 +253,7 @@ class TestAutomationSun(unittest.TestCase):
def test_if_action_after_with_offset(self): def test_if_action_after_with_offset(self):
"""Test if action was after offset.""" """Test if action was after offset."""
self.hass.states.set(sun.ENTITY_ID, sun.STATE_ABOVE_HORIZON, { self.hass.states.set(sun.ENTITY_ID, sun.STATE_ABOVE_HORIZON, {
sun.STATE_ATTR_NEXT_RISING: '14:00:00 16-09-2015', sun.STATE_ATTR_NEXT_RISING: '2015-09-16T14:00:00Z',
}) })
_setup_component(self.hass, automation.DOMAIN, { _setup_component(self.hass, automation.DOMAIN, {
@ -290,8 +290,8 @@ class TestAutomationSun(unittest.TestCase):
def test_if_action_before_and_after_during(self): def test_if_action_before_and_after_during(self):
"""Test if action was before and after during.""" """Test if action was before and after during."""
self.hass.states.set(sun.ENTITY_ID, sun.STATE_ABOVE_HORIZON, { self.hass.states.set(sun.ENTITY_ID, sun.STATE_ABOVE_HORIZON, {
sun.STATE_ATTR_NEXT_RISING: '10:00:00 16-09-2015', sun.STATE_ATTR_NEXT_RISING: '2015-09-16T10:00:00Z',
sun.STATE_ATTR_NEXT_SETTING: '15:00:00 16-09-2015', sun.STATE_ATTR_NEXT_SETTING: '2015-09-16T15:00:00Z',
}) })
_setup_component(self.hass, automation.DOMAIN, { _setup_component(self.hass, automation.DOMAIN, {
@ -337,7 +337,7 @@ class TestAutomationSun(unittest.TestCase):
import pytz import pytz
self.hass.states.set(sun.ENTITY_ID, sun.STATE_ABOVE_HORIZON, { self.hass.states.set(sun.ENTITY_ID, sun.STATE_ABOVE_HORIZON, {
sun.STATE_ATTR_NEXT_SETTING: '17:30:00 16-09-2015', sun.STATE_ATTR_NEXT_SETTING: '2015-09-16T17:30:00Z',
}) })
_setup_component(self.hass, automation.DOMAIN, { _setup_component(self.hass, automation.DOMAIN, {

View file

@ -2,6 +2,7 @@
import os import os
import unittest import unittest
import tempfile import tempfile
from unittest.mock import patch
import homeassistant.components.notify as notify import homeassistant.components.notify as notify
from homeassistant.components.notify import ( from homeassistant.components.notify import (
@ -31,8 +32,11 @@ class TestNotifyFile(unittest.TestCase):
} }
})) }))
def test_notify_file(self): @patch('homeassistant.util.dt.utcnow')
def test_notify_file(self, mock_utcnow):
"""Test the notify file output.""" """Test the notify file output."""
mock_utcnow.return_value = dt_util.as_utc(dt_util.now())
with tempfile.TemporaryDirectory() as tempdirname: with tempfile.TemporaryDirectory() as tempdirname:
filename = os.path.join(tempdirname, 'notify.txt') filename = os.path.join(tempdirname, 'notify.txt')
message = 'one, two, testing, testing' message = 'one, two, testing, testing'
@ -46,7 +50,7 @@ class TestNotifyFile(unittest.TestCase):
})) }))
title = '{} notifications (Log started: {})\n{}\n'.format( title = '{} notifications (Log started: {})\n{}\n'.format(
ATTR_TITLE_DEFAULT, ATTR_TITLE_DEFAULT,
dt_util.strip_microseconds(dt_util.utcnow()), dt_util.utcnow().isoformat(),
'-' * 80) '-' * 80)
self.hass.services.call('notify', 'test', {'message': message}, self.hass.services.call('notify', 'test', {'message': message},

View file

@ -68,7 +68,7 @@ class TestComponentHistory(unittest.TestCase):
"""Test humanify filter too frequent sensor values.""" """Test humanify filter too frequent sensor values."""
entity_id = 'sensor.bla' entity_id = 'sensor.bla'
pointA = dt_util.strip_microseconds(dt_util.utcnow().replace(minute=2)) pointA = dt_util.utcnow().replace(minute=2)
pointB = pointA.replace(minute=5) pointB = pointA.replace(minute=5)
pointC = pointA + timedelta(minutes=logbook.GROUP_BY_MINUTES) pointC = pointA + timedelta(minutes=logbook.GROUP_BY_MINUTES)

View file

@ -66,13 +66,13 @@ class TestMqttEventStream(unittest.TestCase):
mock_sub.assert_called_with(self.hass, sub_topic, ANY) mock_sub.assert_called_with(self.hass, sub_topic, ANY)
@patch('homeassistant.components.mqtt.publish') @patch('homeassistant.components.mqtt.publish')
@patch('homeassistant.core.dt_util.datetime_to_str') @patch('homeassistant.core.dt_util.utcnow')
def test_state_changed_event_sends_message(self, mock_datetime, mock_pub): def test_state_changed_event_sends_message(self, mock_utcnow, mock_pub):
""""Test the sending of a new message if event changed.""" """"Test the sending of a new message if event changed."""
now = '00:19:19 11-01-2016' now = dt_util.as_utc(dt_util.now())
e_id = 'fake.entity' e_id = 'fake.entity'
pub_topic = 'bar' pub_topic = 'bar'
mock_datetime.return_value = now mock_utcnow.return_value = now
# Add the eventstream component for publishing events # Add the eventstream component for publishing events
self.assertTrue(self.add_eventstream(pub_topic=pub_topic)) self.assertTrue(self.add_eventstream(pub_topic=pub_topic))
@ -97,11 +97,11 @@ class TestMqttEventStream(unittest.TestCase):
event = {} event = {}
event['event_type'] = EVENT_STATE_CHANGED event['event_type'] = EVENT_STATE_CHANGED
new_state = { new_state = {
"last_updated": now, "last_updated": now.isoformat(),
"state": "on", "state": "on",
"entity_id": e_id, "entity_id": e_id,
"attributes": {}, "attributes": {},
"last_changed": now "last_changed": now.isoformat()
} }
event['event_data'] = {"new_state": new_state, "entity_id": e_id} event['event_data'] = {"new_state": new_state, "entity_id": e_id}

View file

@ -63,4 +63,16 @@ class TestRecorder(unittest.TestCase):
db_events = recorder.query_events( db_events = recorder.query_events(
'SELECT * FROM events WHERE event_type = ?', (event_type, )) 'SELECT * FROM events WHERE event_type = ?', (event_type, ))
self.assertEqual(events, db_events) assert len(events) == 1
assert len(db_events) == 1
event = events[0]
db_event = db_events[0]
assert event.event_type == db_event.event_type
assert event.data == db_event.data
assert event.origin == db_event.origin
# Recorder uses SQLite and stores datetimes as integer unix timestamps
assert event.time_fired.replace(microsecond=0) == \
db_event.time_fired.replace(microsecond=0)

View file

@ -128,7 +128,7 @@ class TestEvent(unittest.TestCase):
'event_type': event_type, 'event_type': event_type,
'data': data, 'data': data,
'origin': 'LOCAL', 'origin': 'LOCAL',
'time_fired': dt_util.datetime_to_str(now), 'time_fired': now,
} }
self.assertEqual(expected, event.as_dict()) self.assertEqual(expected, event.as_dict())
@ -225,13 +225,14 @@ class TestState(unittest.TestCase):
def test_repr(self): def test_repr(self):
"""Test state.repr.""" """Test state.repr."""
self.assertEqual("<state happy.happy=on @ 12:00:00 08-12-1984>", self.assertEqual("<state happy.happy=on @ 1984-12-08T12:00:00+00:00>",
str(ha.State( str(ha.State(
"happy.happy", "on", "happy.happy", "on",
last_changed=datetime(1984, 12, 8, 12, 0, 0)))) last_changed=datetime(1984, 12, 8, 12, 0, 0))))
self.assertEqual( self.assertEqual(
"<state happy.happy=on; brightness=144 @ 12:00:00 08-12-1984>", "<state happy.happy=on; brightness=144 @ "
"1984-12-08T12:00:00+00:00>",
str(ha.State("happy.happy", "on", {"brightness": 144}, str(ha.State("happy.happy", "on", {"brightness": 144},
datetime(1984, 12, 8, 12, 0, 0)))) datetime(1984, 12, 8, 12, 0, 0))))

View file

@ -7,6 +7,7 @@ import homeassistant.bootstrap as bootstrap
import homeassistant.remote as remote import homeassistant.remote as remote
import homeassistant.components.http as http import homeassistant.components.http as http
from homeassistant.const import HTTP_HEADER_HA_AUTH from homeassistant.const import HTTP_HEADER_HA_AUTH
import homeassistant.util.dt as dt_util
from tests.common import get_test_instance_port, get_test_home_assistant from tests.common import get_test_instance_port, get_test_home_assistant
@ -194,6 +195,9 @@ class TestRemoteMethods(unittest.TestCase):
# Default method raises TypeError if non HA object # Default method raises TypeError if non HA object
self.assertRaises(TypeError, ha_json_enc.default, 1) self.assertRaises(TypeError, ha_json_enc.default, 1)
now = dt_util.utcnow()
self.assertEqual(now.isoformat(), ha_json_enc.default(now))
class TestRemoteClasses(unittest.TestCase): class TestRemoteClasses(unittest.TestCase):
"""Test the homeassistant.remote module.""" """Test the homeassistant.remote module."""

View file

@ -107,31 +107,16 @@ class TestDateUtil(unittest.TestCase):
datetime(1986, 7, 9, tzinfo=dt_util.UTC), datetime(1986, 7, 9, tzinfo=dt_util.UTC),
dt_util.utc_from_timestamp(521251200)) dt_util.utc_from_timestamp(521251200))
def test_datetime_to_str(self): def test_parse_datetime_converts_correctly(self):
"""Test datetime_to_str.""" """Test parse_datetime converts strings."""
self.assertEqual( assert \
"12:00:00 09-07-1986", datetime(1986, 7, 9, 12, 0, 0, tzinfo=dt_util.UTC) == \
dt_util.datetime_to_str(datetime(1986, 7, 9, 12, 0, 0))) dt_util.parse_datetime("1986-07-09T12:00:00Z")
def test_datetime_to_local_str(self): utcnow = dt_util.utcnow()
"""Test datetime_to_local_str."""
self.assertEqual(
dt_util.datetime_to_str(dt_util.now()),
dt_util.datetime_to_local_str(dt_util.utcnow()))
def test_str_to_datetime_converts_correctly(self): assert utcnow == dt_util.parse_datetime(utcnow.isoformat())
"""Test str_to_datetime converts strings."""
self.assertEqual(
datetime(1986, 7, 9, 12, 0, 0, tzinfo=dt_util.UTC),
dt_util.str_to_datetime("12:00:00 09-07-1986"))
def test_str_to_datetime_returns_none_for_incorrect_format(self): def test_parse_datetime_returns_none_for_incorrect_format(self):
"""Test str_to_datetime returns None if incorrect format.""" """Test parse_datetime returns None if incorrect format."""
self.assertIsNone(dt_util.str_to_datetime("not a datetime string")) self.assertIsNone(dt_util.parse_datetime("not a datetime string"))
def test_strip_microseconds(self):
"""Test the now method."""
test_time = datetime(2015, 1, 1, microsecond=5000)
self.assertNotEqual(0, test_time.microsecond)
self.assertEqual(0, dt_util.strip_microseconds(test_time).microsecond)

View file

@ -39,7 +39,7 @@ class TestUtil(unittest.TestCase):
self.assertEqual("True", util.repr_helper(True)) self.assertEqual("True", util.repr_helper(True))
self.assertEqual("test=1", self.assertEqual("test=1",
util.repr_helper({"test": 1})) util.repr_helper({"test": 1}))
self.assertEqual("12:00:00 09-07-1986", self.assertEqual("1986-07-09T12:00:00+00:00",
util.repr_helper(datetime(1986, 7, 9, 12, 0, 0))) util.repr_helper(datetime(1986, 7, 9, 12, 0, 0)))
def test_convert(self): def test_convert(self):