Skip to content

Commit

Permalink
Add datetime::is_leap_year (#8711)
Browse files Browse the repository at this point in the history
Part 1 of #8677 

This PR adds `datetime::is_leap_year`. The function returns `true` for datetime column rows with leap year; `false` for rows with non leap years, and `null` for null rows.

Authors:
  - Michael Wang (https://github.com/isVoid)

Approvers:
  - Mark Harris (https://github.com/harrism)
  - Karthikeyan (https://github.com/karthikeyann)

URL: #8711
  • Loading branch information
isVoid authored Jul 13, 2021
1 parent 7521c3f commit b0d86d2
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 1 deletion.
17 changes: 17 additions & 0 deletions cpp/include/cudf/datetime.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,23 @@ std::unique_ptr<cudf::column> add_calendrical_months(
cudf::column_view const& timestamps,
cudf::column_view const& months,
rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource());

/**
* @brief Check if the year of the given date is a leap year
*
* `output[i] == true` if year of `column[i]` is a leap year
* `output[i] == false` if year of `column[i]` is not a leap year
* `output[i] is null` if `column[i]` is null
*
* @param[in] cudf::column_view of the input datetime values
*
* @returns cudf::column of datatype BOOL8 truth value of the corresponding date
* @throw cudf::logic_error if input column datatype is not a TIMESTAMP
*/
std::unique_ptr<cudf::column> is_leap_year(
cudf::column_view const& column,
rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource());

/** @} */ // end of group
} // namespace datetime
} // namespace cudf
11 changes: 11 additions & 0 deletions cpp/include/cudf/detail/datetime.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,17 @@ std::unique_ptr<cudf::column> add_calendrical_months(
cudf::column_view const& months,
rmm::cuda_stream_view stream = rmm::cuda_stream_default,
rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource());

/**
* @copydoc cudf::is_leap_year(cudf::column_view const&, rmm::mr::device_memory_resource *)
*
* @param stream CUDA stream used for device memory operations and kernel launches.
*/
std::unique_ptr<cudf::column> is_leap_year(
cudf::column_view const& column,
rmm::cuda_stream_view stream = rmm::cuda_stream_default,
rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource());

} // namespace detail
} // namespace datetime
} // namespace cudf
28 changes: 27 additions & 1 deletion cpp/src/datetime/datetime_ops.cu
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
#include <cudf/column/column_factories.hpp>
#include <cudf/column/column_view.hpp>
#include <cudf/datetime.hpp>
#include <cudf/detail/datetime.hpp>
#include <cudf/detail/null_mask.hpp>
#include <cudf/detail/nvtx/ranges.hpp>
#include <cudf/null_mask.hpp>
#include <cudf/table/table_view.hpp>
#include <cudf/types.hpp>
#include <cudf/utilities/traits.hpp>
Expand Down Expand Up @@ -127,6 +127,17 @@ struct extract_day_num_of_year {
}
};

struct is_leap_year_op {
template <typename Timestamp>
CUDA_DEVICE_CALLABLE bool operator()(Timestamp const ts) const
{
using namespace cuda::std::chrono;
auto const days_since_epoch = floor<days>(ts);
auto const date = year_month_day(days_since_epoch);
return date.year().is_leap();
}
};

// Apply the functor for every element/row in the input column to create the output column
template <typename TransformFunctor, typename OutputColT>
struct launch_functor {
Expand Down Expand Up @@ -357,6 +368,14 @@ std::unique_ptr<column> day_of_year(column_view const& column,
return detail::apply_datetime_op<detail::extract_day_num_of_year, cudf::type_id::INT16>(
column, stream, mr);
}

std::unique_ptr<column> is_leap_year(column_view const& column,
rmm::cuda_stream_view stream,
rmm::mr::device_memory_resource* mr)
{
return apply_datetime_op<is_leap_year_op, type_id::BOOL8>(column, stream, mr);
}

} // namespace detail

std::unique_ptr<column> extract_year(column_view const& column, rmm::mr::device_memory_resource* mr)
Expand Down Expand Up @@ -426,5 +445,12 @@ std::unique_ptr<cudf::column> add_calendrical_months(cudf::column_view const& ti
return detail::add_calendrical_months(
timestamp_column, months_column, rmm::cuda_stream_default, mr);
}

std::unique_ptr<column> is_leap_year(column_view const& column, rmm::mr::device_memory_resource* mr)
{
CUDF_FUNC_RANGE();
return detail::is_leap_year(column, rmm::cuda_stream_default, mr);
}

} // namespace datetime
} // namespace cudf
35 changes: 35 additions & 0 deletions cpp/tests/datetime/datetime_ops_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
#include <cudf/types.hpp>
#include <cudf/wrappers/timestamps.hpp>

#define XXX false // stub for null values

template <typename T>
struct NonTimestampTest : public cudf::test::BaseFixture {
cudf::data_type type() { return cudf::data_type{cudf::type_to_id<T>()}; }
Expand Down Expand Up @@ -532,4 +534,37 @@ TEST_F(BasicDatetimeOpsTest, TestAddMonthsWithSecondsAndNullValues)
true);
}

TEST_F(BasicDatetimeOpsTest, TestIsLeapYear)
{
using namespace cudf::test;
using namespace cudf::datetime;
using namespace cuda::std::chrono;

// Time in seconds since epoch
// Dates converted using epochconverter.com
auto timestamps_s =
cudf::test::fixed_width_column_wrapper<cudf::timestamp_s, cudf::timestamp_s::rep>{
{
1594332839L, // 2020-07-09 10:13:59 GMT - leap year
0L, // null
915148800L, // 1999-01-01 00:00:00 GMT - non leap year
-11663029161L, // 1600-5-31 05:40:39 GMT - leap year
707904541L, // 1992-06-07 08:09:01 GMT - leap year
2181048447L, // 1900-11-20 09:12:33 GMT - non leap year
0L, // UNIX EPOCH 1970-01-01 00:00:00 GMT - non leap year
-12212553600L, // First full year of Gregorian Calandar 1583-01-01 00:00:00 - non-leap-year
0L, // null
13591632822L, // 2400-09-13 13:33:42 GMT - leap year
4539564243L, // 2113-11-08 06:04:03 GMT - non leap year
0L // null
},
{true, false, true, true, true, true, true, true, false, true, true, false}};

CUDF_TEST_EXPECT_COLUMNS_EQUAL(
*is_leap_year(timestamps_s),
cudf::test::fixed_width_column_wrapper<bool>{
{true, XXX, false, true, true, false, false, false, XXX, true, false, XXX},
{true, false, true, true, true, true, true, true, false, true, true, false}});
}

CUDF_TEST_PROGRAM_MAIN()

0 comments on commit b0d86d2

Please sign in to comment.