top | item 3545935

Introducing Times (for Python)

128 points| nvie81 | 14 years ago |nvie.com

18 comments

order

jessedhillon|14 years ago

IMO dateutil provides this in a more transparent way by providing tzinfo subclasses:

    py> from dateutil.tz import gettz
    py> from datetime import datetime

    py> new_york = gettz('America/New York')
    py> los_angeles = gettz('America/Los Angeles')
    py> minsk = gettz('Europe/Minsk')

    py> fmt = "%Y-%m-%d %H:%M%z"
    py> datetime.now(new_york).strftime(fmt)
      '2012-02-03 01:51-0500'
    py> datetime.now(los_angeles).strftime(fmt)
      '2012-02-02 22:51-0800'
    py> datetime.now(los_angeles).astimezone(minsk).strftime(fmt)
      '2012-02-03 09:51+0300'
For me, the most annoying thing about Python is that out of the box, AFAICT, you cannot get a TZ-aware datetime. In fact, while tzinfo is part of the standard library, I don't know of any included solution for making TZ-aware datetime, short of writing your own subclasses for each timezone.

EDIT: datetime.utcnow() is TZ-aware, I meant any other timezone.

j2labs|14 years ago

I agree that dateutil is excellent. I think it's also worth mentioning how powerful dateutil's `parse()` is. You can pass virtually any string representation of a date to it and it will return a Python datetime.

masklinn|14 years ago

> For me, the most annoying thing about Python is that out of the box, AFAICT, you cannot get a TZ-aware datetime.

That's because the stdlib would have to get updated every 3-6 months, which it is not. Hence delegating to e.g. pytz for timezones provision.

> I don't know of any included solution for making TZ-aware datetime, short of writing your own subclasses for each timezone.

There isn't indeed, because there can't be an stdlib-provided way to do this which is actually correct.

mvanveen|14 years ago

dateutil is really great; j2labs is quick to point out `parse()` which I also think is worth mentioning on its own.

I think this project still fills a nice niche. datetime.now(los_angeles).astimezone(minsk).strftime(fmt) is long enough that you're going to have to find the StackOverflow thread in question to implement this, and maybe go to the stdlib. This library makes that a lot less painful.

If you go to the source, you'll see how tiny it is! It's nowhere near the complexity of dateutil. I'd love to see some of dateutil's darker corners wrapped into this library.

latchkey|14 years ago

This is good, but for my app, we just decided to do all of the presentation on the client side using javascript. This prevents us from having to know or store the users tz at all. That way, whatever tz they are in is the correct one. =)

We thought this might be an issue for things like emailing purchase receipts to people since that is something that can't just be 'rendered' on the client, but we ended up just asking ourselves "What would amazon do?" and looked at an emailed Amazon receipt.

You will notice that there isn't a single time in the receipt other than when the email was sent. Looked at a receipt from Apple. Same thing. Problem solved by just removing the timestamp from the receipt.

Thought that was an interesting tidbit that I hadn't thought of before.

This is the JS library I used and am pretty happy with: http://momentjs.com/

mvanveen|14 years ago

Interesting.

I recently was evaluating JS and Python for handling time in my app. The goal was not to localize to a specific timezone for the client, but rather to inform the client of where in the world a current time is.

I ended up picking the Python backend solution because it was easier to reason about with the more abstract `datetime` primitives.

tantalor|14 years ago

I don't think its fair to say one should "never work with local times". I frequently work with "floating time", the opposite of "fixed time".

You can accept user input as (year, month, day, hour, minute, second), convert to a common timezone, and store as seconds since epoch. To display, convert back to the common timezone, but omit the timezone identifier. This is useful for events which are local for the user, since they don't care about their own timezone.

See the iCalendar spec,

They are used to represent the same hour, minute, and second value regardless of which time zone is currently being observed. For example, an event can be defined that indicates that an individual will be busy from 11:00 AM to 1:00 PM every day, no matter which time zone the person is in. In these cases, a local time can be specified.

http://www.kanzaki.com/docs/ical/dateTime.html

tantalor|14 years ago

I don't think "2012-02-03 09:30:00+0100" is valid ISO 8601 because of the space; it should be a "T" to be a proper "combined date and time" representation.

The date and time representations may appear in proximity to each other, often separated by a space or sometimes by other characters. In these cases they occupy two separate fields in a data system, rather than a single combined representation. This is usually done for human readability. Unlike the previous examples, "2007-04-05 14:30" is considered two separate, but acceptable, representations—one for date and the other for time. It is then left to the reader to interpret the two separate representations as meaning a single time point based on the context.

http://en.wikipedia.org/wiki/ISO_8601

That said, I prefer the "separated date and time" representation myself.

phzbOx|14 years ago

Great initiative. Dealing with timezone is a pain and I totally agree with you that storing universal time is the way to go. It's a little bit like the 'validating input problem': Strongly validating and converting input/output so that the entire application can be safe. I.e. it's not to the sqrt function to validate its parameters. (Although it'd throw an exception if it fails.)

jvdh|14 years ago

This solves one of my problems with datetime and timezones in Python. The other being that it is totally awkward and bafflingly hard to add and substract from datetime objects.

csytan|14 years ago

Why do you find it difficult?

    >>> datetime.datetime.now() - datetime.timedelta(days=1)
    datetime.datetime(2012, 2, 1, 23, 33, 30, 591993)