1# Copyright (c) 2014 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5# This module contains some commonly used time conversion function.
6
7import datetime
8import logging
9import time
10
11from autotest_lib.client.common_lib import decorators
12
13
14try:
15    import pytz
16except ImportError:
17    pytz = None
18    logging.error('Could not import pytz.')
19
20
21try:
22    import tzlocal
23except ImportError:
24    tzlocal = None
25    logging.error('Could not import tzlocal.')
26
27
28# This format is used to parse datetime value in MySQL database and should not
29# be modified.
30TIME_FMT = '%Y-%m-%d %H:%M:%S'
31TIME_FMT_MICRO = '%Y-%m-%d %H:%M:%S.%f'
32
33def time_string_to_datetime(time_string, handle_type_error=False):
34    """Convert a string of time to a datetime object.
35
36    The format of date string must match '%Y-%m-%d %H:%M:%S' or
37    '%Y-%m-%d %H:%M:%S.%f'.
38
39    @param time_string: String of date, e.g., 2014-12-05 15:32:45
40    @param handle_type_error: Set to True to prevent the method raise
41            TypeError if given time_string is corrupted. Default is False.
42
43    @return: A datetime object with time of the given date string.
44
45    """
46    try:
47        try:
48            return datetime.datetime.strptime(time_string, TIME_FMT)
49        except ValueError:
50            return datetime.datetime.strptime(time_string, TIME_FMT_MICRO)
51    except TypeError:
52        if handle_type_error:
53            return None
54        else:
55            raise
56
57
58def date_string_to_epoch_time(date_string):
59    """Parse a date time string into seconds since the epoch.
60
61    @param date_string: A string, formatted according to `TIME_FMT`.
62
63    @return The number of seconds since the UNIX epoch, as a float.
64
65    """
66    return time.mktime(time.strptime(date_string, TIME_FMT))
67
68
69def epoch_time_to_date_string(epoch_time, fmt_string=TIME_FMT):
70    """Convert epoch time (float) to a human readable date string.
71
72    @param epoch_time The number of seconds since the UNIX epoch, as
73                      a float.
74    @param fmt_string: A string describing the format of the datetime
75        string output.
76
77    @returns: string formatted in the following way: "yyyy-mm-dd hh:mm:ss"
78    """
79    if epoch_time:
80        return datetime.datetime.fromtimestamp(
81                int(epoch_time)).strftime(fmt_string)
82    return None
83
84
85def to_epoch_time(value):
86    """Convert the given value to epoch time.
87
88    Convert the given value to epoch time if it is a datetime object or a string
89    can be converted to datetime object.
90    If the given value is a number, this function assume the value is a epoch
91    time value, and returns the value itself.
92
93    @param value: A datetime object or a number.
94    @returns: epoch time if value is datetime.datetime,
95              otherwise returns the value.
96    @raise ValueError: If value is not a datetime object or a number.
97    """
98    if isinstance(value, basestring):
99        value = time_string_to_datetime(value)
100    if isinstance(value, datetime.datetime):
101        return time.mktime(value.timetuple()) + 0.000001 * value.microsecond
102    if not isinstance(value, int) and not isinstance(value, float):
103        raise ValueError('Value should be a datetime object, string or a '
104                         'number. Unexpected value: %s.' % value)
105    return value
106
107
108@decorators.test_module_available(pytz, raise_error=True)
109@decorators.test_module_available(tzlocal, raise_error=True)
110def to_utc_timestamp(datetime_val):
111    """Transforms a datetime object into a utc timestamp.
112
113    @param datetime_val: A datetime timestamp.
114
115    @returns A datetime as a UTC floating point timestamp in seconds since
116             epoch.
117    """
118    epoch = datetime.datetime(1970, 1, 1, tzinfo=pytz.utc)
119    local_datetime = datetime_val.replace(tzinfo=tzlocal.get_localzone())
120    utc_datetime = local_datetime.astimezone(tz=pytz.utc)
121    return (utc_datetime - epoch).total_seconds()