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