Last active
August 30, 2022 00:08
-
-
Save stupidbodo/d9882e26ae885b21e9f9 to your computer and use it in GitHub Desktop.
Handling Datetime in Python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Default timezone is UTC and precisions is up to microseconds | |
#====================================== | |
# Workflow | |
#====================================== | |
# 1) Convert string to it's native datetime type. | |
# 2) Do all manipulation when the value is in datetime type | |
# 3) Convert it back to the type you need(if needed). | |
# This means that if you want to convert string "1970-01-01" to | |
# a 'datetime.datetime' type, you should first convert it to 'datetime.date' type: | |
# 1) Convert "1970-01-01" to 'datetime.date' | |
# 2) Convert from 'datetime.date' to 'datetime.datetime' | |
# This workflow clears up confusion as you won't be skipping around | |
# conversion steps, making it easy to create utility functions. | |
#====================================== | |
# Explanation | |
#====================================== | |
# To do any manipulation for date/datetime in string(like "1970-01-01"), | |
# you need to convert it to datetime type first | |
# What this means is that suppose you want to add 5 seconds to string "1970-01-01" and convert | |
# it to a datetime string like "1970-01-01 00:00:05.000000", you should use the following workflow: | |
# 1) Convert string "1970-01-01" to type 'datetime.date' | |
# 2) Convert it from 'datetime.date' to 'datetime.datetime' | |
# 3) Use timedelta to add 5 seconds | |
# 4) Use str(some_datetime_value) to convert it back to string. | |
# Using our utilities functions below, it would be something like this: | |
# date_value = convert_str_to_date("1970-01-01") | |
# datetime_value = convert_date_to_datetime_start(date_value) | |
# result = str(datetime_addition(datetime_value, 5, "seconds")) | |
# Suppose in above example, you want to add 2880 minutes(2 days equivalent) | |
# instead and get the result back in a date string like "1970-01-03", | |
# you should use the following workflow: | |
# 1) Convert string "1970-01-01" to type 'datetime.date' | |
# 2) Use timedelta to add 2880 minutes | |
# 4) Use str(some_date_value) to convert it back to string. | |
# Using our utilities functions below, it would be something like this: | |
# date_value = convert_str_to_date("1970-01-01") | |
# result = str(datetime_addition(date_value, 2880, "minutes")) | |
#====================================== | |
# Date/Datetime Conversion | |
#====================================== | |
""" | |
Default Timezone: UTC | |
Precision: Microseconds | |
""" | |
import pytz | |
from datetime import date, timedelta, datetime, time | |
def start_time(): | |
return time(0, 00, 00, 000000) | |
def end_time(): | |
return time(23, 59, 59, 999999) | |
def convert_str_to_date(date_str, format="%Y-%m-%d"): | |
""" | |
Given date in string and format, convert it to a 'datetime.date' type. | |
Return is datetime naive | |
""" | |
return datetime.strptime(date_str, format) | |
def convert_str_to_datetime(date_str, format="%Y-%m-%d %H:%M:%S.%f"): | |
""" | |
Note: %z directive is only supported from Python 3.2 onwards. | |
Given datetime in string and format, convert it to a 'datetime.datetime' type. | |
Return is datetime naive | |
""" | |
return datetime.strptime(date_str, format) | |
def convert_date_to_datetime_start(date_value): | |
""" | |
Description | |
---------- | |
Given date , convert it to datetime at end of day. Precision is up to seconds. | |
Return is datetime naive | |
Example | |
---------- | |
From "1970-01-01" to "1970-01-01 00:00:00.000000" | |
Parameters | |
---------- | |
date_value: <type 'datetime.date'> | |
Returns | |
--------- | |
<type 'datetime.datetime'> | |
Note | |
---------- | |
Provided date_value must be a 'datetime.date' type, if you have date in string, | |
use function:convert_str_to_date to convert it 'datetime.date' first. | |
To avoid confusion , it is best to avoid skipping conversion steps. | |
""" | |
return datetime.combine(date_value, start_time()) | |
def convert_date_to_datetime_end(date_value): | |
""" | |
Description | |
---------- | |
Given date , convert it to datetime at end of day. Precision is up to seconds. | |
Return is datetime naive | |
Example | |
---------- | |
From "1970-01-01" to "1970-01-01 23:59:59:999999" | |
Parameters | |
---------- | |
date_value: <type 'datetime.date'> | |
Returns | |
--------- | |
<type 'datetime.datetime'> | |
Note | |
---------- | |
Provided date_value must be a 'datetime.date' type, if you have date in string, | |
use function:convert_str_to_date to convert it 'datetime.date' first. | |
To avoid confusion , it is best to avoid skipping conversion steps. | |
""" | |
return datetime.combine(date_value, end_time()) | |
def convert_datetime_to_date(datetime_value): | |
""" | |
Convert datetime to date | |
From <type 'datetime.datetime'> to <type 'datetime.date'> | |
""" | |
return datetime_value.date() | |
#====================================== | |
# Timezone Functions | |
#====================================== | |
def add_timezone_to_datetime_naive(datetime_naive, timezone='UTC'): | |
return pytz.timezone(timezone).localize(datetime_naive) | |
def convert_datetime_aware_to_datetime_naive(datetime_aware): | |
return datetime_aware.replace(tzinfo=None) | |
def convert_datetime_to_another_timezone(datetime_value, timezone): | |
""" | |
Description | |
---------- | |
Given timezone aware datetime object, convert it to another timezone. | |
Example | |
---------- | |
From "1970-01-01 00:00:00.000000+0000" to "1970-01-01 08:00:00.000000+0800" | |
Parameters | |
---------- | |
datetime_value: <type 'datetime.datetime'> | |
timezone: string (but must be valid timezone for pytz) | |
Returns | |
--------- | |
<type 'datetime.datetime'> | |
""" | |
return datetime_value.astimezone(pytz.timezone(timezone)) | |
def utc_now_in_date(): | |
return datetime.now(pytz.utc).date() | |
def utc_now_in_datetime(): | |
return datetime.now(pytz.utc) | |
#====================================== | |
# Addition/Substraction Date/Datetime | |
#====================================== | |
# Use timedelta to perform addition/subtraction in date/datetime types | |
def datetime_addition(datetime_to_add, interval_num, interval_type): | |
""" | |
Description | |
---------- | |
Generic function for datetime addition. | |
Example | |
---------- | |
Given "1970-01-01 00:00:00:000000+0000" as datetime_to_add, 5 as interval_num, | |
"seconds" as interval_type, return "1970-01-01 00:00:05:000000+0000" | |
Parameters | |
---------- | |
datetime_to_add: Any type in <type 'datetime.*'> class. | |
interval_num: integer | |
interval_type: string | |
Returns | |
--------- | |
Depending on datetime_to_add you pass in. Will return specific type from <type 'datetime.*'> class | |
Note | |
-------- | |
This function accepts any type in datetime class. | |
When you pass in <type 'datetime.date'> type like "1970-01-01" | |
and try something like adding 2880 minutes(2 days equivalent), you will get "1970-01-03" | |
""" | |
params = {interval_type: interval_num} | |
return datetime_to_add + timedelta(**params) | |
def datetime_subtraction(datetime_to_subtract, interval_num, interval_type): | |
""" | |
Description | |
---------- | |
Generic function for datetime sustraction. | |
Example | |
---------- | |
Given "1970-01-01" as datetime_to_subtract, 5 as interval_num, "days" as interval_type, | |
return "1969-12-27" | |
Parameters | |
---------- | |
datetime_to_subtract: Any type in <type 'datetime.*'> class. | |
interval_num: integer | |
interval_type: string | |
Returns | |
--------- | |
Depending on datetime_to_subtract you pass in. | |
Will return specific type from <type 'datetime.*'> class | |
Note | |
-------- | |
This function accepts any type in datetime class. | |
When you pass in <type 'datetime.date'> type like "1970-01-03" | |
and try something like subtracting 2880 minutes(2 days equivalent), you will get "1970-01-01" | |
""" | |
params = {interval_type: interval_num} | |
return datetime_to_subtract - timedelta(**params) | |
def add_days_to_date(date_to_add, days_in_number): | |
return datetime_addition(date_to_add, days_in_number, "days") | |
def add_days_to_datetime(datetime_to_add, days_in_number): | |
return datetime_addition(datetime_to_add, days_in_number, "days") | |
def subtract_days_from_date(date_to_subtract, days_in_number): | |
return datetime_subtraction(date_to_subtract, days_in_number, "days") | |
def subtract_days_from_datetime(datetime_to_subtract, days_in_number): | |
return datetime_subtraction(datetime_to_subtract, days_in_number, "days") | |
#====================================== | |
# Utilities | |
#====================================== | |
def last_day_of_month(any_day): | |
""" | |
Taken from http://stackoverflow.com/a/13565185/1446284 | |
""" | |
next_month = any_day.replace(day=28) + timedelta(days=4) # this will never fail | |
return next_month - timedelta(days=next_month.day) | |
#====================================== | |
# Ready-Made Functions | |
#====================================== | |
def get_today(timezone='UTC'): | |
return datetime.now(pytz.timezone(timezone)).date() | |
def get_today_start_in_datetime(timezone='UTC'): | |
""" | |
Return start of today in <type 'datetime.datetime'> | |
Return is timezone aware datetime | |
""" | |
today = get_today(timezone) | |
datetime_naive = datetime.combine(today, start_time()) | |
return add_timezone_to_datetime_naive(datetime_naive, timezone) | |
def get_today_end_in_datetime(timezone='UTC'): | |
""" | |
Return end of today in <type 'datetime.datetime'> | |
Return is timezone aware datetime | |
""" | |
today = get_today(timezone) | |
datetime_naive = datetime.combine(today, end_time()) | |
return add_timezone_to_datetime_naive(datetime_naive, timezone) | |
def first_day_of_month(d): | |
""" | |
Given date return first day of the month in <type 'datetime.date'> | |
""" | |
return date(d.year, d.month, 1) | |
def first_day_of_this_month(timezone='UTC'): | |
""" | |
Return first day of this month in <type 'datetime.date'>. | |
""" | |
today = datetime.now(pytz.timezone(timezone)).date() | |
return date(today.year, today.month, 1) | |
def first_day_of_this_month_in_datetime(timezone='UTC'): | |
""" | |
Return first day of this month in <type 'datetime.datetime'> | |
Return is timezone aware datetime | |
""" | |
first_day = first_day_of_this_month(timezone) | |
datetime_naive = datetime.combine(first_day, start_time()) | |
return add_timezone_to_datetime_naive(datetime_naive, timezone) | |
def last_day_of_this_month(timezone='UTC'): | |
""" | |
Return last day of this month in <type 'datetime.date'> | |
""" | |
today = datetime.now(pytz.timezone(timezone)).date() | |
return last_day_of_month(today) | |
def last_day_of_this_month_in_datetime(timezone='UTC'): | |
""" | |
Return last day of this month in <type 'datetime.datetime'> | |
Return is timezone aware datetime | |
""" | |
last_day = last_day_of_this_month(timezone) | |
datetime_naive = datetime.combine(last_day, end_time()) | |
return add_timezone_to_datetime_naive(datetime_naive, timezone) | |
def first_day_of_last_month(timezone='UTC'): | |
""" | |
Return first day of last month in <type 'datetime.date'> | |
""" | |
return first_day_of_month(last_day_of_last_month(timezone)) | |
def first_day_of_last_month_in_datetime(timezone='UTC'): | |
""" | |
Return first day of last month in <type 'datetime.datetime'> | |
Return is timezone aware datetime | |
""" | |
first_day = first_day_of_last_month(timezone) | |
datetime_naive = datetime.combine(first_day, start_time()) | |
return add_timezone_to_datetime_naive(datetime_naive, timezone) | |
def last_day_of_last_month(timezone='UTC'): | |
""" | |
Return last day of last month in <type 'datetime.date'> | |
""" | |
last_month_date = first_day_of_this_month(timezone) - timedelta(days=1) | |
return last_month_date | |
def last_day_of_last_month_in_datetime(timezone='UTC'): | |
""" | |
Return last day of last month in <type 'datetime.datetime'> | |
Return is timezone aware datetime | |
""" | |
last_day = last_day_of_last_month(timezone) | |
datetime_naive = datetime.combine(last_day, end_time()) | |
return add_timezone_to_datetime_naive(datetime_naive, timezone) | |
def convert_datetime_to_unix_timestamp(datetime_value): | |
""" | |
Takes datetime naive object and return value in unix timestamp seconds. | |
Return is float. | |
""" | |
epoch = datetime.utcfromtimestamp(0) | |
delta = datetime_value - epoch | |
return delta.total_seconds() | |
def convert_datetime_to_unix_timestamp_millis(datetime_value): | |
""" | |
Takes datetime naive object and return value in unix timestamp milliseconds. | |
Return is float. | |
""" | |
return convert_datetime_to_unix_timestamp(datetime_value) * 1000.0 | |
def get_current_timestamp(): | |
"""Get current timestamp, uses UTC | |
Returns: | |
int | |
""" | |
current_time = datetime.now(pytz.timezone('UTC')) | |
return int(time.mktime(current_time.timetuple())) | |
def get_day_of_month(): | |
return datetime.now(pytz.utc).day | |
def get_day_of_week(): | |
return datetime.now(pytz.utc).weekday() | |
def get_hour_of_day(): | |
return datetime.now(pytz.utc).hour | |
def time_difference_in_ms(start, end): | |
"""Get time difference in milliseconds | |
Args: | |
start (datetime.datetime) | |
end (datetime.datetime) | |
Returns: | |
Integer time difference in milliseconds | |
""" | |
time_difference = end - start | |
return int(time_difference.total_seconds() * 1000) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment