2 PHP date() style date formatting
3 See http://www.php.net/date for format strings
7 >>> d = datetime.datetime.now()
9 >>> print df.format('jS F Y H:i')
10 7th October 2003 11:39
14 from django
.utils
.dates
import MONTHS
, MONTHS_3
, MONTHS_AP
, WEEKDAYS
, WEEKDAYS_ABBR
15 from django
.utils
.tzinfo
import LocalTimezone
16 from django
.utils
.translation
import ugettext
as _
17 from django
.utils
.encoding
import force_unicode
18 from calendar
import isleap
, monthrange
21 re_formatchars
= re
.compile(r
'(?<!\\)([aAbBdDfFgGhHiIjlLmMnNOPrsStTUwWyYzZ])')
22 re_escaped
= re
.compile(r
'\\(.)')
24 class Formatter(object):
25 def format(self
, formatstr
):
27 for i
, piece
in enumerate(re_formatchars
.split(force_unicode(formatstr
))):
29 pieces
.append(force_unicode(getattr(self
, piece
)()))
31 pieces
.append(re_escaped
.sub(r
'\1', piece
))
32 return u
''.join(pieces
)
34 class TimeFormat(Formatter
):
35 def __init__(self
, t
):
40 if self
.data
.hour
> 11:
46 if self
.data
.hour
> 11:
51 "Swatch Internet time"
52 raise NotImplementedError
56 Time, in 12-hour hours and minutes, with minutes left off if they're
58 Examples: '1', '1:30', '2:05', '2'
59 Proprietary extension.
61 if self
.data
.minute
== 0:
63 return u
'%s:%s' % (self
.g(), self
.i())
66 "Hour, 12-hour format without leading zeros; i.e. '1' to '12'"
67 if self
.data
.hour
== 0:
69 if self
.data
.hour
> 12:
70 return self
.data
.hour
- 12
74 "Hour, 24-hour format without leading zeros; i.e. '0' to '23'"
78 "Hour, 12-hour format; i.e. '01' to '12'"
79 return u
'%02d' % self
.g()
82 "Hour, 24-hour format; i.e. '00' to '23'"
83 return u
'%02d' % self
.G()
86 "Minutes; i.e. '00' to '59'"
87 return u
'%02d' % self
.data
.minute
91 Time, in 12-hour hours, minutes and 'a.m.'/'p.m.', with minutes left off
92 if they're zero and the strings 'midnight' and 'noon' if appropriate.
93 Examples: '1 a.m.', '1:30 p.m.', 'midnight', 'noon', '12:30 p.m.'
94 Proprietary extension.
96 if self
.data
.minute
== 0 and self
.data
.hour
== 0:
98 if self
.data
.minute
== 0 and self
.data
.hour
== 12:
100 return u
'%s %s' % (self
.f(), self
.a())
103 "Seconds; i.e. '00' to '59'"
104 return u
'%02d' % self
.data
.second
106 class DateFormat(TimeFormat
):
107 year_days
= [None, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
109 def __init__(self
, dt
):
110 # Accepts either a datetime or date object.
112 self
.timezone
= getattr(dt
, 'tzinfo', None)
113 if hasattr(self
.data
, 'hour') and not self
.timezone
:
114 self
.timezone
= LocalTimezone(dt
)
117 "Month, textual, 3 letters, lowercase; e.g. 'jan'"
118 return MONTHS_3
[self
.data
.month
]
121 "Day of the month, 2 digits with leading zeros; i.e. '01' to '31'"
122 return u
'%02d' % self
.data
.day
125 "Day of the week, textual, 3 letters; e.g. 'Fri'"
126 return WEEKDAYS_ABBR
[self
.data
.weekday()]
129 "Month, textual, long; e.g. 'January'"
130 return MONTHS
[self
.data
.month
]
133 "'1' if Daylight Savings Time, '0' otherwise."
134 if self
.timezone
.dst(self
.data
):
140 "Day of the month without leading zeros; i.e. '1' to '31'"
144 "Day of the week, textual, long; e.g. 'Friday'"
145 return WEEKDAYS
[self
.data
.weekday()]
148 "Boolean for whether it is a leap year; i.e. True or False"
149 return isleap(self
.data
.year
)
152 "Month; i.e. '01' to '12'"
153 return u
'%02d' % self
.data
.month
156 "Month, textual, 3 letters; e.g. 'Jan'"
157 return MONTHS_3
[self
.data
.month
].title()
160 "Month without leading zeros; i.e. '1' to '12'"
161 return self
.data
.month
164 "Month abbreviation in Associated Press style. Proprietary extension."
165 return MONTHS_AP
[self
.data
.month
]
168 "Difference to Greenwich time in hours; e.g. '+0200'"
170 return u
"%+03d%02d" % (seconds
// 3600, (seconds
// 60) % 60)
173 "RFC 2822 formatted date; e.g. 'Thu, 21 Dec 2000 16:01:07 +0200'"
174 return self
.format('D, j M Y H:i:s O')
177 "English ordinal suffix for the day of the month, 2 characters; i.e. 'st', 'nd', 'rd' or 'th'"
178 if self
.data
.day
in (11, 12, 13): # Special case
180 last
= self
.data
.day
% 10
190 "Number of days in the given month; i.e. '28' to '31'"
191 return u
'%02d' % monthrange(self
.data
.year
, self
.data
.month
)[1]
194 "Time zone of this machine; e.g. 'EST' or 'MDT'"
195 name
= self
.timezone
.tzname(self
.data
)
197 name
= self
.format('O')
201 "Seconds since the Unix epoch (January 1 1970 00:00:00 GMT)"
202 off
= self
.timezone
.utcoffset(self
.data
)
203 return int(time
.mktime(self
.data
.timetuple())) + off
.seconds
* 60
206 "Day of the week, numeric, i.e. '0' (Sunday) to '6' (Saturday)"
207 return (self
.data
.weekday() + 1) % 7
210 "ISO-8601 week number of year, weeks starting on Monday"
211 # Algorithm from http://www.personal.ecu.edu/mccartyr/ISOwdALG.txt
213 jan1_weekday
= self
.data
.replace(month
=1, day
=1).weekday() + 1
214 weekday
= self
.data
.weekday() + 1
215 day_of_year
= self
.z()
216 if day_of_year
<= (8 - jan1_weekday
) and jan1_weekday
> 4:
217 if jan1_weekday
== 5 or (jan1_weekday
== 6 and isleap(self
.data
.year
-1)):
222 if isleap(self
.data
.year
):
226 if (i
- day_of_year
) < (4 - weekday
):
229 j
= day_of_year
+ (7 - weekday
) + (jan1_weekday
- 1)
236 "Year, 2 digits; e.g. '99'"
237 return unicode(self
.data
.year
)[2:]
240 "Year, 4 digits; e.g. '1999'"
241 return self
.data
.year
244 "Day of the year; i.e. '0' to '365'"
245 doy
= self
.year_days
[self
.data
.month
] + self
.data
.day
246 if self
.L() and self
.data
.month
> 2:
252 Time zone offset in seconds (i.e. '-43200' to '43200'). The offset for
253 timezones west of UTC is always negative, and for those east of UTC is
256 offset
= self
.timezone
.utcoffset(self
.data
)
257 # Only days can be negative, so negative offsets have days=-1 and
258 # seconds positive. Positive offsets have days=0
259 return offset
.days
* 86400 + offset
.seconds
261 def format(value
, format_string
):
262 "Convenience function"
263 df
= DateFormat(value
)
264 return df
.format(format_string
)
266 def time_format(value
, format_string
):
267 "Convenience function"
268 tf
= TimeFormat(value
)
269 return tf
.format(format_string
)