1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
|
from datetime import datetime
from pytz import timezone as get_timezone, utc
from ..util.compat import get_localzone_name_compat
from .base import FormatColumn
EPOCH = datetime(1970, 1, 1, tzinfo=utc)
class DateTimeColumn(FormatColumn):
ch_type = 'DateTime'
py_types = (datetime, int)
format = 'I'
def __init__(self, timezone=None, offset_naive=True, **kwargs):
self.timezone = timezone
self.offset_naive = offset_naive
super(DateTimeColumn, self).__init__(**kwargs)
def after_read_items(self, items, nulls_map=None):
tz = self.timezone
fromts = datetime.fromtimestamp
# A bit ugly copy-paste. But it helps save time on items
# processing by avoiding lambda calls or if in loop.
if self.offset_naive:
if tz:
if nulls_map is None:
return tuple(
fromts(item, tz).replace(tzinfo=None)
for item in items
)
else:
return tuple(
(None if is_null else
fromts(items[i], tz).replace(tzinfo=None))
for i, is_null in enumerate(nulls_map)
)
else:
if nulls_map is None:
return tuple(fromts(item) for item in items)
else:
return tuple(
(None if is_null else fromts(items[i]))
for i, is_null in enumerate(nulls_map)
)
else:
if nulls_map is None:
return tuple(fromts(item, tz) for item in items)
else:
return tuple(
(None if is_null else fromts(items[i], tz))
for i, is_null in enumerate(nulls_map)
)
def before_write_items(self, items, nulls_map=None):
timezone = self.timezone
null_value = self.null_value
to_timestamp = datetime.timestamp
for i, item in enumerate(items):
if nulls_map and nulls_map[i]:
items[i] = null_value
continue
if isinstance(item, int):
# support supplying raw integers to avoid
# costly timezone conversions when using datetime
continue
if timezone:
# Set server's timezone for offset-naive datetime.
if item.tzinfo is None:
item = timezone.localize(item)
item = item.astimezone(utc)
else:
# If datetime is offset-aware use it's timezone.
if item.tzinfo is not None:
item = item.astimezone(utc)
items[i] = int(to_timestamp(item))
class DateTime64Column(DateTimeColumn):
ch_type = 'DateTime64'
format = 'q'
max_scale = 6
def __init__(self, scale=0, **kwargs):
self.scale = scale
super(DateTime64Column, self).__init__(**kwargs)
def after_read_items(self, items, nulls_map=None):
scale = float(10 ** self.scale)
tz = self.timezone
fromts = datetime.fromtimestamp
# A bit ugly copy-paste. But it helps save time on items
# processing by avoiding lambda calls or if in loop.
if self.offset_naive:
if tz:
if nulls_map is None:
return tuple(
fromts(item / scale, tz).replace(tzinfo=None)
for item in items
)
else:
return tuple(
(None if is_null else
fromts(items[i] / scale, tz).replace(tzinfo=None))
for i, is_null in enumerate(nulls_map)
)
else:
if nulls_map is None:
return tuple(fromts(item / scale) for item in items)
else:
return tuple(
(None if is_null else fromts(items[i] / scale))
for i, is_null in enumerate(nulls_map)
)
else:
if nulls_map is None:
return tuple(fromts(item / scale, tz) for item in items)
else:
return tuple(
(None if is_null else fromts(items[i] / scale, tz))
for i, is_null in enumerate(nulls_map)
)
def before_write_items(self, items, nulls_map=None):
scale = 10 ** self.scale
frac_scale = 10 ** (self.max_scale - self.scale)
timezone = self.timezone
null_value = self.null_value
to_timestamp = datetime.timestamp
for i, item in enumerate(items):
if nulls_map and nulls_map[i]:
items[i] = null_value
continue
if isinstance(item, int):
# support supplying raw integers to avoid
# costly timezone conversions when using datetime
continue
if timezone:
# Set server's timezone for offset-naive datetime.
if item.tzinfo is None:
item = timezone.localize(item)
item = item.astimezone(utc)
else:
# If datetime is offset-aware use it's timezone.
if item.tzinfo is not None:
item = item.astimezone(utc)
items[i] = (
int(to_timestamp(item)) * scale +
int(item.microsecond / frac_scale)
)
def create_datetime_column(spec, column_options):
if spec.startswith('DateTime64'):
cls = DateTime64Column
spec = spec[11:-1]
params = spec.split(',', 1)
column_options['scale'] = int(params[0])
if len(params) > 1:
spec = params[1].strip() + ')'
else:
cls = DateTimeColumn
spec = spec[9:]
context = column_options['context']
tz_name = timezone = None
offset_naive = True
# Use column's timezone if it's specified.
if spec and spec[-1] == ')':
tz_name = spec[1:-2]
offset_naive = False
else:
if not context.settings.get('use_client_time_zone', False):
local_timezone = get_localzone_name_compat()
if local_timezone != context.server_info.timezone:
tz_name = context.server_info.timezone
if tz_name:
timezone = get_timezone(tz_name)
return cls(timezone=timezone, offset_naive=offset_naive, **column_options)
|