|
39 | 39 | import sys |
40 | 40 | import threading |
41 | 41 | import datetime |
42 | | -from ctypes import memmove, memset, create_string_buffer, cast, byref, string_at, \ |
| 42 | +from ctypes import memmove, memset, create_string_buffer, cast, byref, string_at, sizeof, \ |
43 | 43 | c_char_p, c_void_p, c_byte, c_ulong |
44 | 44 | from .types import Error, DatabaseError, InterfaceError, BCD, \ |
45 | 45 | StateResult, DirectoryCode, BlobInfoCode, SQLDataType, XpbKind, \ |
46 | | - StatementType, StateFlag, CursorFlag, StatementFlag, PreparePrefetchFlag |
| 46 | + StatementType, StateFlag, CursorFlag, StatementFlag, PreparePrefetchFlag, get_timezone |
47 | 47 | from . import fbapi as a |
48 | 48 | from .hooks import APIHook, add_hook |
49 | 49 |
|
@@ -1308,64 +1308,108 @@ def set_offsets(self, metadata: iMessageMetadata, callback: iOffsetsCallbackImp) |
1308 | 1308 | class iUtil(iUtil_v2): |
1309 | 1309 | "Class that wraps IUtil v4 interface for use from Python" |
1310 | 1310 | VERSION = 4 |
| 1311 | + STR_SIZE = 200 |
| 1312 | + def __init__(self, intf): |
| 1313 | + super().__init__(intf) |
| 1314 | + self.year = a.Cardinal(0) |
| 1315 | + self.month = a.Cardinal(0) |
| 1316 | + self.day = a.Cardinal(0) |
| 1317 | + self.hours = a.Cardinal(0) |
| 1318 | + self.minutes = a.Cardinal(0) |
| 1319 | + self.seconds = a.Cardinal(0) |
| 1320 | + self.fractions = a.Cardinal(0) |
| 1321 | + self.str_buf = create_string_buffer(self.STR_SIZE) |
| 1322 | + self.time_tz = create_string_buffer(sizeof(a.ISC_TIME_TZ())) |
| 1323 | + self.timestamp_tz = create_string_buffer(sizeof(a.ISC_TIMESTAMP_TZ())) |
1311 | 1324 | def get_decfloat16(self) -> iDecFloat16: |
1312 | | - "TODO" |
| 1325 | + "Returns iDecFloat16 interface." |
1313 | 1326 | result = self.vtable.getDecFloat16(self, self.status) |
1314 | 1327 | self._check() |
1315 | 1328 | return iDecFloat16(result) |
1316 | 1329 | def get_decfloat34(self) -> iDecFloat34: |
1317 | | - "TODO" |
| 1330 | + "Returns iDecFloat34 interface." |
1318 | 1331 | result = self.vtable.getDecFloat34(self, self.status) |
1319 | 1332 | self._check() |
1320 | 1333 | return iDecFloat34(result) |
1321 | | - def decode_time_tz(self, timetz: a.ISC_TIME_TZ, hours: a.Cardinal, minutes: a.Cardinal, |
1322 | | - seconds: a.Cardinal, fractions: a.Cardinal, zone_bufer: bytes) -> None: |
1323 | | - "TODO" |
| 1334 | + def decode_time_tz(self, timetz: Union[a.ISC_TIME_TZ, bytes]) -> datetime.time: |
| 1335 | + "Decodes TIME WITH TIMEZONE from internal format to datetime.time with tzinfo." |
| 1336 | + if isinstance(timetz, bytes): |
| 1337 | + timetz = a.ISC_TIME_TZ.from_buffer_copy(timetz) |
| 1338 | + self.hours.value = 0 |
| 1339 | + self.minutes.value = 0 |
| 1340 | + self.seconds.value = 0 |
| 1341 | + self.fractions.value = 0 |
| 1342 | + memset(self.str_buf, 0, self.STR_SIZE) |
1324 | 1343 | # procedure decodeTimeTz(this: IUtil; status: IStatus; timeTz: ISC_TIME_TZPtr; |
1325 | 1344 | # hours: CardinalPtr; minutes: CardinalPtr; |
1326 | 1345 | # seconds: CardinalPtr; fractions: CardinalPtr; |
1327 | 1346 | # timeZoneBufferLength: Cardinal; timeZoneBuffer: PAnsiChar) |
1328 | | - self.vtable.decodeTimeTz(self, self.status, byref(timetz), byref(hours), |
1329 | | - byref(minutes), byref(seconds), byref(fractions), |
1330 | | - len(zone_bufer), zone_bufer) |
1331 | | - self._check() |
1332 | | - def decode_timestamp_tz(self, timestamptz: a.ISC_TIMESTAMP_TZ, year: a.Cardinal, |
1333 | | - month: a.Cardinal, day: a.Cardinal, hours: a.Cardinal, |
1334 | | - minutes: a.Cardinal, seconds: a.Cardinal, fractions: a.Cardinal, |
1335 | | - zone_bufer: bytes) -> None: |
1336 | | - "TODO" |
| 1347 | + self.vtable.decodeTimeTz(self, self.status, byref(timetz), byref(self.hours), |
| 1348 | + byref(self.minutes), byref(self.seconds), byref(self.fractions), |
| 1349 | + self.STR_SIZE, self.str_buf) |
| 1350 | + self._check() |
| 1351 | + tz = get_timezone(self.str_buf.value.decode()) |
| 1352 | + return datetime.time(self.hours.value, self.minutes.value, self.seconds.value, |
| 1353 | + self.fractions.value * 100, tz) |
| 1354 | + def decode_timestamp_tz(self, timestamptz: Union[a.ISC_TIMESTAMP_TZ, bytes]) -> datetime.datetime: |
| 1355 | + "Decodes TIMESTAMP WITH TIMEZONE from internal format to datetime.datetime with tzinfo." |
| 1356 | + if isinstance(timestamptz, bytes): |
| 1357 | + timestamptz = a.ISC_TIMESTAMP_TZ.from_buffer_copy(timestamptz) |
| 1358 | + self.year.value = 0 |
| 1359 | + self.month.value = 0 |
| 1360 | + self.day.value = 0 |
| 1361 | + self.hours.value = 0 |
| 1362 | + self.minutes.value = 0 |
| 1363 | + self.seconds.value = 0 |
| 1364 | + self.fractions.value = 0 |
| 1365 | + memset(self.str_buf, 0, self.STR_SIZE) |
1337 | 1366 | # procedure decodeTimeStampTz(this: IUtil; status: IStatus; timeStampTz: ISC_TIMESTAMP_TZPtr; |
1338 | 1367 | # year: CardinalPtr; month: CardinalPtr; day: CardinalPtr; |
1339 | 1368 | # hours: CardinalPtr; minutes: CardinalPtr; |
1340 | 1369 | # seconds: CardinalPtr; fractions: CardinalPtr; |
1341 | 1370 | # timeZoneBufferLength: Cardinal; timeZoneBuffer: PAnsiChar) |
1342 | | - self.vtable.decodeTimeStampTz(self, self.status, byref(timestamptz), byref(year), |
1343 | | - byref(month), byref(day), byref(hours), |
1344 | | - byref(minutes), byref(seconds), byref(fractions), |
1345 | | - len(zone_bufer), zone_bufer) |
1346 | | - self._check() |
1347 | | - def encode_time_tz(self, timetz: a.ISC_TIME_TZ, hours: int, minutes: int, seconds: int, |
1348 | | - fractions: int, tz: str) -> None: |
1349 | | - "TODO" |
| 1371 | + self.vtable.decodeTimeStampTz(self, self.status, byref(timestamptz), byref(self.year), |
| 1372 | + byref(self.month), byref(self.day), byref(self.hours), |
| 1373 | + byref(self.minutes), byref(self.seconds), byref(self.fractions), |
| 1374 | + self.STR_SIZE, self.str_buf) |
| 1375 | + self._check() |
| 1376 | + tz = get_timezone(self.str_buf.value.decode()) |
| 1377 | + return datetime.datetime(self.year.value, self.month.value, self.day.value, |
| 1378 | + self.hours.value, self.minutes.value, self.seconds.value, |
| 1379 | + self.fractions.value * 100, tz) |
| 1380 | + def encode_time_tz(self, time: datetime.time) -> bytes: |
| 1381 | + "Encodes datetime.time with tzinfo into internal format for TIME WITH TIMEZONE." |
| 1382 | + tzname = getattr(time.tzinfo, '_timezone_', None) |
| 1383 | + if not tzname: |
| 1384 | + raise InterfaceError("Time timezone not set or does not have a name") |
| 1385 | + self.str_buf.value = tzname.encode() |
| 1386 | + memset(self.time_tz, 0, 8) |
1350 | 1387 | # procedure encodeTimeTz(this: IUtil; status: IStatus; timeTz: ISC_TIME_TZPtr; |
1351 | 1388 | # hours: Cardinal; minutes: Cardinal; seconds: Cardinal; |
1352 | 1389 | # fractions: Cardinal; timeZone: PAnsiChar) |
1353 | | - self.vtable.encodeTimeTz(self, self.status, byref(timetz), hours, minutes, seconds, |
1354 | | - fractions, tz.encode()) |
1355 | | - self._check() |
1356 | | - def encode_timestamp_tz(self, timestamptz: a.ISC_TIMESTAMP_TZ, year: int, month: int, |
1357 | | - day: int, hours: int, minutes: int, seconds: int, |
1358 | | - fractions: int, tz: str) -> None: |
1359 | | - "TODO" |
| 1390 | + self.vtable.encodeTimeTz(self, self.status, cast(self.time_tz, a.ISC_TIME_TZ_PTR), |
| 1391 | + time.hour, time.minute, time.second, time.microsecond // 100, self.str_buf) |
| 1392 | + self._check() |
| 1393 | + return self.time_tz.raw |
| 1394 | + def encode_timestamp_tz(self, timestamp: datetime.datetime) -> bytes: |
| 1395 | + "Encodes datetime.datetime with tzinfo into internal format for TIMESTAMP WITH TIMEZONE." |
| 1396 | + tzname = getattr(timestamp.tzinfo, '_timezone_', None) |
| 1397 | + if not tzname: |
| 1398 | + raise InterfaceError("Datetime timezone not set or does not have a name") |
| 1399 | + self.str_buf.value = tzname.encode() |
| 1400 | + memset(self.timestamp_tz, 0, 12) |
1360 | 1401 | # procedure encodeTimeStampTz(this: IUtil; status: IStatus; timeStampTz: ISC_TIMESTAMP_TZPtr; |
1361 | 1402 | # year: Cardinal; month: Cardinal; day: Cardinal; |
1362 | 1403 | # hours: Cardinal; minutes: Cardinal; seconds: Cardinal; |
1363 | 1404 | # fractions: Cardinal; timeZone: PAnsiChar) |
1364 | | - self.vtable.encodeTimeStampTz(self, self.status, byref(timestamptz), year, month, |
1365 | | - day, hours, minutes, seconds, fractions, tz.encode()) |
| 1405 | + self.vtable.encodeTimeStampTz(self, self.status, cast(self.timestamp_tz, a.ISC_TIMESTAMP_TZ_PTR), |
| 1406 | + timestamp.year, timestamp.month, timestamp.day, |
| 1407 | + timestamp.hour, timestamp.minute, timestamp.second, |
| 1408 | + timestamp.microsecond // 100, self.str_buf) |
1366 | 1409 | self._check() |
| 1410 | + return self.timestamp_tz.raw |
1367 | 1411 | def get_int128(self) -> iInt128: |
1368 | | - "TODO" |
| 1412 | + "Returns iInt128 interface." |
1369 | 1413 | result = self.vtable.getInt128(self, self.status) |
1370 | 1414 | self._check() |
1371 | 1415 | return iInt128(result) |
@@ -1489,8 +1533,9 @@ def __init__(self, intf): |
1489 | 1533 | def to_str(self, value: a.FB_I128, scale: int) -> str: |
1490 | 1534 | # procedure toString(this: IInt128; status: IStatus; from: FB_I128Ptr; scale: Integer; bufferLength: Cardinal; buffer: PAnsiChar) |
1491 | 1535 | memset(self.str_buf, 0, self.STR_SIZE) |
1492 | | - self.vtable.toString(self, self.status, byref(value), scale, self.STR_SIZE, buffer) |
| 1536 | + self.vtable.toString(self, self.status, byref(value), scale, self.STR_SIZE, self.str_buf) |
1493 | 1537 | self._check() |
| 1538 | + return self.str_buf.value.decode() |
1494 | 1539 | def from_str(self, value: str, scale: int, into: a.FB_I128=None) -> a.FB_I128: |
1495 | 1540 | # procedure fromString(this: IInt128; status: IStatus; scale: Integer; from: PAnsiChar; to_: FB_I128Ptr) |
1496 | 1541 | result = a.FB_I128(0) if into is None else into |
|
0 commit comments