Timestamp#

class pandas::Timestamp#

pandas C++ class.

Example#

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

// Use Timestamp
Timestamp obj;
// ... operations ...

Constructors#

Signature

Location

Example

explicit Timestamp(int64_t value, const std::string& tz = "")

pd_timestamp.h:158

View

explicit Timestamp(const std::string& ts_input, const std::string& tz = "")

pd_timestamp.h:215

View

explicit Timestamp(const numpy::datetime64& dt, const std::string& tz = "")

pd_timestamp.h:449

View

explicit Timestamp(const std::chrono::time_point<std::chrono::system_clock, Duration>& tp, const std::string& tz = "")

pd_timestamp.h:468

View

Data Manipulation#

Signature

Return Type

Location

Example

Timestamp replace(int new_year = -1, int new_month = -1, int new_day = -1, int new_hour = -1, int new_minute = -1, int new_second = -1, int new_microsecond = -1, int new_nanosecond = -1, const std::string& new_tzinfo = "", int new_fold = -1) const

Timestamp

pd_timestamp.h:1427

View

Statistics#

Signature

Return Type

Location

Example

static Timestamp max()

static Timestamp

pd_timestamp.h:801

View

static Timestamp min()

static Timestamp

pd_timestamp.h:797

View

int minute() const

int

pd_timestamp.h:845

View

Comparison#

Signature

Return Type

Location

Example

Timestamp next_jan4_ts(next_jan4_nanos, "")

Timestamp

pd_timestamp.h:1286

Combining#

Signature

Return Type

Location

Example

static Timestamp combine(const std::tuple<int,int,int>& date, const std::tuple<int,int,int,int>& time)

static Timestamp

pd_timestamp.h:535

View

Time Series#

Signature

Return Type

Location

Example

Timestamp tz_convert(const std::string& tz_str) const

Timestamp

pd_timestamp.h:1517

View

Timestamp tz_localize(const std::string& tz_str) const

Timestamp

pd_timestamp.h:1487

View

I/O#

Signature

Return Type

Location

Example

numpy::datetime64 to_datetime64() const

numpy::datetime64

pd_timestamp.h:1018

View

double to_julian_date() const

double

pd_timestamp.h:1103

View

int64_t to_numpy(const std::string& dtype = "numpy::datetime64[ns]", bool copy = false) const

int64_t

pd_timestamp.h:1032

View

std::tm to_pydatetime() const

std::tm

pd_timestamp.h:1039

View

Type Checking#

Signature

Return Type

Location

Example

bool is_leap_year() const

bool

pd_timestamp.h:941

View

bool is_month_end() const

bool

pd_timestamp.h:953

View

bool is_month_start() const

bool

pd_timestamp.h:947

View

bool is_quarter_end() const

bool

pd_timestamp.h:965

View

bool is_quarter_start() const

bool

pd_timestamp.h:959

View

bool is_year_end() const

bool

pd_timestamp.h:977

View

bool is_year_start() const

bool

pd_timestamp.h:971

View

Other Methods#

Signature

Return Type

Location

Example

static Timestamp NaT()

static Timestamp

pd_timestamp.h:805

View

Timestamp as_unit(const std::string& unit, bool round_ok = true) const

Timestamp

pd_timestamp.h:1458

View

numpy::datetime64 asm8() const { return to_datetime64()

numpy::datetime64

pd_timestamp.h:1024

View

Timestamp astimezone(const std::string& tz_str) const

Timestamp

pd_timestamp.h:1539

View

Timestamp ceil(const std::string& freq, const std::string& ambiguous = "raise", const std::string& nonexistent = "raise") const

Timestamp

pd_timestamp.h:1348

View

static int64_t componentsToNanos(int year, int month, int day, int hour, int minute, int second, int microsecond, int nanosecond)

static int64_t

pd_timestamp.h:40

View

void computeComponents() const

void

pd_timestamp.h:92

std::string ctime() const

std::string

pd_timestamp.h:1212

View

std::tuple<int,int,int> date() const

std::tuple<int,int,int>

pd_timestamp.h:1081

View

int day() const

int

pd_timestamp.h:833

View

std::string day_name(const std::string& locale = "") const

std::string

pd_timestamp.h:1217

View

int day_of_week() const { return dayofweek()

int

pd_timestamp.h:895

View

int day_of_year() const { return dayofyear()

int

pd_timestamp.h:912

View

int dayofweek() const

int

pd_timestamp.h:878

View

int dayofyear() const

int

pd_timestamp.h:898

View

int days_in_month() const

int

pd_timestamp.h:931

View

numpy::timedelta64 dst() const

numpy::timedelta64

pd_timestamp.h:1553

View

static int64_t float_epoch_to_nanos(double val, int64_t ns_mult)

static int64_t

pd_timestamp.h:505

Timestamp floor(const std::string& freq, const std::string& ambiguous = "raise", const std::string& nonexistent = "raise") const

Timestamp

pd_timestamp.h:1320

View

int fold() const

int

pd_timestamp.h:1009

View

static Timestamp fromisocalendar(int iso_year, int iso_week, int iso_weekday)

static Timestamp

pd_timestamp.h:544

View

static Timestamp fromisoformat(const std::string& date_string)

static Timestamp

pd_timestamp.h:574

View

static Timestamp fromordinal(int64_t ordinal, const std::string& tz = "")

static Timestamp

pd_timestamp.h:526

View

static Timestamp fromtimestamp(double ts, const std::string& tz = "")

static Timestamp

pd_timestamp.h:515

View

int hour() const

int

pd_timestamp.h:839

View

void invalidateCache()

void

pd_timestamp.h:88

bool isNaT() const

bool

pd_timestamp.h:813

View

std::tuple<int,int,int> isocalendar() const

std::tuple<int,int,int>

pd_timestamp.h:1241

View

std::string isoformat(char sep = 'T', const std::string& timespec = "auto") const

std::string

pd_timestamp.h:1153

View

int isoweekday() const

int

pd_timestamp.h:1300

View

Timestamp jan4_ts(jan4_nanos, "")

Timestamp

pd_timestamp.h:555

Timestamp jan4_ts(jan4_nanos, "")

Timestamp

pd_timestamp.h:1263

int microsecond() const

int

pd_timestamp.h:857

View

int month() const

int

pd_timestamp.h:827

View

std::string month_name(const std::string& locale = "") const

std::string

pd_timestamp.h:1225

View

int nanosecond() const

int

pd_timestamp.h:863

View

Timestamp normalize() const

Timestamp

pd_timestamp.h:1405

View

static Timestamp now(const std::string& tz = "")

static Timestamp

pd_timestamp.h:486

View

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

static int64_t

pd_timestamp.h:136

Timestamp prev_jan4_ts(prev_jan4_nanos, "")

Timestamp

pd_timestamp.h:1278

int quarter() const

int

pd_timestamp.h:924

View

Timestamp result(floored, "")

Timestamp

pd_timestamp.h:1335

View

Timestamp result(floored, "")

Timestamp

pd_timestamp.h:1366

View

Timestamp result(rounded, "")

Timestamp

pd_timestamp.h:1399

View

Timestamp result(value_, "")

Timestamp

pd_timestamp.h:1476

View

Timestamp result(utc_value, "")

Timestamp

pd_timestamp.h:1510

View

Timestamp result(value_, "")

Timestamp

pd_timestamp.h:1526

View

Timestamp result(value_, "")

Timestamp

pd_timestamp.h:1533

View

Timestamp result(value_ + delta_nanos, "")

Timestamp

pd_timestamp.h:1581

View

Timestamp result(value_ - delta_nanos, "")

Timestamp

pd_timestamp.h:1595

View

Timestamp result(value_ + delta.value(), "")

Timestamp

pd_timestamp.h:1628

View

Timestamp result(value_ - delta.value(), "")

Timestamp

pd_timestamp.h:1640

View

Timestamp round(const std::string& freq, const std::string& ambiguous = "raise", const std::string& nonexistent = "raise") const

Timestamp

pd_timestamp.h:1379

View

int second() const

int

pd_timestamp.h:851

View

void setTimezone(std::shared_ptr<numpy::TimezoneInfo> tz)

void

pd_timestamp.h:997

View

std::istringstream ss(input)

std::istringstream

pd_timestamp.h:392

View

std::string strftime(const std::string& format) const

std::string

pd_timestamp.h:1203

View

static Timestamp strptime(const std::string& date_string, const std::string& format)

static Timestamp

pd_timestamp.h:601

View

Timestamp thu_ts(value_ + static_cast<int64_t>(days_to_thursday) \* NANOS_PER_DAY, "")

Timestamp

pd_timestamp.h:1257

std::tuple<int,int,int,int> time() const

std::tuple<int,int,int,int>

pd_timestamp.h:1088

View

double timestamp() const

double

pd_timestamp.h:1058

View

std::tuple<int,int,int,int,int,int> timetuple() const

std::tuple<int,int,int,int,int,int>

pd_timestamp.h:1064

View

std::tuple<int,int,int,int,int> timetz() const

std::tuple<int,int,int,int,int>

pd_timestamp.h:1095

View

std::shared_ptr<numpy::TimezoneInfo> timezone() const

std::shared_ptr<numpy::TimezoneInfo>

pd_timestamp.h:995

View

std::string toString() const

std::string

pd_timestamp.h:1130

View

std::string toStringWithTime() const

std::string

pd_timestamp.h:1147

static Timestamp today(const std::string& tz = "")

static Timestamp

pd_timestamp.h:494

View

int toordinal() const

int

pd_timestamp.h:1112

View

std::string tz() const

std::string

pd_timestamp.h:987

View

std::string tz_display() const

std::string

pd_timestamp.h:991

std::string tzinfo() const { return tz()

std::string

pd_timestamp.h:1002

View

std::string tzname() const

std::string

pd_timestamp.h:1004

View

Timestamp utc_ts(value_, "")

Timestamp

pd_timestamp.h:1074

static Timestamp utcfromtimestamp(double ts)

static Timestamp

pd_timestamp.h:521

View

static Timestamp utcnow()

static Timestamp

pd_timestamp.h:500

View

numpy::timedelta64 utcoffset() const

numpy::timedelta64

pd_timestamp.h:1544

View

std::tuple<int,int,int,int,int,int> utctimetuple() const

std::tuple<int,int,int,int,int,int>

pd_timestamp.h:1070

View

int64_t value() const

int64_t

pd_timestamp.h:869

View

int week() const

int

pd_timestamp.h:915

View

int weekday() const

int

pd_timestamp.h:1305

View

int weekofyear() const { return week()

int

pd_timestamp.h:921

int year() const

int

pd_timestamp.h:821

View

Code Examples#

The following examples are extracted from the test suite.

Timestamp (pd_test_3_all.cpp:1501)
1491            }
1492        }
1493    }
1494
1495    std::cout << " -> tests passed" << std::endl;
1496}
1497
1498void pd_test_3_all_pandas_numpy_type_aliases() {
1499    std::cout << "========= pandas:: aliases for numpy time types =====";
1500
1501    // Case A: pandas::Timestamp == pandas::Timestamp (same type)
1502    {
1503        static_assert(std::is_same_v<pandas::Timestamp, pandas::Timestamp>,
1504                      "pandas::Timestamp must alias pandas::Timestamp");
1505        pandas::Timestamp ts1("2021-01-01");
1506        pandas::Timestamp  ts2("2021-01-01");
1507        // Stronger than toString()-comparison: pandas::Timestamp::operator==
1508        // compares the underlying nanosecond value directly, catching
1509        // representation drift that string formatting could mask.
1510        if (!(ts1 == ts2)) {
1511            throw std::runtime_error("alias Timestamp: ctor value mismatch");
Timestamp (pd_test_3_all.cpp:1501)
1491            }
1492        }
1493    }
1494
1495    std::cout << " -> tests passed" << std::endl;
1496}
1497
1498void pd_test_3_all_pandas_numpy_type_aliases() {
1499    std::cout << "========= pandas:: aliases for numpy time types =====";
1500
1501    // Case A: pandas::Timestamp == pandas::Timestamp (same type)
1502    {
1503        static_assert(std::is_same_v<pandas::Timestamp, pandas::Timestamp>,
1504                      "pandas::Timestamp must alias pandas::Timestamp");
1505        pandas::Timestamp ts1("2021-01-01");
1506        pandas::Timestamp  ts2("2021-01-01");
1507        // Stronger than toString()-comparison: pandas::Timestamp::operator==
1508        // compares the underlying nanosecond value directly, catching
1509        // representation drift that string formatting could mask.
1510        if (!(ts1 == ts2)) {
1511            throw std::runtime_error("alias Timestamp: ctor value mismatch");
Timestamp (pd_test_3_all.cpp:1501)
1491            }
1492        }
1493    }
1494
1495    std::cout << " -> tests passed" << std::endl;
1496}
1497
1498void pd_test_3_all_pandas_numpy_type_aliases() {
1499    std::cout << "========= pandas:: aliases for numpy time types =====";
1500
1501    // Case A: pandas::Timestamp == pandas::Timestamp (same type)
1502    {
1503        static_assert(std::is_same_v<pandas::Timestamp, pandas::Timestamp>,
1504                      "pandas::Timestamp must alias pandas::Timestamp");
1505        pandas::Timestamp ts1("2021-01-01");
1506        pandas::Timestamp  ts2("2021-01-01");
1507        // Stronger than toString()-comparison: pandas::Timestamp::operator==
1508        // compares the underlying nanosecond value directly, catching
1509        // representation drift that string formatting could mask.
1510        if (!(ts1 == ts2)) {
1511            throw std::runtime_error("alias Timestamp: ctor value mismatch");
Timestamp (pd_test_3_all.cpp:1501)
1491            }
1492        }
1493    }
1494
1495    std::cout << " -> tests passed" << std::endl;
1496}
1497
1498void pd_test_3_all_pandas_numpy_type_aliases() {
1499    std::cout << "========= pandas:: aliases for numpy time types =====";
1500
1501    // Case A: pandas::Timestamp == pandas::Timestamp (same type)
1502    {
1503        static_assert(std::is_same_v<pandas::Timestamp, pandas::Timestamp>,
1504                      "pandas::Timestamp must alias pandas::Timestamp");
1505        pandas::Timestamp ts1("2021-01-01");
1506        pandas::Timestamp  ts2("2021-01-01");
1507        // Stronger than toString()-comparison: pandas::Timestamp::operator==
1508        // compares the underlying nanosecond value directly, catching
1509        // representation drift that string formatting could mask.
1510        if (!(ts1 == ts2)) {
1511            throw std::runtime_error("alias Timestamp: ctor value mismatch");
replace (pd_test_1_all.cpp:6623)
6613                }
6614            }
6615
6616            // Test replace
6617            {
6618                std::map<std::string, std::vector<numpy::float64>> float_data;
6619                float_data["X"] = {1.0, 2.0, 3.0};
6620                float_data["Y"] = {2.0, 2.0, 4.0};
6621                pandas::DataFrame df_repl(float_data);
6622
6623                auto replaced = df_repl.replace(2.0, 99.0);
6624                // Check some value was replaced (crude check via string)
6625                std::string val_str = replaced.col<numpy::float64>("X").get_value_str(1);
6626                if (val_str.find("99") == std::string::npos) {
6627                    std::cout << "  [FAIL] : in pd_test_dataframe_manipulation() : replace didn't work" << std::endl;
6628                    throw std::runtime_error("pd_test_dataframe_manipulation failed: replace");
6629                }
6630            }
6631
6632            // Test drop_duplicates
6633            {
max (pd_test_1_all.cpp:771)
761        pandas::CategoricalArray arr = pandas::CategoricalArray::from_codes(codes, cats, true);  // ordered
762
763        // Test min
764        std::optional<std::string> min_val = arr.min();
765        if (!min_val.has_value() || *min_val != "low") {
766            std::cout << "  [FAIL] : in pd_test_categorical_array_ordered_operations() : min != 'low'" << std::endl;
767            throw std::runtime_error("pd_test_categorical_array_ordered_operations failed: min != 'low'");
768        }
769
770        // Test max
771        std::optional<std::string> max_val = arr.max();
772        if (!max_val.has_value() || *max_val != "high") {
773            std::cout << "  [FAIL] : in pd_test_categorical_array_ordered_operations() : max != 'high'" << std::endl;
774            throw std::runtime_error("pd_test_categorical_array_ordered_operations failed: max != 'high'");
775        }
776
777        // Test unordered throws for min/max
778        pandas::CategoricalArray unordered = arr.as_unordered();
779        bool threw = false;
780        try {
781            unordered.min();
min (pd_test_1_all.cpp:764)
754    }
755
756    void pd_test_categorical_array_ordered_operations() {
757        std::cout << "========= CategoricalArray: ordered operations (min/max) ======================= ";
758
759        std::vector<std::string> cats = {"low", "medium", "high"};
760        std::vector<numpy::int32> codes = {0, 2, 1, 0, -1};  // low, high, medium, low, NA
761        pandas::CategoricalArray arr = pandas::CategoricalArray::from_codes(codes, cats, true);  // ordered
762
763        // Test min
764        std::optional<std::string> min_val = arr.min();
765        if (!min_val.has_value() || *min_val != "low") {
766            std::cout << "  [FAIL] : in pd_test_categorical_array_ordered_operations() : min != 'low'" << std::endl;
767            throw std::runtime_error("pd_test_categorical_array_ordered_operations failed: min != 'low'");
768        }
769
770        // Test max
771        std::optional<std::string> max_val = arr.max();
772        if (!max_val.has_value() || *max_val != "high") {
773            std::cout << "  [FAIL] : in pd_test_categorical_array_ordered_operations() : max != 'high'" << std::endl;
774            throw std::runtime_error("pd_test_categorical_array_ordered_operations failed: max != 'high'");
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) {
combine (pd_test_2_all.cpp:1700)
1690        std::cout << "====================================== [OK] pd_test_between_time test suite ========================== " << std::endl;
1691        return 0;
1692    }
1693
1694} // namespace dataframe_tests
1695// ------------------- pd_test_between_time.cpp (end) -----------------------------
1696
1697// ------------------- pd_test_combine.cpp (start) -----------------------------
1698// dataframe_tests/pd_test_combine.cpp
1699// Test for DataFrame.combine() - column-wise combine with another DataFrame
1700
1701#include <iostream>
1702#include <cmath>
1703#include <stdexcept>
1704#include "../pandas/pd_dataframe.h"
1705
1706// CRITICAL: No using namespace directives
1707
1708namespace dataframe_tests {
1709    namespace dataframe_tests_combine {
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_datetime64 (pd_test_timestamp_scalar.cpp:507)
497    void np_test_timestamp_conversions() {
498      std::cout << "========= timestamp: conversion methods ==========================";
499
500      auto pass = true;
501      std::string fail_msg;
502
503      pandas::Timestamp ts(2024, 6, 15, 12, 30, 45, 123456, 789);
504
505      // to_datetime64
506      numpy::datetime64 dt = ts.to_datetime64();
507      if (dt.isNaT()) { pass = false; fail_msg = "to_datetime64 should not return NaT"; }
508
509      // asm8 alias
510      numpy::datetime64 asm8 = ts.asm8();
511      if (asm8.isNaT()) { pass = false; fail_msg = "asm8 should not return NaT"; }
512
513      // to_numpy
514      int64_t value = ts.to_numpy();
515      if (value == 0) { pass = false; fail_msg = "to_numpy should return non-zero"; }
to_julian_date (pd_test_3_all.cpp:20400)
20390// CRITICAL: No using namespace directives
20391
20392namespace dataframe_tests {
20393namespace dataframe_tests_misc {
20394
20395// ============================================================================
20396// Test DatetimeIndex.to_julian_date
20397// ============================================================================
20398
20399void pd_test_to_julian_date() {
20400    std::cout << "========= DatetimeIndex.to_julian_date() ==================";
20401
20402    // Create DatetimeIndex with known dates
20403    std::vector<std::optional<numpy::datetime64>> dates = {
20404        numpy::datetime64("2000-01-01"),
20405        numpy::datetime64("2000-01-02"),
20406        numpy::datetime64("2000-01-03")
20407    };
20408    pandas::DatetimeArray arr(dates);
20409    pandas::DatetimeIndex idx(arr, "test_dates");
to_numpy (pd_test_1_all.cpp:16764)
16754        // =====================================================================
16755        // to_numpy Tests
16756        // =====================================================================
16757
16758        void pd_test_ndframe_to_numpy() {
16759            std::cout << "========= to_numpy =============================================" << std::endl;
16760
16761            pandas::Series<int> s({10, 20, 30});
16762
16763            auto arr = s.to_numpy();
16764
16765            bool passed = arr.getSize() == 3;
16766            if (!passed) {
16767                std::cout << "  [FAIL] : in pd_test_ndframe_to_numpy() : size" << std::endl;
16768                throw std::runtime_error("pd_test_ndframe_to_numpy failed: size");
16769            }
16770
16771            passed = arr.getElementAt({0}) == 10 && arr.getElementAt({1}) == 20 && arr.getElementAt({2}) == 30;
16772            if (!passed) {
16773                std::cout << "  [FAIL] : in pd_test_ndframe_to_numpy() : values" << std::endl;
to_pydatetime (pd_test_timestamp_scalar.cpp:519)
509      // asm8 alias
510      numpy::datetime64 asm8 = ts.asm8();
511      if (asm8.isNaT()) { pass = false; fail_msg = "asm8 should not return NaT"; }
512
513      // to_numpy
514      int64_t value = ts.to_numpy();
515      if (value == 0) { pass = false; fail_msg = "to_numpy should return non-zero"; }
516
517      // to_pydatetime
518      std::tm tm = ts.to_pydatetime();
519      if (tm.tm_year != 124) { pass = false; fail_msg = "tm_year should be 124 (2024-1900)"; }
520      if (tm.tm_mon != 5) { pass = false; fail_msg = "tm_mon should be 5 (June)"; }
521
522      // timestamp
523      double posix = ts.timestamp();
524      if (posix <= 0) { pass = false; fail_msg = "POSIX timestamp should be positive"; }
525
526      // timetuple
527      auto [y, mo, d, h, mi, s] = ts.timetuple();
528      if (y != 2024 || mo != 6) { pass = false; fail_msg = "timetuple values incorrect"; }
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_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
NaT (pd_test_1_all.cpp:1305)
1295        pandas::DatetimeArray arr(std::vector<std::string>{
1296            "2023-06-15",
1297            "NaT",
1298            "2023-01-01",
1299            "2023-12-31"
1300        });
1301
1302        // argsort ascending
1303        auto indices = arr.argsort(true, "last");
1304        // Expected order: 2023-01-01(2), 2023-06-15(0), 2023-12-31(3), NaT(1)
1305        if (indices.getElementAt({0}) != 2) {
1306            std::cout << "  [FAIL] : argsort: first should be index 2 (2023-01-01)" << std::endl;
1307            throw std::runtime_error("pd_test_datetime_array_sorting failed: argsort first");
1308        }
1309        if (indices.getElementAt({3}) != 1) {
1310            std::cout << "  [FAIL] : argsort: last should be index 1 (NaT)" << std::endl;
1311            throw std::runtime_error("pd_test_datetime_array_sorting failed: NaT position");
1312        }
1313
1314        // argmin
as_unit (pd_test_1_all.cpp:9361)
9351    data.setElementAt({1}, numpy::datetime64(2000000000LL, numpy::DateTimeUnit::Nanosecond));  // 2 seconds in ns
9352
9353    numpy::NDArray<numpy::bool_> mask(std::vector<size_t>{2});
9354    mask.setElementAt({0}, numpy::bool_(false));
9355    mask.setElementAt({1}, numpy::bool_(false));
9356
9357    pandas::DatetimeArray arr(data, mask);
9358    pandas::DatetimeTDMixin idx(arr, "test");
9359
9360    // Convert to microseconds
9361    pandas::DatetimeTDMixin us_idx = idx.as_unit("us");
9362
9363    // Convert to same unit (should return identical)
9364    pandas::DatetimeTDMixin same_idx = idx.as_unit("ns");
9365
9366    bool passed = (us_idx.size() == 2 && same_idx.size() == 2 &&
9367                   us_idx.name().has_value() && *us_idx.name() == "test");
9368    if (!passed) {
9369        std::cout << "  [FAIL] : in pd_test_datetime_as_unit() : as_unit check failed" << std::endl;
9370        throw std::runtime_error("pd_test_datetime_as_unit failed");
9371    }
asm8 (pd_test_timestamp_scalar.cpp:511)
501      auto pass = true;
502      std::string fail_msg;
503
504      pandas::Timestamp ts(2024, 6, 15, 12, 30, 45, 123456, 789);
505
506      // to_datetime64
507      numpy::datetime64 dt = ts.to_datetime64();
508      if (dt.isNaT()) { pass = false; fail_msg = "to_datetime64 should not return NaT"; }
509
510      // asm8 alias
511      numpy::datetime64 asm8 = ts.asm8();
512      if (asm8.isNaT()) { pass = false; fail_msg = "asm8 should not return NaT"; }
513
514      // to_numpy
515      int64_t value = ts.to_numpy();
516      if (value == 0) { pass = false; fail_msg = "to_numpy should return non-zero"; }
517
518      // to_pydatetime
519      std::tm tm = ts.to_pydatetime();
520      if (tm.tm_year != 124) { pass = false; fail_msg = "tm_year should be 124 (2024-1900)"; }
521      if (tm.tm_mon != 5) { pass = false; fail_msg = "tm_mon should be 5 (June)"; }
astimezone (pd_test_timestamp_scalar.cpp:757)
747        fail_msg = "tz_localize should set timezone";
748      }
749
750      // tz_convert
751      pandas::Timestamp converted = localized.tz_convert("Europe/London");
752      if (converted.tz() != "Europe/London") {
753        pass = false;
754        fail_msg = "tz_convert should change timezone";
755      }
756
757      // astimezone (alias)
758      pandas::Timestamp ast = localized.astimezone("UTC");
759      if (ast.tz() != "UTC") {
760        pass = false;
761        fail_msg = "astimezone should convert to UTC";
762      }
763
764      // utcoffset
765      numpy::timedelta64 offset = localized.utcoffset();
766      if (offset.isNaT()) {
767        pass = false;
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;
componentsToNanos (pd_test_3_all.cpp:25149)
25139void pd_test_datetime_utils_nanos_epoch() {
25140    std::cout << "========= nanos_to_timestamp_str: epoch ================";
25141    auto result = pandas::datetime_utils::nanos_to_timestamp_str(0);
25142    if (result != "1970-01-01 00:00:00")
25143        throw std::runtime_error("nanos_to_str epoch: got " + result);
25144    std::cout << " -> tests passed" << std::endl;
25145}
25146
25147void pd_test_datetime_utils_nanos_simple() {
25148    std::cout << "========= nanos_to_timestamp_str: 2023-06-15 12:30:45 ==";
25149    int64_t ns = pandas::Timestamp::componentsToNanos(2023, 6, 15, 12, 30, 45, 0, 0);
25150    auto result = pandas::datetime_utils::nanos_to_timestamp_str(ns);
25151    if (result != "2023-06-15 12:30:45")
25152        throw std::runtime_error("nanos_to_str simple: got " + result);
25153    std::cout << " -> tests passed" << std::endl;
25154}
25155
25156void pd_test_datetime_utils_nanos_pre_epoch() {
25157    std::cout << "========= nanos_to_timestamp_str: pre-epoch 1960 =======";
25158    int64_t ns = pandas::Timestamp::componentsToNanos(1960, 1, 1, 0, 0, 0, 0, 0);
25159    auto result = pandas::datetime_utils::nanos_to_timestamp_str(ns);
ctime (pd_test_timestamp_scalar.cpp:583)
573      // isoformat with space separator
574      std::string iso_space = ts.isoformat(' ');
575      if (iso_space.find(" ") == std::string::npos) { pass = false; fail_msg = "isoformat with space separator"; }
576
577      // strftime
578      std::string fmt = ts.strftime("%Y/%m/%d");
579      if (fmt != "2024/06/15") { pass = false; fail_msg = "strftime format"; }
580
581      // ctime
582      std::string ct = ts.ctime();
583      if (ct.find("2024") == std::string::npos) { pass = false; fail_msg = "ctime should contain year"; }
584
585      // day_name
586      std::string dayname = ts.day_name();
587      if (dayname != "Saturday") { pass = false; fail_msg = "2024-06-15 is Saturday"; }
588
589      // month_name
590      std::string monthname = ts.month_name();
591      if (monthname != "June") { pass = false; fail_msg = "Month should be June"; }
date (pd_test_2_all.cpp:21549)
21539    std::cout << "  -- test_pivot_grouper_mixed_plain --" << std::endl;
21540
21541    // Mix plain column and grouper
21542    auto df = make_df(
21543        {"date", "region", "product", "sales"},
21544        {{"2023-01-15", "2023-01-20", "2023-02-10", "2023-02-25"},
21545         {"East", "West", "East", "West"},
21546         {"A", "B", "A", "B"},
21547         {"100", "200", "150", "250"}});
21548
21549    // index: region (plain) + date (grouper with freq)
21550    std::vector<std::pair<std::string, std::string>> idx_groupers = {
21551        {"region", ""},    // plain column (empty freq)
21552        {"date", "ME"}     // grouper with monthly freq
21553    };
21554    std::vector<std::pair<std::string, std::string>> col_groupers = {};
21555
21556    auto result = pandas::detail::pivot_table_with_grouper(
21557        df, {"sales"}, idx_groupers, col_groupers, "sum");
21558
21559    check(result.nrows() > 0, "has_rows");
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}
day_of_week (pd_test_3_all.cpp:22018)
22008void test_dt_aliases() {
22009    std::cout << "========= dt accessor aliases ====================";
22010
22011    std::vector<numpy::datetime64> dates = {
22012        numpy::datetime64("2023-01-15"),
22013        numpy::datetime64("2023-06-20")
22014    };
22015    pandas::Series<numpy::datetime64> s(dates);
22016
22017    auto dow1 = s.dt().dayofweek();
22018    auto dow2 = s.dt().day_of_week();
22019    auto dow3 = s.dt().weekday();
22020
22021    if (dow1[0] != dow2[0] || dow1[0] != dow3[0]) {
22022        throw std::runtime_error("dt aliases: dayofweek/day_of_week/weekday mismatch");
22023    }
22024
22025    auto doy1 = s.dt().dayofyear();
22026    auto doy2 = s.dt().day_of_year();
22027
22028    if (doy1[0] != doy2[0]) {
day_of_year (pd_test_3_all.cpp:22026)
22016    auto dow1 = s.dt().dayofweek();
22017    auto dow2 = s.dt().day_of_week();
22018    auto dow3 = s.dt().weekday();
22019
22020    if (dow1[0] != dow2[0] || dow1[0] != dow3[0]) {
22021        throw std::runtime_error("dt aliases: dayofweek/day_of_week/weekday mismatch");
22022    }
22023
22024    auto doy1 = s.dt().dayofyear();
22025    auto doy2 = s.dt().day_of_year();
22026
22027    if (doy1[0] != doy2[0]) {
22028        throw std::runtime_error("dt aliases: dayofyear/day_of_year mismatch");
22029    }
22030
22031    std::cout << " -> tests passed" << std::endl;
22032}
22033
22034void test_dt_days_in_month() {
22035    std::cout << "========= dt.days_in_month ======================";
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();
days_in_month (pd_test_1_all.cpp:2766)
2756            std::cout << "  [FAIL] : day[0] should be 15" << std::endl;
2757            throw std::runtime_error("pd_test_period_array_day_components failed: day[0]");
2758        }
2759        auto d1 = days[1];
2760        if (!d1.has_value() || d1.value() != 25) {
2761            std::cout << "  [FAIL] : day[1] should be 25" << std::endl;
2762            throw std::runtime_error("pd_test_period_array_day_components failed: day[1]");
2763        }
2764
2765        // Days in month
2766        auto dim = arr.days_in_month();
2767        auto dim0 = dim[0];
2768        if (!dim0.has_value() || dim0.value() != 31) {
2769            std::cout << "  [FAIL] : days_in_month[0] should be 31 (March)" << std::endl;
2770            throw std::runtime_error("pd_test_period_array_day_components failed: days_in_month[0]");
2771        }
2772        auto dim1 = dim[1];
2773        if (!dim1.has_value() || dim1.value() != 31) {
2774            std::cout << "  [FAIL] : days_in_month[1] should be 31 (December)" << std::endl;
2775            throw std::runtime_error("pd_test_period_array_day_components failed: days_in_month[1]");
2776        }
dst (pd_test_timestamp_scalar.cpp:772)
762      }
763
764      // utcoffset
765      numpy::timedelta64 offset = localized.utcoffset();
766      if (offset.isNaT()) {
767        pass = false;
768        fail_msg = "utcoffset should not be NaT";
769      }
770
771      // dst
772      numpy::timedelta64 dst_val = localized.dst();
773      if (dst_val.isNaT()) {
774        pass = false;
775        fail_msg = "dst should not be NaT";
776      }
777
778      if (!pass) {
779        std::cout << "  [FAIL] : in np_test_timestamp_tz_methods() : " << fail_msg;
780        throw std::runtime_error("np_test_timestamp_tz_methods failed: " + fail_msg);
781      }
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;
fold (pd_test_timestamp_scalar.cpp:478)
468      std::string fail_msg;
469
470      // Naive timestamp
471      pandas::Timestamp naive(2024, 6, 15);
472      if (!naive.tz().empty()) { pass = false; fail_msg = "Naive should have empty tz"; }
473      if (!naive.tzinfo().empty()) { pass = false; fail_msg = "Naive should have empty tzinfo"; }
474
475      // UTC timestamp
476      pandas::Timestamp utc(2024, 6, 15, 12, 0, 0, 0, 0, "UTC");
477      if (utc.tz() != "UTC") { pass = false; fail_msg = "Should have UTC timezone"; }
478      if (utc.fold() != 0) { pass = false; fail_msg = "fold should be 0"; }
479
480      // Named timezone
481      pandas::Timestamp ny(2024, 6, 15, 12, 0, 0, 0, 0, "America/New_York");
482      if (ny.tz() != "America/New_York") { pass = false; fail_msg = "Should have NY timezone"; }
483      std::string tzname = ny.tzname();
484      if (tzname != "EDT" && tzname != "EST") { pass = false; fail_msg = "Summer should be EDT or EST"; }
485
486      if (!pass) {
487        std::cout << "  [FAIL] : in np_test_timestamp_timezone() : " << fail_msg;
488        throw std::runtime_error("np_test_timestamp_timezone failed: " + fail_msg);
fromisocalendar (pd_test_timestamp_scalar.cpp:153)
143      }
144
145      std::cout << " -> tests passed" << std::endl;
146    }
147
148    // ============================================================================
149    // STATIC FACTORY METHODS - Plan 11 (fromisocalendar, fromisoformat, strptime)
150    // ============================================================================
151
152    void np_test_timestamp_fromisocalendar() {
153      std::cout << "========= Timestamp::fromisocalendar() ===============";
154
155      // Test 1: Basic ISO calendar to date conversion
156      // 2024, week 1, Monday (Jan 1, 2024 was Monday of week 1)
157      pandas::Timestamp ts1 = pandas::Timestamp::fromisocalendar(2024, 1, 1);
158      if (ts1.year() != 2024 || ts1.month() != 1 || ts1.day() != 1) {
159        throw std::runtime_error("fromisocalendar failed: 2024-W01-1 should be 2024-01-01");
160      }
161
162      // Test 2: 2020, week 1, Wednesday (Jan 1, 2020 was Wednesday)
163      pandas::Timestamp ts2 = pandas::Timestamp::fromisocalendar(2020, 1, 3);
fromisoformat (pd_test_timestamp_scalar.cpp:210)
200        threw = true;
201      }
202      if (!threw) {
203        throw std::runtime_error("fromisocalendar failed: should throw for weekday 8");
204      }
205
206      std::cout << " -> tests passed" << std::endl;
207    }
208
209    void np_test_timestamp_fromisoformat() {
210      std::cout << "========= Timestamp::fromisoformat() ==================";
211
212      // Test 1: Date only
213      pandas::Timestamp ts1 = pandas::Timestamp::fromisoformat("2024-03-15");
214      if (ts1.year() != 2024 || ts1.month() != 3 || ts1.day() != 15) {
215        throw std::runtime_error("fromisoformat failed: date only parsing");
216      }
217
218      // Test 2: Date and time with T separator
219      pandas::Timestamp ts2 = pandas::Timestamp::fromisoformat("2024-03-15T14:30:00");
220      if (ts2.year() != 2024 || ts2.month() != 3 || ts2.day() != 15 ||
fromordinal (pd_test_timestamp_scalar.cpp:118)
108      }
109
110      // fromtimestamp - Unix epoch
111      pandas::Timestamp epoch = pandas::Timestamp::fromtimestamp(0.0);
112      if (epoch.year() != 1970 || epoch.month() != 1 || epoch.day() != 1) {
113        pass = false;
114        fail_msg = "fromtimestamp(0) should be epoch";
115      }
116
117      // fromordinal
118      pandas::Timestamp from_ord = pandas::Timestamp::fromordinal(738678);
119      if (from_ord.isNaT()) {
120        pass = false;
121        fail_msg = "fromordinal should not return NaT";
122      }
123
124      // combine
125      pandas::Timestamp combined = pandas::Timestamp::combine({2024, 6, 15}, {12, 30, 45, 0});
126      if (combined.year() != 2024 || combined.hour() != 12) {
127        pass = false;
128        fail_msg = "combine() values incorrect";
fromtimestamp (pd_test_timestamp_scalar.cpp:111)
101      }
102
103      // utcnow()
104      pandas::Timestamp utcnow = pandas::Timestamp::utcnow();
105      if (utcnow.isNaT() || utcnow.tz() != "UTC") {
106        pass = false;
107        fail_msg = "utcnow() should have UTC timezone";
108      }
109
110      // fromtimestamp - Unix epoch
111      pandas::Timestamp epoch = pandas::Timestamp::fromtimestamp(0.0);
112      if (epoch.year() != 1970 || epoch.month() != 1 || epoch.day() != 1) {
113        pass = false;
114        fail_msg = "fromtimestamp(0) should be epoch";
115      }
116
117      // fromordinal
118      pandas::Timestamp from_ord = pandas::Timestamp::fromordinal(738678);
119      if (from_ord.isNaT()) {
120        pass = false;
121        fail_msg = "fromordinal should not return NaT";
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) {
isNaT (pd_test_3_all.cpp:1523)
1513    }
1514
1515    // Case B: pandas::Timedelta == pandas::Timedelta
1516    {
1517        static_assert(std::is_same_v<pandas::Timedelta, pandas::Timedelta>,
1518                      "pandas::Timedelta must alias pandas::Timedelta");
1519    }
1520
1521    // Case C: pandas::NaT is a NaT-valued Timestamp
1522    {
1523        if (!pandas::NaT.isNaT()) {
1524            throw std::runtime_error("pandas::NaT is not a NaT value");
1525        }
1526        pandas::Timestamp default_ts;
1527        if (default_ts.isNaT() != pandas::NaT.isNaT()) {
1528            throw std::runtime_error(
1529                "pandas::NaT and default Timestamp NaT-state mismatch");
1530        }
1531    }
1532
1533    // Case D: round-trip - reproduces the failing test pattern
isocalendar (pd_test_3_all.cpp:22134)
22124}
22125
22126void test_dt_isocalendar() {
22127    std::cout << "========= dt.isocalendar =========================";
22128
22129    std::vector<numpy::datetime64> dates = {
22130        numpy::datetime64("2023-01-15")
22131    };
22132    pandas::Series<numpy::datetime64> s(dates);
22133
22134    auto iso = s.dt().isocalendar();
22135    if (iso.year.size() != 1 || iso.week.size() != 1 || iso.day.size() != 1) {
22136        throw std::runtime_error("dt.isocalendar: wrong size");
22137    }
22138    if (iso.year[0] != 2023) {
22139        throw std::runtime_error("dt.isocalendar: wrong year");
22140    }
22141
22142    std::cout << " -> tests passed" << std::endl;
22143}
isoformat (pd_test_timedelta_scalar.cpp:465)
455        if (nat.toString() != "NaT") {
456            std::cout << "[FAIL] np_test_timedelta_strings: toString(NaT) expected 'NaT', got '"
457                      << nat.toString() << "'" << std::endl;
458            errors++;
459        }
460    }
461
462    // isoformat
463    {
464        pandas::Timedelta td(1, 2, 30);
465        std::string iso = td.isoformat();
466        if (iso.find("P1D") == std::string::npos) {
467            std::cout << "[FAIL] np_test_timedelta_strings: isoformat expected 'P1D...', got '"
468                      << iso << "'" << std::endl;
469            errors++;
470        }
471    }
472
473    // components_str
474    {
475        pandas::Timedelta td(1, 2, 30, 45, 100, 200, 300);
isoweekday (pd_test_timestamp_scalar.cpp:620)
610      std::string fail_msg;
611
612      pandas::Timestamp ts(2024, 6, 15);
613
614      // isocalendar
615      auto [iso_year, iso_week, iso_day] = ts.isocalendar();
616      if (iso_year != 2024) { pass = false; fail_msg = "ISO year should be 2024"; }
617      if (iso_week < 1 || iso_week > 53) { pass = false; fail_msg = "ISO week range"; }
618      if (iso_day < 1 || iso_day > 7) { pass = false; fail_msg = "ISO day range 1-7"; }
619
620      // isoweekday (1=Monday, 7=Sunday)
621      int isowd = ts.isoweekday();
622      if (isowd != 6) { pass = false; fail_msg = "2024-06-15 is Saturday = 6"; }
623
624      // weekday (0=Monday, 6=Sunday)
625      int wd = ts.weekday();
626      if (wd != 5) { pass = false; fail_msg = "2024-06-15 is Saturday = 5"; }
627
628      if (!pass) {
629        std::cout << "  [FAIL] : in np_test_timestamp_calendar() : " << fail_msg;
630        throw std::runtime_error("np_test_timestamp_calendar failed: " + fail_msg);
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}
now (pd_test_3_all.cpp:20077)
20067    bool passed = months_default[0] == "June" && days_default[0] == "Monday";
20068    if (!passed) {
20069        std::cout << "  [FAIL] : in pd_test_name_locale_params()" << std::endl;
20070        throw std::runtime_error("pd_test_name_locale_params failed");
20071    }
20072
20073    std::cout << " -> tests passed" << std::endl;
20074}
20075
20076// ============================================================================
20077// Test Period.now() with tz parameter
20078// ============================================================================
20079
20080void pd_test_period_now_tz() {
20081    std::cout << "========= Period.now(tz) ================================";
20082
20083    // Test now() with default (local time)
20084    auto p_local = pandas::Period::now("D");
20085
20086    // Test now() with UTC timezone
20087    auto p_utc = pandas::Period::now("D", "UTC");
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) {
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) {
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) {
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) {
setTimezone (pd_test_3_all.cpp:25427)
25417void pd_test_dst_offset_spring_forward() {
25418    std::cout << "========= apply_offset_dst_aware: spring forward =======";
25419    auto tz = numpy::getTimezone("US/Eastern");
25420    if (!tz) {
25421        std::cout << " -> skipped (timezone not available)" << std::endl;
25422        return;
25423    }
25424    // March 11, 2023 12:00 EST = 17:00 UTC
25425    int64_t utc_ns = pandas::Timestamp::componentsToNanos(2023, 3, 11, 17, 0, 0, 0, 0);
25426    pandas::Timestamp ts(utc_ns, "");
25427    ts.setTimezone(tz);
25428    pandas::DateOffset off(1, false, 0, 0, 0, 1); // +1 day
25429    auto result = pandas::datetime_utils::apply_offset_dst_aware(off, ts);
25430    if (!result.timezone())
25431        throw std::runtime_error("apply_offset DST: timezone lost");
25432    std::cout << " -> tests passed" << std::endl;
25433}
25434
25435void pd_test_dst_offset_naive_stays_naive() {
25436    std::cout << "========= apply_offset_dst_aware: naive stays naive ====";
25437    int64_t ns = pandas::Timestamp::componentsToNanos(2023, 1, 1, 0, 0, 0, 0, 0);
ss (pd_test_3_all.cpp:27670)
27660            fail++;
27661        } else {
27662            auto cats = str_s->get_cat_categories();
27663            if (cats.size() != 3) {
27664                std::cout << "    FAIL: expected 3 categories, got " << cats.size() << std::endl;
27665                fail++;
27666            }
27667        }
27668    }
27669
27670    pandas::Series<std::string> ss({"a", "b", "a", "c"}, "strs");
27671    auto result2 = ss.astype("category");
27672    auto* str_s2 = dynamic_cast<pandas::Series<std::string>*>(result2.get());
27673    if (!str_s2) {
27674        std::cout << "    FAIL: expected Series<string> for string->category" << std::endl;
27675        fail++;
27676    } else {
27677        if (str_s2->dtype_name() != "category") {
27678            std::cout << "    FAIL: dtype should be category" << std::endl;
27679            fail++;
27680        }
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}
strptime (pd_test_timestamp_scalar.cpp:280)
270        threw = true;
271      }
272      if (!threw) {
273        throw std::runtime_error("fromisoformat failed: should throw for too short string");
274      }
275
276      std::cout << " -> tests passed" << std::endl;
277    }
278
279    void np_test_timestamp_strptime() {
280      std::cout << "========= Timestamp::strptime() =======================";
281
282      // Test 1: Basic date parsing
283      pandas::Timestamp ts1 = pandas::Timestamp::strptime("2024-03-15", "%Y-%m-%d");
284      if (ts1.year() != 2024 || ts1.month() != 3 || ts1.day() != 15) {
285        throw std::runtime_error("strptime failed: basic date parsing");
286      }
287
288      // Test 2: Date and time parsing
289      pandas::Timestamp ts2 = pandas::Timestamp::strptime("2024-03-15 14:30:45", "%Y-%m-%d %H:%M:%S");
290      if (ts2.year() != 2024 || ts2.month() != 3 || ts2.day() != 15 ||
time (pd_test_timestamp_scalar.cpp:535)
525      if (posix <= 0) { pass = false; fail_msg = "POSIX timestamp should be positive"; }
526
527      // timetuple
528      auto [y, mo, d, h, mi, s] = ts.timetuple();
529      if (y != 2024 || mo != 6) { pass = false; fail_msg = "timetuple values incorrect"; }
530
531      // date/time tuples
532      auto [dy, dm, dd] = ts.date();
533      if (dy != 2024) { pass = false; fail_msg = "date tuple year"; }
534
535      auto [th, tmi, ts2, tus] = ts.time();
536      if (th != 12) { pass = false; fail_msg = "time tuple hour"; }
537
538      // Julian date
539      double jd = ts.to_julian_date();
540      if (jd < 2400000) { pass = false; fail_msg = "Julian date should be reasonable"; }
541
542      // Ordinal
543      int ord = ts.toordinal();
544      if (ord <= 0) { pass = false; fail_msg = "Ordinal should be positive"; }
timestamp (pd_test_2_all.cpp:16572)
16562            pandas::DataFrame df(data);
16563            df.set_index(std::make_unique<pandas::PeriodIndex>(period_idx));
16564
16565            // Verify it's a PeriodIndex
16566            const pandas::PeriodIndex* pidx = dynamic_cast<const pandas::PeriodIndex*>(&df.index());
16567            if (!pidx) {
16568                std::cout << "  [FAIL] : in pd_test_to_timestamp_basic() : index is not PeriodIndex" << std::endl;
16569                throw std::runtime_error("pd_test_to_timestamp_basic failed: index is not PeriodIndex");
16570            }
16571
16572            // Convert to timestamp (start of period - default)
16573            pandas::DataFrame df_start = df.to_timestamp();
16574
16575            // Verify it's now a DatetimeIndex
16576            const pandas::DatetimeIndex* dtidx = dynamic_cast<const pandas::DatetimeIndex*>(&df_start.index());
16577            if (!dtidx) {
16578                std::cout << "  [FAIL] : in pd_test_to_timestamp_basic() : result index is not DatetimeIndex" << std::endl;
16579                throw std::runtime_error("pd_test_to_timestamp_basic failed: result index is not DatetimeIndex");
16580            }
16581
16582            // Verify data is preserved
timetuple (pd_test_timestamp_scalar.cpp:528)
518      // to_pydatetime
519      std::tm tm = ts.to_pydatetime();
520      if (tm.tm_year != 124) { pass = false; fail_msg = "tm_year should be 124 (2024-1900)"; }
521      if (tm.tm_mon != 5) { pass = false; fail_msg = "tm_mon should be 5 (June)"; }
522
523      // timestamp
524      double posix = ts.timestamp();
525      if (posix <= 0) { pass = false; fail_msg = "POSIX timestamp should be positive"; }
526
527      // timetuple
528      auto [y, mo, d, h, mi, s] = ts.timetuple();
529      if (y != 2024 || mo != 6) { pass = false; fail_msg = "timetuple values incorrect"; }
530
531      // date/time tuples
532      auto [dy, dm, dd] = ts.date();
533      if (dy != 2024) { pass = false; fail_msg = "date tuple year"; }
534
535      auto [th, tmi, ts2, tus] = ts.time();
536      if (th != 12) { pass = false; fail_msg = "time tuple hour"; }
537
538      // Julian date
timetz (pd_test_timestamp_scalar.cpp:1042)
1032      // Test 3: utctimetuple() - returns (year, month, day, hour, minute, second) in UTC
1033      pandas::Timestamp ts_utc(2024, 6, 15, 14, 30, 45, 0, 0, "UTC");
1034      auto utc_tuple = ts_utc.utctimetuple();
1035      if (pass && std::get<0>(utc_tuple) != 2024) { pass = false; fail_msg = "utctimetuple year mismatch"; }
1036      if (pass && std::get<1>(utc_tuple) != 6) { pass = false; fail_msg = "utctimetuple month mismatch"; }
1037      if (pass && std::get<2>(utc_tuple) != 15) { pass = false; fail_msg = "utctimetuple day mismatch"; }
1038      if (pass && std::get<3>(utc_tuple) != 14) { pass = false; fail_msg = "utctimetuple hour mismatch"; }
1039      if (pass && std::get<4>(utc_tuple) != 30) { pass = false; fail_msg = "utctimetuple minute mismatch"; }
1040      if (pass && std::get<5>(utc_tuple) != 45) { pass = false; fail_msg = "utctimetuple second mismatch"; }
1041
1042      // Test 4: timetz() - returns (hour, minute, second, microsecond, tz_offset_seconds)
1043      pandas::Timestamp ts_tz(2024, 6, 15, 14, 30, 45, 123456, 0, "UTC");
1044      auto time_tuple = ts_tz.timetz();
1045      if (pass && std::get<0>(time_tuple) != 14) { pass = false; fail_msg = "timetz hour mismatch"; }
1046      if (pass && std::get<1>(time_tuple) != 30) { pass = false; fail_msg = "timetz minute mismatch"; }
1047      if (pass && std::get<2>(time_tuple) != 45) { pass = false; fail_msg = "timetz second mismatch"; }
1048      if (pass && std::get<3>(time_tuple) != 123456) { pass = false; fail_msg = "timetz microsecond mismatch"; }
1049      // UTC offset should be 0 for UTC timezone
1050      if (pass && std::get<4>(time_tuple) != 0) { pass = false; fail_msg = "timetz UTC offset should be 0, got " + std::to_string(std::get<4>(time_tuple)); }
1051
1052      if (!pass) {
timezone (pd_test_2_all.cpp:17898)
17888            // Use UTC timestamps and localize to a timezone first
17889            std::vector<std::optional<numpy::datetime64>> datetimes;
17890            // 2018-09-15 05:30:00 UTC = 1537076200 seconds since epoch
17891            datetimes.push_back(numpy::datetime64(1537076200000000000LL, numpy::DateTimeUnit::Nanosecond));
17892            // 2018-09-16 05:30:00 UTC
17893            datetimes.push_back(numpy::datetime64(1537162600000000000LL, numpy::DateTimeUnit::Nanosecond));
17894
17895            pandas::DatetimeArray dt_arr(datetimes);
17896            pandas::DatetimeIndex dt_idx(dt_arr, "timestamp");
17897
17898            // Localize to US/Eastern timezone (timezone-aware)
17899            pandas::DatetimeIndex tz_aware_idx = dt_idx.tz_localize("US/Eastern");
17900
17901            // Create DataFrame with timezone-aware DatetimeIndex
17902            std::map<std::string, std::vector<double>> data;
17903            data["value"] = {1.0, 2.0};
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());
toString (pd_test_1_all.cpp:9539)
9529void pd_test_timedelta_rounding_params() {
9530    std::cout << "========= Timedelta rounding with DST params =====";
9531
9532    // Create a Timedelta: 1h 30m 45s
9533    // Constructor is: (days, hours, minutes, seconds, ...)
9534    pandas::Timedelta td(0, 1, 30, 45);  // 0 days, 1h, 30m, 45s
9535
9536    // Test floor with ambiguous/nonexistent params
9537    pandas::Timedelta floored = td.floor("h", "raise", "raise");
9538    std::cout << std::endl << "  floor('h'): " << floored.toString();
9539
9540    // Test ceil with ambiguous/nonexistent params
9541    pandas::Timedelta ceiled = td.ceil("h", "raise", "raise");
9542    std::cout << std::endl << "  ceil('h'): " << ceiled.toString();
9543
9544    // Test round with ambiguous/nonexistent params
9545    pandas::Timedelta rounded = td.round("h", "raise", "raise");
9546    std::cout << std::endl << "  round('h'): " << rounded.toString();
9547
9548    // Verify results:
today (pd_test_5_all.cpp:108359)
108349    auto spec = get_fill_spec(row.fill_kind);
108350
108351    // patch 3 (Int64/boolean/string nullable-extension): route through the
108352    // nullable runner so dtype_override_ is set BEFORE reindex_with_spec.
108353    if (row.storage == "Int64")
108354        return run_nullable_int64({10, 20, 30}, new_idx, spec);
108355    if (row.storage == "boolean")
108356        return run_nullable_bool({true, false, true}, new_idx, spec);
108357    if (row.storage == "string_ea")
108358        return run_nullable_string({"x", "y", "z"}, new_idx, spec);
108359    // patch 4 (Series<numpy::float16>): no instantiation today (see plan body
108360    // for the extern-template prescription). Signal a SKIP up the chain so
108361    // the row is counted as deferred, not failed. The marker uses the same
108362    // <EXC>:F16_NOT_INSTANTIATED contract that check_row's SKIP branch
108363    // recognises.
108364    if (row.storage == "float16")
108365        throw std::runtime_error("F16_NOT_INSTANTIATED: see plan patch 4");
108366
108367    // Phase-3B nullable-extension storages — no PandasCore constructor today
108368    // (add_column_nullable<T> only specialises for int64_t/bool/std::string at
108369    // pd_dataframe.h:5946-5947). When Phase-3B lands, replace these throws
toordinal (pd_test_timestamp_scalar.cpp:543)
533      if (dy != 2024) { pass = false; fail_msg = "date tuple year"; }
534
535      auto [th, tmi, ts2, tus] = ts.time();
536      if (th != 12) { pass = false; fail_msg = "time tuple hour"; }
537
538      // Julian date
539      double jd = ts.to_julian_date();
540      if (jd < 2400000) { pass = false; fail_msg = "Julian date should be reasonable"; }
541
542      // Ordinal
543      int ord = ts.toordinal();
544      if (ord <= 0) { pass = false; fail_msg = "Ordinal should be positive"; }
545
546      if (!pass) {
547        std::cout << "  [FAIL] : in np_test_timestamp_conversions() : " << fail_msg;
548        throw std::runtime_error("np_test_timestamp_conversions failed: " + fail_msg);
549      }
550
551      std::cout << " -> tests passed" << std::endl;
552    }
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());
tzinfo (pd_test_timestamp_scalar.cpp:473)
463    void np_test_timestamp_timezone() {
464      std::cout << "========= timestamp: timezone properties =========================";
465
466      auto pass = true;
467      std::string fail_msg;
468
469      // Naive timestamp
470      pandas::Timestamp naive(2024, 6, 15);
471      if (!naive.tz().empty()) { pass = false; fail_msg = "Naive should have empty tz"; }
472      if (!naive.tzinfo().empty()) { pass = false; fail_msg = "Naive should have empty tzinfo"; }
473
474      // UTC timestamp
475      pandas::Timestamp utc(2024, 6, 15, 12, 0, 0, 0, 0, "UTC");
476      if (utc.tz() != "UTC") { pass = false; fail_msg = "Should have UTC timezone"; }
477      if (utc.fold() != 0) { pass = false; fail_msg = "fold should be 0"; }
478
479      // Named timezone
480      pandas::Timestamp ny(2024, 6, 15, 12, 0, 0, 0, 0, "America/New_York");
481      if (ny.tz() != "America/New_York") { pass = false; fail_msg = "Should have NY timezone"; }
482      std::string tzname = ny.tzname();
tzname (pd_test_timestamp_scalar.cpp:483)
473      if (!naive.tzinfo().empty()) { pass = false; fail_msg = "Naive should have empty tzinfo"; }
474
475      // UTC timestamp
476      pandas::Timestamp utc(2024, 6, 15, 12, 0, 0, 0, 0, "UTC");
477      if (utc.tz() != "UTC") { pass = false; fail_msg = "Should have UTC timezone"; }
478      if (utc.fold() != 0) { pass = false; fail_msg = "fold should be 0"; }
479
480      // Named timezone
481      pandas::Timestamp ny(2024, 6, 15, 12, 0, 0, 0, 0, "America/New_York");
482      if (ny.tz() != "America/New_York") { pass = false; fail_msg = "Should have NY timezone"; }
483      std::string tzname = ny.tzname();
484      if (tzname != "EDT" && tzname != "EST") { pass = false; fail_msg = "Summer should be EDT or EST"; }
485
486      if (!pass) {
487        std::cout << "  [FAIL] : in np_test_timestamp_timezone() : " << fail_msg;
488        throw std::runtime_error("np_test_timestamp_timezone failed: " + fail_msg);
489      }
490
491      std::cout << " -> tests passed" << std::endl;
492    }
utcfromtimestamp (pd_test_timestamp_scalar.cpp:1020)
1010      }
1011      if (pass && ts_from_nanos.month() != 6) {
1012        pass = false;
1013        fail_msg = "Timestamp(int64_t) month should be 6, got " + std::to_string(ts_from_nanos.month());
1014      }
1015      if (pass && ts_from_nanos.day() != 15) {
1016        pass = false;
1017        fail_msg = "Timestamp(int64_t) day should be 15, got " + std::to_string(ts_from_nanos.day());
1018      }
1019
1020      // Test 2: utcfromtimestamp(double ts) - creates UTC timestamp from POSIX timestamp
1021      // 1718454645.0 = 2024-06-15 12:30:45 UTC (verified with Python)
1022      double posix_ts = 1718454645.0;
1023      pandas::Timestamp utc_ts = pandas::Timestamp::utcfromtimestamp(posix_ts);
1024      if (pass && utc_ts.year() != 2024) { pass = false; fail_msg = "utcfromtimestamp year mismatch, got " + std::to_string(utc_ts.year()); }
1025      if (pass && utc_ts.month() != 6) { pass = false; fail_msg = "utcfromtimestamp month mismatch, got " + std::to_string(utc_ts.month()); }
1026      if (pass && utc_ts.day() != 15) { pass = false; fail_msg = "utcfromtimestamp day mismatch, got " + std::to_string(utc_ts.day()); }
1027      if (pass && utc_ts.hour() != 12) { pass = false; fail_msg = "utcfromtimestamp hour mismatch, got " + std::to_string(utc_ts.hour()); }
1028      if (pass && utc_ts.minute() != 30) { pass = false; fail_msg = "utcfromtimestamp minute mismatch, got " + std::to_string(utc_ts.minute()); }
1029      if (pass && utc_ts.second() != 45) { pass = false; fail_msg = "utcfromtimestamp second mismatch, got " + std::to_string(utc_ts.second()); }
1030      if (pass && utc_ts.tz() != "UTC") { pass = false; fail_msg = "utcfromtimestamp should have UTC timezone, got '" + utc_ts.tz() + "'"; }
utcnow (pd_test_timestamp_scalar.cpp:103)
 93        fail_msg = "now() should not return NaT";
 94      }
 95
 96      // today()
 97      pandas::Timestamp today = pandas::Timestamp::today();
 98      if (today.hour() != 0 || today.minute() != 0 || today.second() != 0) {
 99        pass = false;
100        fail_msg = "today() should be at midnight";
101      }
102
103      // utcnow()
104      pandas::Timestamp utcnow = pandas::Timestamp::utcnow();
105      if (utcnow.isNaT() || utcnow.tz() != "UTC") {
106        pass = false;
107        fail_msg = "utcnow() should have UTC timezone";
108      }
109
110      // fromtimestamp - Unix epoch
111      pandas::Timestamp epoch = pandas::Timestamp::fromtimestamp(0.0);
112      if (epoch.year() != 1970 || epoch.month() != 1 || epoch.day() != 1) {
113        pass = false;
utcoffset (pd_test_timestamp_scalar.cpp:765)
755      }
756
757      // astimezone (alias)
758      pandas::Timestamp ast = localized.astimezone("UTC");
759      if (ast.tz() != "UTC") {
760        pass = false;
761        fail_msg = "astimezone should convert to UTC";
762      }
763
764      // utcoffset
765      numpy::timedelta64 offset = localized.utcoffset();
766      if (offset.isNaT()) {
767        pass = false;
768        fail_msg = "utcoffset should not be NaT";
769      }
770
771      // dst
772      numpy::timedelta64 dst_val = localized.dst();
773      if (dst_val.isNaT()) {
774        pass = false;
775        fail_msg = "dst should not be NaT";
utctimetuple (pd_test_timestamp_scalar.cpp:1032)
1022      double posix_ts = 1718454645.0;
1023      pandas::Timestamp utc_ts = pandas::Timestamp::utcfromtimestamp(posix_ts);
1024      if (pass && utc_ts.year() != 2024) { pass = false; fail_msg = "utcfromtimestamp year mismatch, got " + std::to_string(utc_ts.year()); }
1025      if (pass && utc_ts.month() != 6) { pass = false; fail_msg = "utcfromtimestamp month mismatch, got " + std::to_string(utc_ts.month()); }
1026      if (pass && utc_ts.day() != 15) { pass = false; fail_msg = "utcfromtimestamp day mismatch, got " + std::to_string(utc_ts.day()); }
1027      if (pass && utc_ts.hour() != 12) { pass = false; fail_msg = "utcfromtimestamp hour mismatch, got " + std::to_string(utc_ts.hour()); }
1028      if (pass && utc_ts.minute() != 30) { pass = false; fail_msg = "utcfromtimestamp minute mismatch, got " + std::to_string(utc_ts.minute()); }
1029      if (pass && utc_ts.second() != 45) { pass = false; fail_msg = "utcfromtimestamp second mismatch, got " + std::to_string(utc_ts.second()); }
1030      if (pass && utc_ts.tz() != "UTC") { pass = false; fail_msg = "utcfromtimestamp should have UTC timezone, got '" + utc_ts.tz() + "'"; }
1031
1032      // Test 3: utctimetuple() - returns (year, month, day, hour, minute, second) in UTC
1033      pandas::Timestamp ts_utc(2024, 6, 15, 14, 30, 45, 0, 0, "UTC");
1034      auto utc_tuple = ts_utc.utctimetuple();
1035      if (pass && std::get<0>(utc_tuple) != 2024) { pass = false; fail_msg = "utctimetuple year mismatch"; }
1036      if (pass && std::get<1>(utc_tuple) != 6) { pass = false; fail_msg = "utctimetuple month mismatch"; }
1037      if (pass && std::get<2>(utc_tuple) != 15) { pass = false; fail_msg = "utctimetuple day mismatch"; }
1038      if (pass && std::get<3>(utc_tuple) != 14) { pass = false; fail_msg = "utctimetuple hour mismatch"; }
1039      if (pass && std::get<4>(utc_tuple) != 30) { pass = false; fail_msg = "utctimetuple minute mismatch"; }
1040      if (pass && std::get<5>(utc_tuple) != 45) { pass = false; fail_msg = "utctimetuple second mismatch"; }
1041
1042      // Test 4: timetz() - returns (hour, minute, second, microsecond, tz_offset_seconds)
value (pd_test_1_all.cpp:88)
78        // T & T = T, T & F = F, T & NA = NA
79        // F & T = F, F & F = F, F & NA = F (False dominates)
80        // NA & T = NA, NA & F = F, NA & NA = NA
81
82        pandas::BooleanArray t({std::optional<bool>(true)});
83        pandas::BooleanArray f({std::optional<bool>(false)});
84        pandas::BooleanArray na({std::nullopt});
85
86        // T & T = T
87        auto tt = (t & t);
88        if (!tt[0].has_value() || !tt[0].value()) {
89            std::cout << "  [FAIL] : in pd_test_boolean_array_kleene_and() : T & T should be T" << std::endl;
90            throw std::runtime_error("pd_test_boolean_array_kleene_and failed: T & T");
91        }
92
93        // T & F = F
94        auto tf = (t & f);
95        if (!tf[0].has_value() || tf[0].value()) {
96            std::cout << "  [FAIL] : in pd_test_boolean_array_kleene_and() : T & F should be F" << std::endl;
97            throw std::runtime_error("pd_test_boolean_array_kleene_and failed: T & F");
98        }
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;