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