Skip to content

Instantly share code, notes, and snippets.

@stupidbodo
Last active August 30, 2022 00:08
Show Gist options
  • Save stupidbodo/d9882e26ae885b21e9f9 to your computer and use it in GitHub Desktop.
Save stupidbodo/d9882e26ae885b21e9f9 to your computer and use it in GitHub Desktop.
Handling Datetime in Python
# 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