Feed Sign in with OpenID OpenID

Simon Willison’s Weblog

PHP’s date() function in Python

In switching from PHP to Python I’m discovering an increasing number of PHP functions that I’ve learnt to rely on but have no direct equivalent in the Python standard library. Often Python simply provides a different way of approaching the problem, but old habits die hard and I’ve been replicating some of PHP’s functionality in Python for my own personal use.

Python 2.3 introduced the datetime module, which has comprehensive support for performing calculations on dates. Users of earlier Python versions can still benefit from the module thanks to a pure Python implementation available here. datetime objects can be formatted as strings using the strftime method, documented here; PHP offers a similar function. strftime() is a powerful function which takes full account of the current locale when formatting dates. PHP’s date() function ignores the locale but provides a far richer set of formatting options, including my favourite: the ability to display a date with an ordinal, for example “7th October”.

I’ve always preferred date(), so I’ve ported it to to Python. My version supports most of PHP’s date format options, raising a NotImplemented exception for any that are unsupported. Usage looks like this:

>>> import datetime
>>> from DateFormat import DateFormat
>>> d = datetime.datetime.now()
>>> df = DateFormat(d)
>>> print df.format('jS F Y H:i')

The class works using a neat piece of introspection. Each of the available formatting options is implemented as a method of the class which returns that part of the date formatted in the correct way. For example, the ’a’ command (for returning ’am’ or ’pm’ in lower case) looks like this:

    def a(self):
        '"am" or "pm"'
        if self.date.hour > 12:
            return 'pm'
        else:
            return 'am'

The format method simply cycles through the characters in the format string, attempting to call the method of that name each time round using getattr(). If a method doesn’t exist (i.e the character isn’t one of the special formatting commands) a try/except block catches the thrown AttributeError. The whole method looks like this:

    def format(self, formatstr):
        result = ''
        for char in formatstr:
            try:
                result += str(getattr(self, char)())
            except AttributeError:
                result += char
        return result

I might alter the interface a bit in the future, maybe creating an extended version of the datetime class itself, but for the moment this serves my purposes just fine.

This is PHP’s date() function in Python by Simon Willison, posted on 7th October 2003.

View blog reactions

Next: There goes the neighbourhood

Previous: How I obtained my US Visa

10 comments

  1. Go Simon ! That's an awesome idea. Suggestion though -- don't follow php's ridiculous issues w/ string options where sometimes is subject, arguments and sometimes else arguments, subject. That drives me insane.

    Scott Johnson - 7th October 2003 12:06 - #

  2. That's a really nice implementation.
    One question - What's a 'try/catch blog'? :-)

    Danny Shepherd - 7th October 2003 12:47 - #

  3. Cool --

    One Pythonic thing that may not make a huge difference when working with short strings like this, but definitely becomes a problem when creating longer strings a piece at a time is that the += operation needs to reallocate the entire string everytime to add to the end. Instead use the idiom (apologies if this is obvious, but traffic on comp.lang.python indicates that it's not widely known) of building your string by appending substrings to a list, then returning "".join(listOfSubstrings) to create the final string for output.

    Much faster.

    Bg Porter - 7th October 2003 14:30 - #

  4. You may want to try mxDateTime too. I use it for most of my Date work in Python now, it can make life much easier, especially if you are working with MySQL since its DB API layer understands the mxDateTime objects and coerces them properly.

    John Beimler - 7th October 2003 15:35 - #

  5. ummm .... 12:01 is PM, not AM.

    if self.date.hour > 12:
    return 'pm'
    else:
    return 'am'

    Eric Scheid - 8th October 2003 10:06 - #

  6. Very good point, I'm fixing that now. It's true what they say about open source and bug detection - I'd probably have missed that one for weeks if you hadn't pointed it out for me.

    Simon Willison - 8th October 2003 14:41 - #

  7. I didn't know where datetime came from, so here is a version without it. Call like:
    >>> import time
    >>> from DateFormat import DateFormat
    >>> d = time.time()
    >>> df = DateFormat(d)
    >>> print df.format('jS F Y H:i')
    
    Add this code to DateFormat.py:
    import time
    class Date:
        def __init__(self, *arg):
            self.year = arg[0]
            self.month = arg[1]
            self.day = arg[2]
            self.hour = arg[3]
            self.minute = arg[4]
            self.second = arg[5]
            self.weekday = arg[6]
            self.julian_day = arg[7]
            self.dst = arg[8]
                                                                                                                                                                
    class DateFormat:
        """ Takes a time.time() """
        weekdays = 'Monday Tuesday Wednesday Thursday Friday Saturday Sunday'.split()
        months = 'January February March April May June July August September October November December'.split()
        def __init__(self, d):
            self.date = Date(*time.gmtime(d))
    ...
    

    dblank - 21st November 2004 04:35 - #

  8. Also, there was a bug in:
        def s(self, s):
    
    should be:
        def s(self):
    

    dsblank - 21st November 2004 05:02 - #

  9. rererererre

    kit - 27th September 2005 10:41 - #

  10. how i split 2006-08-01 13:58:54 this formate August 1st, 2006 at 12:13 am

    kumar - 1st August 2006 10:53 - #

Comments are closed.

Previously hosted at http://simon.incutio.com/archive/2003/10/07/dateInPython

A django site