Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions google/cloud/bigquery/external_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,20 @@ def schema(self, value):
prop = {"fields": [field.to_api_repr() for field in value]}
self._properties["schema"] = prop

@property
def date_format(self) -> Optional[str]:
"""Optional[str]: Format used to parse DATE values. Supports C-style and SQL-style values.

See:
https://cloud.google.com/bigquery/docs/reference/rest/v2/tables#ExternalDataConfiguration.FIELDS.date_format
"""
result = self._properties.get("dateFormat")
return typing.cast(str, result)

@date_format.setter
def date_format(self, value: Optional[str]):
self._properties["dateFormat"] = value

@property
def time_zone(self) -> Optional[str]:
"""Optional[str]: Time zone used when parsing timestamp values that do not
Expand Down
20 changes: 20 additions & 0 deletions google/cloud/bigquery/job/load.py
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,19 @@ def source_format(self):
def source_format(self, value):
self._set_sub_prop("sourceFormat", value)

@property
def date_format(self) -> Optional[str]:
"""Optional[str]: Date format used for parsing DATE values.

See:
https://cloud.google.com/bigquery/docs/reference/rest/v2/Job#JobConfigurationLoad.FIELDS.date_format
"""
return self._get_sub_prop("dateFormat")

@date_format.setter
def date_format(self, value: Optional[str]):
self._set_sub_prop("dateFormat", value)

@property
def time_zone(self) -> Optional[str]:
"""Optional[str]: Default time zone that will apply when parsing timestamp
Expand Down Expand Up @@ -903,6 +916,13 @@ def clustering_fields(self):
"""
return self.configuration.clustering_fields

@property
def date_format(self):
"""See
:attr:`google.cloud.bigquery.job.LoadJobConfig.date_format`.
"""
return self.configuration.date_format

@property
def time_zone(self):
"""See
Expand Down
14 changes: 9 additions & 5 deletions tests/unit/job/test_load.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@ def _setUpConstants(self):
self.OUTPUT_BYTES = 23456
self.OUTPUT_ROWS = 345
self.REFERENCE_FILE_SCHEMA_URI = "gs://path/to/reference"

self.DATE_FORMAT = "%Y-%m-%d"
self.TIME_ZONE = "UTC"

def _make_resource(self, started=False, ended=False):
resource = super(TestLoadJob, self)._make_resource(started, ended)
config = resource["configuration"]["load"]
config["sourceUris"] = [self.SOURCE1]

config["dateFormat"] = self.DATE_FORMAT
config["timeZone"] = self.TIME_ZONE
config["destinationTable"] = {
"projectId": self.PROJECT,
Expand Down Expand Up @@ -147,7 +147,6 @@ def _verifyResourceProperties(self, job, resource):
)
else:
self.assertIsNone(job.reference_file_schema_uri)

if "destinationEncryptionConfiguration" in config:
self.assertIsNotNone(job.destination_encryption_configuration)
self.assertEqual(
Expand All @@ -156,6 +155,10 @@ def _verifyResourceProperties(self, job, resource):
)
else:
self.assertIsNone(job.destination_encryption_configuration)
if "dateFormat" in config:
self.assertEqual(job.date_format, config["dateFormat"])
else:
self.assertIsNone(job.date_format)
if "timeZone" in config:
self.assertEqual(job.time_zone, config["timeZone"])
else:
Expand Down Expand Up @@ -202,7 +205,7 @@ def test_ctor(self):
self.assertIsNone(job.clustering_fields)
self.assertIsNone(job.schema_update_options)
self.assertIsNone(job.reference_file_schema_uri)

self.assertIsNone(job.date_format)
self.assertIsNone(job.time_zone)

def test_ctor_w_config(self):
Expand Down Expand Up @@ -599,6 +602,7 @@ def test_begin_w_alternate_client(self):
]
},
"schemaUpdateOptions": [SchemaUpdateOption.ALLOW_FIELD_ADDITION],
"dateFormat": self.DATE_FORMAT,
"timeZone": self.TIME_ZONE,
}
RESOURCE["configuration"]["load"] = LOAD_CONFIGURATION
Expand Down Expand Up @@ -628,7 +632,7 @@ def test_begin_w_alternate_client(self):
config.write_disposition = WriteDisposition.WRITE_TRUNCATE
config.schema_update_options = [SchemaUpdateOption.ALLOW_FIELD_ADDITION]
config.reference_file_schema_uri = "gs://path/to/reference"

config.date_format = self.DATE_FORMAT
config.time_zone = self.TIME_ZONE

with mock.patch(
Expand Down
19 changes: 19 additions & 0 deletions tests/unit/job/test_load_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,22 @@ def test_write_disposition_setter(self):
config._properties["load"]["writeDisposition"], write_disposition
)

def test_date_format_missing(self):
config = self._get_target_class()()
self.assertIsNone(config.date_format)

def test_date_format_hit(self):
date_format = "%Y-%m-%d"
config = self._get_target_class()()
config._properties["load"]["dateFormat"] = date_format
self.assertEqual(config.date_format, date_format)

def test_date_format_setter(self):
date_format = "YYYY/MM/DD"
config = self._get_target_class()()
config.date_format = date_format
self.assertEqual(config._properties["load"]["dateFormat"], date_format)

def test_time_zone_missing(self):
config = self._get_target_class()()
self.assertIsNone(config.time_zone)
Expand Down Expand Up @@ -942,6 +958,7 @@ def test_column_name_character_map_none(self):
},
"useAvroLogicalTypes": True,
"writeDisposition": "WRITE_TRUNCATE",
"dateFormat": "%Y-%m-%d",
"timeZone": "America/New_York",
"parquetOptions": {"enableListInference": True},
"columnNameCharacterMap": "V2",
Expand Down Expand Up @@ -983,6 +1000,7 @@ def test_from_api_repr(self):
)
self.assertTrue(config.use_avro_logical_types)
self.assertEqual(config.write_disposition, WriteDisposition.WRITE_TRUNCATE)
self.assertEqual(config.date_format, "%Y-%m-%d")
self.assertEqual(config.time_zone, "America/New_York")
self.assertTrue(config.parquet_options.enable_list_inference)
self.assertEqual(config.column_name_character_map, ColumnNameCharacterMap.V2)
Expand Down Expand Up @@ -1017,6 +1035,7 @@ def test_to_api_repr(self):
)
config.use_avro_logical_types = True
config.write_disposition = WriteDisposition.WRITE_TRUNCATE
config.date_format = "%Y-%m-%d"
config.time_zone = "America/New_York"
parquet_options = ParquetOptions()
parquet_options.enable_list_inference = True
Expand Down
7 changes: 5 additions & 2 deletions tests/unit/test_external_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

class TestExternalConfig(unittest.TestCase):
SOURCE_URIS = ["gs://foo", "gs://bar"]

DATE_FORMAT = "MM/DD/YYYY"
TIME_ZONE = "America/Los_Angeles"

BASE_RESOURCE = {
Expand All @@ -35,6 +35,7 @@ class TestExternalConfig(unittest.TestCase):
"autodetect": True,
"ignoreUnknownValues": False,
"compression": "compression",
"dateFormat": DATE_FORMAT,
"timeZone": TIME_ZONE,
}

Expand Down Expand Up @@ -82,6 +83,7 @@ def test_to_api_repr_base(self):
ec.connection_id = "path/to/connection"
ec.schema = [schema.SchemaField("full_name", "STRING", mode="REQUIRED")]

ec.date_format = self.DATE_FORMAT
ec.time_zone = self.TIME_ZONE
exp_schema = {
"fields": [{"name": "full_name", "type": "STRING", "mode": "REQUIRED"}]
Expand All @@ -96,6 +98,7 @@ def test_to_api_repr_base(self):
"compression": "compression",
"connectionId": "path/to/connection",
"schema": exp_schema,
"dateFormat": self.DATE_FORMAT,
"timeZone": self.TIME_ZONE,
}
self.assertEqual(got_resource, exp_resource)
Expand Down Expand Up @@ -132,7 +135,7 @@ def _verify_base(self, ec):
self.assertEqual(ec.ignore_unknown_values, False)
self.assertEqual(ec.max_bad_records, 17)
self.assertEqual(ec.source_uris, self.SOURCE_URIS)

self.assertEqual(ec.date_format, self.DATE_FORMAT)
self.assertEqual(ec.time_zone, self.TIME_ZONE)

def test_to_api_repr_source_format(self):
Expand Down