Mercurial > babel > old > babel-test
annotate babel/dates.py @ 12:a2c54ef107c2
* Removed pkg_resources/setuptools requirement from various places.
* Fixed copyright year in a couple of file headers.
* Some cleanup.
author | cmlenz |
---|---|
date | Thu, 31 May 2007 08:38:54 +0000 |
parents | 9132c9218745 |
children | 76985c08a339 |
rev | line source |
---|---|
1 | 1 # -*- coding: utf-8 -*- |
2 # | |
3 # Copyright (C) 2007 Edgewall Software | |
4 # All rights reserved. | |
5 # | |
6 # This software is licensed as described in the file COPYING, which | |
7 # you should have received as part of this distribution. The terms | |
8 # are also available at http://babel.edgewall.org/wiki/License. | |
9 # | |
10 # This software consists of voluntary contributions made by many | |
11 # individuals. For the exact contribution history, see the revision | |
12 # history and logs, available at http://babel.edgewall.org/log/. | |
13 | |
14 """Locale dependent formatting and parsing of dates and times. | |
15 | |
16 The default locale for the functions in this module is determined by the | |
17 following environment variables, in that order: | |
18 | |
19 * ``LC_TIME``, | |
20 * ``LC_ALL``, and | |
21 * ``LANG`` | |
22 """ | |
23 | |
24 from datetime import date, datetime, time | |
25 | |
26 from babel.core import Locale | |
27 from babel.util import default_locale | |
28 | |
29 __all__ = ['format_date', 'format_datetime', 'format_time', 'parse_date', | |
30 'parse_datetime', 'parse_time'] | |
31 __docformat__ = 'restructuredtext en' | |
32 | |
33 LC_TIME = default_locale('LC_TIME') | |
34 | |
35 def get_period_names(locale=LC_TIME): | |
36 """Return the names for day periods (AM/PM) used by the locale. | |
37 | |
38 >>> get_period_names(locale='en_US')['am'] | |
39 u'AM' | |
40 | |
41 :param locale: the `Locale` object, or a locale string | |
42 :return: the dictionary of period names | |
43 :rtype: `dict` | |
44 """ | |
45 return Locale.parse(locale).periods | |
46 | |
47 def get_day_names(width='wide', context='format', locale=LC_TIME): | |
48 """Return the day names used by the locale for the specified format. | |
49 | |
50 >>> get_day_names('wide', locale='en_US')[1] | |
51 u'Monday' | |
52 >>> get_day_names('abbreviated', locale='es')[1] | |
53 u'lun' | |
54 >>> get_day_names('narrow', context='stand-alone', locale='de_DE')[1] | |
55 u'M' | |
56 | |
57 :param width: the width to use, one of "wide", "abbreviated", or "narrow" | |
58 :param context: the context, either "format" or "stand-alone" | |
59 :param locale: the `Locale` object, or a locale string | |
60 :return: the dictionary of day names | |
61 :rtype: `dict` | |
62 """ | |
63 return Locale.parse(locale).days[context][width] | |
64 | |
65 def get_month_names(width='wide', context='format', locale=LC_TIME): | |
66 """Return the month names used by the locale for the specified format. | |
67 | |
68 >>> get_month_names('wide', locale='en_US')[1] | |
69 u'January' | |
70 >>> get_month_names('abbreviated', locale='es')[1] | |
71 u'ene' | |
72 >>> get_month_names('narrow', context='stand-alone', locale='de_DE')[1] | |
73 u'J' | |
74 | |
75 :param width: the width to use, one of "wide", "abbreviated", or "narrow" | |
76 :param context: the context, either "format" or "stand-alone" | |
77 :param locale: the `Locale` object, or a locale string | |
78 :return: the dictionary of month names | |
79 :rtype: `dict` | |
80 """ | |
81 return Locale.parse(locale).months[context][width] | |
82 | |
83 def get_quarter_names(width='wide', context='format', locale=LC_TIME): | |
84 """Return the quarter names used by the locale for the specified format. | |
85 | |
86 >>> get_quarter_names('wide', locale='en_US')[1] | |
87 u'1st quarter' | |
88 >>> get_quarter_names('abbreviated', locale='de_DE')[1] | |
89 u'Q1' | |
90 | |
91 :param width: the width to use, one of "wide", "abbreviated", or "narrow" | |
92 :param context: the context, either "format" or "stand-alone" | |
93 :param locale: the `Locale` object, or a locale string | |
94 :return: the dictionary of quarter names | |
95 :rtype: `dict` | |
96 """ | |
97 return Locale.parse(locale).quarters[context][width] | |
98 | |
99 def get_era_names(width='wide', locale=LC_TIME): | |
100 """Return the era names used by the locale for the specified format. | |
101 | |
102 >>> get_era_names('wide', locale='en_US')[1] | |
103 u'Anno Domini' | |
104 >>> get_era_names('abbreviated', locale='de_DE')[1] | |
105 u'n. Chr.' | |
106 | |
107 :param width: the width to use, either "wide" or "abbreviated" | |
108 :param locale: the `Locale` object, or a locale string | |
109 :return: the dictionary of era names | |
110 :rtype: `dict` | |
111 """ | |
112 return Locale.parse(locale).eras[width] | |
113 | |
114 def get_date_format(format='medium', locale=LC_TIME): | |
115 """Return the date formatting patterns used by the locale for the specified | |
116 format. | |
117 | |
118 >>> get_date_format(locale='en_US') | |
12
a2c54ef107c2
* Removed pkg_resources/setuptools requirement from various places.
cmlenz
parents:
8
diff
changeset
|
119 <DateTimePattern u'MMM d, yyyy'> |
1 | 120 >>> get_date_format('full', locale='de_DE') |
12
a2c54ef107c2
* Removed pkg_resources/setuptools requirement from various places.
cmlenz
parents:
8
diff
changeset
|
121 <DateTimePattern u'EEEE, d. MMMM yyyy'> |
1 | 122 |
123 :param format: the format to use, one of "full", "long", "medium", or | |
124 "short" | |
125 :param locale: the `Locale` object, or a locale string | |
126 :return: the date format pattern | |
127 :rtype: `dict` | |
128 """ | |
129 return Locale.parse(locale).date_formats[format] | |
130 | |
131 def get_time_format(format='medium', locale=LC_TIME): | |
132 """Return the time formatting patterns used by the locale for the specified | |
133 format. | |
134 | |
135 >>> get_time_format(locale='en_US') | |
12
a2c54ef107c2
* Removed pkg_resources/setuptools requirement from various places.
cmlenz
parents:
8
diff
changeset
|
136 <DateTimePattern u'h:mm:ss a'> |
1 | 137 >>> get_time_format('full', locale='de_DE') |
12
a2c54ef107c2
* Removed pkg_resources/setuptools requirement from various places.
cmlenz
parents:
8
diff
changeset
|
138 <DateTimePattern u"H:mm' Uhr 'z"> |
1 | 139 |
140 :param format: the format to use, one of "full", "long", "medium", or | |
141 "short" | |
142 :param locale: the `Locale` object, or a locale string | |
143 :return: the time format pattern | |
144 :rtype: `dict` | |
145 """ | |
146 return Locale.parse(locale).time_formats[format] | |
147 | |
148 def format_date(date, format='medium', locale=LC_TIME): | |
149 """Returns a date formatted according to the given pattern. | |
150 | |
151 >>> d = date(2007, 04, 01) | |
152 >>> format_date(d, locale='en_US') | |
153 u'Apr 1, 2007' | |
154 >>> format_date(d, format='full', locale='de_DE') | |
155 u'Sonntag, 1. April 2007' | |
156 | |
157 :param date: the ``date`` object | |
158 :param format: one of "full", "long", "medium", or "short" | |
159 :param locale: a `Locale` object or a locale string | |
160 :rtype: `unicode` | |
161 """ | |
162 locale = Locale.parse(locale) | |
163 if format in ('full', 'long', 'medium', 'short'): | |
164 format = get_date_format(format, locale=locale) | |
165 pattern = parse_pattern(format) | |
166 return parse_pattern(format).apply(date, locale) | |
167 | |
168 def format_datetime(datetime, format='medium', locale=LC_TIME): | |
169 """Returns a date formatted according to the given pattern. | |
170 | |
171 :param datetime: the ``date`` object | |
172 :param format: one of "full", "long", "medium", or "short" | |
173 :param locale: a `Locale` object or a locale string | |
174 :rtype: `unicode` | |
175 """ | |
176 raise NotImplementedError | |
177 | |
178 def format_time(time, format='medium', locale=LC_TIME): | |
179 """Returns a time formatted according to the given pattern. | |
180 | |
181 >>> t = time(15, 30) | |
182 >>> format_time(t, locale='en_US') | |
183 u'3:30:00 PM' | |
184 >>> format_time(t, format='short', locale='de_DE') | |
185 u'15:30' | |
186 | |
187 :param time: the ``time`` object | |
188 :param format: one of "full", "long", "medium", or "short" | |
189 :param locale: a `Locale` object or a locale string | |
190 :rtype: `unicode` | |
191 """ | |
192 locale = Locale.parse(locale) | |
193 if format in ('full', 'long', 'medium', 'short'): | |
194 format = get_time_format(format, locale=locale) | |
195 return parse_pattern(format).apply(time, locale) | |
196 | |
197 def parse_date(string, locale=LC_TIME): | |
198 raise NotImplementedError | |
199 | |
200 def parse_datetime(string, locale=LC_TIME): | |
201 raise NotImplementedError | |
202 | |
203 def parse_time(string, locale=LC_TIME): | |
204 raise NotImplementedError | |
205 | |
206 | |
12
a2c54ef107c2
* Removed pkg_resources/setuptools requirement from various places.
cmlenz
parents:
8
diff
changeset
|
207 class DateTimePattern(object): |
1 | 208 |
209 def __init__(self, pattern, format): | |
210 self.pattern = pattern | |
211 self.format = format | |
212 | |
213 def __repr__(self): | |
214 return '<%s %r>' % (type(self).__name__, self.pattern) | |
215 | |
216 def __unicode__(self): | |
217 return self.pattern | |
218 | |
219 def __mod__(self, other): | |
220 assert type(other) is DateTimeFormat | |
221 return self.format % other | |
222 | |
223 def apply(self, datetime, locale): | |
224 return self % DateTimeFormat(datetime, locale) | |
225 | |
226 | |
227 class DateTimeFormat(object): | |
228 | |
229 def __init__(self, value, locale): | |
230 assert isinstance(value, (date, datetime, time)) | |
231 self.value = value | |
232 self.locale = Locale.parse(locale) | |
233 | |
234 def __getitem__(self, name): | |
235 # TODO: a number of fields missing here | |
236 if name[0] == 'G': | |
237 return self.format_era(len(name)) | |
238 elif name[0] == 'y': | |
239 return self.format_year(self.value.year, len(name)) | |
240 elif name[0] == 'Y': | |
241 return self.format_year(self.value.isocalendar()[0], len(name)) | |
242 elif name[0] == 'Q': | |
243 return self.format_quarter(len(name)) | |
244 elif name[0] == 'q': | |
245 return self.format_quarter(len(name), context='stand-alone') | |
246 elif name[0] == 'M': | |
247 return self.format_month(len(name)) | |
248 elif name[0] == 'L': | |
249 return self.format_month(len(name), context='stand-alone') | |
250 elif name[0] == 'd': | |
251 return self.format(self.value.day, len(name)) | |
252 elif name[0] == 'E': | |
253 return self.format_weekday(len(name)) | |
8
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
254 elif name[0] == 'e': |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
255 return self.format_weekday(len(name), add_firstday=True) |
1 | 256 elif name[0] == 'c': |
257 return self.format_weekday(len(name), context='stand-alone') | |
258 elif name[0] == 'a': | |
259 return self.format_period() | |
260 elif name[0] == 'h': | |
261 return self.format(self.value.hour % 12, len(name)) | |
262 elif name[0] == 'H': | |
263 return self.format(self.value.hour, len(name)) | |
264 elif name[0] == 'm': | |
265 return self.format(self.value.minute, len(name)) | |
266 elif name[0] == 's': | |
267 return self.format(self.value.second, len(name)) | |
268 else: | |
269 raise KeyError('Unsupported date/time field %r' % name[0]) | |
270 | |
271 def format_era(self, num): | |
272 width = {3: 'abbreviated', 4: 'wide', 5: 'narrow'}[max(3, num)] | |
273 era = int(self.value.year >= 0) | |
274 return get_era_names(width, self.locale)[era] | |
275 | |
276 def format_year(self, value, num): | |
277 year = self.format(value, num) | |
278 if num == 2: | |
279 year = year[-2:] | |
280 return year | |
281 | |
282 def format_month(self, num, context='format'): | |
283 if num <= 2: | |
284 return ('%%0%dd' % num) % self.value.month | |
285 width = {3: 'abbreviated', 4: 'wide', 5: 'narrow'}[num] | |
286 return get_month_names(width, context, self.locale)[self.value.month] | |
287 | |
8
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
288 def format_weekday(self, num, add_firstday=False, context='format'): |
1 | 289 width = {3: 'abbreviated', 4: 'wide', 5: 'narrow'}[max(3, num)] |
290 weekday = self.value.weekday() + 1 | |
8
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
291 if add_firstday: |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
292 weekday += self.locale.first_week_day |
1 | 293 return get_day_names(width, context, self.locale)[weekday] |
294 | |
295 def format_period(self): | |
296 period = {0: 'am', 1: 'pm'}[int(self.value.hour > 12)] | |
297 return get_period_names(locale=self.locale)[period] | |
298 | |
299 def format(self, value, length): | |
300 return ('%%0%dd' % length) % value | |
301 | |
302 | |
303 PATTERN_CHARS = { | |
304 'G': 5, # era | |
305 'y': None, 'Y': None, 'u': None, # year | |
306 'Q': 4, 'q': 4, # quarter | |
307 'M': 5, 'L': 5, # month | |
308 'w': 2, 'W': 1, # week | |
309 'd': 2, 'D': 3, 'F': 1, 'g': None, # day | |
310 'E': 5, 'e': 5, 'c': 5, # week day | |
311 'a': 1, # period | |
312 'h': 2, 'H': 2, 'K': 2, 'k': 2, # hour | |
313 'm': 2, # minute | |
314 's': 2, 'S': None, 'A': None, # second | |
315 'z': 4, 'Z': 4, 'v': 4 # zone | |
316 } | |
317 | |
318 def parse_pattern(pattern): | |
319 """Parse date, time, and datetime format patterns. | |
320 | |
321 >>> parse_pattern("MMMMd").format | |
322 u'%(MMMM)s%(d)s' | |
323 >>> parse_pattern("MMM d, yyyy").format | |
324 u'%(MMM)s %(d)s, %(yyyy)s' | |
325 >>> parse_pattern("H:mm' Uhr 'z").format | |
326 u'%(H)s:%(mm)s Uhr %(z)s' | |
327 | |
328 :param pattern: the formatting pattern to parse | |
329 """ | |
12
a2c54ef107c2
* Removed pkg_resources/setuptools requirement from various places.
cmlenz
parents:
8
diff
changeset
|
330 if type(pattern) is DateTimePattern: |
1 | 331 return pattern |
332 | |
333 result = [] | |
334 quotebuf = None | |
335 charbuf = [] | |
336 fieldchar = [''] | |
337 fieldnum = [0] | |
338 | |
339 def append_chars(): | |
340 result.append(''.join(charbuf).replace('%', '%%')) | |
341 del charbuf[:] | |
342 | |
343 def append_field(): | |
344 limit = PATTERN_CHARS[fieldchar[0]] | |
345 if limit is not None and fieldnum[0] > limit: | |
346 raise ValueError('Invalid length for field: %r' | |
347 % (fieldchar[0] * fieldnum[0])) | |
348 result.append('%%(%s)s' % (fieldchar[0] * fieldnum[0])) | |
349 fieldchar[0] = '' | |
350 fieldnum[0] = 0 | |
351 | |
352 for idx, char in enumerate(pattern): | |
353 if quotebuf is None: | |
354 if char == "'": # quote started | |
355 if fieldchar[0]: | |
356 append_field() | |
357 elif charbuf: | |
358 append_chars() | |
359 quotebuf = [] | |
360 elif char in PATTERN_CHARS: | |
361 if charbuf: | |
362 append_chars() | |
363 if char == fieldchar[0]: | |
364 fieldnum[0] += 1 | |
365 else: | |
366 if fieldchar[0]: | |
367 append_field() | |
368 fieldchar[0] = char | |
369 fieldnum[0] = 1 | |
370 else: | |
371 if fieldchar[0]: | |
372 append_field() | |
373 charbuf.append(char) | |
374 | |
375 elif quotebuf is not None: | |
376 if char == "'": # quote ended | |
377 charbuf.extend(quotebuf) | |
378 quotebuf = None | |
379 else: # inside quote | |
380 quotebuf.append(char) | |
381 | |
382 if fieldchar[0]: | |
383 append_field() | |
384 elif charbuf: | |
385 append_chars() | |
386 | |
12
a2c54ef107c2
* Removed pkg_resources/setuptools requirement from various places.
cmlenz
parents:
8
diff
changeset
|
387 return DateTimePattern(pattern, u''.join(result)) |