diff --git a/generated/nidaqmx/_grpc_interpreter.py b/generated/nidaqmx/_grpc_interpreter.py index 70a9d4113..35286d8b3 100644 --- a/generated/nidaqmx/_grpc_interpreter.py +++ b/generated/nidaqmx/_grpc_interpreter.py @@ -381,11 +381,10 @@ def cfg_samp_clk_timing( samps_per_chan=samps_per_chan)) def cfg_time_start_trig(self, task, when, timescale): - ts = GrpcTimestamp() response = self._invoke( self._client.CfgTimeStartTrig, grpc_types.CfgTimeStartTrigRequest( - task=task, when=convert_time_to_timestamp(when,ts), + task=task, when=convert_time_to_timestamp(when), timescale_raw=timescale)) def cfg_watchdog_ao_expir_states( @@ -3135,12 +3134,11 @@ def set_trig_attribute_string(self, task, attribute, value): task=task, attribute_raw=attribute, value=value)) def set_trig_attribute_timestamp(self, task, attribute, value): - ts = GrpcTimestamp() response = self._invoke( self._client.SetTrigAttributeTimestamp, grpc_types.SetTrigAttributeTimestampRequest( task=task, attribute_raw=attribute, - value=convert_time_to_timestamp(value,ts))) + value=convert_time_to_timestamp(value))) def set_trig_attribute_uint32(self, task, attribute, value): response = self._invoke( diff --git a/generated/nidaqmx/_grpc_time.py b/generated/nidaqmx/_grpc_time.py index e4e2beb1e..744eb6501 100644 --- a/generated/nidaqmx/_grpc_time.py +++ b/generated/nidaqmx/_grpc_time.py @@ -14,7 +14,7 @@ _YS_PER_FS = 10**9 -def convert_time_to_timestamp(dt: Union[std_datetime, ht_datetime], ts: GrpcTimestamp) -> None: +def convert_time_to_timestamp(dt: Union[std_datetime, ht_datetime], ts: Optional[GrpcTimestamp] = GrpcTimestamp()) -> GrpcTimestamp: utc_dt = dt.astimezone(tz=timezone.utc) seconds = int(utc_dt.timestamp()) @@ -30,6 +30,7 @@ def convert_time_to_timestamp(dt: Union[std_datetime, ht_datetime], ts: GrpcTime nanos = utc_dt.microsecond * _NS_PER_US ts.FromNanoseconds(seconds * _NS_PER_S + nanos) + return ts def convert_timestamp_to_time(ts: GrpcTimestamp, tzinfo: Optional[timezone] = None) -> ht_datetime: diff --git a/generated/nidaqmx/_lib_time.py b/generated/nidaqmx/_lib_time.py index 6d2656fc0..6bee16e3c 100644 --- a/generated/nidaqmx/_lib_time.py +++ b/generated/nidaqmx/_lib_time.py @@ -2,7 +2,7 @@ import ctypes import functools -from datetime import timezone +from datetime import timezone, timedelta from datetime import datetime as std_datetime from hightime import datetime as ht_datetime from typing import Optional, Union @@ -58,6 +58,12 @@ def from_datetime(cls, dt: Union[std_datetime, ht_datetime]) -> AbsoluteTime: return AbsoluteTime(lsb=lsb, msb=timestamp_1904_epoch) + def convert_to_local_timezone(self, expected_time_utc): + current_time_utc = ht_datetime.now(timezone.utc) + local_timezone_offset = current_time_utc.astimezone().utcoffset() + local_expected_time = expected_time_utc + local_timezone_offset + return local_expected_time + def to_datetime(self, tzinfo: Optional[timezone] = None) -> ht_datetime: # First, calculate whole seconds by converting from the 1904 to 1970 epoch. timestamp_1904_epoch = self.msb @@ -79,12 +85,18 @@ def to_datetime(self, tzinfo: Optional[timezone] = None) -> ht_datetime: ) yoctosecond = remainder_yoctoseconds - # Start with UTC - dt = ht_datetime.fromtimestamp(timestamp_1970_epoch, timezone.utc) + if not is_positive: + datetime_1904 = ht_datetime(1904, 1, 1) + dt = datetime_1904 + timedelta(seconds=self.msb) + else: + dt = ht_datetime.fromtimestamp(timestamp_1970_epoch, timezone.utc) # Add in precision dt = dt.replace(microsecond=microsecond, femtosecond=femtosecond, yoctosecond=yoctosecond) # Then convert to requested timezone - return dt.astimezone(tz=tzinfo) + if not is_positive: + return self.convert_to_local_timezone(dt) + else: + return dt.astimezone(tz=tzinfo) def __str__(self) -> str: return f"AbsoluteTime(lsb=0x{self.lsb:x}, msb=0x{self.msb:x})" diff --git a/src/codegen/templates/grpc_interpreter/default_grpc_function_call.py.mako b/src/codegen/templates/grpc_interpreter/default_grpc_function_call.py.mako index 4ab420c3a..4dada7895 100644 --- a/src/codegen/templates/grpc_interpreter/default_grpc_function_call.py.mako +++ b/src/codegen/templates/grpc_interpreter/default_grpc_function_call.py.mako @@ -40,9 +40,6 @@ %for parameter_name, parameter_dtype in get_numpy_array_params(function).items(): _validate_array_dtype(${parameter_name}, ${parameter_dtype}) %endfor -%endif -%if "convert_time_to_timestamp" in grpc_interpreter_params: - ts = GrpcTimestamp() %endif response = self._invoke( self._client.${snake_to_pascal(function.function_name)}, diff --git a/src/codegen/utilities/interpreter_helpers.py b/src/codegen/utilities/interpreter_helpers.py index 8d86eedcf..f0a5711c7 100644 --- a/src/codegen/utilities/interpreter_helpers.py +++ b/src/codegen/utilities/interpreter_helpers.py @@ -356,7 +356,7 @@ def get_grpc_interpreter_call_params(func, params): elif param.is_grpc_enum or (param.is_enum and not param.is_list): grpc_params.append(f"{name}_raw={param.parameter_name}") elif param.type == "CVIAbsoluteTime": - grpc_params.append(f"{name}=convert_time_to_timestamp({param.parameter_name},ts)") + grpc_params.append(f"{name}=convert_time_to_timestamp({param.parameter_name})") else: if is_write_bytes_param(param): grpc_params.append(f"{name}={param.parameter_name}.tobytes()") diff --git a/src/handwritten/_grpc_time.py b/src/handwritten/_grpc_time.py index e4e2beb1e..744eb6501 100644 --- a/src/handwritten/_grpc_time.py +++ b/src/handwritten/_grpc_time.py @@ -14,7 +14,7 @@ _YS_PER_FS = 10**9 -def convert_time_to_timestamp(dt: Union[std_datetime, ht_datetime], ts: GrpcTimestamp) -> None: +def convert_time_to_timestamp(dt: Union[std_datetime, ht_datetime], ts: Optional[GrpcTimestamp] = GrpcTimestamp()) -> GrpcTimestamp: utc_dt = dt.astimezone(tz=timezone.utc) seconds = int(utc_dt.timestamp()) @@ -30,6 +30,7 @@ def convert_time_to_timestamp(dt: Union[std_datetime, ht_datetime], ts: GrpcTime nanos = utc_dt.microsecond * _NS_PER_US ts.FromNanoseconds(seconds * _NS_PER_S + nanos) + return ts def convert_timestamp_to_time(ts: GrpcTimestamp, tzinfo: Optional[timezone] = None) -> ht_datetime: diff --git a/src/handwritten/_lib_time.py b/src/handwritten/_lib_time.py index 6d2656fc0..6bee16e3c 100644 --- a/src/handwritten/_lib_time.py +++ b/src/handwritten/_lib_time.py @@ -2,7 +2,7 @@ import ctypes import functools -from datetime import timezone +from datetime import timezone, timedelta from datetime import datetime as std_datetime from hightime import datetime as ht_datetime from typing import Optional, Union @@ -58,6 +58,12 @@ def from_datetime(cls, dt: Union[std_datetime, ht_datetime]) -> AbsoluteTime: return AbsoluteTime(lsb=lsb, msb=timestamp_1904_epoch) + def convert_to_local_timezone(self, expected_time_utc): + current_time_utc = ht_datetime.now(timezone.utc) + local_timezone_offset = current_time_utc.astimezone().utcoffset() + local_expected_time = expected_time_utc + local_timezone_offset + return local_expected_time + def to_datetime(self, tzinfo: Optional[timezone] = None) -> ht_datetime: # First, calculate whole seconds by converting from the 1904 to 1970 epoch. timestamp_1904_epoch = self.msb @@ -79,12 +85,18 @@ def to_datetime(self, tzinfo: Optional[timezone] = None) -> ht_datetime: ) yoctosecond = remainder_yoctoseconds - # Start with UTC - dt = ht_datetime.fromtimestamp(timestamp_1970_epoch, timezone.utc) + if not is_positive: + datetime_1904 = ht_datetime(1904, 1, 1) + dt = datetime_1904 + timedelta(seconds=self.msb) + else: + dt = ht_datetime.fromtimestamp(timestamp_1970_epoch, timezone.utc) # Add in precision dt = dt.replace(microsecond=microsecond, femtosecond=femtosecond, yoctosecond=yoctosecond) # Then convert to requested timezone - return dt.astimezone(tz=tzinfo) + if not is_positive: + return self.convert_to_local_timezone(dt) + else: + return dt.astimezone(tz=tzinfo) def __str__(self) -> str: return f"AbsoluteTime(lsb=0x{self.lsb:x}, msb=0x{self.msb:x})" diff --git a/tests/component/_task_modules/test_triggers_properties.py b/tests/component/_task_modules/test_triggers_properties.py index 470a7d6c9..6f4dcec03 100644 --- a/tests/component/_task_modules/test_triggers_properties.py +++ b/tests/component/_task_modules/test_triggers_properties.py @@ -7,7 +7,7 @@ from nidaqmx.error_codes import DAQmxErrors from nidaqmx.errors import DaqError from nidaqmx.task import Task -from tests.unit._time_utils import JAN_01_2002_HIGHTIME +from tests.unit._time_utils import JAN_01_2002_HIGHTIME, JAN_01_1904_HIGHTIME @pytest.fixture() @@ -119,15 +119,20 @@ def test___ai_task___reset_uint32_property___returns_default_value(ai_voltage_ta assert ai_voltage_task.triggers.reference_trigger.pretrig_samples == 2 -def test___ai_voltage_field_daq_task___get_timestamp_property___throws_os_error( +def test___ai_voltage_field_daq_task___get_timestamp_property___returns_default_value( ai_voltage_field_daq_task: Task, ): ai_voltage_field_daq_task.timing.cfg_samp_clk_timing(1000) - # timestamp 1904-01-01 is out of range of supported timestamp values - with pytest.raises(OSError) as os_error: - ai_voltage_field_daq_task.triggers.start_trigger.trig_when - assert os_error.value.errno == 22 + when_value = ai_voltage_field_daq_task.triggers.start_trigger.trig_when + + localized_default_value = convert_to_local_timezone(JAN_01_1904_HIGHTIME) + assert when_value.year == localized_default_value.year + assert when_value.month == localized_default_value.month + assert when_value.day == localized_default_value.day + assert when_value.hour == localized_default_value.hour + assert when_value.minute == localized_default_value.minute + assert when_value.second == localized_default_value.second def test___ai_voltage_field_daq_task___set_timestamp_property___returns_assigned_value( @@ -148,7 +153,7 @@ def test___ai_voltage_field_daq_task___set_timestamp_property___returns_assigned assert when_value.second == localized_value_to_test.second -def test___ai_voltage_field_daq_task___reset_timestamp_property___throws_os_error( +def test___ai_voltage_field_daq_task___reset_timestamp_property___returns_default_value( ai_voltage_field_daq_task: Task, ): ai_voltage_field_daq_task.timing.cfg_samp_clk_timing(1000) @@ -156,7 +161,11 @@ def test___ai_voltage_field_daq_task___reset_timestamp_property___throws_os_erro del ai_voltage_field_daq_task.triggers.start_trigger.trig_when - # timestamp 1904-01-01 is out of range of supported timestamp values - with pytest.raises(OSError) as os_error: - ai_voltage_field_daq_task.triggers.start_trigger.trig_when - assert os_error.value.errno == 22 + when_value = ai_voltage_field_daq_task.triggers.start_trigger.trig_when + localized_default_value = convert_to_local_timezone(JAN_01_1904_HIGHTIME) + assert when_value.year == localized_default_value.year + assert when_value.month == localized_default_value.month + assert when_value.day == localized_default_value.day + assert when_value.hour == localized_default_value.hour + assert when_value.minute == localized_default_value.minute + assert when_value.second == localized_default_value.second diff --git a/tests/unit/_time_utils.py b/tests/unit/_time_utils.py index 365a02f85..640091054 100644 --- a/tests/unit/_time_utils.py +++ b/tests/unit/_time_utils.py @@ -10,3 +10,5 @@ JAN_01_2002_DATETIME = std_datetime(2002, 1, 1, tzinfo=timezone.utc) JAN_01_2002_HIGHTIME = ht_datetime(2002, 1, 1, tzinfo=timezone.utc) +JAN_01_1904_DATETIME = std_datetime(1904, 1, 1, tzinfo=timezone.utc) +JAN_01_1904_HIGHTIME = ht_datetime(1904, 1, 1, tzinfo=timezone.utc) diff --git a/tests/unit/test_grpc_time.py b/tests/unit/test_grpc_time.py index 43673d35c..b6a7c0eb1 100644 --- a/tests/unit/test_grpc_time.py +++ b/tests/unit/test_grpc_time.py @@ -1,17 +1,17 @@ -from datetime import datetime as std_datetime -from datetime import timedelta, timezone +from datetime import datetime as std_datetime, timedelta, timezone import pytest from hightime import datetime as ht_datetime from tests.unit._time_utils import ( - JAN_01_2002_TIMESTAMP_1970_EPOCH, JAN_01_2002_DATETIME, JAN_01_2002_HIGHTIME, + JAN_01_2002_TIMESTAMP_1970_EPOCH, ) try: from google.protobuf.timestamp_pb2 import Timestamp as GrpcTimestamp + import nidaqmx._grpc_time as grpc_time import nidaqmx._stubs.nidaqmx_pb2 as nidaqmx_pb2 except ImportError: @@ -22,8 +22,7 @@ @pytest.mark.parametrize("from_dt", [(JAN_01_2002_DATETIME), (JAN_01_2002_HIGHTIME)]) def test___utc_datetime___convert_to_timestamp___is_reversible(from_dt): - to_ts = GrpcTimestamp() - grpc_time.convert_time_to_timestamp(from_dt, to_ts) + to_ts = grpc_time.convert_time_to_timestamp(from_dt) roundtrip_dt = grpc_time.convert_timestamp_to_time(to_ts, tzinfo=timezone.utc) total_nanoseconds = to_ts.ToNanoseconds() @@ -73,8 +72,7 @@ def test___tz_datetime___convert_to_timestamp___is_reversible( ): from_dt = datetime_cls(2002, 1, 1, tzinfo=tzinfo) - to_ts = GrpcTimestamp() - grpc_time.convert_time_to_timestamp(from_dt, to_ts) + to_ts = grpc_time.convert_time_to_timestamp(from_dt) roundtrip_dt = grpc_time.convert_timestamp_to_time(to_ts, tzinfo=tzinfo) assert to_ts.seconds == JAN_01_2002_TIMESTAMP_1970_EPOCH + expected_offset @@ -104,8 +102,7 @@ def test___datetime_with_microseconds___convert_to_timestamp___is_reversible( ): from_dt = base_dt.replace(microsecond=microsecond) - to_ts = GrpcTimestamp() - grpc_time.convert_time_to_timestamp(from_dt, to_ts) + to_ts = grpc_time.convert_time_to_timestamp(from_dt) roundtrip_dt = grpc_time.convert_timestamp_to_time(to_ts, tzinfo=timezone.utc) assert to_ts.seconds == JAN_01_2002_TIMESTAMP_1970_EPOCH @@ -135,8 +132,7 @@ def test___datetime_with_femtoseconds___convert_to_timestamp___is_reversible( ): from_dt = base_dt.replace(femtosecond=femtosecond) - to_ts = GrpcTimestamp() - grpc_time.convert_time_to_timestamp(from_dt, to_ts) + to_ts = grpc_time.convert_time_to_timestamp(from_dt) roundtrip_dt = grpc_time.convert_timestamp_to_time(to_ts, tzinfo=timezone.utc) assert to_ts.seconds == JAN_01_2002_TIMESTAMP_1970_EPOCH