Skip to content

Commit

Permalink
Added separate set of method for java
Browse files Browse the repository at this point in the history
to improve user experience.
  • Loading branch information
marcin-cebo committed Oct 5, 2024
1 parent 2f23838 commit 8406f55
Show file tree
Hide file tree
Showing 7 changed files with 263 additions and 95 deletions.
4 changes: 4 additions & 0 deletions pubnub-gson/pubnub-gson-api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,8 @@ dependencies {
api(project(":pubnub-kotlin:pubnub-kotlin-core-api"))
implementation(libs.slf4j)
implementation(libs.jetbrains.annotations)

testImplementation(libs.mockito)
testImplementation(libs.junit.jupiter)
testImplementation(libs.junit.vintage.engine)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package com.pubnub.api.java.util;

import java.time.Instant;

public class TimetokenUtil {
private static final long MINIMAL_TIMETOKEN_VALUE = 10_000_000_000_000_000L;
private static final long MAXIMUM_TIMETOKEN_VALUE = 99_999_999_999_999_999L;
private static final long MINIMAL_UNIX_TIME_VALUE = 1_000_000_000_000L;
private static final long MAXIMUM_UNIX_TIME_VALUE = 9_999_999_999_999L;
private static final int UNIX_TIME_LENGTH_WHEN_IN_MILLISECONDS = 13;

/**
* Converts a PubNub timetoken (a unique identifier for each message sent and received in a PubNub channel that is
* a number of 100-nanosecond intervals since January 1, 1970) to Instant object representing
* the corresponding date and time.
*
* @param timetoken PubNub timetoken
* @return Instant representing the corresponding date and time.
* @throws IllegalArgumentException if the timetoken does not have 17 digits.
*/
public static Instant timetokenToInstant(long timetoken) {
if (isLengthDifferentThan17Digits(timetoken)) {
throw new IllegalArgumentException("Timetoken should have 17 digits");
}

// Convert timetoken to seconds and nanoseconds components
long epochSeconds = timetoken / 10_000_000; // Divide by 10^7 to get seconds
int epochNanoseconds = (int) ((timetoken % 10_000_000) * 100); // The remainder, multiplied by 100 to get nanoseconds

return Instant.ofEpochSecond(epochSeconds, epochNanoseconds);
}

/**
* Converts Instant to a PubNub timetoken.
*
* A PubNub timetoken is a 17-digit number representing the number of 100-nanosecond intervals since January 1, 1970.
*
* @param instant The Instant object to be converted to a PubNub timetoken.
* @return A 17-digit long representing the PubNub timetoken for the given Instant.
*/
public static long instantToTimetoken(Instant instant) {
long epochSeconds = instant.getEpochSecond();
int epochNanoseconds = instant.getNano();

long pnTimetokenInNanosecondsWithoutNanoPrecision = epochSeconds * 10_000_000;
long nanosecondsForPubNubTimetoken = epochNanoseconds / 100; // PubNub timetoken for nanoseconds store only 7 digits instead of 9

return pnTimetokenInNanosecondsWithoutNanoPrecision + nanosecondsForPubNubTimetoken;
}

/**
* Converts a Unix timestamp (in millis) to a PubNub timetoken.
*
* @param unixTime The Unix timestamp to be converted to a PubNub timetoken.
* @return A 17-digit long representing the PubNub timetoken corresponding to the given Unix timestamp.
* @throws IllegalArgumentException if the unixTime does not have 13 digits.
*/
public static long unixToTimetoken(long unixTime) {
if (isLengthDifferentThan13Digits(unixTime)) {
throw new IllegalArgumentException("Unix timetoken should have " + UNIX_TIME_LENGTH_WHEN_IN_MILLISECONDS + " digits.");
}

return unixTime * 10_000; // PubNub timetoken has 17 digits
}

/**
* Converts a PubNub timetoken to a Unix timestamp (in millis).
*
* A PubNub timetoken is a 17-digit number representing the number of 100-nanosecond intervals since January 1, 1970.
* This function converts the PubNub timetoken to a Unix timestamp by reducing the precision to millis.
* Note that precision finer than millis is lost in this conversion.
*
* @param timetoken The PubNub timetoken to be converted to a Unix timestamp.
* @return A long representing the Unix timestamp in millis corresponding to the given timetoken.
*/
public static long timetokenToUnix(long timetoken) {
return timetoken / 10_000; // PubNub timetoken has 17 digits
}

private static boolean isLengthDifferentThan17Digits(long timetoken) {
return timetoken < MINIMAL_TIMETOKEN_VALUE || timetoken > MAXIMUM_TIMETOKEN_VALUE;
}

private static boolean isLengthDifferentThan13Digits(long unixTime) {
return unixTime < MINIMAL_UNIX_TIME_VALUE || unixTime > MAXIMUM_UNIX_TIME_VALUE;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.pubnub.api.java.util;

import org.junit.jupiter.api.Test;
//import com.pubnub.api.utils.TimetokenUtil;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

public class TimetokenUtilsTest {

@Test
public void canConvertTimestampToDateTimeThenFromDateTimeToTimestamp() {

// given 2024-09-30 11:24:20.623211800
long timetoken = 17276954606232118L;

// when
Instant instant = TimetokenUtil.timetokenToInstant(timetoken);
long timetokenResult = TimetokenUtil.instantToTimetoken(instant);

// then
LocalDateTime localDateTimeInUTC = LocalDateTime.ofInstant(instant, ZoneId.of("UTC"));
assertEquals("2024-09-30", localDateTimeInUTC.toLocalDate().toString());
assertEquals("11:24:20.623211800", localDateTimeInUTC.toLocalTime().toString());
assertEquals(timetoken, timetokenResult);
}

@Test
public void canConvertUnixTimeInMillisecondsToTimetoken() {
// given 2024-10-02 11:02:15.316
long unixTime = 1727866935316L;

// when
long timetoken = TimetokenUtil.unixToTimetoken(unixTime);

// then
Instant instant = TimetokenUtil.timetokenToInstant(timetoken);
LocalDateTime localDateTimeInUTC = LocalDateTime.ofInstant(instant, ZoneId.of("UTC"));
assertEquals("2024-10-02", localDateTimeInUTC.toLocalDate().toString());
assertEquals("11:02:15.316", localDateTimeInUTC.toLocalTime().toString());
}

@Test
public void canConvertUnixTimeNotHaving10Or13Digits() {
long unixTime4Digits = 1_000_000_000L;
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
TimetokenUtil.unixToTimetoken(unixTime4Digits);
});
assertEquals("Unix timetoken should have 13 digits.", exception.getMessage());
}

@Test
public void canConvertTimestampToUnixTime() {
// given 2024-09-30 11:24:20.623211800
long timetoken = 17276954606232118L;

// when
long unixTime = TimetokenUtil.timetokenToUnix(timetoken);

// then
Instant instant = Instant.ofEpochMilli(unixTime);
LocalDateTime toLocalDateTime = LocalDateTime.ofInstant(instant, ZoneId.of("UTC"));
assertEquals("2024-09-30", toLocalDateTime.toLocalDate().toString());
assertEquals("11:24:20.623", toLocalDateTime.toLocalTime().toString());
}
}

1 change: 0 additions & 1 deletion pubnub-kotlin/pubnub-kotlin-api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ kotlin {
dependencies {
api(project(":pubnub-kotlin:pubnub-kotlin-core-api"))
implementation(libs.kotlinx.atomicfu)
implementation(libs.kotlinx.datetime)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package com.pubnub.api.utils

import kotlinx.datetime.Instant
import kotlin.jvm.JvmStatic

/**
* Utility object for converting PubNub timetokens to various date-time representations and vice versa.
*
* This utility provides methods for converting between PubNub timetokens, Unix timestamps, and date-time objects.
* A PubNub timetoken is a 17-digit number that represents the number of 100-nanosecond intervals since
* January 1, 1970 (UTC). These methods allow for easy conversion between different time representations
* used in PubNub, Unix, and standard date-time formats.
*/
class TimetokenUtil {
companion object {
private const val MINIMAL_TIMETOKE_VALUE = 10_000_000_000_000_000
private const val MAXIMUM_TIMETOKEN_VALUE = 99_999_999_999_999_999
private const val MINIMAL_UNIX_TIME_VALUE = 1_000_000_000_000
private const val MAXIMUM_UNIX_TIME_VALUE = 9_999_999_999_999
private const val UNIX_TIME_LENGHT_WHEN_IN_MILLISECONDS = 13

/**
* Converts a PubNub timetoken (a unique identifier for each message sent and received in a PubNub channel that is
* a number of 100-nanosecond intervals since January 1, 1970) to LocalDateTime object representing
* the corresponding date and time.
*
* @param timetoken PubNub timetoken
* @return [Instant] representing the corresponding data and time.
* @throws IllegalArgumentException if the timetoken does not have 17 digits.
*/
@JvmStatic
fun timetokenToInstant(timetoken: Long): Instant {
if (isLengthDifferentThan17Digits(timetoken)) {
throw IllegalArgumentException("Timetoken should have 17 digits")
}
// Convert timetoken to seconds and nanoseconds components
val epochSeconds = timetoken / 10_000_000 // Divide by 10^7 to get seconds
val epochNanoseconds =
((timetoken % 10_000_000) * 100).toInt() // The remainder, multiplied by 100 to get nanoseconds

return Instant.fromEpochSeconds(epochSeconds, epochNanoseconds)
}

/**
* Converts [Instant] to a PubNub timetoken
*
* A PubNub timetoken is a 17-digit number representing the number of 100-nanosecond intervals since January 1, 1970.
*
* @param instant The [Instant] object to be converted to a PubNub timetoken.
* @return A 17-digit [Long] representing the PubNub timetoken for the given [Instant].
*/
@JvmStatic
fun instantToTimetoken(instant: Instant): Long {
val epochSeconds: Long = instant.epochSeconds
val epochNanoseconds: Int = instant.nanosecondsOfSecond

val pnTimetokenInNanosecondsWithoutNanoPrecision = epochSeconds * 10_000_000
val nanosecondsForPubNubTimetoken =
epochNanoseconds / 100 // PubNub timetoken for nanoseconds store only 7 digits instead of 9
return pnTimetokenInNanosecondsWithoutNanoPrecision + nanosecondsForPubNubTimetoken
}

/**
* Converts a Unix timestamp (in millis) to a PubNub timetoken
*
* @param unixTime The Unix timestamp to be converted to a PubNub timetoken.
* @return A 17-digit [Long] representing the PubNub timetoken corresponding to the given Unix timestamp.
* @throws IllegalArgumentException if the unixTime does not have 13 digits.
*/
@JvmStatic
fun unixToTimetoken(unixTime: Long): Long {
if (isLengthDifferentThan13Digits(unixTime)) {
throw IllegalArgumentException("Unix timetoken should have $UNIX_TIME_LENGHT_WHEN_IN_MILLISECONDS digits.")
}
return unixTime * 10_000 // PubNub timetoken has 17 digits
}

/**
* Converts a PubNub timetoken to a Unix timestamp (in millis).
*
* A PubNub timetoken is a 17-digit number representing the number of 100-nanosecond intervals since January 1, 1970.
* This function converts the PubNub timetoken to a Unix timestamp by reducing the precision to millis.
* Note that precision finer than millis is lost in this conversion.
*
* @param timetoken The PubNub timetoken to be converted to a Unix timestamp.
* @return A [Long] representing the Unix timestamp in millis corresponding to the given timetoken.
*/
@JvmStatic
fun timetokenToUnix(timetoken: Long): Long {
return (timetoken / 10_000) // PubNub timetoken has 17 digits
}

private fun isLengthDifferentThan17Digits(timetoken: Long): Boolean {
return timetoken !in MINIMAL_TIMETOKE_VALUE..MAXIMUM_TIMETOKEN_VALUE
}

private fun isLengthDifferentThan13Digits(unixTime: Long): Boolean {
return unixTime !in MINIMAL_UNIX_TIME_VALUE..MAXIMUM_UNIX_TIME_VALUE
}
}
}
1 change: 1 addition & 0 deletions pubnub-kotlin/pubnub-kotlin-core-api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ kotlin {
val commonMain by getting {
dependencies {
implementation(libs.kotlinx.atomicfu)
api(libs.kotlinx.datetime)
}
}

Expand Down

This file was deleted.

0 comments on commit 8406f55

Please sign in to comment.