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!!!


No comments:

Popular Posts