Skip to content

Commit 025e4b0

Browse files
committed
historic dates support
1 parent 7c3fa02 commit 025e4b0

File tree

3 files changed

+121
-105
lines changed

3 files changed

+121
-105
lines changed

sqlalchemy_iris/base.py

Lines changed: 5 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import datetime
2-
from decimal import Decimal
31
import intersystems_iris.dbapi._DBAPI as dbapi
42
from . import information_schema as ischema
3+
from . import types
54
from sqlalchemy import exc
65
from sqlalchemy.orm import aliased
76
from sqlalchemy.engine import default
@@ -539,110 +538,11 @@ def create_cursor(self):
539538
return cursor
540539

541540

542-
HOROLOG_ORDINAL = datetime.date(1840, 12, 31).toordinal()
543-
544-
545-
class _IRISDate(sqltypes.Date):
546-
def bind_processor(self, dialect):
547-
def process(value):
548-
if value is None:
549-
return None
550-
horolog = value.toordinal() - HOROLOG_ORDINAL
551-
return str(horolog)
552-
553-
return process
554-
555-
def result_processor(self, dialect, coltype):
556-
def process(value):
557-
if value is None:
558-
return None
559-
if isinstance(value, str) and '-' in value:
560-
return datetime.datetime.strptime(value, '%Y-%m-%d').date()
561-
horolog = int(value) + HOROLOG_ORDINAL
562-
return datetime.date.fromordinal(horolog)
563-
564-
return process
565-
566-
567-
class _IRISTimeStamp(sqltypes.DateTime):
568-
def bind_processor(self, dialect):
569-
def process(value: datetime.datetime):
570-
if value is not None:
571-
# value = int(value.timestamp() * 1000000)
572-
# value += (2 ** 60) if value > 0 else -(2 ** 61 * 3)
573-
return value.strftime('%Y-%m-%d %H:%M:%S.%f')
574-
return value
575-
576-
return process
577-
578-
def result_processor(self, dialect, coltype):
579-
def process(value):
580-
if isinstance(value, str):
581-
if '.' not in value:
582-
value += '.0'
583-
return datetime.datetime.strptime(value, '%Y-%m-%d %H:%M:%S.%f')
584-
if isinstance(value, int):
585-
value -= (2 ** 60) if value > 0 else -(2 ** 61 * 3)
586-
value = value / 1000000
587-
value = datetime.datetime.utcfromtimestamp(value)
588-
return value
589-
590-
return process
591-
592-
593-
class _IRISDateTime(sqltypes.DateTime):
594-
def bind_processor(self, dialect):
595-
def process(value):
596-
if value is not None:
597-
return value.strftime('%Y-%m-%d %H:%M:%S.%f')
598-
return value
599-
600-
return process
601-
602-
def result_processor(self, dialect, coltype):
603-
def process(value):
604-
if isinstance(value, str):
605-
if '.' not in value:
606-
value += '.0'
607-
return datetime.datetime.strptime(value, '%Y-%m-%d %H:%M:%S.%f')
608-
return value
609-
610-
return process
611-
612-
613-
class _IRISTime(sqltypes.DateTime):
614-
def bind_processor(self, dialect):
615-
def process(value):
616-
if value is not None:
617-
return value.strftime('%H:%M:%S.%f')
618-
return value
619-
620-
return process
621-
622-
def result_processor(self, dialect, coltype):
623-
def process(value):
624-
if isinstance(value, str):
625-
if '.' not in value:
626-
value += '.0'
627-
return datetime.datetime.strptime(value, '%H:%M:%S.%f').time()
628-
if isinstance(value, int) or isinstance(value, Decimal):
629-
horolog = value
630-
hour = int(horolog // 3600)
631-
horolog -= int(hour * 3600)
632-
minute = int(horolog // 60)
633-
second = int(horolog % 60)
634-
micro = int(value % 1 * 1000000)
635-
return datetime.time(hour, minute, second, micro)
636-
return value
637-
638-
return process
639-
640-
641541
colspecs = {
642-
sqltypes.Date: _IRISDate,
643-
sqltypes.DateTime: _IRISDateTime,
644-
sqltypes.TIMESTAMP: _IRISTimeStamp,
645-
sqltypes.Time: _IRISTime,
542+
sqltypes.Date: types.IRISDate,
543+
sqltypes.DateTime: types.IRISDateTime,
544+
sqltypes.TIMESTAMP: types.IRISTimeStamp,
545+
sqltypes.Time: types.IRISTime,
646546
}
647547

648548

sqlalchemy_iris/requirements.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,18 @@ def supports_distinct_on(self):
2424
@property
2525
def reflects_pk_names(self):
2626
return exclusions.open()
27+
28+
@property
29+
def date_historic(self):
30+
"""target dialect supports representation of Python
31+
datetime.datetime() objects with historic (pre 1970) values."""
32+
33+
return exclusions.open()
34+
35+
@property
36+
def datetime_historic(self):
37+
"""target dialect supports representation of Python
38+
datetime.datetime() objects with historic (pre 1970) values."""
39+
40+
return exclusions.open()
41+

sqlalchemy_iris/types.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import datetime
2+
from decimal import Decimal
3+
from sqlalchemy.sql import sqltypes
4+
5+
HOROLOG_ORDINAL = datetime.date(1840, 12, 31).toordinal()
6+
7+
8+
class IRISDate(sqltypes.Date):
9+
def bind_processor(self, dialect):
10+
def process(value):
11+
if value is None:
12+
return None
13+
horolog = value.toordinal() - HOROLOG_ORDINAL
14+
return str(horolog)
15+
16+
return process
17+
18+
def result_processor(self, dialect, coltype):
19+
def process(value):
20+
if value is None:
21+
return None
22+
if isinstance(value, str) and '-' in value[1:]:
23+
return datetime.datetime.strptime(value, '%Y-%m-%d').date()
24+
horolog = int(value) + HOROLOG_ORDINAL
25+
return datetime.date.fromordinal(horolog)
26+
27+
return process
28+
29+
30+
class IRISTimeStamp(sqltypes.DateTime):
31+
def bind_processor(self, dialect):
32+
def process(value: datetime.datetime):
33+
if value is not None:
34+
# value = int(value.timestamp() * 1000000)
35+
# value += (2 ** 60) if value > 0 else -(2 ** 61 * 3)
36+
return value.strftime('%Y-%m-%d %H:%M:%S.%f')
37+
return value
38+
39+
return process
40+
41+
def result_processor(self, dialect, coltype):
42+
def process(value):
43+
if isinstance(value, str):
44+
if '.' not in value:
45+
value += '.0'
46+
return datetime.datetime.strptime(value, '%Y-%m-%d %H:%M:%S.%f')
47+
if isinstance(value, int):
48+
value -= (2 ** 60) if value > 0 else -(2 ** 61 * 3)
49+
value = value / 1000000
50+
value = datetime.datetime.utcfromtimestamp(value)
51+
return value
52+
53+
return process
54+
55+
56+
class IRISDateTime(sqltypes.DateTime):
57+
def bind_processor(self, dialect):
58+
def process(value):
59+
if value is not None:
60+
return value.strftime('%Y-%m-%d %H:%M:%S.%f')
61+
return value
62+
63+
return process
64+
65+
def result_processor(self, dialect, coltype):
66+
def process(value):
67+
if isinstance(value, str):
68+
if '.' not in value:
69+
value += '.0'
70+
return datetime.datetime.strptime(value, '%Y-%m-%d %H:%M:%S.%f')
71+
return value
72+
73+
return process
74+
75+
76+
class IRISTime(sqltypes.DateTime):
77+
def bind_processor(self, dialect):
78+
def process(value):
79+
if value is not None:
80+
return value.strftime('%H:%M:%S.%f')
81+
return value
82+
83+
return process
84+
85+
def result_processor(self, dialect, coltype):
86+
def process(value):
87+
if isinstance(value, str):
88+
if '.' not in value:
89+
value += '.0'
90+
return datetime.datetime.strptime(value, '%H:%M:%S.%f').time()
91+
if isinstance(value, int) or isinstance(value, Decimal):
92+
horolog = value
93+
hour = int(horolog // 3600)
94+
horolog -= int(hour * 3600)
95+
minute = int(horolog // 60)
96+
second = int(horolog % 60)
97+
micro = int(value % 1 * 1000000)
98+
return datetime.time(hour, minute, second, micro)
99+
return value
100+
101+
return process

0 commit comments

Comments
 (0)