@@ -582,11 +582,13 @@ __device__ static cudf::timestamp_us convert_timestamp_between_timezones(
582582 cudf::timestamp_us ts,
583583 orc_base_offset_info writer_2015_year_base_offset,
584584 tz_side_info const & writer,
585- tz_side_info const & reader)
585+ tz_side_info const & reader,
586+ bool writer_reader_rules_differ)
586587{
587588 int64_t const decoded_us = static_cast <int64_t >(
588589 cuda::std::chrono::duration_cast<cudf::duration_us>(ts.time_since_epoch ()).count ());
589590 int64_t const adjusted_us = apply_orc_base_offset (decoded_us, writer_2015_year_base_offset);
591+ if (!writer_reader_rules_differ) { return cudf::timestamp_us{cudf::duration_us{adjusted_us}}; }
590592
591593 // Floor-divide to get epoch millis (handles negative timestamps correctly)
592594 int64_t const epoch_millis =
@@ -665,7 +667,8 @@ CUDF_KERNEL void __launch_bounds__(CONVERT_TZ_BLOCK_SIZE)
665667 cudf::size_type input_offset,
666668 orc_base_offset_info writer_2015_year_base_offset,
667669 orc_tz_side_kernel_args writer_args,
668- orc_tz_side_kernel_args reader_args)
670+ orc_tz_side_kernel_args reader_args,
671+ bool writer_reader_rules_differ)
669672{
670673 // Shared memory layout: writer transitions, writer offsets, reader transitions, reader offsets
671674 extern __shared__ char smem[];
@@ -706,8 +709,8 @@ CUDF_KERNEL void __launch_bounds__(CONVERT_TZ_BLOCK_SIZE)
706709 reader_args.raw_offset ,
707710 reader_args.dst ,
708711 reader_args.is_fixed };
709- output[idx] =
710- convert_timestamp_between_timezones ( input[idx], writer_2015_year_base_offset, writer, reader);
712+ output[idx] = convert_timestamp_between_timezones (
713+ input[idx], writer_2015_year_base_offset, writer, reader, writer_reader_rules_differ );
711714 }
712715}
713716
@@ -716,7 +719,8 @@ std::unique_ptr<column> convert_timezones(cudf::column_view const& input,
716719 spark_rapids_jni::orc_tz_side writer,
717720 spark_rapids_jni::orc_tz_side reader,
718721 rmm::cuda_stream_view stream,
719- rmm::device_async_resource_ref mr)
722+ rmm::device_async_resource_ref mr,
723+ bool writer_reader_rules_differ)
720724{
721725 SRJ_FUNC_RANGE ();
722726
@@ -789,12 +793,116 @@ std::unique_ptr<column> convert_timezones(cudf::column_view const& input,
789793 input.offset (),
790794 writer_2015_year_base_offset,
791795 writer_args,
792- reader_args);
796+ reader_args,
797+ writer_reader_rules_differ);
793798 CUDF_CHECK_CUDA (stream.value ());
794799
795800 return results;
796801}
797802
803+ __device__ static int64_t wrapping_subtract (int64_t lhs, int64_t rhs)
804+ {
805+ return static_cast <int64_t >(static_cast <uint64_t >(lhs) - static_cast <uint64_t >(rhs));
806+ }
807+
808+ template <typename T>
809+ __device__ static T convert_orc_from_utc_value (T value, tz_side_info const & reader);
810+
811+ template <>
812+ __device__ cudf::timestamp_us convert_orc_from_utc_value (cudf::timestamp_us value,
813+ tz_side_info const & reader)
814+ {
815+ auto const value_us = value.time_since_epoch ().count ();
816+ auto const value_ms = spark_rapids_jni::integer_utils::floor_div (value_us, MICROS_PER_MILLI );
817+ auto const offset_lookup_ms = wrapping_subtract (value_ms, reader.raw_offset );
818+ auto const offset_ms = get_transition_index (offset_lookup_ms, reader);
819+ auto const result_us =
820+ wrapping_subtract (value_us, static_cast <int64_t >(offset_ms) * MICROS_PER_MILLI );
821+ return cudf::timestamp_us{cudf::duration_us{result_us}};
822+ }
823+
824+ template <typename T>
825+ CUDF_KERNEL void __launch_bounds__ (CONVERT_TZ_BLOCK_SIZE )
826+ convert_orc_from_utc_kernel(T const * __restrict__ input,
827+ cudf::bitmask_type const * __restrict__ null_mask,
828+ T* __restrict__ output,
829+ cudf::size_type num_rows,
830+ cudf::size_type input_offset,
831+ orc_tz_side_kernel_args reader_args)
832+ {
833+ extern __shared__ char smem[];
834+ char * ptr = smem;
835+ int64_t const *rt_begin, *rt_end;
836+ int32_t const * ro_begin;
837+ stage_side_transitions (reader_args.trans ,
838+ reader_args.offsets ,
839+ reader_args.trans_count ,
840+ ptr,
841+ rt_begin,
842+ rt_end,
843+ ro_begin);
844+ __syncthreads ();
845+
846+ cudf::size_type idx = blockIdx .x * blockDim .x + threadIdx .x ;
847+ if (idx < num_rows) {
848+ if (null_mask && !cudf::bit_is_set (null_mask, idx + input_offset)) { return ; }
849+ tz_side_info const reader{rt_begin,
850+ rt_end,
851+ ro_begin,
852+ reader_args.initial_offset ,
853+ reader_args.raw_offset ,
854+ reader_args.dst };
855+ output[idx] = convert_orc_from_utc_value (input[idx], reader);
856+ }
857+ }
858+
859+ template <typename T>
860+ std::unique_ptr<column> convert_orc_from_utc_typed (cudf::column_view const & input,
861+ spark_rapids_jni::orc_tz_side reader,
862+ rmm::cuda_stream_view stream,
863+ rmm::device_async_resource_ref mr)
864+ {
865+ auto results = cudf::make_fixed_width_column (input.type (),
866+ input.size (),
867+ cudf::copy_bitmask (input, stream, mr),
868+ input.null_count (),
869+ stream,
870+ mr);
871+ if (input.size () == 0 ) { return results; }
872+
873+ int64_t const * reader_trans_ptr =
874+ reader.tz_info_table ? reader.tz_info_table ->column (0 ).begin <int64_t >() : nullptr ;
875+ int32_t const * reader_offsets_ptr =
876+ reader.tz_info_table ? reader.tz_info_table ->column (1 ).begin <int32_t >() : nullptr ;
877+ int32_t reader_trans_count = reader.tz_info_table ? reader.tz_info_table ->column (0 ).size () : 0 ;
878+ size_t smem_bytes = 0 ;
879+ if (reader_trans_count > 0 && reader_trans_count <= MAX_SMEM_TRANSITIONS ) {
880+ smem_bytes = reader_trans_count * (sizeof (int64_t ) + sizeof (int32_t ));
881+ }
882+
883+ auto const reader_args = orc_tz_side_kernel_args{reader_trans_ptr,
884+ reader_offsets_ptr,
885+ reader_trans_count,
886+ reader.initial_offset ,
887+ reader.raw_offset ,
888+ reader.dst };
889+ int32_t num_blocks = cudf::util::div_rounding_up_safe (input.size (), CONVERT_TZ_BLOCK_SIZE );
890+ auto const launch_config = cuda::make_config (cuda::grid_dims (num_blocks),
891+ cuda::block_dims<CONVERT_TZ_BLOCK_SIZE >(),
892+ cuda::dynamic_shared_memory<char []>(smem_bytes));
893+ cuda::launch (stream.value (),
894+ launch_config,
895+ convert_orc_from_utc_kernel<T>,
896+ input.begin <T>(),
897+ input.null_mask (),
898+ results->mutable_view ().begin <T>(),
899+ input.size (),
900+ input.offset (),
901+ reader_args);
902+ CUDF_CHECK_CUDA (stream.value ());
903+ return results;
904+ }
905+
798906// =================== ORC timezones end ===================
799907
800908} // namespace
@@ -875,9 +983,22 @@ std::unique_ptr<cudf::column> convert_orc_writer_reader_timezones(
875983 orc_tz_side writer,
876984 orc_tz_side reader,
877985 rmm::cuda_stream_view stream,
878- rmm::device_async_resource_ref mr)
986+ rmm::device_async_resource_ref mr,
987+ bool writer_reader_rules_differ)
988+ {
989+ return convert_timezones (
990+ input, writer_2015_year_base_offset_us, writer, reader, stream, mr, writer_reader_rules_differ);
991+ }
992+
993+ std::unique_ptr<cudf::column> convert_orc_from_utc (cudf::column_view const & input,
994+ orc_tz_side reader,
995+ rmm::cuda_stream_view stream,
996+ rmm::device_async_resource_ref mr)
879997{
880- return convert_timezones (input, writer_2015_year_base_offset_us, writer, reader, stream, mr);
998+ validate_timezone_table (reader.tz_info_table );
999+ CUDF_EXPECTS (input.type ().id () == cudf::type_id::TIMESTAMP_MICROSECONDS ,
1000+ " ORC convertFromUtc input must be TIMESTAMP_MICROSECONDS" );
1001+ return convert_orc_from_utc_typed<cudf::timestamp_us>(input, reader, stream, mr);
8811002}
8821003
8831004std::unique_ptr<cudf::column> convert_orc_writer_reader_timezones (
0 commit comments