Skip to content

Conversation

@Shamzik
Copy link
Contributor

@Shamzik Shamzik commented Dec 8, 2025

This first part of implementing Date/Time classes. Here implemented:

  • DateInterval;
  • DateTimeZone;
  • DateTimeImmutable;
  • DateTime.

@Shamzik Shamzik marked this pull request as ready for review December 10, 2025 10:26
@Shamzik Shamzik self-assigned this Dec 10, 2025
@Shamzik Shamzik added the k2 k2 related label Dec 10, 2025
@Shamzik Shamzik added this to the next milestone Dec 10, 2025
@Shamzik Shamzik changed the title [k2] add Date/Time classes (except DateTime) to light runtime [k2] add Date/Time classes to light runtime Dec 10, 2025
@Shamzik Shamzik requested a review from apolyakov December 12, 2025 11:44
@DrDet DrDet modified the milestones: 14.12.2025, next Dec 12, 2025
Copy link
Contributor

@apolyakov apolyakov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've reviewed and noted some comments. Please, take into account that these comments should be applied to whole PR, not only the places where I left those comments

@Shamzik Shamzik requested a review from apolyakov January 16, 2026 16:14
@Shamzik Shamzik force-pushed the kshamazov/datetime branch from 58b34bb to eb0dc06 Compare January 19, 2026 15:57
@Shamzik Shamzik requested a review from apolyakov January 21, 2026 12:10
@Shamzik Shamzik requested a review from PetrShumilov January 21, 2026 14:42
std::expected<rel_time, error_container> parse_interval(std::string_view formatted_interval) noexcept {
timelib_time* b{nullptr};
timelib_time* e{nullptr};
kphp::memory::libc_alloc_guard _{};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not create it until it's actually used

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved down

kphp::memory::libc_alloc_guard _{};
vk::final_action e_deleter{[e]() { free(e); }};
vk::final_action b_deleter{[b]() { free(b); }};
timelib_rel_time* p{nullptr};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to see usage of your timelib types, e.g. kphp::timelib::rel_time here. It can be a test for their usability

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't the best place to test type wrappers. Wrapped types should be used with wrapped functions, not within wrapped functions. Here, we're using raw timelib functions, which require raw timelib types.

Of course, we could define our own pointer holder, which would provide access to the pointer by reference. But that seems as overkill to me. See the "kshamazov/datetime-structs" branch for an implementation example.

return std::unexpected{error_container{errors, kphp::timelib::details::error_container_destructor}};
}

time add_time_interval(const kphp::timelib::time& t, const kphp::timelib::rel_time& interval) noexcept {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to define everything in cpp file

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK


} // namespace

time_offset construct_time_offset(const kphp::timelib::time& t) noexcept {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to see fully qualified type here. I don't want to make C++ compiler's job, i.e. name lookup

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

now->tz_info = time->tz_info;
now->zone_type = TIMELIB_ZONETYPE_ID;

namespace chrono = std::chrono;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case it's not a problem, but let's not introduce bad pattern into runtime: do not mix objects that are designed to be allocated/deallocated in the context of libc_alloc_guard with objects that are not designed for that, e.g. every other object

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand you. But I moved chrono objects up

return std::make_pair(std::move(res), std::move(errors));
}

void set_date(kphp::timelib::time& t, int64_t y, int64_t m, int64_t d) noexcept {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see any reason to define such function in the header

instance_timelib_zone_cache.put(tzinfo);
return tzinfo;
int errc{}; // it's intentionally declared as 'int' since timelib_parse_tzfile accepts 'int'
kphp::timelib::tzinfo tzinfo{timelib_parse_tzfile(name.data(), tzdb, std::addressof(errc)), kphp::timelib::details::tzinfo_destructor};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems you've missed libc_alloc_guard here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

if (tzinfo == nullptr || tzinfo->name == nullptr) [[unlikely]] {
return;
}
put(std::move(tzinfo));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's refactor this a bit to make sure put is not called while libc_alloc_guard is active

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@Shamzik Shamzik force-pushed the kshamazov/datetime branch from 89d240b to 92b7f45 Compare January 30, 2026 15:07
@Shamzik Shamzik requested a review from PetrShumilov January 30, 2026 16:06

namespace kphp::timelib {

using static_buf = std::array<char, 128>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's actually unused

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed


inline std::expected<std::pair<kphp::timelib::time_holder, kphp::timelib::error_container_holder>, kphp::timelib::error_container_holder>
parse_time(std::string_view formatted_time) noexcept {
timelib_error_container* errors{};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better to make kphp::timelib::error_container_holder{nullptr, ...} constructed as soon as possible, e.g. before any branches to make memory leak here impossible. Same for other functions

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

t->y = y;
t->m = 1;
t->d = 1;
std::memset(std::addressof(t->relative), 0, sizeof(t->relative));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing include for <cstring>

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

return std::unexpected{kphp::timelib::error_container_holder{errors, kphp::timelib::details::error_container_destructor}};
}

kphp::timelib::rel_time_holder get_time_interval(const kphp::timelib::time_holder& time1, const kphp::timelib::time_holder& time2, bool absolute) noexcept {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's move this definition to header file as its small enough to be inlined

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

if (datetime.empty()) [[unlikely]] {
std::expected<std::pair<kphp::timelib::time_holder, kphp::timelib::error_container_holder>, kphp::timelib::error_container_holder>
parse_time(std::string_view formatted_time, const kphp::timelib::time_holder& t) noexcept {
auto expected{parse_time(formatted_time)};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use fully qualified names. Otherwise, it's so simple to understand where the function goes from

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

inline class_instance<C$DateTimeZone> f$DateTimeZone$$__construct(const class_instance<C$DateTimeZone>& self, const string& timezone) noexcept {
auto expected_tzi{kphp::timelib::get_timezone_info({timezone.c_str(), timezone.size()})};
if (!expected_tzi.has_value()) [[unlikely]] {
static constexpr std::string_view before_timezone{"DateTimeZone::__construct(): Unknown or bad timezone ("};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use capital letters for static variables. Please, also check other files

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

}

inline string f$DateTimeZone$$getName(const class_instance<C$DateTimeZone>& self) noexcept {
return string{self->tzi->get()->name};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. We can't create new string on every getName invocation
  2. We can't skip safety checks. In case you're 'almost sure' bad thing won't happen, add kphp::log::assertion

result.set_value(string{"weekday", 7}, string{weekday.data(), static_cast<string::size_type>(weekday.size())});
result.set_value(string{"month", 5}, string{month.data(), static_cast<string::size_type>(month.size())});
result.set_value(string{"weekday", 7}, string{weekday});
result.set_value(string{"month", 5}, string{month});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: actually we can use std::strlen here as in modern compilers it's implemented as constexpr

return date_time;
}

class_instance<C$DateTimeImmutable> f$DateTimeImmutable$$createFromMutable(const class_instance<C$DateTime>& object) noexcept {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's move this to the header file

}
};

inline class_instance<C$DateInterval> f$DateInterval$$__construct(const class_instance<C$DateInterval>& self, const string& duration) noexcept {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be moved to cpp file

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

k2 k2 related

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants