3
\$\begingroup\$

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()
\$\endgroup\$
2
\$\begingroup\$

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<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()
    )
| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

Your comment for ... = ....replace(".", "") is simply clear dot. It is obvious what that line is accomplishing; the comment should explain why the code does that, not what it is doing.


Take a look:

>>> x = "here!"
>>> x.replace("there", "nowhere")
'here!'

There is no error. Using .replace() works even if there is nothing to replace. Therefore, your timestring = timestring.replace(...) if ... else timestring lines are overly complicated. Just do the replace whether or not it actually accomplishes anything.


Why do you bother using datetime? The time module has everything you need ... more simply. The datetime module is useful when doing complicated things with dates such as adding two hours to such-and-such a time. The time module, on the other hand, is useful for cases when you mostly care about seconds since the Epoch. That is what you are doing here. I would change:

result = time.mktime(datetime.datetime.strptime(timestring, "%B %d, %Y, %I %p").timetuple())

to

result = time.mktime(time.strptime(timestring, "%B %d, %Y, %I %p"))

Shwew, that is so much shorter.

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.