diff --git a/builtin-functions/kphp-light/stdlib/time-functions.txt b/builtin-functions/kphp-light/stdlib/time-functions.txt index 617dc6e0e1..a81cfd3aa0 100644 --- a/builtin-functions/kphp-light/stdlib/time-functions.txt +++ b/builtin-functions/kphp-light/stdlib/time-functions.txt @@ -18,6 +18,8 @@ function gmdate ($format ::: string, $timestamp ::: int = PHP_INT_MIN) ::: strin function gmmktime ($h ::: int = PHP_INT_MIN, $m ::: int = PHP_INT_MIN, $s ::: int = PHP_INT_MIN, $month ::: int = PHP_INT_MIN, $day ::: int = PHP_INT_MIN, $year ::: int = PHP_INT_MIN) ::: int; +function localtime ($timestamp ::: int = PHP_INT_MIN, $is_associative ::: bool = false) ::: mixed[]; + function time() ::: int; function strtotime ($time ::: string, $timestamp ::: int = PHP_INT_MIN) ::: int | false; @@ -140,7 +142,5 @@ define('DATE_RFC3339', "Y-m-d\TH:i:sP"); define('DATE_RSS', "D, d M Y H:i:s O"); define('DATE_W3C', "Y-m-d\TH:i:sP"); -/** @kphp-extern-func-info stub generation-required */ -function localtime ($timestamp ::: int = PHP_INT_MIN, $is_associative ::: bool = false) ::: mixed[]; /** @kphp-extern-func-info stub generation-required */ function strftime ($format ::: string, $timestamp ::: int = PHP_INT_MIN) ::: string; diff --git a/runtime-light/stdlib/time/time-functions.h b/runtime-light/stdlib/time/time-functions.h index 07a8de203c..38c88d49a4 100644 --- a/runtime-light/stdlib/time/time-functions.h +++ b/runtime-light/stdlib/time/time-functions.h @@ -174,6 +174,40 @@ inline int64_t f$gmmktime(int64_t hour = std::numeric_limits::min(), in year != std::numeric_limits::min() ? std::make_optional(kphp::time::impl::fix_year(year)) : std::nullopt); } +inline array f$localtime(int64_t timestamp = std::numeric_limits::min(), bool is_associative = false) noexcept { + if (timestamp == std::numeric_limits::min()) { + namespace chrono = std::chrono; + timestamp = static_cast(chrono::time_point_cast(chrono::system_clock::now()).time_since_epoch().count()); + } + tm t{}; + time_t time{timestamp}; + tm* tp{k2::localtime_r(std::addressof(time), std::addressof(t))}; + if (tp == nullptr) { + kphp::log::warning("unknown error in localtime with timestamp {}", timestamp); + std::memset(std::addressof(t), 0, sizeof(tm)); + } + + if (!is_associative) { + return array::create(t.tm_sec, t.tm_min, t.tm_hour, t.tm_mday, t.tm_mon, t.tm_year, t.tm_wday, t.tm_yday, t.tm_isdst); + } + + const auto& time_image_state{TimeImageState::get()}; + + array result{array_size{9, false}}; + + result.set_value(time_image_state.TM_SEC_STR, t.tm_sec); + result.set_value(time_image_state.TM_MIN_STR, t.tm_min); + result.set_value(time_image_state.TM_HOUR_STR, t.tm_hour); + result.set_value(time_image_state.TM_MDAY_STR, t.tm_mday); + result.set_value(time_image_state.TM_MON_STR, t.tm_mon); + result.set_value(time_image_state.TM_YEAR_STR, t.tm_year); + result.set_value(time_image_state.TM_WDAY_STR, t.tm_wday); + result.set_value(time_image_state.TM_YDAY_STR, t.tm_yday); + result.set_value(time_image_state.TM_ISDST_STR, t.tm_isdst); + + return result; +} + inline string f$date(const string& format, int64_t timestamp = std::numeric_limits::min()) noexcept { if (timestamp == std::numeric_limits::min()) { namespace chrono = std::chrono; diff --git a/runtime-light/stdlib/time/time-state.h b/runtime-light/stdlib/time/time-state.h index e76193f2b1..8727569b65 100644 --- a/runtime-light/stdlib/time/time-state.h +++ b/runtime-light/stdlib/time/time-state.h @@ -5,8 +5,10 @@ #pragma once #include +#include #include "common/mixin/not_copyable.h" +#include "common/php-functions.h" #include "runtime-common/core/runtime-core.h" #include "runtime-common/stdlib/time/timelib-constants.h" #include "runtime-light/core/reference-counter/reference-counter-functions.h" @@ -21,6 +23,15 @@ struct TimeImageState final : private vk::not_copyable { string WARNINGS_STR{WARNINGS_.data(), static_cast(WARNINGS_.size())}; string ERROR_COUNT_STR{ERROR_COUNT_.data(), static_cast(ERROR_COUNT_.size())}; string ERRORS_STR{ERRORS_.data(), static_cast(ERRORS_.size())}; + string TM_SEC_STR{TM_SEC_.data(), static_cast(TM_SEC_.size())}; + string TM_MIN_STR{TM_MIN_.data(), static_cast(TM_MIN_.size())}; + string TM_HOUR_STR{TM_HOUR_.data(), static_cast(TM_HOUR_.size())}; + string TM_MDAY_STR{TM_MDAY_.data(), static_cast(TM_MDAY_.size())}; + string TM_MON_STR{TM_MON_.data(), static_cast(TM_MON_.size())}; + string TM_YEAR_STR{TM_YEAR_.data(), static_cast(TM_YEAR_.size())}; + string TM_WDAY_STR{TM_WDAY_.data(), static_cast(TM_WDAY_.size())}; + string TM_YDAY_STR{TM_YDAY_.data(), static_cast(TM_YDAY_.size())}; + string TM_ISDST_STR{TM_ISDST_.data(), static_cast(TM_ISDST_.size())}; kphp::timelib::timezone_cache timelib_zone_cache{kphp::timelib::timezones::MOSCOW, kphp::timelib::timezones::GMT3}; @@ -35,6 +46,24 @@ struct TimeImageState final : private vk::not_copyable { kphp::core::is_reference_counter_recursive(ERROR_COUNT_STR, ExtraRefCnt::for_global_const))); kphp::log::assertion((kphp::core::set_reference_counter_recursive(ERRORS_STR, ExtraRefCnt::for_global_const), kphp::core::is_reference_counter_recursive(ERRORS_STR, ExtraRefCnt::for_global_const))); + kphp::log::assertion((kphp::core::set_reference_counter_recursive(TM_SEC_STR, ExtraRefCnt::for_global_const), + kphp::core::is_reference_counter_recursive(TM_SEC_STR, ExtraRefCnt::for_global_const))); + kphp::log::assertion((kphp::core::set_reference_counter_recursive(TM_MIN_STR, ExtraRefCnt::for_global_const), + kphp::core::is_reference_counter_recursive(TM_MIN_STR, ExtraRefCnt::for_global_const))); + kphp::log::assertion((kphp::core::set_reference_counter_recursive(TM_HOUR_STR, ExtraRefCnt::for_global_const), + kphp::core::is_reference_counter_recursive(TM_HOUR_STR, ExtraRefCnt::for_global_const))); + kphp::log::assertion((kphp::core::set_reference_counter_recursive(TM_MDAY_STR, ExtraRefCnt::for_global_const), + kphp::core::is_reference_counter_recursive(TM_MDAY_STR, ExtraRefCnt::for_global_const))); + kphp::log::assertion((kphp::core::set_reference_counter_recursive(TM_MON_STR, ExtraRefCnt::for_global_const), + kphp::core::is_reference_counter_recursive(TM_MON_STR, ExtraRefCnt::for_global_const))); + kphp::log::assertion((kphp::core::set_reference_counter_recursive(TM_YEAR_STR, ExtraRefCnt::for_global_const), + kphp::core::is_reference_counter_recursive(TM_YEAR_STR, ExtraRefCnt::for_global_const))); + kphp::log::assertion((kphp::core::set_reference_counter_recursive(TM_WDAY_STR, ExtraRefCnt::for_global_const), + kphp::core::is_reference_counter_recursive(TM_WDAY_STR, ExtraRefCnt::for_global_const))); + kphp::log::assertion((kphp::core::set_reference_counter_recursive(TM_YDAY_STR, ExtraRefCnt::for_global_const), + kphp::core::is_reference_counter_recursive(TM_YDAY_STR, ExtraRefCnt::for_global_const))); + kphp::log::assertion((kphp::core::set_reference_counter_recursive(TM_ISDST_STR, ExtraRefCnt::for_global_const), + kphp::core::is_reference_counter_recursive(TM_ISDST_STR, ExtraRefCnt::for_global_const))); } static const TimeImageState& get() noexcept; @@ -46,6 +75,15 @@ struct TimeImageState final : private vk::not_copyable { static constexpr std::string_view WARNINGS_ = "warnings"; static constexpr std::string_view ERROR_COUNT_ = "error_count"; static constexpr std::string_view ERRORS_ = "errors"; + static constexpr std::string_view TM_SEC_ = "tm_sec"; + static constexpr std::string_view TM_MIN_ = "tm_min"; + static constexpr std::string_view TM_HOUR_ = "tm_hour"; + static constexpr std::string_view TM_MDAY_ = "tm_mday"; + static constexpr std::string_view TM_MON_ = "tm_mon"; + static constexpr std::string_view TM_YEAR_ = "tm_year"; + static constexpr std::string_view TM_WDAY_ = "tm_wday"; + static constexpr std::string_view TM_YDAY_ = "tm_yday"; + static constexpr std::string_view TM_ISDST_ = "tm_isdst"; }; struct TimeInstanceState final : private vk::not_copyable { diff --git a/tests/phpt/dl/484_localtime.php b/tests/phpt/dl/484_localtime.php new file mode 100644 index 0000000000..6e8901e241 --- /dev/null +++ b/tests/phpt/dl/484_localtime.php @@ -0,0 +1,17 @@ +@ok +