## Tuesday, April 22, 2014

### Python - local time to UTC time conversion using strings

Recently I came across a problem of converting local time with offset format to  UTC time format using strings in Python. I did lot of googling and unable to find the solution. So I wrote the code in python and sharing here for others.  Input format for the local time is ISO 8601.

Algorithm to convert from local time stamp with offset to UTC time:

1. Check for the format "%Y-%M-%D %H:%M:%z" if matches remove z and return true as its UTC format
2. Split input value basing on space, so that we will get two items as date and time. if not return false as its invalid format
3. Split date item from step2 with '-' as date items are separated by '-' and it should return 3 items day, month and year. If not return false
4. Split time item from step2 with '+' or '-' to separate local time and offset. It should return two items other wise return false
5. Split local time from step4 with ':' to get three items HH, MM and SS if not return false
6. Split offset from step4 with ':' to get two offset items HH, MM if not return false
7. Add MM items from Steps 5 and 6. If MM value is greater than 60 increment HH in local item in step 5 and assign remaining MM to local item in step 5
8. Add HH items from Steps 5 and 6. If HH value is greater than 24 increment DD in date item in step 3 and assign remaining HH to local item in step 5
9.  Similarly for Day, Month and same logic applies for leap year as well.
10. After performing above steps rejoin data and time into one string and return that final string.

Below is the source code in Python:

```import datetime
import calendar
import re

HRS_IN_A_DAY = 24
MONTHS_IN_YEAR = 12
MINS_IN_HR = 60
FEB = 2

def is_int(input_value):
try:
int(input_value)
return True
except ValueError:
return False

def to_double_digit(value):
"""
converts to double digit by prefixing zero to the value
"""
return '0'+str(value) if (value<10) else str(value)

not_found = -1
def convert_local_time_to_utc(l_time_str):```
```    """
Validate the input format and if invalid returns false
if valid, converts into UTC timestamp and returns new utc timestamp
"""
try:
datetime.datetime.strptime(l_time_str,"%Y-%m-%d %H:%M:%Sz")
print "success"
except (ValueError, TypeError) as e:
date_str_items = l_time_str.split(' ')
if (len(date_str_items)!=2):
return False
date_item_list = date_str_items[0].split('-')
if(len(date_item_list)!=3):
return False
time_offset = re.split('-|\+',date_str_items[1])
if(len(time_offset)!=2):
return False
local_time_items = time_offset[0].split(':')
#print time_offset
if(len(local_time_items)!=3):
return False
offset_items = time_offset[1].split(':')
if(len(offset_items)!=2):
return False
plus_found = l_time_str.find('+')

# validating for int values as date accepts only int
if not is_int(date_item_list[0]):
return False
year_value = int(date_item_list[0])
if not is_int(date_item_list[1]):
return False
month_value = int(date_item_list[1])
if not is_int(date_item_list[2]):
return False
date_value = int(date_item_list[2])
if not is_int(local_time_items[0]):
return False
local_hh = int(local_time_items[0])
if not is_int(local_time_items[1]):
return False
local_mm = int(local_time_items[1])
if not is_int(offset_items[0]):
return False
off_hh = int(offset_items[0])
if not is_int(offset_items[1]):
return False
off_mm = int(offset_items[1])

if plus_found == not_found:
local_mm += off_mm
if local_mm >= MINS_IN_HR:
local_hh += 1
local_mm -= MINS_IN_HR
local_hh += off_hh
if local_hh >= HRS_IN_A_DAY:
date_value+=1
local_hh-= HRS_IN_A_DAY
days_in_month = calendar.mdays[month_value]
if calendar.isleap(year_value) and month_value == FEB:
days_in_month += 1
if date_value > days_in_month:
month_value += 1
date_value -= days_in_month
if month_value > MONTHS_IN_YEAR:
year_value += 1
month_value -= MONTHS_IN_YEAR
else:
local_mm -= off_mm
if local_mm < 0:
local_hh -= 1
local_mm += MINS_IN_HR
local_hh -= off_hh
if local_hh < 0:
date_value -= 1
local_hh += HRS_IN_A_DAY
if date_value < 1:
month_value -= 1
if month_value < 1:
month_value += MONTHS_IN_YEAR
year_value -= 1
date_value += calendar.mdays[month_value]
if calendar.isleap(year_value) and month_value == FEB:
date_value += 1
#restoring new values into list
local_time_items[0] = to_double_digit(local_hh)
local_time_items[1] = to_double_digit(local_mm)
date_item_list[0] = to_double_digit(year_value)
date_item_list[1] = to_double_digit(month_value)
date_item_list[2] = to_double_digit(date_value)
#making into timestamp format "YYYY-MM-DD HH:MM:SS"
final_date = "-".join(date_item_list)+" "+":".join(local_time_items)
return final_date

print "Enter the date in the form of \"YYYY-MM-DD HH:MM:SS+HH:MM\" or \"YYYY-MM-DD HH:MM:SS-HH:MM\""
input_date = raw_input("Enter local date:")
while input_date:
ret_val = convert_local_time_to_utc(input_date)
if ret_val:
print "converted UTC is ",ret_val
else:
print "Invalid format"
input_date = raw_input("Enter local date:")
```

Output:

Enter the date in the form of "YYYY-MM-DD HH:MM:SS+HH:MM" or "YYYY-MM-DD HH:MM:SS-HH:MM"
Enter local date:2000-02-28 22:48:59-05:21
2000-02-29 04:09:59
converted UTC is  2000-02-29 04:09:59
Enter local date:2000-02-28 22:48:59+05:21
2000-02-28 17:27:59
converted UTC is  2000-02-28 17:27:59
Enter local date:sdfd
Invalid format
Enter local date:2000-02-28 22:48:59
Invalid format
Enter local date:
Process finished with exit code 0

Happy Coding!!!