# Buggy code
def create_time_series_list(self, v_data, option_resource_type, metric_prefix):
    """Create the TimeSeries object based on the view data"""
    time_series_list = []
    aggregation_type = v_data.view.aggregation.aggregation_type
    tag_agg = v_data.tag_value_aggregation_data_map
    for tag_value, agg in tag_agg.items():
        series = monitoring_v3.types.TimeSeries()
        series.metric.type = namespaced_view_name(v_data.view.name, metric_prefix)
        set_metric_labels(series, v_data.view, tag_value)
        set_monitored_resource(series, option_resource_type)

        point = series.points.add()
        if aggregation_type is aggregation.Type.DISTRIBUTION:
            dist_value = point.value.distribution_value
            dist_value.count = agg.count_data
            dist_value.mean = agg.mean_data

            sum_of_sqd = agg.sum_of_sqd_deviations
            dist_value.sum_of_squared_deviation = sum_of_sqd

            # Uncomment this when stackdriver supports Range
            # point.value.distribution_value.range.min = agg_data.min
            # point.value.distribution_value.range.max = agg_data.max
            bounds = dist_value.bucket_options.explicit_buckets.bounds
            buckets = dist_value.bucket_counts

            # Stackdriver expects a first bucket for samples in (-inf, 0),
            # but we record positive samples only, and our first bucket is
            # [0, first_bound).
            bounds.extend([0])
            buckets.extend([0])
            bounds.extend(list(map(float, agg.bounds)))
            buckets.extend(list(map(int, agg.counts_per_bucket)))
        elif aggregation_type is aggregation.Type.COUNT:
            point.value.int64_value = agg.count_data
        elif aggregation_type is aggregation.Type.SUM:
            if isinstance(v_data.view.measure, measure.MeasureInt):
                # TODO: Add implementation of sum aggregation that does not
                # store it's data as a float.
                point.value.int64_value = int(agg.sum_data)
            if isinstance(v_data.view.measure, measure.MeasureFloat):
                point.value.double_value = float(agg.sum_data)
        elif aggregation_type is aggregation.Type.LASTVALUE:
            if isinstance(v_data.view.measure, measure.MeasureInt):
                point.value.int64_value = int(agg.value)
            if isinstance(v_data.view.measure, measure.MeasureFloat):
                point.value.double_value = float(agg.value)
        else:
            raise TypeError(
                "Unsupported aggregation type: %s" % type(v_data.view.aggregation)
            )

        start = datetime.strptime(v_data.start_time, EPOCH_PATTERN)
        end = datetime.strptime(v_data.end_time, EPOCH_PATTERN)

        timestamp_start = (start - EPOCH_DATETIME).total_seconds()
        timestamp_end = (end - EPOCH_DATETIME).total_seconds()

        point.interval.end_time.seconds = int(timestamp_end)

        secs = point.interval.end_time.seconds
        point.interval.end_time.nanos = int((timestamp_end - secs) * 10**9)

        if aggregation_type is not aggregation.Type.LASTVALUE:
            if timestamp_start == timestamp_end:
                # avoiding start_time and end_time to be equal
                timestamp_start = timestamp_start - 1

        start_time = point.interval.start_time
        start_time.seconds = int(timestamp_start)
        start_secs = start_time.seconds
        start_time.nanos = int((timestamp_start - start_secs) * 1e9)

        time_series_list.append(series)

    return time_series_list


# Traceback (google.api_core.exceptions.InvalidArgument)
Traceback (most recent call last):
File "/Users/rsa/dev/metrics/.venv/lib/python3.7/site-packages/google/api_core/grpc_helpers.py", line 57, in error_remapped_callable
return callable_(*args, **kwargs)
File "/Users/rsa/dev/metrics/.venv/lib/python3.7/site-packages/grpc/_channel.py", line 549, in __call__
return _end_unary_response_blocking(state, call, False, None)
File "/Users/rsa/dev/metrics/.venv/lib/python3.7/site-packages/grpc/_channel.py", line 466, in _end_unary_response_blocking
raise _Rendezvous(state, None, None, deadline)
grpc._channel._Rendezvous: <_Rendezvous of RPC that terminated with:
status = StatusCode.INVALID_ARGUMENT
details = "Field timeSeries[0].points[0].interval.start had an invalid value of "2019-03-27T14:35:55.669958-07:00": The start time must be equal to the end time (2019-03-27T14:37:33.72174-07:00) for the gauge metric 'custom.googleapis.com/opencensus/rsatesting_agglast/request_count'."
debug_error_string = "{"created":"@1553722655.800461000","description":"Error received from peer","file":"src/core/lib/surface/call.cc","file_line":1039,"grpc_message":"Field timeSeries[0].points[0].interval.start had an invalid value of "2019-03-27T14:35:55.669958-07:00": The start time must be equal to the end time (2019-03-27T14:37:33.72174-07:00) for the gauge metric 'custom.googleapis.com/opencensus/rsatesting_agglast/request_count'.","grpc_status":3}"


The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File "bunk.py", line 5, in <module>
bunk.record(1, {'http_verb': 'GET', 'response_code': '200'})
File "/Users/rsa/dev/metrics/metrics.py", line 114, in record
mm.record(tagmap)
File "/Users/rsa/dev/metrics/.venv/lib/python3.7/site-packages/opencensus/stats/measurement_map.py", line 118, in record
attachments=self.attachments
File "/Users/rsa/dev/metrics/.venv/lib/python3.7/site-packages/opencensus/stats/measure_to_view_map.py", line 123, in record
self.export(view_datas)
File "/Users/rsa/dev/metrics/.venv/lib/python3.7/site-packages/opencensus/stats/measure_to_view_map.py", line 134, in export
e.export(view_datas_copy)
File "/Users/rsa/dev/metrics/.venv/lib/python3.7/site-packages/opencensus/ext/stackdriver/stats_exporter/__init__.py", line 158, in export
self.transport.export(view_data)
File "/Users/rsa/dev/metrics/.venv/lib/python3.7/site-packages/opencensus/common/transports/sync.py", line 23, in export
self.exporter.emit(datas)
File "/Users/rsa/dev/metrics/.venv/lib/python3.7/site-packages/opencensus/ext/stackdriver/stats_exporter/__init__.py", line 153, in emit
self.handle_upload(view_data)
File "/Users/rsa/dev/metrics/.venv/lib/python3.7/site-packages/opencensus/ext/stackdriver/stats_exporter/__init__.py", line 165, in handle_upload
self.upload_stats(view_data)
File "/Users/rsa/dev/metrics/.venv/lib/python3.7/site-packages/opencensus/ext/stackdriver/stats_exporter/__init__.py", line 177, in upload_stats
time_series_batch)
File "/Users/rsa/dev/metrics/.venv/lib/python3.7/site-packages/google/cloud/monitoring_v3/gapic/metric_service_client.py", line 897, in create_time_series
request, retry=retry, timeout=timeout, metadata=metadata
File "/Users/rsa/dev/metrics/.venv/lib/python3.7/site-packages/google/api_core/gapic_v1/method.py", line 143, in __call__
return wrapped_func(*args, **kwargs)
File "/Users/rsa/dev/metrics/.venv/lib/python3.7/site-packages/google/api_core/retry.py", line 270, in retry_wrapped_func
on_error=on_error,
File "/Users/rsa/dev/metrics/.venv/lib/python3.7/site-packages/google/api_core/retry.py", line 179, in retry_target
return target()
File "/Users/rsa/dev/metrics/.venv/lib/python3.7/site-packages/google/api_core/timeout.py", line 214, in func_with_timeout
return func(*args, **kwargs)
File "/Users/rsa/dev/metrics/.venv/lib/python3.7/site-packages/google/api_core/grpc_helpers.py", line 59, in error_remapped_callable
six.raise_from(exceptions.from_grpc_error(exc), exc)
File "<string>", line 3, in raise_from
google.api_core.exceptions.InvalidArgument: 400 Field timeSeries[0].points[0].interval.start had an invalid value of "2019-03-27T14:35:55.669958-07:00": The start time must be equal to the end time (2019-03-27T14:37:33.72174-07:00) for the gauge metric 'custom.googleapis.com/opencensus/rsatesting_agglast/request_count'.

# Fixed code
def create_time_series_list(self, v_data, option_resource_type, metric_prefix):
    """Create the TimeSeries object based on the view data"""
    time_series_list = []
    aggregation_type = v_data.view.aggregation.aggregation_type
    tag_agg = v_data.tag_value_aggregation_data_map
    for tag_value, agg in tag_agg.items():
        series = monitoring_v3.types.TimeSeries()
        series.metric.type = namespaced_view_name(v_data.view.name, metric_prefix)
        set_metric_labels(series, v_data.view, tag_value)
        set_monitored_resource(series, option_resource_type)

        point = series.points.add()
        if aggregation_type is aggregation.Type.DISTRIBUTION:
            dist_value = point.value.distribution_value
            dist_value.count = agg.count_data
            dist_value.mean = agg.mean_data

            sum_of_sqd = agg.sum_of_sqd_deviations
            dist_value.sum_of_squared_deviation = sum_of_sqd

            # Uncomment this when stackdriver supports Range
            # point.value.distribution_value.range.min = agg_data.min
            # point.value.distribution_value.range.max = agg_data.max
            bounds = dist_value.bucket_options.explicit_buckets.bounds
            buckets = dist_value.bucket_counts

            # Stackdriver expects a first bucket for samples in (-inf, 0),
            # but we record positive samples only, and our first bucket is
            # [0, first_bound).
            bounds.extend([0])
            buckets.extend([0])
            bounds.extend(list(map(float, agg.bounds)))
            buckets.extend(list(map(int, agg.counts_per_bucket)))
        elif aggregation_type is aggregation.Type.COUNT:
            point.value.int64_value = agg.count_data
        elif aggregation_type is aggregation.Type.SUM:
            if isinstance(v_data.view.measure, measure.MeasureInt):
                # TODO: Add implementation of sum aggregation that does not
                # store it's data as a float.
                point.value.int64_value = int(agg.sum_data)
            if isinstance(v_data.view.measure, measure.MeasureFloat):
                point.value.double_value = float(agg.sum_data)
        elif aggregation_type is aggregation.Type.LASTVALUE:
            if isinstance(v_data.view.measure, measure.MeasureInt):
                point.value.int64_value = int(agg.value)
            if isinstance(v_data.view.measure, measure.MeasureFloat):
                point.value.double_value = float(agg.value)
        else:
            raise TypeError(
                "Unsupported aggregation type: %s" % type(v_data.view.aggregation)
            )

        start = datetime.strptime(v_data.start_time, EPOCH_PATTERN)
        end = datetime.strptime(v_data.end_time, EPOCH_PATTERN)

        timestamp_start = (start - EPOCH_DATETIME).total_seconds()
        timestamp_end = (end - EPOCH_DATETIME).total_seconds()

        point.interval.end_time.seconds = int(timestamp_end)

        secs = point.interval.end_time.seconds
        point.interval.end_time.nanos = int((timestamp_end - secs) * 10**9)

        if aggregation_type is not aggregation.Type.LASTVALUE:
            if timestamp_start == timestamp_end:
                # avoiding start_time and end_time to be equal
                timestamp_start = timestamp_start - 1
        else:
            # For LastValue (Gauge), start and end time must be the same.
            timestamp_start = timestamp_end

        start_time = point.interval.start_time
        start_time.seconds = int(timestamp_start)
        start_secs = start_time.seconds
        start_time.nanos = int((timestamp_start - start_secs) * 1e9)

        time_series_list.append(series)

    return time_series_list