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