Skip to content

Commit

Permalink
Merge pull request #6862 from realm/je/fix-timestamp
Browse files Browse the repository at this point in the history
  • Loading branch information
jedelbo authored Aug 8, 2023
2 parents 29105db + cd4a6c8 commit 4876aff
Show file tree
Hide file tree
Showing 9 changed files with 10,733 additions and 14,067 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
### Fixed
* <How do the end-user experience this issue? what was the impact?> ([#????](https://github.com/realm/realm-core/issues/????), since v?.?.?)
* Fix failed assertion for unknown app server errors ([#6758](https://github.com/realm/realm-core/issues/6758), since v12.9.0).
* Change JSON selialization format back to follow ISO 8601 - and add output of nanoseconds ([#6855](https://github.com/realm/realm-core/issues/6855), since 13.17.0)

### Breaking changes
* None.
Expand Down
2 changes: 1 addition & 1 deletion src/realm/obj.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -922,7 +922,7 @@ void out_mixed_json(std::ostream& out, const Mixed& val)
}
case type_Timestamp:
out << "\"";
out << util::serializer::print_value<Timestamp>(val.get_timestamp());
out << val.get<Timestamp>();
out << "\"";
break;
case type_Decimal:
Expand Down
25 changes: 5 additions & 20 deletions src/realm/timestamp.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,9 @@ class Timestamp {
return size_t(m_seconds) ^ size_t(m_nanoseconds);
}

// Buffer must be at least 32 bytes long
const char* to_string(char* buffer) const;

template <class Ch, class Tr>
friend std::basic_ostream<Ch, Tr>& operator<<(std::basic_ostream<Ch, Tr>& out, const Timestamp&);
static constexpr int32_t nanoseconds_per_second = 1000000000;
Expand All @@ -196,26 +199,8 @@ class Timestamp {
template <class C, class T>
inline std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& out, const Timestamp& d)
{
if (d.is_null()) {
out << "null";
return out;
}
auto seconds = time_t(d.get_seconds());
struct tm buf;
#ifdef _MSC_VER
bool success = gmtime_s(&buf, &seconds) == 0;
#else
bool success = gmtime_r(&seconds, &buf) != nullptr;
#endif
if (success) {
// We need a buffer for formatting dates.
// Max size is 20 bytes (incl terminating zero) "YYYY-MM-DD HH:MM:SS"\0
char buffer[30];
if (strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &buf)) {
out << buffer;
}
}

char buffer[32];
out << d.to_string(buffer);
return out;
}
// LCOV_EXCL_STOP
Expand Down
96 changes: 96 additions & 0 deletions src/realm/util/serializer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,103 @@
#include <cctype>
#include <cmath>


namespace realm {

/* Uses Fliegel & Van Flandern algorithm */
static constexpr long date_to_julian(int y, int m, int d)
{
return (1461 * (y + 4800 + (m - 14) / 12)) / 4 + (367 * (m - 2 - 12 * ((m - 14) / 12))) / 12 -
(3 * ((y + 4900 + (m - 14) / 12) / 100)) / 4 + d - 32075;
}

static void julian_to_date(int jd, int* y, int* m, int* d)
{
int L = jd + 68569;
int n = (4 * L) / 146097;
int i, j;

L = L - (146097 * n + 3) / 4;
i = (4000 * (L + 1)) / 1461001;
L = L - (1461 * i) / 4 + 31;
j = (80 * L) / 2447;
*d = L - (2447 * j) / 80;
L = j / 11;
*m = j + 2 - (12 * L);
*y = 100 * (n - 49) + i + L;
}

// Confirmed to work for all val < 16389
static void out_dec(char** buffer, unsigned val, int width)
{
int w = width;
char* p = *buffer;
while (width > 0) {
width--;
unsigned div10 = (val * 6554) >> 16;
p[width] = char(val - div10 * 10) + '0';
val = div10;
}
*buffer += w;
}

static constexpr long epoc_julian_days = date_to_julian(1970, 1, 1); // 2440588
static constexpr int seconds_in_a_day = 24 * 60 * 60;

const char* Timestamp::to_string(char* buffer) const
{
if (is_null()) {
return "null";
}
int64_t seconds = get_seconds();
int32_t nano = get_nanoseconds();
if (nano < 0) {
nano += Timestamp::nanoseconds_per_second;
seconds--;
}

int64_t days = seconds / seconds_in_a_day;
int julian_days = int(days + epoc_julian_days);

int seconds_in_day = int(seconds - days * seconds_in_a_day);
if (seconds_in_day < 0) {
seconds_in_day += seconds_in_a_day;
julian_days--;
}

int year;
int month;
int day;
int hours = seconds_in_day / 3600;
int remainingSeconds = seconds_in_day - hours * 3600;
int minutes = remainingSeconds / 60;
int secs = remainingSeconds - minutes * 60;

julian_to_date(julian_days, &year, &month, &day);

char* p = buffer;
if (year < 0) {
*p++ = '-';
year = -year;
}
out_dec(&p, year, 4);
*p++ = '-';
out_dec(&p, month, 2);
*p++ = '-';
out_dec(&p, day, 2);
*p++ = ' ';
out_dec(&p, hours, 2);
*p++ = ':';
out_dec(&p, minutes, 2);
*p++ = ':';
out_dec(&p, secs, 2);
*p = '\0';
if (nano) {
snprintf(p, 32 - (p - buffer), ".%09d", nano);
}
return buffer;
}

namespace util {
namespace serializer {

Expand Down
Loading

0 comments on commit 4876aff

Please sign in to comment.