@@ -27,6 +27,18 @@ def _getlang():
27
27
# Figure out what the current language is set to.
28
28
return locale .getlocale (locale .LC_TIME )
29
29
30
+ def _findall (haystack , needle ):
31
+ # Find all positions of needle in haystack.
32
+ if not needle :
33
+ return
34
+ i = 0
35
+ while True :
36
+ i = haystack .find (needle , i )
37
+ if i < 0 :
38
+ break
39
+ yield i
40
+ i += len (needle )
41
+
30
42
class LocaleTime (object ):
31
43
"""Stores and handles locale-specific information related to time.
32
44
@@ -101,7 +113,8 @@ def __calc_am_pm(self):
101
113
am_pm = []
102
114
for hour in (1 , 22 ):
103
115
time_tuple = time .struct_time ((1999 ,3 ,17 ,hour ,44 ,55 ,2 ,76 ,0 ))
104
- am_pm .append (time .strftime ("%p" , time_tuple ).lower ())
116
+ # br_FR has AM/PM info (' ',' ').
117
+ am_pm .append (time .strftime ("%p" , time_tuple ).lower ().strip ())
105
118
self .am_pm = am_pm
106
119
107
120
def __calc_date_time (self ):
@@ -113,42 +126,114 @@ def __calc_date_time(self):
113
126
# values within the format string is very important; it eliminates
114
127
# possible ambiguity for what something represents.
115
128
time_tuple = time .struct_time ((1999 ,3 ,17 ,22 ,44 ,55 ,2 ,76 ,0 ))
116
- date_time = [None , None , None ]
117
- date_time [0 ] = time .strftime ("%c" , time_tuple ).lower ()
118
- date_time [1 ] = time .strftime ("%x" , time_tuple ).lower ()
119
- date_time [2 ] = time .strftime ("%X" , time_tuple ).lower ()
120
- replacement_pairs = [('%' , '%%' ), (self .f_weekday [2 ], '%A' ),
121
- (self .f_month [3 ], '%B' ), (self .a_weekday [2 ], '%a' ),
122
- (self .a_month [3 ], '%b' ), (self .am_pm [1 ], '%p' ),
129
+ time_tuple2 = time .struct_time ((1999 ,1 ,3 ,1 ,1 ,1 ,6 ,3 ,0 ))
130
+ replacement_pairs = [
123
131
('1999' , '%Y' ), ('99' , '%y' ), ('22' , '%H' ),
124
132
('44' , '%M' ), ('55' , '%S' ), ('76' , '%j' ),
125
133
('17' , '%d' ), ('03' , '%m' ), ('3' , '%m' ),
126
134
# '3' needed for when no leading zero.
127
135
('2' , '%w' ), ('10' , '%I' )]
128
- replacement_pairs .extend ([(tz , "%Z" ) for tz_values in self .timezone
129
- for tz in tz_values ])
130
- for offset ,directive in ((0 ,'%c' ), (1 ,'%x' ), (2 ,'%X' )):
131
- current_format = date_time [offset ]
132
- for old , new in replacement_pairs :
136
+ date_time = []
137
+ for directive in ('%c' , '%x' , '%X' ):
138
+ current_format = time .strftime (directive , time_tuple ).lower ()
139
+ current_format = current_format .replace ('%' , '%%' )
140
+ # The month and the day of the week formats are treated specially
141
+ # because of a possible ambiguity in some locales where the full
142
+ # and abbreviated names are equal or names of different types
143
+ # are equal. See doc of __find_month_format for more details.
144
+ lst , fmt = self .__find_weekday_format (directive )
145
+ if lst :
146
+ current_format = current_format .replace (lst [2 ], fmt , 1 )
147
+ lst , fmt = self .__find_month_format (directive )
148
+ if lst :
149
+ current_format = current_format .replace (lst [3 ], fmt , 1 )
150
+ if self .am_pm [1 ]:
133
151
# Must deal with possible lack of locale info
134
152
# manifesting itself as the empty string (e.g., Swedish's
135
153
# lack of AM/PM info) or a platform returning a tuple of empty
136
154
# strings (e.g., MacOS 9 having timezone as ('','')).
137
- if old :
138
- current_format = current_format .replace (old , new )
155
+ current_format = current_format .replace (self .am_pm [1 ], '%p' )
156
+ for tz_values in self .timezone :
157
+ for tz in tz_values :
158
+ if tz :
159
+ current_format = current_format .replace (tz , "%Z" )
160
+ for old , new in replacement_pairs :
161
+ current_format = current_format .replace (old , new )
139
162
# If %W is used, then Sunday, 2005-01-03 will fall on week 0 since
140
163
# 2005-01-03 occurs before the first Monday of the year. Otherwise
141
164
# %U is used.
142
- time_tuple = time .struct_time ((1999 ,1 ,3 ,1 ,1 ,1 ,6 ,3 ,0 ))
143
- if '00' in time .strftime (directive , time_tuple ):
165
+ if '00' in time .strftime (directive , time_tuple2 ):
144
166
U_W = '%W'
145
167
else :
146
168
U_W = '%U'
147
- date_time [offset ] = current_format .replace ('11' , U_W )
169
+ current_format = current_format .replace ('11' , U_W )
170
+ date_time .append (current_format )
148
171
self .LC_date_time = date_time [0 ]
149
172
self .LC_date = date_time [1 ]
150
173
self .LC_time = date_time [2 ]
151
174
175
+ def __find_month_format (self , directive ):
176
+ """Find the month format appropriate for the current locale.
177
+
178
+ In some locales (for example French and Hebrew), the default month
179
+ used in __calc_date_time has the same name in full and abbreviated
180
+ form. Also, the month name can by accident match other part of the
181
+ representation: the day of the week name (for example in Morisyen)
182
+ or the month number (for example in Japanese). Thus, cycle months
183
+ of the year and find all positions that match the month name for
184
+ each month, If no common positions are found, the representation
185
+ does not use the month name.
186
+ """
187
+ full_indices = abbr_indices = None
188
+ for m in range (1 , 13 ):
189
+ time_tuple = time .struct_time ((1999 , m , 17 , 22 , 44 , 55 , 2 , 76 , 0 ))
190
+ datetime = time .strftime (directive , time_tuple ).lower ()
191
+ indices = set (_findall (datetime , self .f_month [m ]))
192
+ if full_indices is None :
193
+ full_indices = indices
194
+ else :
195
+ full_indices &= indices
196
+ indices = set (_findall (datetime , self .a_month [m ]))
197
+ if abbr_indices is None :
198
+ abbr_indices = indices
199
+ else :
200
+ abbr_indices &= indices
201
+ if not full_indices and not abbr_indices :
202
+ return None , None
203
+ if full_indices :
204
+ return self .f_month , '%B'
205
+ if abbr_indices :
206
+ return self .a_month , '%b'
207
+ return None , None
208
+
209
+ def __find_weekday_format (self , directive ):
210
+ """Find the day of the week format appropriate for the current locale.
211
+
212
+ Similar to __find_month_format().
213
+ """
214
+ full_indices = abbr_indices = None
215
+ for wd in range (7 ):
216
+ time_tuple = time .struct_time ((1999 , 3 , 17 , 22 , 44 , 55 , wd , 76 , 0 ))
217
+ datetime = time .strftime (directive , time_tuple ).lower ()
218
+ indices = set (_findall (datetime , self .f_weekday [wd ]))
219
+ if full_indices is None :
220
+ full_indices = indices
221
+ else :
222
+ full_indices &= indices
223
+ if self .f_weekday [wd ] != self .a_weekday [wd ]:
224
+ indices = set (_findall (datetime , self .a_weekday [wd ]))
225
+ if abbr_indices is None :
226
+ abbr_indices = indices
227
+ else :
228
+ abbr_indices &= indices
229
+ if not full_indices and not abbr_indices :
230
+ return None , None
231
+ if full_indices :
232
+ return self .f_weekday , '%A'
233
+ if abbr_indices :
234
+ return self .a_weekday , '%a'
235
+ return None , None
236
+
152
237
def __calc_timezone (self ):
153
238
# Set self.timezone by using time.tzname.
154
239
# Do not worry about possibility of time.tzname[0] == time.tzname[1]
@@ -186,7 +271,7 @@ def __init__(self, locale_time=None):
186
271
'd' : r"(?P<d>3[0-1]|[1-2]\d|0[1-9]|[1-9]| [1-9])" ,
187
272
'f' : r"(?P<f>[0-9]{1,6})" ,
188
273
'H' : r"(?P<H>2[0-3]|[0-1]\d|\d)" ,
189
- 'I' : r"(?P<I>1[0-2]|0[1-9]|[1-9])" ,
274
+ 'I' : r"(?P<I>1[0-2]|0[1-9]|[1-9]| [1-9] )" ,
190
275
'G' : r"(?P<G>\d\d\d\d)" ,
191
276
'j' : r"(?P<j>36[0-6]|3[0-5]\d|[1-2]\d\d|0[1-9]\d|00[1-9]|[1-9]\d|0[1-9]|[1-9])" ,
192
277
'm' : r"(?P<m>1[0-2]|0[1-9]|[1-9])" ,
@@ -330,8 +415,8 @@ def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"):
330
415
_regex_cache [format ] = format_regex
331
416
found = format_regex .match (data_string )
332
417
if not found :
333
- raise ValueError ("time data %r does not match format %r" %
334
- (data_string , format ))
418
+ raise ValueError ("time data %r does not match format %r :: /%s/ " %
419
+ (data_string , format , format_regex . pattern ))
335
420
if len (data_string ) != found .end ():
336
421
raise ValueError ("unconverted data remains: %s" %
337
422
data_string [found .end ():])
0 commit comments