DatetimeIndexOpsMixin#

class pandas::DatetimeIndexOpsMixin#

Mixin class providing shared functionality.

Example#

#include <pandas/pandas.h>
using namespace pandas;

// Create DatetimeIndexOpsMixin
DatetimeIndexOpsMixin<int64_t> idx({1, 2, 3}, "my_index");
size_t len = idx.size();

Constructors#

Signature

Location

Example

explicit DatetimeIndexOpsMixin(const ArrayType& array, const std::optional<std::string>& name = std::nullopt, bool copy = false)

pd_datetime_index_ops_mixin.h:140

explicit DatetimeIndexOpsMixin(ArrayType&& array, const std::optional<std::string>& name = std::nullopt)

pd_datetime_index_ops_mixin.h:150

DatetimeIndexOpsMixin(const DatetimeIndexOpsMixin& other)

pd_datetime_index_ops_mixin.h:159

DatetimeIndexOpsMixin(DatetimeIndexOpsMixin&& other) noexcept

pd_datetime_index_ops_mixin.h:167

Indexing / Selection#

Signature

Return Type

Location

Example

std::optional<std::string> get_freq() const override

std::optional<std::string>

pd_datetime_index_ops_mixin.h:216

View

Data Manipulation#

Signature

Return Type

Location

Example

DatetimeIndexOpsMixin rename(const std::optional<std::string>& new_name) const

DatetimeIndexOpsMixin

pd_datetime_index_ops_mixin.h:1474

View

Statistics#

Signature

Return Type

Location

Example

std::optional<value_type> mean(std::optional<int> axis = std::nullopt, bool skipna = true) const

std::optional<value_type>

pd_datetime_index_ops_mixin.h:1348

View

IntegerArray<numpy::int32> minute() const

IntegerArray<numpy::int32>

pd_datetime_index_ops_mixin.h:692

View

Time Series#

Signature

Return Type

Location

Example

DatetimeIndexOpsMixin asfreq(const std::string& freq, const std::string& how = "start") const

DatetimeIndexOpsMixin

pd_datetime_index_ops_mixin.h:1273

View

DatetimeIndexOpsMixin shift(int64_t periods, const std::optional<std::string>& freq = std::nullopt) const

DatetimeIndexOpsMixin

pd_datetime_index_ops_mixin.h:423

View

ArrayType shifted(result)

ArrayType

pd_datetime_index_ops_mixin.h:455

ArrayType shifted(result)

ArrayType

pd_datetime_index_ops_mixin.h:477

PeriodArray to_period(const std::string& freq) const

PeriodArray

pd_datetime_index_ops_mixin.h:1306

View

DatetimeArray to_timestamp(const std::string& how = "start") const

DatetimeArray

pd_datetime_index_ops_mixin.h:1292

View

DatetimeIndexOpsMixin tz_convert(const std::string& tz, const std::string& tz_display = "") const

DatetimeIndexOpsMixin

pd_datetime_index_ops_mixin.h:1213

View

DatetimeIndexOpsMixin tz_localize(const std::string& tz, const std::string& ambiguous = "raise", const std::string& nonexistent = "raise", const std::string& tz_display = "") const

DatetimeIndexOpsMixin

pd_datetime_index_ops_mixin.h:1192

View

I/O#

Signature

Return Type

Location

Example

std::string to_string() const override

std::string

pd_datetime_index_ops_mixin.h:1413

View

Conversion#

Signature

Return Type

Location

Example

DatetimeIndexOpsMixin copy() const

DatetimeIndexOpsMixin

pd_datetime_index_ops_mixin.h:1467

View

Type Checking#

Signature

Return Type

Location

Example

BooleanArray is_leap_year() const

BooleanArray

pd_datetime_index_ops_mixin.h:1116

View

BooleanArray is_month_end() const

BooleanArray

pd_datetime_index_ops_mixin.h:962

View

BooleanArray is_month_start() const

BooleanArray

pd_datetime_index_ops_mixin.h:934

View

BooleanArray is_negative() const

BooleanArray

pd_datetime_index_ops_mixin.h:1146

View

BooleanArray is_positive() const

BooleanArray

pd_datetime_index_ops_mixin.h:1134

View

BooleanArray is_quarter_end() const

BooleanArray

pd_datetime_index_ops_mixin.h:1024

View

BooleanArray is_quarter_start() const

BooleanArray

pd_datetime_index_ops_mixin.h:992

View

BooleanArray is_year_end() const

BooleanArray

pd_datetime_index_ops_mixin.h:1087

View

BooleanArray is_year_start() const

BooleanArray

pd_datetime_index_ops_mixin.h:1058

View

BooleanArray is_zero() const

BooleanArray

pd_datetime_index_ops_mixin.h:1158

View

Other Methods#

Signature

Return Type

Location

Example

DatetimeIndexOpsMixin ceil(const std::string& freq) const

DatetimeIndexOpsMixin

pd_datetime_index_ops_mixin.h:361

View

ArrayType ceiled(result)

ArrayType

pd_datetime_index_ops_mixin.h:390

std::unique_ptr<IndexBase> clone() const override

std::unique_ptr<IndexBase>

pd_datetime_index_ops_mixin.h:1402

View

IntegerArray<numpy::int32> day() const

IntegerArray<numpy::int32>

pd_datetime_index_ops_mixin.h:666

View

std::vector<std::string> day_name( const std::optional<std::string>& locale = std::nullopt) const

std::vector<std::string>

pd_datetime_index_ops_mixin.h:600

View

IntegerArray<numpy::int32> dayofweek() const

IntegerArray<numpy::int32>

pd_datetime_index_ops_mixin.h:784

View

IntegerArray<numpy::int32> dayofyear() const

IntegerArray<numpy::int32>

pd_datetime_index_ops_mixin.h:803

View

DatetimeIndexOpsMixin floor(const std::string& freq) const

DatetimeIndexOpsMixin

pd_datetime_index_ops_mixin.h:317

View

ArrayType floored(result)

ArrayType

pd_datetime_index_ops_mixin.h:346

static std::string format_datetime_strftime(const numpy::datetime64& val, const std::string& format)

static std::string

pd_datetime_index_ops_mixin.h:1520

static std::string format_period_strftime(numpy::int64 ordinal, const std::string& format, PeriodFrequency freq)

static std::string

pd_datetime_index_ops_mixin.h:1546

std::optional<std::string> freq() const

std::optional<std::string>

pd_datetime_index_ops_mixin.h:205

View

std::string freqstr() const

std::string

pd_datetime_index_ops_mixin.h:228

View

gmtime_r(&secs, &tm_time)

pd_datetime_index_ops_mixin.h:1535

gmtime_s(&tm_time, &secs)

pd_datetime_index_ops_mixin.h:1533

IntegerArray<numpy::int32> hour() const

IntegerArray<numpy::int32>

pd_datetime_index_ops_mixin.h:678

View

std::optional<std::string> inferred_freq() const

std::optional<std::string>

pd_datetime_index_ops_mixin.h:241

IntegerArray<numpy::int32> microsecond() const

IntegerArray<numpy::int32>

pd_datetime_index_ops_mixin.h:720

View

IntegerArray<numpy::int32> month() const

IntegerArray<numpy::int32>

pd_datetime_index_ops_mixin.h:654

View

std::vector<std::string> month_name( const std::optional<std::string>& locale = std::nullopt) const

std::vector<std::string>

pd_datetime_index_ops_mixin.h:553

View

IntegerArray<numpy::int32> nanosecond() const

IntegerArray<numpy::int32>

pd_datetime_index_ops_mixin.h:753

View

DatetimeIndexOpsMixin normalize() const

DatetimeIndexOpsMixin

pd_datetime_index_ops_mixin.h:1177

View

static int64_t parse_freq_to_nanoseconds(const std::string& freq)

static int64_t

pd_datetime_index_ops_mixin.h:1495

static int64_t period_ordinal_to_nanoseconds(numpy::int64 ordinal, PeriodFrequency freq)

static int64_t

pd_datetime_index_ops_mixin.h:1561

IntegerArray<numpy::int32> quarter() const

IntegerArray<numpy::int32>

pd_datetime_index_ops_mixin.h:815

View

DatetimeIndexOpsMixin result(std::move(localized), this->name())

DatetimeIndexOpsMixin

pd_datetime_index_ops_mixin.h:1198

View

DatetimeIndexOpsMixin result(std::move(converted), this->name())

DatetimeIndexOpsMixin

pd_datetime_index_ops_mixin.h:1216

View

DatetimeIndexOpsMixin result(\*this)

DatetimeIndexOpsMixin

pd_datetime_index_ops_mixin.h:1475

View

DatetimeIndexOpsMixin round(const std::string& freq, int decimals = 0) const

DatetimeIndexOpsMixin

pd_datetime_index_ops_mixin.h:273

View

ArrayType rounded(result)

ArrayType

pd_datetime_index_ops_mixin.h:302

IntegerArray<numpy::int32> second() const

IntegerArray<numpy::int32>

pd_datetime_index_ops_mixin.h:706

View

void set_freq(const std::optional<std::string>& freq)

void

pd_datetime_index_ops_mixin.h:252

View

DatetimeIndexOpsMixin snap(const std::string& freq = "S") const

DatetimeIndexOpsMixin

pd_datetime_index_ops_mixin.h:405

View

std::vector<std::string> strftime(const std::string& date_format) const

std::vector<std::string>

pd_datetime_index_ops_mixin.h:501

View

IntegerArray<numpy::int64> td_days() const

IntegerArray<numpy::int64>

pd_datetime_index_ops_mixin.h:870

View

IntegerArray<numpy::int64> td_microseconds() const

IntegerArray<numpy::int64>

pd_datetime_index_ops_mixin.h:894

IntegerArray<numpy::int64> td_nanoseconds() const

IntegerArray<numpy::int64>

pd_datetime_index_ops_mixin.h:906

IntegerArray<numpy::int64> td_seconds() const

IntegerArray<numpy::int64>

pd_datetime_index_ops_mixin.h:882

FloatingArray<double> total_seconds() const

FloatingArray<double>

pd_datetime_index_ops_mixin.h:918

View

IndexTypeId type_id() const override

IndexTypeId

pd_datetime_index_ops_mixin.h:1406

View

std::string tz() const

std::string

pd_datetime_index_ops_mixin.h:1228

View

bool tz_aware() const

bool

pd_datetime_index_ops_mixin.h:1254

View

std::string tz_display() const

std::string

pd_datetime_index_ops_mixin.h:1241

IntegerArray<numpy::int32> week() const

IntegerArray<numpy::int32>

pd_datetime_index_ops_mixin.h:827

View

IntegerArray<numpy::int32> weekday() const

IntegerArray<numpy::int32>

pd_datetime_index_ops_mixin.h:795

View

IntegerArray<numpy::int32> weekofyear() const

IntegerArray<numpy::int32>

pd_datetime_index_ops_mixin.h:858

IntegerArray<numpy::int32> year() const

IntegerArray<numpy::int32>

pd_datetime_index_ops_mixin.h:642

View

Code Examples#

The following examples are extracted from the test suite.

get_freq (pd_test_2_all.cpp:20397)
20387    std::vector<numpy::datetime64> ts = {
20388        numpy::datetime64(0LL, numpy::DateTimeUnit::Day),
20389        numpy::datetime64(1LL, numpy::DateTimeUnit::Day),
20390        numpy::datetime64(2LL, numpy::DateTimeUnit::Day)
20391    };
20392    auto dt_idx = std::make_unique<pandas::DatetimeIndex>(ts);
20393    dt_idx->set_freq(std::string("D"));
20394    df.set_index(std::move(dt_idx));
20395
20396    auto s = df.extract_column_as_numeric_series("val");
20397    check(s.get_freq().has_value(), "freq propagated");
20398    if (s.get_freq().has_value()) {
20399        check(s.get_freq().value() == "D", "freq value D");
20400    }
20401
20402    // Test MultiIndex propagation
20403    pandas::DataFrame df2;
20404    std::vector<numpy::float64> vals2 = {10.0, 20.0};
20405    df2.insert(0, "A", std::make_unique<pandas::Series<numpy::float64>>(vals2, "A"), true);
20406    std::vector<std::vector<std::string>> arrays = {{"x", "y"}, {"1", "2"}};
20407    std::vector<std::optional<std::string>> names = {std::string("first"), std::string("second")};
rename (pd_test_1_all.cpp:5816)
5806    std::cout << " -> tests passed" << std::endl;
5807}
5808
5809void pd_test_categorical_index_rename() {
5810    std::cout << "========= rename ======================================";
5811
5812    pandas::CategoricalArray arr({"x", "y"});
5813    pandas::CategoricalIndex idx(arr, "old_name");
5814
5815    pandas::CategoricalIndex renamed = idx.rename("new_name");
5816
5817    bool passed = (renamed.name().has_value() && *renamed.name() == "new_name" &&
5818                   renamed.size() == idx.size() && renamed.categories() == idx.categories());
5819    if (!passed) {
5820        std::cout << "  [FAIL] : in pd_test_categorical_index_rename()" << std::endl;
5821        throw std::runtime_error("pd_test_categorical_index_rename failed");
5822    }
5823
5824    std::cout << " -> tests passed" << std::endl;
5825}
mean (pd_test_1_all.cpp:282)
272            std::optional<bool>(true),
273            std::optional<bool>(true)
274        });
275
276        auto s = arr.sum();
277        if (!s.has_value() || s.value() != 3) {
278            std::cout << "  [FAIL] : in pd_test_boolean_array_reductions() : sum should be 3" << std::endl;
279            throw std::runtime_error("pd_test_boolean_array_reductions failed: sum");
280        }
281
282        auto m = arr.mean();
283        if (!m.has_value() || std::abs(m.value() - 0.75) > 0.001) {
284            std::cout << "  [FAIL] : in pd_test_boolean_array_reductions() : mean should be 0.75" << std::endl;
285            throw std::runtime_error("pd_test_boolean_array_reductions failed: mean");
286        }
287
288        std::cout << " -> tests passed" << std::endl;
289    }
290
291    void pd_test_boolean_array_dtype() {
292        std::cout << "========= BooleanArray: dtype ======================= ";
minute (pd_test_1_all.cpp:7505)
7495    std::cout << "========= minute property =============================";
7496
7497    std::vector<std::optional<numpy::datetime64>> values = {
7498        make_dt(0),                    // Minute 0
7499        make_dt(30 * NS_PER_MIN),      // Minute 30
7500        make_dt(59 * NS_PER_MIN)       // Minute 59
7501    };
7502    pandas::DatetimeArray arr(values);
7503    pandas::DatetimeIndex idx(arr);
7504
7505    auto minutes = idx.minute();
7506
7507    bool passed = (minutes.size() == 3);
7508    auto m0 = minutes[0];
7509    auto m1 = minutes[1];
7510    auto m2 = minutes[2];
7511    passed = passed && m0.has_value() && *m0 == 0;
7512    passed = passed && m1.has_value() && *m1 == 30;
7513    passed = passed && m2.has_value() && *m2 == 59;
7514
7515    if (!passed) {
asfreq (pd_test_1_all.cpp:2869)
2859        std::cout << "========= PeriodArray: asfreq ======================= ";
2860
2861        // Monthly to quarterly
2862        pandas::PeriodArray arr_m(std::vector<std::string>{
2863            "2024-01",
2864            "2024-04",
2865            "2024-07",
2866            "NaT"
2867        }, "M");
2868
2869        auto arr_q = arr_m.asfreq("Q");
2870        if (arr_q.size() != 4) {
2871            std::cout << "  [FAIL] : asfreq size should be 4" << std::endl;
2872            throw std::runtime_error("pd_test_period_array_asfreq failed: size");
2873        }
2874        if (arr_q.freqstr() != "Q") {
2875            std::cout << "  [FAIL] : asfreq freqstr should be 'Q'" << std::endl;
2876            throw std::runtime_error("pd_test_period_array_asfreq failed: freqstr");
2877        }
2878
2879        // Check NaT is preserved
shift (pd_test_1_all.cpp:5188)
5178            // First element should be NaN
5179            val = d["A"].get_value_str(0);
5180            passed = std::isnan(std::stod(val));
5181            if (!passed) {
5182                std::cout << "  [FAIL] : in pd_test_arithmetic_dataframe_diff_shift() : diff NaN failed" << std::endl;
5183                throw std::runtime_error("pd_test_arithmetic_dataframe_diff_shift failed: diff NaN failed");
5184            }
5185
5186            // shift: [NaN, 1, 3, 6]
5187            auto s = df.shift();
5188            val = s["A"].get_value_str(1);
5189            passed = std::abs(std::stod(val) - 1.0) < 0.001;
5190            if (!passed) {
5191                std::cout << "  [FAIL] : in pd_test_arithmetic_dataframe_diff_shift() : shift failed" << std::endl;
5192                throw std::runtime_error("pd_test_arithmetic_dataframe_diff_shift failed: shift failed");
5193            }
5194
5195            std::cout << " -> tests passed" << std::endl;
5196        }
to_period (pd_test_2_all.cpp:14554)
14544        std::cout << "====================================== [OK] pd_test_to_parquet test suite ========================" << std::endl;
14545        return 0;
14546    }
14547
14548} // namespace dataframe_tests
14549// ------------------- pd_test_to_parquet.cpp (end) -----------------------------
14550
14551// ------------------- pd_test_to_period.cpp (start) -----------------------------
14552// dataframe_tests/pd_test_to_period.cpp
14553// Test suite for DataFrame.to_period() method
14554
14555#include <iostream>
14556#include <stdexcept>
14557#include <vector>
14558#include <string>
14559#include <map>
14560
14561#include "../pandas/pd_dataframe.h"
14562
14563// CRITICAL: No using namespace directives
to_timestamp (pd_test_1_all.cpp:2830)
2820    void pd_test_period_array_to_timestamp() {
2821        std::cout << "========= PeriodArray: to_timestamp ======================= ";
2822
2823        pandas::PeriodArray arr(std::vector<std::string>{
2824            "2024-01",
2825            "2024-06",
2826            "NaT"
2827        }, "M");
2828
2829        // to_timestamp with start
2830        auto ts_start = arr.to_timestamp("start");
2831        if (ts_start.size() != 3) {
2832            std::cout << "  [FAIL] : to_timestamp size should be 3" << std::endl;
2833            throw std::runtime_error("pd_test_period_array_to_timestamp failed: size");
2834        }
2835
2836        auto ts0 = ts_start[0];
2837        if (!ts0.has_value()) {
2838            std::cout << "  [FAIL] : ts_start[0] should have value" << std::endl;
2839            throw std::runtime_error("pd_test_period_array_to_timestamp failed: ts_start[0]");
2840        }
tz_convert (pd_test_2_all.cpp:17874)
17864        std::cout << "====================================== [OK] pd_test_transform test suite ========================== " << std::endl;
17865        return 0;
17866    }
17867
17868} // namespace dataframe_tests
17869// ------------------- pd_test_transform.cpp (end) -----------------------------
17870
17871// ------------------- pd_test_tz_convert.cpp (start) -----------------------------
17872// dataframe_tests/pd_test_tz_convert.cpp
17873// Test for DataFrame.tz_convert() method
17874
17875#include <iostream>
17876#include <stdexcept>
17877#include <cmath>
17878#include "../pandas/pd_dataframe.h"
17879
17880namespace dataframe_tests {
17881    namespace dataframe_tests_tz_convert {
17882
17883        void pd_test_tz_convert_basic() {
tz_localize (pd_test_1_all.cpp:1431)
1421            "2023-06-15"
1422        });
1423
1424        // Initially should be timezone-naive
1425        if (arr.is_tz_aware()) {
1426            std::cout << "  [FAIL] : array should be timezone-naive initially" << std::endl;
1427            throw std::runtime_error("pd_test_datetime_array_timezone failed: naive");
1428        }
1429
1430        // Localize to UTC
1431        auto localized = arr.tz_localize("UTC");
1432        if (!localized.is_tz_aware()) {
1433            std::cout << "  [FAIL] : localized array should be timezone-aware" << std::endl;
1434            throw std::runtime_error("pd_test_datetime_array_timezone failed: localize");
1435        }
1436
1437        // Verify timezone name in dtype
1438        auto dt = localized.dtype();
1439        if (!dt.is_tz_aware()) {
1440            std::cout << "  [FAIL] : dtype should be timezone-aware" << std::endl;
1441            throw std::runtime_error("pd_test_datetime_array_timezone failed: dtype tz");
to_string (pd_test_1_all.cpp:2693)
2683        pandas::PeriodArray arr_m(std::vector<std::string>{
2684            "2020-01",
2685            "NaT",
2686            "2025-06"
2687        }, "M");
2688
2689        // Year
2690        auto years = arr_m.year();
2691        auto y0 = years[0];
2692        if (!y0.has_value() || y0.value() != 2020) {
2693            std::cout << "  [FAIL] : year[0] should be 2020, got " << (y0.has_value() ? std::to_string(y0.value()) : "NA") << std::endl;
2694            throw std::runtime_error("pd_test_period_array_year_month_quarter failed: year[0]");
2695        }
2696
2697        auto y1 = years[1];
2698        if (y1.has_value()) {
2699            std::cout << "  [FAIL] : year[1] should be NA (NaT)" << std::endl;
2700            throw std::runtime_error("pd_test_period_array_year_month_quarter failed: year[1] should be NA");
2701        }
2702
2703        auto y2 = years[2];
copy (pd_test_1_all.cpp:5798)
5788// ============================================================================
5789// Copy/Rename Tests
5790// ============================================================================
5791
5792void pd_test_categorical_index_copy() {
5793    std::cout << "========= copy ========================================";
5794
5795    pandas::CategoricalArray arr({"a", "b", "c"});
5796    pandas::CategoricalIndex idx(arr, "original");
5797
5798    pandas::CategoricalIndex copied = idx.copy();
5799
5800    bool passed = (copied.size() == idx.size() && copied.name() == idx.name() &&
5801                   copied.categories() == idx.categories() && copied.ordered() == idx.ordered());
5802    if (!passed) {
5803        std::cout << "  [FAIL] : in pd_test_categorical_index_copy()" << std::endl;
5804        throw std::runtime_error("pd_test_categorical_index_copy failed");
5805    }
5806
5807    std::cout << " -> tests passed" << std::endl;
5808}
is_leap_year (pd_test_1_all.cpp:1280)
1270        }
1271
1272        // is_month_end
1273        auto me = arr.is_month_end();
1274        if (!me[1].has_value() || !me[1].value()) {
1275            std::cout << "  [FAIL] : 2023-03-31 should be month end" << std::endl;
1276            throw std::runtime_error("pd_test_datetime_array_boolean_props failed: month end");
1277        }
1278
1279        // is_leap_year
1280        auto ly = arr.is_leap_year();
1281        if (!ly[2].has_value() || !ly[2].value()) {
1282            std::cout << "  [FAIL] : 2024 should be leap year" << std::endl;
1283            throw std::runtime_error("pd_test_datetime_array_boolean_props failed: leap year");
1284        }
1285        if (!ly[0].has_value() || ly[0].value()) {
1286            std::cout << "  [FAIL] : 2023 should not be leap year" << std::endl;
1287            throw std::runtime_error("pd_test_datetime_array_boolean_props failed: not leap year");
1288        }
1289
1290        std::cout << " -> tests passed" << std::endl;
is_month_end (pd_test_1_all.cpp:1273)
1263        }
1264
1265        // is_month_start
1266        auto ms = arr.is_month_start();
1267        if (!ms[0].has_value() || !ms[0].value()) {
1268            std::cout << "  [FAIL] : 2023-01-01 should be month start" << std::endl;
1269            throw std::runtime_error("pd_test_datetime_array_boolean_props failed: month start");
1270        }
1271
1272        // is_month_end
1273        auto me = arr.is_month_end();
1274        if (!me[1].has_value() || !me[1].value()) {
1275            std::cout << "  [FAIL] : 2023-03-31 should be month end" << std::endl;
1276            throw std::runtime_error("pd_test_datetime_array_boolean_props failed: month end");
1277        }
1278
1279        // is_leap_year
1280        auto ly = arr.is_leap_year();
1281        if (!ly[2].has_value() || !ly[2].value()) {
1282            std::cout << "  [FAIL] : 2024 should be leap year" << std::endl;
1283            throw std::runtime_error("pd_test_datetime_array_boolean_props failed: leap year");
is_month_start (pd_test_1_all.cpp:1266)
1256        if (!ys[0].has_value() || !ys[0].value()) {
1257            std::cout << "  [FAIL] : 2023-01-01 should be year start" << std::endl;
1258            throw std::runtime_error("pd_test_datetime_array_boolean_props failed: year start");
1259        }
1260        if (!ys[1].has_value() || ys[1].value()) {
1261            std::cout << "  [FAIL] : 2023-03-31 should not be year start" << std::endl;
1262            throw std::runtime_error("pd_test_datetime_array_boolean_props failed: not year start");
1263        }
1264
1265        // is_month_start
1266        auto ms = arr.is_month_start();
1267        if (!ms[0].has_value() || !ms[0].value()) {
1268            std::cout << "  [FAIL] : 2023-01-01 should be month start" << std::endl;
1269            throw std::runtime_error("pd_test_datetime_array_boolean_props failed: month start");
1270        }
1271
1272        // is_month_end
1273        auto me = arr.is_month_end();
1274        if (!me[1].has_value() || !me[1].value()) {
1275            std::cout << "  [FAIL] : 2023-03-31 should be month end" << std::endl;
1276            throw std::runtime_error("pd_test_datetime_array_boolean_props failed: month end");
is_negative (pd_test_1_all.cpp:4269)
4259        if (!pos[0].has_value() || !pos[0].value()) {
4260            std::cout << "  [FAIL] : 1 day should be positive" << std::endl;
4261            throw std::runtime_error("pd_test_timedelta_array_boolean_props failed: is_positive");
4262        }
4263        if (!pos[1].has_value() || pos[1].value()) {
4264            std::cout << "  [FAIL] : -5 hours should not be positive" << std::endl;
4265            throw std::runtime_error("pd_test_timedelta_array_boolean_props failed: not positive");
4266        }
4267
4268        // is_negative
4269        auto neg = arr.is_negative();
4270        if (!neg[1].has_value() || !neg[1].value()) {
4271            std::cout << "  [FAIL] : -5 hours should be negative" << std::endl;
4272            throw std::runtime_error("pd_test_timedelta_array_boolean_props failed: is_negative");
4273        }
4274        if (!neg[0].has_value() || neg[0].value()) {
4275            std::cout << "  [FAIL] : 1 day should not be negative" << std::endl;
4276            throw std::runtime_error("pd_test_timedelta_array_boolean_props failed: not negative");
4277        }
4278
4279        // is_zero
is_positive (pd_test_1_all.cpp:4258)
4248        std::cout << "========= TimedeltaArray: boolean properties ======================= ";
4249
4250        pandas::TimedeltaArray arr({
4251            std::optional<numpy::timedelta64>(numpy::timedelta64(1, numpy::DateTimeUnit::Day)),   // positive
4252            std::optional<numpy::timedelta64>(numpy::timedelta64(-5, numpy::DateTimeUnit::Hour)), // negative
4253            std::optional<numpy::timedelta64>(numpy::timedelta64(0, numpy::DateTimeUnit::Second)), // zero
4254            std::nullopt
4255        });
4256
4257        // is_positive
4258        auto pos = arr.is_positive();
4259        if (!pos[0].has_value() || !pos[0].value()) {
4260            std::cout << "  [FAIL] : 1 day should be positive" << std::endl;
4261            throw std::runtime_error("pd_test_timedelta_array_boolean_props failed: is_positive");
4262        }
4263        if (!pos[1].has_value() || pos[1].value()) {
4264            std::cout << "  [FAIL] : -5 hours should not be positive" << std::endl;
4265            throw std::runtime_error("pd_test_timedelta_array_boolean_props failed: not positive");
4266        }
4267
4268        // is_negative
is_quarter_end (pd_test_3_all.cpp:25056)
25046    };
25047    pandas::Series<numpy::datetime64> s(dates);
25048    pandas::DatetimeProperties<pandas::Series<numpy::datetime64>> dt(s);
25049    if (dt.has_nat()) throw std::runtime_error("has_nat should be false for clean series");
25050    auto ms = dt.is_month_start();
25051    if (ms[0] != true || ms[1] != false) throw std::runtime_error("is_month_start failed");
25052    auto me = dt.is_month_end();
25053    if (me[1] != true || me[0] != false) throw std::runtime_error("is_month_end failed");
25054    auto qs = dt.is_quarter_start();
25055    if (qs[0] != true || qs[1] != false) throw std::runtime_error("is_quarter_start failed");
25056    auto qe = dt.is_quarter_end();
25057    if (qe[2] != true || qe[0] != false) throw std::runtime_error("is_quarter_end failed");
25058    auto ys = dt.is_year_start();
25059    if (ys[0] != true || ys[1] != false) throw std::runtime_error("is_year_start failed");
25060    auto ye = dt.is_year_end();
25061    if (ye[3] != true || ye[0] != false) throw std::runtime_error("is_year_end failed");
25062    std::cout << " -> tests passed" << std::endl;
25063}
25064
25065void pd_test_dt_bool_na_with_nat() {
25066    std::cout << "========= pd_test_dt_bool_na: series with NaT ==========";
is_quarter_start (pd_test_3_all.cpp:25054)
25044        numpy::datetime64("2024-03-31"),
25045        numpy::datetime64("2024-12-31")
25046    };
25047    pandas::Series<numpy::datetime64> s(dates);
25048    pandas::DatetimeProperties<pandas::Series<numpy::datetime64>> dt(s);
25049    if (dt.has_nat()) throw std::runtime_error("has_nat should be false for clean series");
25050    auto ms = dt.is_month_start();
25051    if (ms[0] != true || ms[1] != false) throw std::runtime_error("is_month_start failed");
25052    auto me = dt.is_month_end();
25053    if (me[1] != true || me[0] != false) throw std::runtime_error("is_month_end failed");
25054    auto qs = dt.is_quarter_start();
25055    if (qs[0] != true || qs[1] != false) throw std::runtime_error("is_quarter_start failed");
25056    auto qe = dt.is_quarter_end();
25057    if (qe[2] != true || qe[0] != false) throw std::runtime_error("is_quarter_end failed");
25058    auto ys = dt.is_year_start();
25059    if (ys[0] != true || ys[1] != false) throw std::runtime_error("is_year_start failed");
25060    auto ye = dt.is_year_end();
25061    if (ye[3] != true || ye[0] != false) throw std::runtime_error("is_year_end failed");
25062    std::cout << " -> tests passed" << std::endl;
25063}
is_year_end (pd_test_3_all.cpp:25060)
25050    auto ms = dt.is_month_start();
25051    if (ms[0] != true || ms[1] != false) throw std::runtime_error("is_month_start failed");
25052    auto me = dt.is_month_end();
25053    if (me[1] != true || me[0] != false) throw std::runtime_error("is_month_end failed");
25054    auto qs = dt.is_quarter_start();
25055    if (qs[0] != true || qs[1] != false) throw std::runtime_error("is_quarter_start failed");
25056    auto qe = dt.is_quarter_end();
25057    if (qe[2] != true || qe[0] != false) throw std::runtime_error("is_quarter_end failed");
25058    auto ys = dt.is_year_start();
25059    if (ys[0] != true || ys[1] != false) throw std::runtime_error("is_year_start failed");
25060    auto ye = dt.is_year_end();
25061    if (ye[3] != true || ye[0] != false) throw std::runtime_error("is_year_end failed");
25062    std::cout << " -> tests passed" << std::endl;
25063}
25064
25065void pd_test_dt_bool_na_with_nat() {
25066    std::cout << "========= pd_test_dt_bool_na: series with NaT ==========";
25067    std::vector<numpy::datetime64> dates = {
25068        numpy::datetime64("2024-01-01"),
25069        numpy::datetime64(),  // NaT
25070        numpy::datetime64("2024-12-31")
is_year_start (pd_test_1_all.cpp:1255)
1245        std::cout << "========= DatetimeArray: boolean properties ======================= ";
1246
1247        pandas::DatetimeArray arr(std::vector<std::string>{
1248            "2023-01-01",   // year start, month start
1249            "2023-03-31",   // quarter end, month end
1250            "2024-02-29",   // leap year (2024 is leap year)
1251            "2023-12-31"    // year end, month end
1252        });
1253
1254        // is_year_start
1255        auto ys = arr.is_year_start();
1256        if (!ys[0].has_value() || !ys[0].value()) {
1257            std::cout << "  [FAIL] : 2023-01-01 should be year start" << std::endl;
1258            throw std::runtime_error("pd_test_datetime_array_boolean_props failed: year start");
1259        }
1260        if (!ys[1].has_value() || ys[1].value()) {
1261            std::cout << "  [FAIL] : 2023-03-31 should not be year start" << std::endl;
1262            throw std::runtime_error("pd_test_datetime_array_boolean_props failed: not year start");
1263        }
1264
1265        // is_month_start
is_zero (pd_test_1_all.cpp:4280)
4270        if (!neg[1].has_value() || !neg[1].value()) {
4271            std::cout << "  [FAIL] : -5 hours should be negative" << std::endl;
4272            throw std::runtime_error("pd_test_timedelta_array_boolean_props failed: is_negative");
4273        }
4274        if (!neg[0].has_value() || neg[0].value()) {
4275            std::cout << "  [FAIL] : 1 day should not be negative" << std::endl;
4276            throw std::runtime_error("pd_test_timedelta_array_boolean_props failed: not negative");
4277        }
4278
4279        // is_zero
4280        auto zero = arr.is_zero();
4281        if (!zero[2].has_value() || !zero[2].value()) {
4282            std::cout << "  [FAIL] : 0 seconds should be zero" << std::endl;
4283            throw std::runtime_error("pd_test_timedelta_array_boolean_props failed: is_zero");
4284        }
4285
4286        // NA should propagate
4287        if (pos[3].has_value() || neg[3].has_value() || zero[3].has_value()) {
4288            std::cout << "  [FAIL] : NaT should propagate to boolean props" << std::endl;
4289            throw std::runtime_error("pd_test_timedelta_array_boolean_props failed: NaT propagation");
4290        }
ceil (pd_test_1_all.cpp:4949)
4939                throw std::runtime_error("pd_test_arithmetic_series_round failed: round failed");
4940            }
4941
4942            auto f = a.floor();
4943            passed = std::abs(f[0] - 1.0) < 0.001 && std::abs(f[2] - 3.0) < 0.001 && std::abs(f[3] - (-2.0)) < 0.001;
4944            if (!passed) {
4945                std::cout << "  [FAIL] : in pd_test_arithmetic_series_round() : floor failed" << std::endl;
4946                throw std::runtime_error("pd_test_arithmetic_series_round failed: floor failed");
4947            }
4948
4949            auto c = a.ceil();
4950            passed = std::abs(c[0] - 2.0) < 0.001 && std::abs(c[2] - 4.0) < 0.001 && std::abs(c[3] - (-1.0)) < 0.001;
4951            if (!passed) {
4952                std::cout << "  [FAIL] : in pd_test_arithmetic_series_round() : ceil failed" << std::endl;
4953                throw std::runtime_error("pd_test_arithmetic_series_round failed: ceil failed");
4954            }
4955
4956            // Round with decimals
4957            pandas::Series<double> b({1.234, 2.567, 3.891});
4958            auto r2 = b.round(2);
4959            passed = std::abs(r2[0] - 1.23) < 0.001 && std::abs(r2[1] - 2.57) < 0.001;
clone (pd_test_1_all.cpp:5776)
5766    std::cout << " -> tests passed" << std::endl;
5767}
5768
5769void pd_test_categorical_index_clone() {
5770    std::cout << "========= clone =======================================";
5771
5772    pandas::CategoricalArray arr({"p", "q", "r"});
5773    pandas::CategoricalIndex idx(arr, "original");
5774
5775    std::unique_ptr<pandas::IndexBase> cloned = idx.clone();
5776
5777    bool passed = (cloned != nullptr && cloned->size() == idx.size() &&
5778                   cloned->name() == idx.name());
5779    if (!passed) {
5780        std::cout << "  [FAIL] : in pd_test_categorical_index_clone()" << std::endl;
5781        throw std::runtime_error("pd_test_categorical_index_clone failed");
5782    }
5783
5784    std::cout << " -> tests passed" << std::endl;
5785}
day (pd_test_1_all.cpp:1193)
1183            std::cout << "  [FAIL] : month[0] should be 3" << std::endl;
1184            throw std::runtime_error("pd_test_datetime_array_component_month_day failed: month[0]");
1185        }
1186        auto m1 = months[1];
1187        if (!m1.has_value() || m1.value() != 12) {
1188            std::cout << "  [FAIL] : month[1] should be 12" << std::endl;
1189            throw std::runtime_error("pd_test_datetime_array_component_month_day failed: month[1]");
1190        }
1191
1192        // Day
1193        auto days = arr.day();
1194        auto d0 = days[0];
1195        if (!d0.has_value() || d0.value() != 15) {
1196            std::cout << "  [FAIL] : day[0] should be 15" << std::endl;
1197            throw std::runtime_error("pd_test_datetime_array_component_month_day failed: day[0]");
1198        }
1199        auto d1 = days[1];
1200        if (!d1.has_value() || d1.value() != 25) {
1201            std::cout << "  [FAIL] : day[1] should be 25" << std::endl;
1202            throw std::runtime_error("pd_test_datetime_array_component_month_day failed: day[1]");
1203        }
day_name (pd_test_1_all.cpp:8474)
8464void pd_test_datetime_mixin_day_name() {
8465    std::cout << "========= day_name ====================================";
8466
8467    std::vector<std::optional<numpy::datetime64>> values = {
8468        numpy::datetime64(0LL, numpy::DateTimeUnit::Nanosecond)  // 1970-01-01 = Thursday
8469    };
8470    pandas::DatetimeArray arr(values);
8471    pandas::DatetimeMixinIndex idx(arr);
8472
8473    std::vector<std::string> names = idx.day_name();
8474
8475    bool passed = (names.size() == 1 && names[0] == "Thursday");
8476    if (!passed) {
8477        std::cout << "  [FAIL] : in pd_test_datetime_mixin_day_name() got: " << names[0] << std::endl;
8478        throw std::runtime_error("pd_test_datetime_mixin_day_name failed");
8479    }
8480
8481    std::cout << " -> tests passed" << std::endl;
8482}
dayofweek (pd_test_1_all.cpp:7565)
7555    // 1970-01-01 was a Thursday (day 3)
7556    std::vector<std::optional<numpy::datetime64>> values = {
7557        make_dt(0),                // Thursday (3)
7558        make_dt(NS_PER_DAY),       // Friday (4)
7559        make_dt(2 * NS_PER_DAY),   // Saturday (5)
7560        make_dt(3 * NS_PER_DAY)    // Sunday (6)
7561    };
7562    pandas::DatetimeArray arr(values);
7563    pandas::DatetimeIndex idx(arr);
7564
7565    auto dow = idx.dayofweek();
7566
7567    bool passed = (dow.size() == 4);
7568    if (!passed) {
7569        std::cout << "  [FAIL] : in pd_test_datetime_index_dayofweek()" << std::endl;
7570        throw std::runtime_error("pd_test_datetime_index_dayofweek failed");
7571    }
7572
7573    std::cout << " -> tests passed" << std::endl;
7574}
dayofyear (pd_test_3_all.cpp:18582)
18572    auto seconds = s.dt().second();
18573    if (seconds[0] != 45 || seconds[1] != 30 || seconds[2] != 59) {
18574        std::cout << "  [FAIL] : second() failed" << std::endl;
18575        throw std::runtime_error("pd_test_dt_time_components: second() failed");
18576    }
18577
18578    std::cout << " -> tests passed" << std::endl;
18579}
18580
18581// ============================================================================
18582// Test dt().dayofweek(), dt().dayofyear(), dt().quarter()
18583// ============================================================================
18584
18585void pd_test_dt_derived_properties() {
18586    std::cout << "========= Series.dt().dayofweek/dayofyear/quarter() ======";
18587
18588    // 2020-01-01 is a Wednesday (dayofweek=2), dayofyear=1, Q1
18589    // 2020-07-04 is a Saturday (dayofweek=5), dayofyear=186, Q3
18590    pandas::Series<std::string> s({"2020-01-01", "2020-07-04"});
18591
18592    auto dow = s.dt().dayofweek();
floor (pd_test_1_all.cpp:4942)
4932            pandas::Series<double> a({1.4, 2.5, 3.6, -1.4, -2.5});
4933
4934            auto r = a.round();
4935            bool passed = std::abs(r[0] - 1.0) < 0.001 && std::abs(r[2] - 4.0) < 0.001;
4936            if (!passed) {
4937                std::cout << "  [FAIL] : in pd_test_arithmetic_series_round() : round failed" << std::endl;
4938                throw std::runtime_error("pd_test_arithmetic_series_round failed: round failed");
4939            }
4940
4941            auto f = a.floor();
4942            passed = std::abs(f[0] - 1.0) < 0.001 && std::abs(f[2] - 3.0) < 0.001 && std::abs(f[3] - (-2.0)) < 0.001;
4943            if (!passed) {
4944                std::cout << "  [FAIL] : in pd_test_arithmetic_series_round() : floor failed" << std::endl;
4945                throw std::runtime_error("pd_test_arithmetic_series_round failed: floor failed");
4946            }
4947
4948            auto c = a.ceil();
4949            passed = std::abs(c[0] - 2.0) < 0.001 && std::abs(c[2] - 4.0) < 0.001 && std::abs(c[3] - (-1.0)) < 0.001;
4950            if (!passed) {
4951                std::cout << "  [FAIL] : in pd_test_arithmetic_series_round() : ceil failed" << std::endl;
freq (pd_test_1_all.cpp:8233)
8223    std::cout << "========= freq property ===============================";
8224
8225    std::vector<std::optional<numpy::datetime64>> values = {
8226        numpy::datetime64(0LL, numpy::DateTimeUnit::Nanosecond),
8227        numpy::datetime64(86400000000000LL, numpy::DateTimeUnit::Nanosecond)  // 1 day
8228    };
8229    pandas::DatetimeArray arr(values);
8230    pandas::DatetimeMixinIndex idx(arr);
8231
8232    // Default freq is nullopt or inferred
8233    auto f = idx.freq();
8234    std::string fs = idx.freqstr();
8235
8236    bool passed = true;  // freq may or may not be set
8237    if (!passed) {
8238        std::cout << "  [FAIL] : in pd_test_datetime_mixin_freq()" << std::endl;
8239        throw std::runtime_error("pd_test_datetime_mixin_freq failed");
8240    }
8241
8242    std::cout << " -> tests passed" << std::endl;
8243}
freqstr (pd_test_1_all.cpp:2671)
2661        }
2662
2663        pandas::PeriodDtype dtype_y("Y");
2664        if (dtype_y.name() != "period[Y]") {
2665            std::cout << "  [FAIL] : dtype_y.name() should be 'period[Y]'" << std::endl;
2666            throw std::runtime_error("pd_test_period_array_freq_validation failed: dtype name Y");
2667        }
2668
2669        // Test frequency string
2670        pandas::PeriodArray arr(std::vector<std::string>{"2024-01-15"}, "D");
2671        if (arr.freqstr() != "D") {
2672            std::cout << "  [FAIL] : arr.freqstr() should be 'D'" << std::endl;
2673            throw std::runtime_error("pd_test_period_array_freq_validation failed: freqstr");
2674        }
2675
2676        std::cout << " -> tests passed" << std::endl;
2677    }
2678
2679    void pd_test_period_array_year_month_quarter() {
2680        std::cout << "========= PeriodArray: year/month/quarter components ======================= ";
hour (pd_test_1_all.cpp:7476)
7466    std::cout << "========= hour property ===============================";
7467
7468    std::vector<std::optional<numpy::datetime64>> values = {
7469        make_dt(0),                    // Hour 0
7470        make_dt(6 * NS_PER_HOUR),      // Hour 6
7471        make_dt(23 * NS_PER_HOUR)      // Hour 23
7472    };
7473    pandas::DatetimeArray arr(values);
7474    pandas::DatetimeIndex idx(arr);
7475
7476    auto hours = idx.hour();
7477
7478    bool passed = (hours.size() == 3);
7479    auto h0 = hours[0];
7480    auto h1 = hours[1];
7481    auto h2 = hours[2];
7482    passed = passed && h0.has_value() && *h0 == 0;
7483    passed = passed && h1.has_value() && *h1 == 6;
7484    passed = passed && h2.has_value() && *h2 == 23;
7485
7486    if (!passed) {
microsecond (pd_test_timestamp_scalar.cpp:45)
35      // Component constructor
36      pandas::Timestamp ts1(2024, 6, 15, 14, 30, 45);
37      if (ts1.year() != 2024 || ts1.month() != 6 || ts1.day() != 15 ||
38          ts1.hour() != 14 || ts1.minute() != 30 || ts1.second() != 45) {
39        pass = false;
40        fail_msg = "Component constructor values incorrect";
41      }
42
43      // With microseconds and nanoseconds
44      pandas::Timestamp ts2(2024, 1, 1, 12, 0, 0, 123456, 789);
45      if (ts2.microsecond() != 123456 || ts2.nanosecond() != 789) {
46        pass = false;
47        fail_msg = "Microsecond/nanosecond values incorrect";
48      }
49
50      // String constructor - ISO format
51      pandas::Timestamp ts3("2024-06-15T14:30:45");
52      if (ts3.year() != 2024 || ts3.month() != 6 || ts3.day() != 15) {
53        pass = false;
54        fail_msg = "String constructor parse failed";
55      }
month (pd_test_1_all.cpp:1180)
1170    void pd_test_datetime_array_component_month_day() {
1171        std::cout << "========= DatetimeArray: month/day components ======================= ";
1172
1173        pandas::DatetimeArray arr(std::vector<std::string>{
1174            "2023-03-15",
1175            "2023-12-25",
1176            "NaT"
1177        });
1178
1179        // Month
1180        auto months = arr.month();
1181        auto m0 = months[0];
1182        if (!m0.has_value() || m0.value() != 3) {
1183            std::cout << "  [FAIL] : month[0] should be 3" << std::endl;
1184            throw std::runtime_error("pd_test_datetime_array_component_month_day failed: month[0]");
1185        }
1186        auto m1 = months[1];
1187        if (!m1.has_value() || m1.value() != 12) {
1188            std::cout << "  [FAIL] : month[1] should be 12" << std::endl;
1189            throw std::runtime_error("pd_test_datetime_array_component_month_day failed: month[1]");
1190        }
month_name (pd_test_1_all.cpp:8454)
8444void pd_test_datetime_mixin_month_name() {
8445    std::cout << "========= month_name ==================================";
8446
8447    std::vector<std::optional<numpy::datetime64>> values = {
8448        numpy::datetime64(0LL, numpy::DateTimeUnit::Nanosecond)  // 1970-01-01 = January
8449    };
8450    pandas::DatetimeArray arr(values);
8451    pandas::DatetimeMixinIndex idx(arr);
8452
8453    std::vector<std::string> names = idx.month_name();
8454
8455    bool passed = (names.size() == 1 && names[0] == "January");
8456    if (!passed) {
8457        std::cout << "  [FAIL] : in pd_test_datetime_mixin_month_name() got: " << names[0] << std::endl;
8458        throw std::runtime_error("pd_test_datetime_mixin_month_name failed");
8459    }
8460
8461    std::cout << " -> tests passed" << std::endl;
8462}
nanosecond (pd_test_timestamp_scalar.cpp:45)
35      // Component constructor
36      pandas::Timestamp ts1(2024, 6, 15, 14, 30, 45);
37      if (ts1.year() != 2024 || ts1.month() != 6 || ts1.day() != 15 ||
38          ts1.hour() != 14 || ts1.minute() != 30 || ts1.second() != 45) {
39        pass = false;
40        fail_msg = "Component constructor values incorrect";
41      }
42
43      // With microseconds and nanoseconds
44      pandas::Timestamp ts2(2024, 1, 1, 12, 0, 0, 123456, 789);
45      if (ts2.microsecond() != 123456 || ts2.nanosecond() != 789) {
46        pass = false;
47        fail_msg = "Microsecond/nanosecond values incorrect";
48      }
49
50      // String constructor - ISO format
51      pandas::Timestamp ts3("2024-06-15T14:30:45");
52      if (ts3.year() != 2024 || ts3.month() != 6 || ts3.day() != 15) {
53        pass = false;
54        fail_msg = "String constructor parse failed";
55      }
normalize (pd_test_1_all.cpp:8723)
8713void pd_test_datetime_mixin_normalize() {
8714    std::cout << "========= normalize ===================================";
8715
8716    // Create datetime with time component
8717    std::vector<std::optional<numpy::datetime64>> values = {
8718        numpy::datetime64(86400000000000LL + 3600000000000LL, numpy::DateTimeUnit::Nanosecond)  // 1 day + 1 hour
8719    };
8720    pandas::DatetimeArray arr(values);
8721    pandas::DatetimeMixinIndex idx(arr);
8722
8723    pandas::DatetimeMixinIndex normalized = idx.normalize();
8724
8725    bool passed = (normalized.size() == 1);
8726    if (!passed) {
8727        std::cout << "  [FAIL] : in pd_test_datetime_mixin_normalize()" << std::endl;
8728        throw std::runtime_error("pd_test_datetime_mixin_normalize failed");
8729    }
8730
8731    std::cout << " -> tests passed" << std::endl;
8732}
quarter (pd_test_1_all.cpp:1218)
1208    void pd_test_datetime_array_quarter() {
1209        std::cout << "========= DatetimeArray: quarter ======================= ";
1210
1211        pandas::DatetimeArray arr(std::vector<std::string>{
1212            "2023-01-15",  // Q1
1213            "2023-05-20",  // Q2
1214            "2023-09-10",  // Q3
1215            "2023-11-25"   // Q4
1216        });
1217
1218        auto quarters = arr.quarter();
1219
1220        auto q0 = quarters[0];
1221        if (!q0.has_value() || q0.value() != 1) {
1222            std::cout << "  [FAIL] : quarter[0] should be 1" << std::endl;
1223            throw std::runtime_error("pd_test_datetime_array_quarter failed: quarter[0]");
1224        }
1225        auto q1 = quarters[1];
1226        if (!q1.has_value() || q1.value() != 2) {
1227            std::cout << "  [FAIL] : quarter[1] should be 2" << std::endl;
1228            throw std::runtime_error("pd_test_datetime_array_quarter failed: quarter[1]");
result (pd_test_1_all.cpp:15406)
15396    data.setElementAt({0}, numpy::datetime64(100LL, numpy::DateTimeUnit::Nanosecond));
15397    data.setElementAt({1}, numpy::datetime64(200LL, numpy::DateTimeUnit::Nanosecond));
15398
15399    numpy::NDArray<numpy::bool_> mask(std::vector<size_t>{2});
15400    mask.setElementAt({0}, numpy::bool_(false));
15401    mask.setElementAt({1}, numpy::bool_(false));
15402
15403    pandas::DatetimeArray arr(data, mask);
15404    pandas::DatetimeIndexBase idx(arr, "original");
15405
15406    // Create join result (int64 values)
15407    numpy::NDArray<numpy::int64> join_result(std::vector<size_t>{3});
15408    join_result.setElementAt({0}, numpy::int64(500LL));
15409    join_result.setElementAt({1}, numpy::int64(600LL));
15410    join_result.setElementAt({2}, numpy::int64(700LL));
15411
15412    auto new_idx = idx._from_join_target(join_result);
15413
15414    bool passed = (new_idx.size() == 3 &&
15415                   new_idx.name().has_value() && *new_idx.name() == "original");
15416    if (!passed) {
result (pd_test_1_all.cpp:15406)
15396    data.setElementAt({0}, numpy::datetime64(100LL, numpy::DateTimeUnit::Nanosecond));
15397    data.setElementAt({1}, numpy::datetime64(200LL, numpy::DateTimeUnit::Nanosecond));
15398
15399    numpy::NDArray<numpy::bool_> mask(std::vector<size_t>{2});
15400    mask.setElementAt({0}, numpy::bool_(false));
15401    mask.setElementAt({1}, numpy::bool_(false));
15402
15403    pandas::DatetimeArray arr(data, mask);
15404    pandas::DatetimeIndexBase idx(arr, "original");
15405
15406    // Create join result (int64 values)
15407    numpy::NDArray<numpy::int64> join_result(std::vector<size_t>{3});
15408    join_result.setElementAt({0}, numpy::int64(500LL));
15409    join_result.setElementAt({1}, numpy::int64(600LL));
15410    join_result.setElementAt({2}, numpy::int64(700LL));
15411
15412    auto new_idx = idx._from_join_target(join_result);
15413
15414    bool passed = (new_idx.size() == 3 &&
15415                   new_idx.name().has_value() && *new_idx.name() == "original");
15416    if (!passed) {
result (pd_test_1_all.cpp:15406)
15396    data.setElementAt({0}, numpy::datetime64(100LL, numpy::DateTimeUnit::Nanosecond));
15397    data.setElementAt({1}, numpy::datetime64(200LL, numpy::DateTimeUnit::Nanosecond));
15398
15399    numpy::NDArray<numpy::bool_> mask(std::vector<size_t>{2});
15400    mask.setElementAt({0}, numpy::bool_(false));
15401    mask.setElementAt({1}, numpy::bool_(false));
15402
15403    pandas::DatetimeArray arr(data, mask);
15404    pandas::DatetimeIndexBase idx(arr, "original");
15405
15406    // Create join result (int64 values)
15407    numpy::NDArray<numpy::int64> join_result(std::vector<size_t>{3});
15408    join_result.setElementAt({0}, numpy::int64(500LL));
15409    join_result.setElementAt({1}, numpy::int64(600LL));
15410    join_result.setElementAt({2}, numpy::int64(700LL));
15411
15412    auto new_idx = idx._from_join_target(join_result);
15413
15414    bool passed = (new_idx.size() == 3 &&
15415                   new_idx.name().has_value() && *new_idx.name() == "original");
15416    if (!passed) {
round (pd_test_1_all.cpp:1688)
1678    void pd_test_floating_array_rounding() {
1679        std::cout << "========= FloatingArray: rounding ======================= ";
1680
1681        pandas::FloatingArray<double> arr({
1682            std::optional<double>(1.234),
1683            std::optional<double>(2.567),
1684            std::nullopt
1685        });
1686
1687        auto rounded = arr.round(2);
1688        if (std::abs(rounded[0].value() - 1.23) > 0.001 ||
1689            std::abs(rounded[1].value() - 2.57) > 0.001) {
1690            std::cout << "  [FAIL] : in pd_test_floating_array_rounding() : round(2)" << std::endl;
1691            throw std::runtime_error("pd_test_floating_array_rounding failed: round(2)");
1692        }
1693
1694        if (!rounded.is_na(2)) {
1695            std::cout << "  [FAIL] : in pd_test_floating_array_rounding() : round should preserve NA" << std::endl;
1696            throw std::runtime_error("pd_test_floating_array_rounding failed: NA preservation");
1697        }
second (pd_test_1_all.cpp:7534)
7524    std::cout << "========= second property =============================";
7525
7526    std::vector<std::optional<numpy::datetime64>> values = {
7527        make_dt(0),                    // Second 0
7528        make_dt(30 * NS_PER_SEC),      // Second 30
7529        make_dt(59 * NS_PER_SEC)       // Second 59
7530    };
7531    pandas::DatetimeArray arr(values);
7532    pandas::DatetimeIndex idx(arr);
7533
7534    auto seconds = idx.second();
7535
7536    bool passed = (seconds.size() == 3);
7537    auto s0 = seconds[0];
7538    auto s1 = seconds[1];
7539    auto s2 = seconds[2];
7540    passed = passed && s0.has_value() && *s0 == 0;
7541    passed = passed && s1.has_value() && *s1 == 30;
7542    passed = passed && s2.has_value() && *s2 == 59;
7543
7544    if (!passed) {
set_freq (pd_test_1_all.cpp:8254)
8244void pd_test_datetime_mixin_set_freq() {
8245    std::cout << "========= set_freq ====================================";
8246
8247    std::vector<std::optional<numpy::datetime64>> values = {
8248        numpy::datetime64(0LL, numpy::DateTimeUnit::Nanosecond)
8249    };
8250    pandas::DatetimeArray arr(values);
8251    pandas::DatetimeMixinIndex idx(arr);
8252
8253    idx.set_freq("D");
8254    auto f = idx.freq();
8255
8256    bool passed = (f.has_value() && *f == "D");
8257    if (!passed) {
8258        std::cout << "  [FAIL] : in pd_test_datetime_mixin_set_freq()" << std::endl;
8259        throw std::runtime_error("pd_test_datetime_mixin_set_freq failed");
8260    }
8261
8262    std::cout << " -> tests passed" << std::endl;
8263}
snap (pd_test_1_all.cpp:8364)
8354void pd_test_datetime_mixin_snap() {
8355    std::cout << "========= snap ========================================";
8356
8357    std::vector<std::optional<numpy::datetime64>> values = {
8358        numpy::datetime64(1000000000123456789LL, numpy::DateTimeUnit::Nanosecond)
8359    };
8360    pandas::DatetimeArray arr(values);
8361    pandas::DatetimeMixinIndex idx(arr);
8362
8363    pandas::DatetimeMixinIndex snapped = idx.snap("s");
8364
8365    bool passed = (snapped.size() == 1);
8366    if (!passed) {
8367        std::cout << "  [FAIL] : in pd_test_datetime_mixin_snap()" << std::endl;
8368        throw std::runtime_error("pd_test_datetime_mixin_snap failed");
8369    }
8370
8371    std::cout << " -> tests passed" << std::endl;
8372}
strftime (pd_test_1_all.cpp:8434)
8424void pd_test_datetime_mixin_strftime() {
8425    std::cout << "========= strftime ====================================";
8426
8427    std::vector<std::optional<numpy::datetime64>> values = {
8428        numpy::datetime64(0LL, numpy::DateTimeUnit::Nanosecond),  // 1970-01-01
8429        numpy::datetime64(86400000000000LL, numpy::DateTimeUnit::Nanosecond)  // 1970-01-02
8430    };
8431    pandas::DatetimeArray arr(values);
8432    pandas::DatetimeMixinIndex idx(arr);
8433
8434    std::vector<std::string> formatted = idx.strftime("%Y-%m-%d");
8435
8436    bool passed = (formatted.size() == 2 && !formatted[0].empty() && !formatted[1].empty());
8437    if (!passed) {
8438        std::cout << "  [FAIL] : in pd_test_datetime_mixin_strftime()" << std::endl;
8439        throw std::runtime_error("pd_test_datetime_mixin_strftime failed");
8440    }
8441
8442    std::cout << " -> tests passed" << std::endl;
8443}
td_days (pd_test_1_all.cpp:8603)
8593void pd_test_timedelta_mixin_days() {
8594    std::cout << "========= days component (Timedelta) ==================";
8595
8596    std::vector<std::optional<numpy::timedelta64>> values = {
8597        numpy::timedelta64(172800000000000LL, numpy::DateTimeUnit::Nanosecond)  // 2 days
8598    };
8599    pandas::TimedeltaArray arr(values);
8600    pandas::TimedeltaMixinIndex idx(arr);
8601
8602    pandas::IntegerArray<numpy::int64> days = idx.td_days();
8603
8604    bool passed = (days.size() == 1 && days[0].has_value() && *days[0] == 2);
8605    if (!passed) {
8606        int64_t got = days[0].has_value() ? *days[0] : -1;
8607        std::cout << "  [FAIL] : in pd_test_timedelta_mixin_days() expected 2, got " << got << std::endl;
8608        throw std::runtime_error("pd_test_timedelta_mixin_days failed");
8609    }
8610
8611    std::cout << " -> tests passed" << std::endl;
8612}
total_seconds (pd_test_1_all.cpp:4224)
4214    void pd_test_timedelta_array_total_seconds() {
4215        std::cout << "========= TimedeltaArray: total_seconds ======================= ";
4216
4217        pandas::TimedeltaArray arr({
4218            std::optional<numpy::timedelta64>(numpy::timedelta64(1, numpy::DateTimeUnit::Day)),
4219            std::optional<numpy::timedelta64>(numpy::timedelta64(1, numpy::DateTimeUnit::Hour)),
4220            std::nullopt
4221        });
4222
4223        auto total = arr.total_seconds();
4224
4225        auto t0 = total[0];
4226        if (!t0.has_value() || std::abs(t0.value() - 86400.0) > 0.001) {
4227            std::cout << "  [FAIL] : total_seconds[0] should be 86400" << std::endl;
4228            throw std::runtime_error("pd_test_timedelta_array_total_seconds failed: total_seconds[0]");
4229        }
4230
4231        auto t1 = total[1];
4232        if (!t1.has_value() || std::abs(t1.value() - 3600.0) > 0.001) {
4233            std::cout << "  [FAIL] : total_seconds[1] should be 3600" << std::endl;
type_id (pd_test_3_all.cpp:25592)
25582// ------------------- pd_test_value_classify (end) ------------------
25583
25584// ------------------- pd_test_index_type_id (start) ------------------
25585namespace dataframe_tests_index_type_id {
25586
25587void pd_test_index_type_id_dispatch() {
25588    std::cout << "========= IndexTypeId dispatch =======================";
25589
25590    // RangeIndex
25591    ::pandas::RangeIndex ri(0, 5);
25592    if (ri.type_id() != ::pandas::IndexTypeId::RangeIndex)
25593        throw std::runtime_error("RangeIndex type_id failed");
25594
25595    // Index<string>
25596    ::pandas::Index<std::string> si(std::vector<std::string>{"a", "b", "c"});
25597    if (si.type_id() != ::pandas::IndexTypeId::IndexString)
25598        throw std::runtime_error("Index<string> type_id failed");
25599
25600    // Index<int64>
25601    ::pandas::Index<numpy::int64> ii(std::vector<numpy::int64>{1, 2, 3});
25602    if (ii.type_id() != ::pandas::IndexTypeId::IndexInt64)
tz (pd_test_2_all.cpp:17914)
17904            pandas::DataFrame df(data);
17905            df.set_index(std::make_unique<pandas::DatetimeIndex>(tz_aware_idx));
17906
17907            // Verify the index is timezone-aware
17908            const pandas::DatetimeIndex* original_idx = dynamic_cast<const pandas::DatetimeIndex*>(&df.index());
17909            if (!original_idx) {
17910                std::cout << "  [FAIL] : in pd_test_tz_convert_basic() : index is not DatetimeIndex" << std::endl;
17911                throw std::runtime_error("pd_test_tz_convert_basic failed: index is not DatetimeIndex");
17912            }
17913
17914            std::string original_tz = original_idx->tz();
17915            if (original_tz.empty()) {
17916                std::cout << "  [FAIL] : in pd_test_tz_convert_basic() : original index is not timezone-aware" << std::endl;
17917                throw std::runtime_error("pd_test_tz_convert_basic failed: original index is not timezone-aware");
17918            }
17919
17920            // Convert to Asia/Shanghai timezone
17921            pandas::DataFrame df_shanghai = df.tz_convert("Asia/Shanghai");
17922
17923            // Verify result has a DatetimeIndex
17924            const pandas::DatetimeIndex* converted_idx = dynamic_cast<const pandas::DatetimeIndex*>(&df_shanghai.index());
tz_aware (pd_test_1_all.cpp:8745)
8735    std::cout << "========= tz_localize =================================";
8736
8737    std::vector<std::optional<numpy::datetime64>> values = {
8738        numpy::datetime64(0LL, numpy::DateTimeUnit::Nanosecond)
8739    };
8740    pandas::DatetimeArray arr(values);
8741    pandas::DatetimeMixinIndex idx(arr);
8742
8743    pandas::DatetimeMixinIndex localized = idx.tz_localize("UTC");
8744
8745    bool passed = (localized.size() == 1 && localized.tz_aware());
8746    if (!passed) {
8747        std::cout << "  [FAIL] : in pd_test_datetime_mixin_tz_localize()" << std::endl;
8748        throw std::runtime_error("pd_test_datetime_mixin_tz_localize failed");
8749    }
8750
8751    std::cout << " -> tests passed" << std::endl;
8752}
8753
8754// ============================================================================
8755// Conversion Method Tests
week (pd_test_timestamp_scalar.cpp:406)
396      // 2024-06-15 is a Saturday
397      pandas::Timestamp ts(2024, 6, 15);
398
399      if (ts.dayofweek() != 5) { pass = false; fail_msg = "2024-06-15 should be Saturday (5)"; }
400      if (ts.day_of_week() != 5) { pass = false; fail_msg = "day_of_week alias"; }
401      if (ts.dayofyear() != 167) { pass = false; fail_msg = "2024-06-15 should be day 167"; }
402      if (ts.day_of_year() != 167) { pass = false; fail_msg = "day_of_year alias"; }
403      if (ts.quarter() != 2) { pass = false; fail_msg = "June is Q2"; }
404      if (ts.days_in_month() != 30) { pass = false; fail_msg = "June has 30 days"; }
405
406      int week = ts.week();
407      if (week < 1 || week > 53) { pass = false; fail_msg = "week should be 1-53"; }
408
409      if (!pass) {
410        std::cout << "  [FAIL] : in np_test_timestamp_derived() : " << fail_msg;
411        throw std::runtime_error("np_test_timestamp_derived failed: " + fail_msg);
412      }
413
414      std::cout << " -> tests passed" << std::endl;
415    }
weekday (pd_test_3_all.cpp:1471)
1461            std::cout << "  [FAIL] date_range 10-arg form: expected size 10, got "
1462                      << idx.size() << std::endl;
1463            throw std::runtime_error("date_range 10-arg form regressed");
1464        }
1465    }
1466
1467    std::cout << " -> tests passed" << std::endl;
1468}
1469
1470void pd_test_3_all_period_weekday() {
1471    std::cout << "========= PeriodArray.weekday() ======================";
1472
1473    // Create a PeriodArray with some dates
1474    std::vector<std::optional<numpy::int64>> ordinals = {0, 1, 2, 3, 4};  // Days from epoch
1475    pandas::PeriodArray arr(ordinals, "D");
1476
1477    pandas::IntegerArray<numpy::int32> weekdays = arr.weekday();
1478
1479    if (weekdays.size() != 5) {
1480        std::cout << "  [FAIL] : in pd_test_3_all_period_weekday() : size should be 5" << std::endl;
1481        throw std::runtime_error("pd_test_3_all_period_weekday failed: size");
year (pd_test_1_all.cpp:1147)
1137    void pd_test_datetime_array_component_year() {
1138        std::cout << "========= DatetimeArray: year component ======================= ";
1139
1140        pandas::DatetimeArray arr(std::vector<std::string>{
1141            "2020-01-15",
1142            "NaT",
1143            "2025-06-20"
1144        });
1145
1146        auto years = arr.year();
1147
1148        auto y0 = years[0];
1149        if (!y0.has_value() || y0.value() != 2020) {
1150            std::cout << "  [FAIL] : year[0] should be 2020" << std::endl;
1151            throw std::runtime_error("pd_test_datetime_array_component_year failed: year[0]");
1152        }
1153
1154        auto y1 = years[1];
1155        if (y1.has_value()) {
1156            std::cout << "  [FAIL] : year[1] should be NA (NaT)" << std::endl;