45

Possible Duplicate:
Python: Date Ordinal Output?

In Python time.strftime can produce output like "Thursday May 05" easily enough, but I would like to generate a string like "Thursday May 5th" (notice the additional "th" on the date). What is the best way to do this?

Community
  • 1
  • 1
Buttons840
  • 9,239
  • 15
  • 58
  • 85
  • 1
    Why don't you show us the way you are doing it so we can show you a specific solution. – Trufa May 05 '11 at 01:03
  • 13
    His question is quite specific. – Noufal Ibrahim May 05 '11 at 01:14
  • @Noufal Ibrahim: Without code, it's still not really specific enough. Without code it's "do my homework for me". With code, however, we can discuss the error. – S.Lott May 05 '11 at 10:08
  • 5
    S.Lott : I think it's it is. It's straightforward enough to be answerable without a code sample. It's a direct question about a single function (which he's named). – Noufal Ibrahim May 05 '11 at 11:37

5 Answers5

103

strftime doesn't allow you to format a date with a suffix.

Here's a way to get the correct suffix:

if 4 <= day <= 20 or 24 <= day <= 30:
    suffix = "th"
else:
    suffix = ["st", "nd", "rd"][day % 10 - 1]

found here

Update:

Combining a more compact solution based on Jochen's comment with gsteff's answer:

from datetime import datetime as dt

def suffix(d):
    return {1:'st',2:'nd',3:'rd'}.get(d%20, 'th')

def custom_strftime(format, t):
    return t.strftime(format).replace('{S}', str(t.day) + suffix(t.day))

print custom_strftime('%B {S}, %Y', dt.now())

Gives:

May 5th, 2011

Jab
  • 26,853
  • 21
  • 75
  • 114
Acorn
  • 49,061
  • 27
  • 133
  • 172
  • 8
    `suffix = { 1 : "st", 2 : "nd", 3 : "rd" }.get(day % 10, "th")` – Jochen Ritzel May 05 '11 at 01:35
  • 2
    @Jochen Ritzel - That looks even more clever, but what happens once May Elevenst (May 11st) rolls around? – Buttons840 May 05 '11 at 01:51
  • 1
    @Jochen Ritzel Your expression produces 'st' for '11', 'nd' for '12', 'rd' for '13'. No ? – eyquem May 05 '11 at 01:53
  • 1
    The `suffix` implementation above produces the wrong values for 11, 12 and 13. – gsteff May 05 '11 at 01:54
  • time.strftime will take a tuple of 9 elements as its second arg, so t.day wont technically work. I think t[2] from gsteff is still the best. t[2] will work with time_structs returned by time.localtime or the 9 tuples. – Buttons840 May 05 '11 at 02:01
  • 1
    @Buttons840: I'm using `datetime` rather than `time` – Acorn May 05 '11 at 02:04
  • Thanks. Its quite neat, used it for my JavaScrip code with some modifications. – Rutwick Gangurde Jun 22 '20 at 08:31
  • I know I'm late but if the day is modulo 20 then there is no need to check if it is in the teens. I suggested an edit with this correction. – Jab Jun 17 '23 at 02:08
19

This seems to add the appropriate suffix, and remove the ugly leading zeroes in the day number:

#!/usr/bin/python

import time

day_endings = {
    1: 'st',
    2: 'nd',
    3: 'rd',
    21: 'st',
    22: 'nd',
    23: 'rd',
    31: 'st'
}

def custom_strftime(format, t):
    return time.strftime(format, t).replace('{TH}', str(t[2]) + day_endings.get(t[2], 'th'))

print custom_strftime('%B {TH}, %Y', time.localtime())
gsteff
  • 4,764
  • 1
  • 20
  • 17
12
"%s%s"%(day, 'trnshddt'[0xc0006c000000006c>>2*day&3::4])

But seriously, this is locale specific, so you should be doing it during internationalisation

John La Rooy
  • 295,403
  • 53
  • 369
  • 502
  • 3
    the `0xc0006c000000006c>>2*day&3` part looks up 2 bits from a bit field for a corresponding day (hint: try `bin(0xc0006c000000006c)`, the last 2 bits are for day 0, days above 10 are all 0's). Then it takes that number 0-3 for the index of the first char, and skips 4 chars to get the 2nd char. – Mu Mind May 05 '11 at 02:23
  • Any comments on where I can start learning about locale and internationalization? My application does need to speak Spanish, so I'll probably need a more comprehensive solution like you say. – Buttons840 May 05 '11 at 02:30
  • 1
    @Buttons840, Start here http://en.wikipedia.org/wiki/GNU_gettext, when you need to solve specific internationalisation problems, open more questions on SO – John La Rooy May 05 '11 at 03:35
  • Sorry, on 2nd glance, "days above 10 are all 0's" should have been "days above 31 *would* all be 0's". – Mu Mind May 05 '11 at 14:29
  • Gnibbler, how did you assemble this value ``0xc0006c000000006c``? I understand what it means, but how did you get all the bits in the right places? – ThomasH May 05 '11 at 21:12
  • @ThomasH, `hex(0b1100000000000000011011000000000000000000000000000000000001101100)` – John La Rooy May 05 '11 at 21:54
  • @gnibbler That means, you've spelled that binary literal out by hand? Counting '1's and '0's?! How many typos did you make before you got it right ;-). – ThomasH May 06 '11 at 09:44
  • I downvoted this because I think handcrafted, unreadable binary maps are just a bad idea. I can't tell if it's correct (other than by writing a test that tests every value), it doesn't generalize and it's not that much more efficient. If performance matters that much, you shouldn't be doing it in Python. I'll upvote another response. – Roy Leban Mar 19 '12 at 05:55
  • 2
    @Roy, did you miss the last sentence? All the hard coded answers here are "wrong". The correct place to do this stuff is in the i18n library – John La Rooy Mar 19 '12 at 06:01
  • @gribbler: 1) How many people here took you seriously? 2) If I take your second sentence at face value, you could be implying that your code is acceptable if it was just in the right place. 3) Saying it belongs "during internationalization" is not necessarily correct -- you are making assumptions about the problem. Maybe the OP is writing an i18n library. Even if they're not, your response is not useful to solve their actual problem -- did you point them to a library? 4) Sarcasm doesn't fare well on StackOverflow. Submit this to the Obfuscated Python contest instead. – Roy Leban Jul 01 '12 at 09:33
  • Upvoted for hilarity. – rosuav May 14 '18 at 00:28
3

from time import strftime

print strftime('%A %B %dth')

EDIT:

Correcting after having seen the answers of gurus:

from time import strftime

def special_strftime(dic = {'01':'st','21':'st','31':'st',
                            '02':'nd','22':'nd',
                            '03':'rd','23':'rd'}):
    x = strftime('%A %B %d')
    return x + dic.get(x[-2:],'th')


print special_strftime()

.

EDIT 2

Also:

from time import strftime


def special_strftime(dic = {'1':'st','2':'nd','3':'rd'}):

    x = strftime('%A %B %d')
    return x + ('th' if x[-2:] in ('11','12','13')
                else dic.get(x[-1],'th')

print special_strftime()

.

EDIT 3

Finally, it can be simplified:

from time import strftime

def special_strftime(dic = {'1':'st','2':'nd','3':'rd'}):

    x = strftime('%A %B %d')
    return x + ('th' if x[-2]=='1' else dic.get(x[-1],'th')

print special_strftime()
eyquem
  • 26,771
  • 7
  • 38
  • 46
2

You cannot. The time.strftime function and the datetime.datetime.strftime method both (usually) use the platform C library's strftime function, and it (usually) does not offer that format. You would need to use a third-party library, like dateutil.

Thomas Wouters
  • 130,178
  • 23
  • 148
  • 122
  • 3
    Does `dateutil` allow you to format dates? I can't see anything mentioning it in the docs.. – Acorn May 05 '11 at 01:12