Mercurial > babel > old > babel-test
annotate babel/dates.py @ 18:77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
author | cmlenz |
---|---|
date | Thu, 31 May 2007 17:10:47 +0000 |
parents | ed154241c08d |
children | c0c92d11f1ab |
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] | |
15 | 51 u'Tuesday' |
1 | 52 >>> get_day_names('abbreviated', locale='es')[1] |
15 | 53 u'mar' |
1 | 54 >>> get_day_names('narrow', context='stand-alone', locale='de_DE')[1] |
15 | 55 u'D' |
1 | 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 | |
16 | 157 If you don't want to use the locale default formats, you can specify a |
158 custom date pattern: | |
159 | |
160 >>> format_time(d, "EEE, MMM d, ''yy", locale='en') | |
161 u"Sun, Apr 1, '07" | |
162 | |
18
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
163 If the pattern contains time fields, an `AttributeError` will be raised |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
164 when trying to apply the formatting: |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
165 |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
166 >>> format_date(d, "yyyy-MM-dd HH:mm", locale='en_US') |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
167 Traceback (most recent call last): |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
168 ... |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
169 AttributeError: 'datetime.date' object has no attribute 'hour' |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
170 |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
171 This is also true if the value of ``date`` parameter is a ``datetime`` |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
172 object, as this function automatically converts it to a ``date``:: |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
173 |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
174 >>> dt = datetime(2007, 04, 01, 15, 30) |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
175 >>> format_date(dt, "yyyy-MM-dd HH:mm", locale='en_US') |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
176 Traceback (most recent call last): |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
177 ... |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
178 AttributeError: 'datetime.date' object has no attribute 'hour' |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
179 |
1 | 180 :param date: the ``date`` object |
16 | 181 :param format: one of "full", "long", "medium", or "short", or a custom |
182 date/time pattern | |
1 | 183 :param locale: a `Locale` object or a locale string |
184 :rtype: `unicode` | |
185 """ | |
18
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
186 if isinstance(date, datetime): |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
187 date = date.date() |
1 | 188 locale = Locale.parse(locale) |
189 if format in ('full', 'long', 'medium', 'short'): | |
190 format = get_date_format(format, locale=locale) | |
191 pattern = parse_pattern(format) | |
192 return parse_pattern(format).apply(date, locale) | |
193 | |
194 def format_datetime(datetime, format='medium', locale=LC_TIME): | |
195 """Returns a date formatted according to the given pattern. | |
196 | |
197 :param datetime: the ``date`` object | |
16 | 198 :param format: one of "full", "long", "medium", or "short", or a custom |
199 date/time pattern | |
1 | 200 :param locale: a `Locale` object or a locale string |
201 :rtype: `unicode` | |
202 """ | |
18
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
203 locale = Locale.parse(locale) |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
204 if format in ('full', 'long', 'medium', 'short'): |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
205 raise NotImplementedError |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
206 pattern = parse_pattern(format) |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
207 return parse_pattern(format).apply(datetime, locale) |
1 | 208 |
209 def format_time(time, format='medium', locale=LC_TIME): | |
210 """Returns a time formatted according to the given pattern. | |
211 | |
212 >>> t = time(15, 30) | |
213 >>> format_time(t, locale='en_US') | |
214 u'3:30:00 PM' | |
215 >>> format_time(t, format='short', locale='de_DE') | |
216 u'15:30' | |
217 | |
16 | 218 If you don't want to use the locale default formats, you can specify a |
219 custom time pattern: | |
220 | |
221 >>> format_time(t, "hh 'o''clock' a", locale='en') | |
222 u"03 o'clock PM" | |
223 | |
18
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
224 If the pattern contains date fields, an `AttributeError` will be raised |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
225 when trying to apply the formatting: |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
226 |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
227 >>> format_time(t, "yyyy-MM-dd HH:mm", locale='en_US') |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
228 Traceback (most recent call last): |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
229 ... |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
230 AttributeError: 'datetime.time' object has no attribute 'year' |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
231 |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
232 This is also true if the value of ``time`` parameter is a ``datetime`` |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
233 object, as this function automatically converts it to a ``time``:: |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
234 |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
235 >>> dt = datetime(2007, 04, 01, 15, 30) |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
236 >>> format_time(dt, "yyyy-MM-dd HH:mm", locale='en_US') |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
237 Traceback (most recent call last): |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
238 ... |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
239 AttributeError: 'datetime.time' object has no attribute 'year' |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
240 |
1 | 241 :param time: the ``time`` object |
16 | 242 :param format: one of "full", "long", "medium", or "short", or a custom |
243 date/time pattern | |
1 | 244 :param locale: a `Locale` object or a locale string |
245 :rtype: `unicode` | |
246 """ | |
18
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
247 if isinstance(time, (int, long)): |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
248 time = datetime.fromtimestamp(time).time() |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
249 elif isinstance(time, datetime): |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
250 time = time.time() |
1 | 251 locale = Locale.parse(locale) |
252 if format in ('full', 'long', 'medium', 'short'): | |
253 format = get_time_format(format, locale=locale) | |
254 return parse_pattern(format).apply(time, locale) | |
255 | |
256 def parse_date(string, locale=LC_TIME): | |
257 raise NotImplementedError | |
258 | |
259 def parse_datetime(string, locale=LC_TIME): | |
260 raise NotImplementedError | |
261 | |
262 def parse_time(string, locale=LC_TIME): | |
263 raise NotImplementedError | |
264 | |
265 | |
12
a2c54ef107c2
* Removed pkg_resources/setuptools requirement from various places.
cmlenz
parents:
8
diff
changeset
|
266 class DateTimePattern(object): |
1 | 267 |
268 def __init__(self, pattern, format): | |
269 self.pattern = pattern | |
270 self.format = format | |
271 | |
272 def __repr__(self): | |
273 return '<%s %r>' % (type(self).__name__, self.pattern) | |
274 | |
275 def __unicode__(self): | |
276 return self.pattern | |
277 | |
278 def __mod__(self, other): | |
279 assert type(other) is DateTimeFormat | |
280 return self.format % other | |
281 | |
282 def apply(self, datetime, locale): | |
283 return self % DateTimeFormat(datetime, locale) | |
284 | |
285 | |
286 class DateTimeFormat(object): | |
287 | |
288 def __init__(self, value, locale): | |
289 assert isinstance(value, (date, datetime, time)) | |
290 self.value = value | |
291 self.locale = Locale.parse(locale) | |
292 | |
293 def __getitem__(self, name): | |
294 # TODO: a number of fields missing here | |
15 | 295 char = name[0] |
296 num = len(name) | |
297 if char == 'G': | |
298 return self.format_era(char, num) | |
299 elif char in ('y', 'Y'): | |
300 return self.format_year(char, num) | |
301 elif char in ('Q', 'q'): | |
302 return self.format_quarter(char, num) | |
303 elif char in ('M', 'L'): | |
304 return self.format_month(char, num) | |
305 elif char == 'd': | |
306 return self.format(self.value.day, num) | |
307 elif char in ('E', 'e', 'c'): | |
308 return self.format_weekday(char, num) | |
309 elif char == 'a': | |
310 return self.format_period(char) | |
311 elif char == 'h': | |
312 return self.format(self.value.hour % 12, num) | |
313 elif char == 'H': | |
314 return self.format(self.value.hour, num) | |
18
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
315 elif char == 'K': |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
316 return self.format(self.value.hour % 12 - 1, num) |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
317 elif char == 'k': |
77a68f88f6bc
Started documentation for date formatting, plus some code tweaks in that area.
cmlenz
parents:
16
diff
changeset
|
318 return self.format(self.value.hour + 1, num) |
15 | 319 elif char == 'm': |
320 return self.format(self.value.minute, num) | |
321 elif char == 's': | |
322 return self.format(self.value.second, num) | |
1 | 323 else: |
15 | 324 raise KeyError('Unsupported date/time field %r' % char) |
1 | 325 |
15 | 326 def format_era(self, char, num): |
1 | 327 width = {3: 'abbreviated', 4: 'wide', 5: 'narrow'}[max(3, num)] |
328 era = int(self.value.year >= 0) | |
329 return get_era_names(width, self.locale)[era] | |
330 | |
15 | 331 def format_year(self, char, num): |
332 if char.islower(): | |
333 value = self.value.year | |
334 else: | |
335 value = self.value.isocalendar()[0] | |
1 | 336 year = self.format(value, num) |
337 if num == 2: | |
338 year = year[-2:] | |
339 return year | |
340 | |
15 | 341 def format_month(self, char, num): |
1 | 342 if num <= 2: |
343 return ('%%0%dd' % num) % self.value.month | |
344 width = {3: 'abbreviated', 4: 'wide', 5: 'narrow'}[num] | |
15 | 345 context = {3: 'format', 4: 'format', 5: 'stand-alone'}[num] |
1 | 346 return get_month_names(width, context, self.locale)[self.value.month] |
347 | |
15 | 348 def format_weekday(self, char, num): |
349 if num < 3: | |
350 if char.islower(): | |
351 value = 7 - self.locale.first_week_day + self.value.weekday() | |
352 return self.format(value % 7 + 1, num) | |
353 num = 3 | |
354 weekday = self.value.weekday() | |
355 width = {3: 'abbreviated', 4: 'wide', 5: 'narrow'}[num] | |
356 context = {3: 'format', 4: 'format', 5: 'stand-alone'}[num] | |
1 | 357 return get_day_names(width, context, self.locale)[weekday] |
358 | |
15 | 359 def format_period(self, char): |
1 | 360 period = {0: 'am', 1: 'pm'}[int(self.value.hour > 12)] |
361 return get_period_names(locale=self.locale)[period] | |
362 | |
363 def format(self, value, length): | |
364 return ('%%0%dd' % length) % value | |
365 | |
366 | |
367 PATTERN_CHARS = { | |
15 | 368 'G': [1, 2, 3, 4, 5], # era |
369 'y': None, 'Y': None, 'u': None, # year | |
370 'Q': [1, 2, 3, 4], 'q': [1, 2, 3, 4], # quarter | |
371 'M': [1, 2, 3, 4, 5], 'L': [1, 2, 3, 4, 5], # month | |
372 'w': [1, 2], 'W': [1], # week | |
373 'd': [1, 2], 'D': [1, 2, 3], 'F': [1], 'g': None, # day | |
374 'E': [1, 2, 3, 4, 5], 'e': [1, 2, 3, 4, 5], 'c': [1, 3, 4, 5], # week day | |
375 'a': [1], # period | |
376 'h': [1, 2], 'H': [1, 2], 'K': [1, 2], 'k': [1, 2], # hour | |
377 'm': [1, 2], # minute | |
378 's': [1, 2], 'S': None, 'A': None, # second | |
379 'z': [1, 2, 3, 4], 'Z': [1, 2, 3, 4], 'v': [1, 4] # zone | |
1 | 380 } |
381 | |
382 def parse_pattern(pattern): | |
383 """Parse date, time, and datetime format patterns. | |
384 | |
385 >>> parse_pattern("MMMMd").format | |
386 u'%(MMMM)s%(d)s' | |
387 >>> parse_pattern("MMM d, yyyy").format | |
388 u'%(MMM)s %(d)s, %(yyyy)s' | |
16 | 389 |
390 Pattern can contain literal strings in single quotes: | |
391 | |
1 | 392 >>> parse_pattern("H:mm' Uhr 'z").format |
393 u'%(H)s:%(mm)s Uhr %(z)s' | |
394 | |
16 | 395 An actual single quote can be used by using two adjacent single quote |
396 characters: | |
397 | |
398 >>> parse_pattern("hh' o''clock'").format | |
399 u"%(hh)s o'clock" | |
400 | |
1 | 401 :param pattern: the formatting pattern to parse |
402 """ | |
12
a2c54ef107c2
* Removed pkg_resources/setuptools requirement from various places.
cmlenz
parents:
8
diff
changeset
|
403 if type(pattern) is DateTimePattern: |
1 | 404 return pattern |
405 | |
406 result = [] | |
407 quotebuf = None | |
408 charbuf = [] | |
409 fieldchar = [''] | |
410 fieldnum = [0] | |
411 | |
412 def append_chars(): | |
413 result.append(''.join(charbuf).replace('%', '%%')) | |
414 del charbuf[:] | |
415 | |
416 def append_field(): | |
417 limit = PATTERN_CHARS[fieldchar[0]] | |
15 | 418 if limit and fieldnum[0] not in limit: |
1 | 419 raise ValueError('Invalid length for field: %r' |
420 % (fieldchar[0] * fieldnum[0])) | |
421 result.append('%%(%s)s' % (fieldchar[0] * fieldnum[0])) | |
422 fieldchar[0] = '' | |
423 fieldnum[0] = 0 | |
424 | |
16 | 425 for idx, char in enumerate(pattern.replace("''", '\0')): |
1 | 426 if quotebuf is None: |
427 if char == "'": # quote started | |
428 if fieldchar[0]: | |
429 append_field() | |
430 elif charbuf: | |
431 append_chars() | |
432 quotebuf = [] | |
433 elif char in PATTERN_CHARS: | |
434 if charbuf: | |
435 append_chars() | |
436 if char == fieldchar[0]: | |
437 fieldnum[0] += 1 | |
438 else: | |
439 if fieldchar[0]: | |
440 append_field() | |
441 fieldchar[0] = char | |
442 fieldnum[0] = 1 | |
443 else: | |
444 if fieldchar[0]: | |
445 append_field() | |
446 charbuf.append(char) | |
447 | |
448 elif quotebuf is not None: | |
16 | 449 if char == "'": # end of quote |
1 | 450 charbuf.extend(quotebuf) |
451 quotebuf = None | |
452 else: # inside quote | |
453 quotebuf.append(char) | |
454 | |
455 if fieldchar[0]: | |
456 append_field() | |
457 elif charbuf: | |
458 append_chars() | |
459 | |
16 | 460 return DateTimePattern(pattern, u''.join(result).replace('\0', "'")) |