Skip to content

Commit 94c10e5

Browse files
authored
stable-25-3-1: Fixed wrong output for large DT64* values in UI (#29614)
2 parents 4ae4c38 + 39b72f8 commit 94c10e5

File tree

2 files changed

+72
-3
lines changed

2 files changed

+72
-3
lines changed

ydb/core/viewer/viewer_query.h

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,74 @@ class TJsonQuery : public TViewerPipeClient {
653653
}
654654

655655
private:
656+
std::string WriteTime(ui32 value) {
657+
ui32 hour, min, sec;
658+
659+
Y_ASSERT(value < 86400);
660+
hour = value / 3600;
661+
value -= hour * 3600;
662+
min = value / 60;
663+
sec = value - min * 60;
664+
665+
return TStringBuilder() << LeftPad(hour, 2, '0') << ':' << LeftPad(min, 2, '0') << ':' << LeftPad(sec, 2, '0');
666+
}
667+
668+
std::string FormatDate32(const std::chrono::sys_time<NYdb::TWideDays>& tp) {
669+
auto value = tp.time_since_epoch().count();
670+
671+
i32 year;
672+
ui32 month;
673+
ui32 day;
674+
if (!NKikimr::NMiniKQL::SplitDate32(value, year, month, day)) {
675+
return TStringBuilder() << "Error: failed to split Date32: " << value;
676+
}
677+
678+
return TStringBuilder() << year << "-" << LeftPad(month, 2, '0') << '-' << LeftPad(day, 2, '0');
679+
}
680+
681+
std::string FormatDatetime64(const std::chrono::sys_time<NYdb::TWideSeconds>& tp) {
682+
auto value = tp.time_since_epoch().count();
683+
684+
if (Y_UNLIKELY(NUdf::MIN_DATETIME64 > value || value > NUdf::MAX_DATETIME64)) {
685+
return TStringBuilder() << "Error: value out of range for Datetime64: " << value << " (min: " << NUdf::MIN_DATETIME64 << ", max: " << NUdf::MAX_DATETIME64 << ")";
686+
}
687+
688+
auto date = value / 86400;
689+
value -= date * 86400;
690+
if (value < 0) {
691+
date -= 1;
692+
value += 86400;
693+
}
694+
695+
return TStringBuilder() << FormatDate32(std::chrono::sys_time<NYdb::TWideDays>(NYdb::TWideDays(date))) << 'T' << WriteTime(value) << 'Z';
696+
}
697+
698+
std::string FormatTimestamp64(const std::chrono::sys_time<NYdb::TWideMicroseconds>& tp) {
699+
auto value = tp.time_since_epoch().count();
700+
701+
if (Y_UNLIKELY(NUdf::MIN_TIMESTAMP64 > value || value > NUdf::MAX_TIMESTAMP64)) {
702+
return TStringBuilder() << "Error: value out of range for Timestamp64: " << value << " (min: " << NUdf::MIN_TIMESTAMP64 << ", max: " << NUdf::MAX_TIMESTAMP64 << ")";
703+
}
704+
auto date = value / 86400000000ll;
705+
value -= date * 86400000000ll;
706+
if (value < 0) {
707+
date -= 1;
708+
value += 86400000000ll;
709+
}
710+
711+
TStringBuilder out;
712+
out << FormatDate32(std::chrono::sys_time<NYdb::TWideDays>(NYdb::TWideDays(date)));
713+
out << 'T';
714+
715+
const ui32 time = static_cast<ui32>(value / 1000000ull);
716+
value -= time * 1000000ull;
717+
out << WriteTime(time);
718+
if (value) {
719+
out << '.' << LeftPad(value, 6, '0');
720+
}
721+
return out << 'Z';
722+
}
723+
656724
NJson::TJsonValue ColumnPrimitiveValueToJsonValue(NYdb::TValueParser& valueParser) {
657725
switch (const auto primitive = valueParser.GetPrimitiveType()) {
658726
case NYdb::EPrimitiveType::Bool:
@@ -688,11 +756,11 @@ class TJsonQuery : public TViewerPipeClient {
688756
case NYdb::EPrimitiveType::Interval:
689757
return TStringBuilder() << valueParser.GetInterval();
690758
case NYdb::EPrimitiveType::Date32:
691-
return std::format("{:%FT%TZ}", valueParser.GetDate32());
759+
return FormatDate32(valueParser.GetDate32());
692760
case NYdb::EPrimitiveType::Datetime64:
693-
return std::format("{:%FT%TZ}", valueParser.GetDatetime64());
761+
return FormatDatetime64(valueParser.GetDatetime64());
694762
case NYdb::EPrimitiveType::Timestamp64:
695-
return std::format("{:%FT%TZ}", valueParser.GetTimestamp64());
763+
return FormatTimestamp64(valueParser.GetTimestamp64());
696764
case NYdb::EPrimitiveType::Interval64:
697765
return valueParser.GetInterval64().count();
698766
case NYdb::EPrimitiveType::TzDate:

ydb/public/sdk/cpp/examples/time/main.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ void TimeExample(const std::string& endpoint, const std::string& database) {
2727
auto ts2 = parser.ColumnParser("ts2").GetTimestamp64();
2828
auto interval = parser.ColumnParser("interval").GetInterval64();
2929

30+
// Standard output and std::format can overflow for large timestamps
3031
std::cout << "ts1: " << ts1 << std::endl;
3132
std::cout << "ts2: " << ts2 << std::endl;
3233
std::cout << "interval: " << std::chrono::duration_cast<std::chrono::milliseconds>(interval) << std::endl;

0 commit comments

Comments
 (0)