Dan Shi | dfea368 | 2014-08-10 23:38:40 -0700 | [diff] [blame] | 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 | |
| 7 | import datetime |
| 8 | import time |
| 9 | |
Benny Peake | d322d3d | 2017-02-08 15:39:28 -0800 | [diff] [blame] | 10 | from autotest_lib.client.common_lib import decorators |
| 11 | |
Paul Hobbs | 74a5611 | 2017-11-03 19:33:37 +0000 | [diff] [blame] | 12 | |
Benny Peake | d322d3d | 2017-02-08 15:39:28 -0800 | [diff] [blame] | 13 | try: |
| 14 | import pytz |
| 15 | except ImportError: |
| 16 | pytz = None |
Benny Peake | d322d3d | 2017-02-08 15:39:28 -0800 | [diff] [blame] | 17 | |
Paul Hobbs | 74a5611 | 2017-11-03 19:33:37 +0000 | [diff] [blame] | 18 | |
Benny Peake | d322d3d | 2017-02-08 15:39:28 -0800 | [diff] [blame] | 19 | try: |
| 20 | import tzlocal |
| 21 | except ImportError: |
| 22 | tzlocal = None |
Benny Peake | d322d3d | 2017-02-08 15:39:28 -0800 | [diff] [blame] | 23 | |
Dan Shi | dfea368 | 2014-08-10 23:38:40 -0700 | [diff] [blame] | 24 | |
| 25 | # This format is used to parse datetime value in MySQL database and should not |
| 26 | # be modified. |
| 27 | TIME_FMT = '%Y-%m-%d %H:%M:%S' |
MK Ryu | 0c1a37d | 2015-04-30 12:00:55 -0700 | [diff] [blame] | 28 | TIME_FMT_MICRO = '%Y-%m-%d %H:%M:%S.%f' |
Dan Shi | dfea368 | 2014-08-10 23:38:40 -0700 | [diff] [blame] | 29 | |
| 30 | def time_string_to_datetime(time_string, handle_type_error=False): |
| 31 | """Convert a string of time to a datetime object. |
| 32 | |
MK Ryu | 0c1a37d | 2015-04-30 12:00:55 -0700 | [diff] [blame] | 33 | The format of date string must match '%Y-%m-%d %H:%M:%S' or |
| 34 | '%Y-%m-%d %H:%M:%S.%f'. |
Dan Shi | dfea368 | 2014-08-10 23:38:40 -0700 | [diff] [blame] | 35 | |
| 36 | @param time_string: String of date, e.g., 2014-12-05 15:32:45 |
MK Ryu | 0c1a37d | 2015-04-30 12:00:55 -0700 | [diff] [blame] | 37 | @param handle_type_error: Set to True to prevent the method raise |
| 38 | TypeError if given time_string is corrupted. Default is False. |
| 39 | |
Dan Shi | dfea368 | 2014-08-10 23:38:40 -0700 | [diff] [blame] | 40 | @return: A datetime object with time of the given date string. |
MK Ryu | 0c1a37d | 2015-04-30 12:00:55 -0700 | [diff] [blame] | 41 | |
Dan Shi | dfea368 | 2014-08-10 23:38:40 -0700 | [diff] [blame] | 42 | """ |
| 43 | try: |
MK Ryu | 0c1a37d | 2015-04-30 12:00:55 -0700 | [diff] [blame] | 44 | try: |
| 45 | return datetime.datetime.strptime(time_string, TIME_FMT) |
| 46 | except ValueError: |
| 47 | return datetime.datetime.strptime(time_string, TIME_FMT_MICRO) |
Dan Shi | dfea368 | 2014-08-10 23:38:40 -0700 | [diff] [blame] | 48 | except TypeError: |
| 49 | if handle_type_error: |
| 50 | return None |
| 51 | else: |
| 52 | raise |
| 53 | |
| 54 | |
J. Richard Barnette | c9b7933 | 2014-09-22 13:14:33 -0700 | [diff] [blame] | 55 | def date_string_to_epoch_time(date_string): |
| 56 | """Parse a date time string into seconds since the epoch. |
Dan Shi | dfea368 | 2014-08-10 23:38:40 -0700 | [diff] [blame] | 57 | |
MK Ryu | c9c0c3f | 2014-10-27 14:36:01 -0700 | [diff] [blame] | 58 | @param date_string: A string, formatted according to `TIME_FMT`. |
J. Richard Barnette | c9b7933 | 2014-09-22 13:14:33 -0700 | [diff] [blame] | 59 | |
| 60 | @return The number of seconds since the UNIX epoch, as a float. |
| 61 | |
| 62 | """ |
| 63 | return time.mktime(time.strptime(date_string, TIME_FMT)) |
| 64 | |
| 65 | |
Prashanth Balasubramanian | baabef2 | 2014-11-04 12:38:44 -0800 | [diff] [blame] | 66 | def epoch_time_to_date_string(epoch_time, fmt_string=TIME_FMT): |
J. Richard Barnette | c9b7933 | 2014-09-22 13:14:33 -0700 | [diff] [blame] | 67 | """Convert epoch time (float) to a human readable date string. |
| 68 | |
| 69 | @param epoch_time The number of seconds since the UNIX epoch, as |
| 70 | a float. |
Prashanth Balasubramanian | baabef2 | 2014-11-04 12:38:44 -0800 | [diff] [blame] | 71 | @param fmt_string: A string describing the format of the datetime |
| 72 | string output. |
| 73 | |
Dan Shi | dfea368 | 2014-08-10 23:38:40 -0700 | [diff] [blame] | 74 | @returns: string formatted in the following way: "yyyy-mm-dd hh:mm:ss" |
| 75 | """ |
| 76 | if epoch_time: |
| 77 | return datetime.datetime.fromtimestamp( |
Prashanth Balasubramanian | baabef2 | 2014-11-04 12:38:44 -0800 | [diff] [blame] | 78 | int(epoch_time)).strftime(fmt_string) |
Dan Shi | dfea368 | 2014-08-10 23:38:40 -0700 | [diff] [blame] | 79 | return None |
| 80 | |
| 81 | |
| 82 | def to_epoch_time(value): |
| 83 | """Convert the given value to epoch time. |
| 84 | |
| 85 | Convert the given value to epoch time if it is a datetime object or a string |
| 86 | can be converted to datetime object. |
| 87 | If the given value is a number, this function assume the value is a epoch |
| 88 | time value, and returns the value itself. |
| 89 | |
| 90 | @param value: A datetime object or a number. |
MK Ryu | c9c0c3f | 2014-10-27 14:36:01 -0700 | [diff] [blame] | 91 | @returns: epoch time if value is datetime.datetime, |
| 92 | otherwise returns the value. |
Dan Shi | dfea368 | 2014-08-10 23:38:40 -0700 | [diff] [blame] | 93 | @raise ValueError: If value is not a datetime object or a number. |
| 94 | """ |
| 95 | if isinstance(value, basestring): |
| 96 | value = time_string_to_datetime(value) |
| 97 | if isinstance(value, datetime.datetime): |
Dan Shi | f8b71d1 | 2014-09-03 10:31:08 -0700 | [diff] [blame] | 98 | return time.mktime(value.timetuple()) + 0.000001 * value.microsecond |
Dan Shi | dfea368 | 2014-08-10 23:38:40 -0700 | [diff] [blame] | 99 | if not isinstance(value, int) and not isinstance(value, float): |
| 100 | raise ValueError('Value should be a datetime object, string or a ' |
| 101 | 'number. Unexpected value: %s.' % value) |
| 102 | return value |
Benny Peake | d322d3d | 2017-02-08 15:39:28 -0800 | [diff] [blame] | 103 | |
| 104 | |
| 105 | @decorators.test_module_available(pytz, raise_error=True) |
| 106 | @decorators.test_module_available(tzlocal, raise_error=True) |
| 107 | def to_utc_timestamp(datetime_val): |
| 108 | """Transforms a datetime object into a utc timestamp. |
| 109 | |
| 110 | @param datetime_val: A datetime timestamp. |
| 111 | |
| 112 | @returns A datetime as a UTC floating point timestamp in seconds since |
| 113 | epoch. |
| 114 | """ |
Dan Shi | 395697d | 2017-05-15 12:18:41 -0700 | [diff] [blame] | 115 | if datetime_val is None: |
| 116 | return None |
| 117 | |
Benny Peake | d322d3d | 2017-02-08 15:39:28 -0800 | [diff] [blame] | 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) |
Brian Norris | 7b1a83c | 2017-04-18 17:14:21 -0700 | [diff] [blame] | 121 | return (utc_datetime - epoch).total_seconds() |