Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Date type compatibility from Cocoa to Java #2707

Open
stk1m1 opened this issue Apr 29, 2016 · 1 comment
Open

Date type compatibility from Cocoa to Java #2707

stk1m1 opened this issue Apr 29, 2016 · 1 comment

Comments

@stk1m1
Copy link
Contributor

stk1m1 commented Apr 29, 2016

Goal

Make sure Date type is compatible from Cocoa to Java.

Expected Results

Add unit tests for reading Date type data from Cocoa to its original.

Version of Realm and tooling

Realm version(s): 0.90.0

@stk1m1 stk1m1 changed the title Datetime Cocoa -> Java compatibility Date type compatibility from Cocoa to Java Apr 29, 2016
@stk1m1 stk1m1 self-assigned this May 4, 2016
@stk1m1 stk1m1 removed their assignment Jun 9, 2016
@stk1m1
Copy link
Contributor Author

stk1m1 commented Aug 12, 2016

Timestamp Getter

Cocoa Timestamp -> NSDate

// Date convertion utilities
// These use the reference date and shift the seconds rather than just getting
// the time interval since the epoch directly to avoid losing sub-second precision
static inline NSDate *RLMTimestampToNSDate(realm::Timestamp ts) NS_RETURNS_RETAINED {
    if (ts.is_null())
        return nil;
    auto timeInterval = ts.get_seconds() - NSTimeIntervalSince1970 + ts.get_nanoseconds() / 1'000'000'000.0;
    return [[NSDate alloc] initWithTimeIntervalSinceReferenceDate:timeInterval];
}

Java Timestamp -> milliseconds

inline jlong to_milliseconds(const realm::Timestamp& ts)
{
    // From core's reference implementation aka unit test
    // FIXME: check for overflow/underflow
    const int64_t seconds = ts.get_seconds();
    const int32_t nanoseconds = ts.get_nanoseconds();
    const int64_t milliseconds = seconds * 1000 + nanoseconds / 1000000; // This may overflow
    return milliseconds;
}

Timestamp Setter

Cocoa NSDate -> Timestamp

static inline realm::Timestamp RLMTimestampForNSDate(__unsafe_unretained NSDate *const date) {
    auto timeInterval = date.timeIntervalSinceReferenceDate;
    if (isnan(timeInterval))
        return {0, 0}; // Arbitrary choice

    // Clamp dates that we can't represent as a Timestamp to the maximum value
    if (timeInterval >= std::numeric_limits<int64_t>::max() - NSTimeIntervalSince1970)
        return {std::numeric_limits<int64_t>::max(), 1'000'000'000 - 1};
    if (timeInterval - NSTimeIntervalSince1970 < std::numeric_limits<int64_t>::min())
        return {std::numeric_limits<int64_t>::min(), -1'000'000'000 + 1};

    auto seconds = static_cast<int64_t>(timeInterval);
    auto nanoseconds = static_cast<int32_t>((timeInterval - seconds) * 1'000'000'000.0);
    seconds += static_cast<int64_t>(NSTimeIntervalSince1970);

    // Seconds and nanoseconds have to have the same sign
    if (nanoseconds < 0 && seconds > 0) {
        nanoseconds += 1'000'000'000;
        --seconds;
    }
    return {seconds, nanoseconds};
}

Java Date.getTime() -> Timestamp

inline realm::Timestamp from_milliseconds(jlong milliseconds)
{
    // From core's reference implementation aka unit test
    int64_t seconds = milliseconds / 1000;
    int32_t nanoseconds = (milliseconds % 1000) * 1000000;
    return realm::Timestamp(seconds, nanoseconds);
}
  1. As one can see from the snippets above, Cocoa adds NSTimeIntervalSince1970 (978307200.0) to seconds part of Timestamp while Java doesn't.
    If we're to store a timestamp from Cocoa side and read from Java, we'll have a timestamp with 978307200 added. Meanwhile, cocoa would have a timestamp with 978307200 subtracted if it reads a timestamp value from Java.
  2. Cocoa filters timestamp from std::numeric_limits<int64_t>::max() - NSTimeIntervalSince1970 (a positive figure) to std::numeric_limits<int64_t>::min() + NSTimeIntervalSince1970 (a negative figure), and clamps it. Since std::numeric_limits<int64_t>::max()/min() are big numbers, it is hardly likely that this filtering would be recognized as an incompatibility. It would nevertheless cause an issue when a timestamp value gets exchanged between platforms.
  3. Futher, static_case<int64_t> in cocoa could cause the same issue Realm-Java had.

Could this be move to OS for better compatibility?

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

No branches or pull requests

3 participants