@@ -42,6 +42,11 @@ class IncompatibilityWarning(Warning): pass
42
42
where criteria is being ignored as this version [%s] is too old (or not-defined),
43
43
read the file in and write it out to a new file to upgrade (with the copy_to method)
44
44
"""
45
+ class FrequencyWarning (Warning ): pass
46
+ frequency_doc = """
47
+ the frequency of the existing index is [%s] which conflicts with the new freq [%s],
48
+ resetting the frequency to None
49
+ """
45
50
class PerformanceWarning (Warning ): pass
46
51
performance_doc = """
47
52
your performance may suffer as PyTables will pickle object types that it cannot map
@@ -149,9 +154,12 @@ def get_store(path, mode='a', complevel=None, complib=None,
149
154
150
155
### interface to/from ###
151
156
152
- def to_hdf (path_or_buf , key , value , mode = None , complevel = None , complib = None , ** kwargs ):
157
+ def to_hdf (path_or_buf , key , value , mode = None , complevel = None , complib = None , append = None , ** kwargs ):
153
158
""" store this object, close it if we opened it """
154
- f = lambda store : store .put (key , value , ** kwargs )
159
+ if append :
160
+ f = lambda store : store .append (key , value , ** kwargs )
161
+ else :
162
+ f = lambda store : store .put (key , value , ** kwargs )
155
163
156
164
if isinstance (path_or_buf , basestring ):
157
165
with get_store (path_or_buf , mode = mode , complevel = complevel , complib = complib ) as store :
@@ -941,6 +949,7 @@ class IndexCol(object):
941
949
is_an_indexable = True
942
950
is_data_indexable = True
943
951
is_searchable = False
952
+ _info_fields = ['freq' ,'tz' ,'name' ]
944
953
945
954
def __init__ (self , values = None , kind = None , typ = None , cname = None , itemsize = None ,
946
955
name = None , axis = None , kind_attr = None , pos = None , freq = None , tz = None ,
@@ -1121,7 +1130,7 @@ def update_info(self, info):
1121
1130
""" set/update the info for this indexable with the key/value
1122
1131
if validate is True, then raise if an existing value does not match the value """
1123
1132
1124
- for key in [ 'freq' , 'tz' , 'name' ] :
1133
+ for key in self . _info_fields :
1125
1134
1126
1135
value = getattr (self ,key ,None )
1127
1136
@@ -1132,15 +1141,31 @@ def update_info(self, info):
1132
1141
1133
1142
existing_value = idx .get (key )
1134
1143
if key in idx and existing_value != value :
1135
- raise ValueError ("invalid info for [%s] for [%s]" ""
1136
- ", existing_value [%s] conflicts with new value [%s]" % (self .name ,
1137
- key ,existing_value ,value ))
1138
1144
1139
- if value is not None or existing_value is not None :
1140
- idx [key ] = value
1145
+ # frequency just warn
1146
+ if key == 'freq' :
1147
+ ws = frequency_doc % (existing_value ,value )
1148
+ warnings .warn (ws , FrequencyWarning )
1149
+
1150
+ # reset
1151
+ idx [key ] = None
1152
+
1153
+ else :
1154
+ raise ValueError ("invalid info for [%s] for [%s]" ""
1155
+ ", existing_value [%s] conflicts with new value [%s]" % (self .name ,
1156
+ key ,existing_value ,value ))
1157
+ else :
1158
+ if value is not None or existing_value is not None :
1159
+ idx [key ] = value
1141
1160
1142
1161
return self
1143
1162
1163
+ def set_info (self , info ):
1164
+ """ set my state from the passed info """
1165
+ idx = info .get (self .name )
1166
+ if idx is not None :
1167
+ self .__dict__ .update (idx )
1168
+
1144
1169
def get_attr (self ):
1145
1170
""" set the kind for this colummn """
1146
1171
self .kind = getattr (self .attrs , self .kind_attr , None )
@@ -1180,6 +1205,7 @@ class DataCol(IndexCol):
1180
1205
is_an_indexable = False
1181
1206
is_data_indexable = False
1182
1207
is_searchable = False
1208
+ _info_fields = ['tz' ]
1183
1209
1184
1210
@classmethod
1185
1211
def create_for_block (cls , i = None , name = None , cname = None , version = None , ** kwargs ):
@@ -1249,7 +1275,7 @@ def set_kind(self):
1249
1275
if self .typ is None :
1250
1276
self .typ = getattr (self .description ,self .cname ,None )
1251
1277
1252
- def set_atom (self , block , existing_col , min_itemsize , nan_rep , ** kwargs ):
1278
+ def set_atom (self , block , existing_col , min_itemsize , nan_rep , info , ** kwargs ):
1253
1279
""" create and setup my atom from the block b """
1254
1280
1255
1281
self .values = list (block .items )
@@ -1264,10 +1290,27 @@ def set_atom(self, block, existing_col, min_itemsize, nan_rep, **kwargs):
1264
1290
"[date] is not implemented as a table column" )
1265
1291
elif inferred_type == 'datetime' :
1266
1292
if getattr (rvalues [0 ],'tzinfo' ,None ) is not None :
1293
+
1294
+ # if this block has more than one timezone, raise
1295
+ if len (set ([r .tzinfo for r in rvalues ])) != 1 :
1296
+ raise TypeError (
1297
+ "too many timezones in this block, create separate data columns" )
1298
+
1299
+ # convert this column to datetime64[ns] utc, and save the tz
1300
+ index = DatetimeIndex (rvalues )
1301
+ tz = getattr (index ,'tz' ,None )
1302
+ if tz is None :
1303
+ raise TypeError (
1304
+ "invalid timezone specification" )
1305
+
1306
+ values = index .tz_convert ('UTC' ).values .view ('i8' )
1307
+ self .tz = tz
1308
+ self .update_info (info )
1309
+ self .set_atom_datetime64 (block , values .reshape (block .values .shape ))
1310
+
1311
+ else :
1267
1312
raise TypeError (
1268
- "timezone support on datetimes is not yet implemented as a table column" )
1269
- raise TypeError (
1270
- "[datetime] is not implemented as a table column" )
1313
+ "[datetime] is not implemented as a table column" )
1271
1314
elif inferred_type == 'unicode' :
1272
1315
raise TypeError (
1273
1316
"[unicode] is not implemented as a table column" )
@@ -1347,10 +1390,12 @@ def set_atom_data(self, block):
1347
1390
def get_atom_datetime64 (self , block ):
1348
1391
return _tables ().Int64Col (shape = block .shape [0 ])
1349
1392
1350
- def set_atom_datetime64 (self , block ):
1393
+ def set_atom_datetime64 (self , block , values = None ):
1351
1394
self .kind = 'datetime64'
1352
1395
self .typ = self .get_atom_datetime64 (block )
1353
- self .set_data (block .values .view ('i8' ), 'datetime64' )
1396
+ if values is None :
1397
+ values = block .values .view ('i8' )
1398
+ self .set_data (values , 'datetime64' )
1354
1399
1355
1400
@property
1356
1401
def shape (self ):
@@ -1389,7 +1434,18 @@ def convert(self, values, nan_rep):
1389
1434
1390
1435
# reverse converts
1391
1436
if self .dtype == 'datetime64' :
1392
- self .data = np .asarray (self .data , dtype = 'M8[ns]' )
1437
+ # recreate the timezone
1438
+ if self .tz is not None :
1439
+
1440
+ # data should be 2-dim here
1441
+ # we stored as utc, so just set the tz
1442
+
1443
+ index = DatetimeIndex (self .data .ravel (),tz = 'UTC' ).tz_convert (self .tz )
1444
+ self .data = np .array (index .tolist (),dtype = object ).reshape (self .data .shape )
1445
+
1446
+ else :
1447
+ self .data = np .asarray (self .data , dtype = 'M8[ns]' )
1448
+
1393
1449
elif self .dtype == 'date' :
1394
1450
self .data = np .array (
1395
1451
[date .fromtimestamp (v ) for v in self .data ], dtype = object )
@@ -2267,17 +2323,8 @@ def indexables(self):
2267
2323
d = self .description
2268
2324
self ._indexables = []
2269
2325
2270
- # info
2271
- info = getattr (self .attrs ,'info' ,None ) or dict ()
2272
-
2273
2326
# index columns
2274
- def create_index (i , axis , name ):
2275
- kwargs = dict ( name = name , axis = axis , pos = i )
2276
- i = info .get (name )
2277
- if i is not None and len (i ):
2278
- kwargs .update (i )
2279
- return IndexCol (** kwargs )
2280
- self ._indexables .extend ([ create_index (i ,axis ,name ) for i , (axis , name ) in enumerate (self .attrs .index_cols )])
2327
+ self ._indexables .extend ([ IndexCol (name = name ,axis = axis ,pos = i ) for i , (axis , name ) in enumerate (self .attrs .index_cols )])
2281
2328
2282
2329
# values columns
2283
2330
dc = set (self .data_columns )
@@ -2370,6 +2417,7 @@ def read_axes(self, where, **kwargs):
2370
2417
2371
2418
# convert the data
2372
2419
for a in self .axes :
2420
+ a .set_info (self .info )
2373
2421
a .convert (values , nan_rep = self .nan_rep )
2374
2422
2375
2423
return True
@@ -2535,6 +2583,7 @@ def create_axes(self, axes, obj, validate=True, nan_rep=None, data_columns=None,
2535
2583
existing_col = existing_col ,
2536
2584
min_itemsize = min_itemsize ,
2537
2585
nan_rep = nan_rep ,
2586
+ info = self .info ,
2538
2587
** kwargs )
2539
2588
col .set_pos (j )
2540
2589
@@ -2654,6 +2703,7 @@ def read_column(self, column, where = None, **kwargs):
2654
2703
2655
2704
# column must be an indexable or a data column
2656
2705
c = getattr (self .table .cols , column )
2706
+ a .set_info (self .info )
2657
2707
return Series (a .convert (c [:], nan_rep = self .nan_rep ).take_data ())
2658
2708
2659
2709
raise KeyError ("column [%s] not found in the table" % column )
@@ -3365,6 +3415,8 @@ def convert_value(self, v):
3365
3415
3366
3416
if self .kind == 'datetime64' or self .kind == 'datetime' :
3367
3417
v = lib .Timestamp (v )
3418
+ if v .tz is not None :
3419
+ v = v .tz_convert ('UTC' )
3368
3420
return [v .value , v ]
3369
3421
elif isinstance (v , datetime ) or hasattr (v , 'timetuple' ) or self .kind == 'date' :
3370
3422
v = time .mktime (v .timetuple ())
0 commit comments