HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Effective way to handle multiple time string to timestamp

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
handlewaytimemultipletimestampeffectivestring

Problem

I have 4 cases of time string as follow:

  • s = "July 5, 2016, 12:04 a.m."



  • s = "July 5, 2016, 7 a.m."



  • s = "July 5, 2016, midnight."



  • s = "July 5, 2016, noon."



I want to convert string to timestamps. Based on this question, I need to declare 4 cases of pattern. I am looking forward to improve my solution.

The following code is my solution:

import time
import datetime

s1 = "July 5, 2016, 12:04 a.m."
s2 = "July 5, 2016, 7 a.m."
s3 = "July 5, 2016, midnight."
s4 = "July 5, 2016, noon."

def convert(timestring):
    # clear dot
    timestring = timestring.replace(".","")
    # convert s3 and s4 to s1 and s2
    timestring = timestring.replace("midnight", "12:00 am") if "midnight" in timestring else timestring
    timestring = timestring.replace("noon", "12:00 pm") if "noon" in timestring else timestring
    result = None
    try:
        # try to match s2
        print(timestring)
        result = time.mktime(datetime.datetime.strptime(timestring, "%B %d, %Y, %I %p").timetuple())
    except ValueError as e:
        try:
            result = time.mktime(datetime.datetime.strptime(timestring, "%B %d, %Y, %I:%M %p").timetuple())
        except ValueError as e:
            print(e)
    print(result)

def main():
    convert(s1)
    convert(s2)
    convert(s3)
    convert(s4)

if __name__ == '__main__':
    main()

Solution

It's not really appropriate to print the result or the exception message instead of returning the result or throwing the exception.

It's not right to eliminate all dots from the input before proceeding.

I think that a good strategy would be to transform the input into a canonical representation. You've started along that path by replacing "midnight" and "noon" with "12:00 am" and "12:00 pm", respectively. You could go further and set the minutes to ":00" if necessary. To accomplish that, I would use regular expression matching.

A doctest seems very appropriate here, both for documentation and as a unit test.

from datetime import datetime
import re
from time import mktime

def convert(timestring):
    """
    Convert a time string, in one of four accepted formats, in the
    local time zone, into the number of seconds since the Epoch.

    >>> import os, time
    >>> os.environ['TZ'] = 'US/Pacific'
    >>> time.tzset()
    >>> convert("July 5, 2016, 12:04 a.m.")
    1467702240.0
    >>> convert("July 5, 2016, 7 a.m.")
    1467727200.0
    >>> convert("July 5, 2016, midnight.")
    1467702000.0
    >>> convert("July 5, 2016, noon.")
    1467745200.0
    >>> convert("2016-07-05 12:34:56")
    Traceback (most recent call last):
     ...
    ValueError: time data '2016-07-05 12:34:56' does not match format '%B %d, %Y, %I:%M %p'
    """
    match = re.match(
        '(?P.*)\s+'
        '(?:(?Pnoon)|midnight|'
           '(?P1?[0-9])(?::(?P[0-5]?[0-9]))?'
           '\s+(?:(?Pp)|a)\.?m)\.,
        timestring
    )
    if match:
        # Canonicalize the time of day as "HH:MM am/pm"
        timestring = '{date} {hh}:{mm} {ampm}'.format(
            date=match.group('date'),
            hh=match.group('hour') or '12',
            mm=match.group('minute') or '00',
            ampm=['am', 'pm'][bool(match.group('pm') or match.group('noon'))]
        )
    return mktime(
        datetime.strptime(timestring, '%B %d, %Y, %I:%M %p').timetuple()
    )

Code Snippets

from datetime import datetime
import re
from time import mktime

def convert(timestring):
    """
    Convert a time string, in one of four accepted formats, in the
    local time zone, into the number of seconds since the Epoch.

    >>> import os, time
    >>> os.environ['TZ'] = 'US/Pacific'
    >>> time.tzset()
    >>> convert("July 5, 2016, 12:04 a.m.")
    1467702240.0
    >>> convert("July 5, 2016, 7 a.m.")
    1467727200.0
    >>> convert("July 5, 2016, midnight.")
    1467702000.0
    >>> convert("July 5, 2016, noon.")
    1467745200.0
    >>> convert("2016-07-05 12:34:56")
    Traceback (most recent call last):
     ...
    ValueError: time data '2016-07-05 12:34:56' does not match format '%B %d, %Y, %I:%M %p'
    """
    match = re.match(
        '(?P<date>.*)\s+'
        '(?:(?P<noon>noon)|midnight|'
           '(?P<hour>1?[0-9])(?::(?P<minute>[0-5]?[0-9]))?'
           '\s+(?:(?P<pm>p)|a)\.?m)\.$',
        timestring
    )
    if match:
        # Canonicalize the time of day as "HH:MM am/pm"
        timestring = '{date} {hh}:{mm} {ampm}'.format(
            date=match.group('date'),
            hh=match.group('hour') or '12',
            mm=match.group('minute') or '00',
            ampm=['am', 'pm'][bool(match.group('pm') or match.group('noon'))]
        )
    return mktime(
        datetime.strptime(timestring, '%B %d, %Y, %I:%M %p').timetuple()
    )

Context

StackExchange Code Review Q#134006, answer score: 2

Revisions (0)

No revisions yet.