Skip to content

Commit c16cc2a

Browse files
authored
Merge branch 'main' into feat-374142081-add-time-date-options
2 parents aa2d5e5 + 371ad29 commit c16cc2a

File tree

5 files changed

+133
-0
lines changed

5 files changed

+133
-0
lines changed

google/cloud/bigquery/external_config.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -894,6 +894,34 @@ def time_zone(self) -> Optional[str]:
894894
def time_zone(self, value: Optional[str]):
895895
self._properties["timeZone"] = value
896896

897+
@property
898+
def time_format(self) -> Optional[str]:
899+
"""Optional[str]: Format used to parse TIME values. Supports C-style and SQL-style values.
900+
901+
See:
902+
https://cloud.google.com/bigquery/docs/reference/rest/v2/tables#ExternalDataConfiguration.FIELDS.time_format
903+
"""
904+
result = self._properties.get("timeFormat")
905+
return typing.cast(str, result)
906+
907+
@time_format.setter
908+
def time_format(self, value: Optional[str]):
909+
self._properties["timeFormat"] = value
910+
911+
@property
912+
def timestamp_format(self) -> Optional[str]:
913+
"""Optional[str]: Format used to parse TIMESTAMP values. Supports C-style and SQL-style values.
914+
915+
See:
916+
https://cloud.google.com/bigquery/docs/reference/rest/v2/tables#ExternalDataConfiguration.FIELDS.timestamp_format
917+
"""
918+
result = self._properties.get("timestampFormat")
919+
return typing.cast(str, result)
920+
921+
@timestamp_format.setter
922+
def timestamp_format(self, value: Optional[str]):
923+
self._properties["timestampFormat"] = value
924+
897925
@property
898926
def connection_id(self):
899927
"""Optional[str]: [Experimental] ID of a BigQuery Connection API

google/cloud/bigquery/job/load.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,32 @@ def time_zone(self) -> Optional[str]:
588588
def time_zone(self, value: Optional[str]):
589589
self._set_sub_prop("timeZone", value)
590590

591+
@property
592+
def time_format(self) -> Optional[str]:
593+
"""Optional[str]: Date format used for parsing TIME values.
594+
595+
See:
596+
https://cloud.google.com/bigquery/docs/reference/rest/v2/Job#JobConfigurationLoad.FIELDS.time_format
597+
"""
598+
return self._get_sub_prop("timeFormat")
599+
600+
@time_format.setter
601+
def time_format(self, value: Optional[str]):
602+
self._set_sub_prop("timeFormat", value)
603+
604+
@property
605+
def timestamp_format(self) -> Optional[str]:
606+
"""Optional[str]: Date format used for parsing TIMESTAMP values.
607+
608+
See:
609+
https://cloud.google.com/bigquery/docs/reference/rest/v2/Job#JobConfigurationLoad.FIELDS.timestamp_format
610+
"""
611+
return self._get_sub_prop("timestampFormat")
612+
613+
@timestamp_format.setter
614+
def timestamp_format(self, value: Optional[str]):
615+
self._set_sub_prop("timestampFormat", value)
616+
591617
@property
592618
def time_partitioning(self):
593619
"""Optional[google.cloud.bigquery.table.TimePartitioning]: Specifies time-based
@@ -950,6 +976,20 @@ def time_zone(self):
950976
"""
951977
return self.configuration.time_zone
952978

979+
@property
980+
def time_format(self):
981+
"""See
982+
:attr:`google.cloud.bigquery.job.LoadJobConfig.time_format`.
983+
"""
984+
return self.configuration.time_format
985+
986+
@property
987+
def timestamp_format(self):
988+
"""See
989+
:attr:`google.cloud.bigquery.job.LoadJobConfig.timestamp_format`.
990+
"""
991+
return self.configuration.timestamp_format
992+
953993
@property
954994
def schema_update_options(self):
955995
"""See

tests/unit/job/test_load.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ def _setUpConstants(self):
4040
self.DATE_FORMAT = "%Y-%m-%d"
4141
self.DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%S"
4242
self.TIME_ZONE = "UTC"
43+
self.TIME_FORMAT = "%H:%M:%S"
44+
self.TIMESTAMP_FORMAT = "YYYY-MM-DD HH:MM:SS.SSSSSSZ"
4345

4446
def _make_resource(self, started=False, ended=False):
4547
resource = super(TestLoadJob, self)._make_resource(started, ended)
@@ -48,6 +50,9 @@ def _make_resource(self, started=False, ended=False):
4850
config["dateFormat"] = self.DATE_FORMAT
4951
config["datetimeFormat"] = self.DATETIME_FORMAT
5052
config["timeZone"] = self.TIME_ZONE
53+
config["timeFormat"] = self.TIME_FORMAT
54+
config["timestampFormat"] = self.TIMESTAMP_FORMAT
55+
5156
config["destinationTable"] = {
5257
"projectId": self.PROJECT,
5358
"datasetId": self.DS_ID,
@@ -169,6 +174,14 @@ def _verifyResourceProperties(self, job, resource):
169174
self.assertEqual(job.time_zone, config["timeZone"])
170175
else:
171176
self.assertIsNone(job.time_zone)
177+
if "timeFormat" in config:
178+
self.assertEqual(job.time_format, config["timeFormat"])
179+
else:
180+
self.assertIsNone(job.time_format)
181+
if "timestampFormat" in config:
182+
self.assertEqual(job.timestamp_format, config["timestampFormat"])
183+
else:
184+
self.assertIsNone(job.timestamp_format)
172185

173186
def test_ctor(self):
174187
client = _make_client(project=self.PROJECT)
@@ -214,6 +227,8 @@ def test_ctor(self):
214227
self.assertIsNone(job.date_format)
215228
self.assertIsNone(job.datetime_format)
216229
self.assertIsNone(job.time_zone)
230+
self.assertIsNone(job.time_format)
231+
self.assertIsNone(job.timestamp_format)
217232

218233
def test_ctor_w_config(self):
219234
from google.cloud.bigquery.schema import SchemaField
@@ -612,7 +627,10 @@ def test_begin_w_alternate_client(self):
612627
"dateFormat": self.DATE_FORMAT,
613628
"datetimeFormat": self.DATETIME_FORMAT,
614629
"timeZone": self.TIME_ZONE,
630+
"timeFormat": self.TIME_FORMAT,
631+
"timestampFormat": self.TIMESTAMP_FORMAT,
615632
}
633+
616634
RESOURCE["configuration"]["load"] = LOAD_CONFIGURATION
617635
conn1 = make_connection()
618636
client1 = _make_client(project=self.PROJECT, connection=conn1)
@@ -643,6 +661,8 @@ def test_begin_w_alternate_client(self):
643661
config.date_format = self.DATE_FORMAT
644662
config.datetime_format = self.DATETIME_FORMAT
645663
config.time_zone = self.TIME_ZONE
664+
config.time_format = self.TIME_FORMAT
665+
config.timestamp_format = self.TIMESTAMP_FORMAT
646666

647667
with mock.patch(
648668
"google.cloud.bigquery.opentelemetry_tracing._get_final_span_attributes"

tests/unit/job/test_load_config.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,40 @@ def test_time_zone_setter(self):
876876
config.time_zone = time_zone
877877
self.assertEqual(config._properties["load"]["timeZone"], time_zone)
878878

879+
def test_time_format_missing(self):
880+
config = self._get_target_class()()
881+
self.assertIsNone(config.time_format)
882+
883+
def test_time_format_hit(self):
884+
time_format = "%H:%M:%S"
885+
config = self._get_target_class()()
886+
config._properties["load"]["timeFormat"] = time_format
887+
self.assertEqual(config.time_format, time_format)
888+
889+
def test_time_format_setter(self):
890+
time_format = "HH24:MI:SS"
891+
config = self._get_target_class()()
892+
config.time_format = time_format
893+
self.assertEqual(config._properties["load"]["timeFormat"], time_format)
894+
895+
def test_timestamp_format_missing(self):
896+
config = self._get_target_class()()
897+
self.assertIsNone(config.timestamp_format)
898+
899+
def test_timestamp_format_hit(self):
900+
timestamp_format = "%Y-%m-%dT%H:%M:%S.%fZ"
901+
config = self._get_target_class()()
902+
config._properties["load"]["timestampFormat"] = timestamp_format
903+
self.assertEqual(config.timestamp_format, timestamp_format)
904+
905+
def test_timestamp_format_setter(self):
906+
timestamp_format = "YYYY/MM/DD HH24:MI:SS.FF6 TZR"
907+
config = self._get_target_class()()
908+
config.timestamp_format = timestamp_format
909+
self.assertEqual(
910+
config._properties["load"]["timestampFormat"], timestamp_format
911+
)
912+
879913
def test_parquet_options_missing(self):
880914
config = self._get_target_class()()
881915
self.assertIsNone(config.parquet_options)

tests/unit/test_external_config.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ class TestExternalConfig(unittest.TestCase):
2828
DATE_FORMAT = "MM/DD/YYYY"
2929
DATETIME_FORMAT = "MM/DD/YYYY HH24:MI:SS"
3030
TIME_ZONE = "America/Los_Angeles"
31+
TIME_FORMAT = "HH24:MI:SS"
32+
TIMESTAMP_FORMAT = "MM/DD/YYYY HH24:MI:SS.FF6 TZR"
3133

3234
BASE_RESOURCE = {
3335
"sourceFormat": "",
@@ -39,6 +41,8 @@ class TestExternalConfig(unittest.TestCase):
3941
"dateFormat": DATE_FORMAT,
4042
"datetimeFormat": DATETIME_FORMAT,
4143
"timeZone": TIME_ZONE,
44+
"timeFormat": TIME_FORMAT,
45+
"timestampFormat": TIMESTAMP_FORMAT,
4246
}
4347

4448
def test_from_api_repr_base(self):
@@ -88,6 +92,9 @@ def test_to_api_repr_base(self):
8892
ec.date_format = self.DATE_FORMAT
8993
ec.datetime_format = self.DATETIME_FORMAT
9094
ec.time_zone = self.TIME_ZONE
95+
ec.time_format = self.TIME_FORMAT
96+
ec.timestamp_format = self.TIMESTAMP_FORMAT
97+
9198
exp_schema = {
9299
"fields": [{"name": "full_name", "type": "STRING", "mode": "REQUIRED"}]
93100
}
@@ -104,6 +111,8 @@ def test_to_api_repr_base(self):
104111
"dateFormat": self.DATE_FORMAT,
105112
"datetimeFormat": self.DATETIME_FORMAT,
106113
"timeZone": self.TIME_ZONE,
114+
"timeFormat": self.TIME_FORMAT,
115+
"timestampFormat": self.TIMESTAMP_FORMAT,
107116
}
108117
self.assertEqual(got_resource, exp_resource)
109118

@@ -142,6 +151,8 @@ def _verify_base(self, ec):
142151
self.assertEqual(ec.date_format, self.DATE_FORMAT)
143152
self.assertEqual(ec.datetime_format, self.DATETIME_FORMAT)
144153
self.assertEqual(ec.time_zone, self.TIME_ZONE)
154+
self.assertEqual(ec.time_format, self.TIME_FORMAT)
155+
self.assertEqual(ec.timestamp_format, self.TIMESTAMP_FORMAT)
145156

146157
def test_to_api_repr_source_format(self):
147158
ec = external_config.ExternalConfig("CSV")

0 commit comments

Comments
 (0)