Skip to content

Commit

Permalink
Ref: using Calendar to generate Dates (#1111)
Browse files Browse the repository at this point in the history
  • Loading branch information
marandaneto authored Dec 18, 2020
1 parent 753cf99 commit 671f9e0
Show file tree
Hide file tree
Showing 14 changed files with 136 additions and 122 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* Ref: Make Attachment immutable (#1120)
* Fix inheriting sampling decision from parent (#1100)
* Fixes and Tests: Session serialization and deserialization
* Ref: using Calendar to generate Dates
* Fix: Exception only sets a stack trace if there are frames
* Feat: set isSideLoaded info tags
* Enhancement: Read tracesSampleRate from AndroidManifest
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ final class DefaultAndroidEventProcessor implements EventProcessor {
@TestOnly static final String SIDE_LOADED = "sideLoaded";

// it could also be a parameter and get from Sentry.init(...)
private static final @Nullable Date appStartTime = DateUtils.getCurrentDateTimeOrNull();
private static final @Nullable Date appStartTime = DateUtils.getCurrentDateTime();

@TestOnly final Context context;

Expand Down Expand Up @@ -396,9 +396,9 @@ private TimeZone getTimeZone() {
@SuppressWarnings("JdkObsolete")
private @Nullable Date getBootTime() {
try {
// if user changes time, will give a wrong answer, consider ACTION_TIME_CHANGED
return DateUtils.getDateTime(
new Date(System.currentTimeMillis() - SystemClock.elapsedRealtime()));
// if user changes the clock, will give a wrong answer, consider ACTION_TIME_CHANGED.
// currentTimeMillis returns UTC already
return DateUtils.getDateTime(System.currentTimeMillis() - SystemClock.elapsedRealtime());
} catch (IllegalArgumentException e) {
logger.log(SentryLevel.ERROR, e, "Error getting the device's boot time.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import io.sentry.util.CollectionUtils;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
Expand Down Expand Up @@ -131,8 +130,7 @@ public void append(final @NotNull LogEvent eventObject) {
// for the Android compatibility we must use old Java Date class
@SuppressWarnings("JdkObsolete")
final @NotNull SentryEvent createEvent(final @NotNull LogEvent loggingEvent) {
final SentryEvent event =
new SentryEvent(DateUtils.getDateTime(new Date(loggingEvent.getTimeMillis())));
final SentryEvent event = new SentryEvent(DateUtils.getDateTime(loggingEvent.getTimeMillis()));
final Message message = new Message();
message.setMessage(loggingEvent.getMessage().getFormat());
message.setFormatted(loggingEvent.getMessage().getFormattedMessage());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@ import org.apache.logging.log4j.spi.ExtendedLogger
import org.awaitility.kotlin.await

class SentryAppenderTest {
private class Fixture() {
private class Fixture {
val loggerContext = LogManager.getContext() as LoggerContext
lateinit var transport: ITransport
val utcTimeZone: ZoneId = ZoneId.of("UTC")

fun getSut(transport: ITransport = mock(), minimumBreadcrumbLevel: Level? = null, minimumEventLevel: Level? = null): ExtendedLogger {
this.transport = transport
Expand Down Expand Up @@ -103,14 +104,14 @@ class SentryAppenderTest {
@Test
fun `event date is in UTC`() {
val logger = fixture.getSut(minimumEventLevel = Level.DEBUG)
val utcTime = LocalDateTime.now(ZoneId.of("UTC"))
val utcTime = LocalDateTime.now(fixture.utcTimeZone)

logger.debug("testing event date")

await.untilAsserted {
verify(fixture.transport).send(checkEvent { event ->
val eventTime = Instant.ofEpochMilli(event.timestamp.time)
.atZone(ZoneId.systemDefault())
.atZone(fixture.utcTimeZone)
.toLocalDateTime()

assertTrue { eventTime.plusSeconds(1).isAfter(utcTime) }
Expand Down Expand Up @@ -249,7 +250,7 @@ class SentryAppenderTest {
@Test
fun `attaches breadcrumbs with level higher than minimumBreadcrumbLevel`() {
val logger = fixture.getSut(minimumEventLevel = Level.WARN, minimumBreadcrumbLevel = Level.DEBUG)
val utcTime = LocalDateTime.now(ZoneId.of("UTC"))
val utcTime = LocalDateTime.now(fixture.utcTimeZone)

logger.debug("this should be a breadcrumb #1")
logger.info("this should be a breadcrumb #2")
Expand All @@ -260,7 +261,7 @@ class SentryAppenderTest {
assertEquals(2, event.breadcrumbs.size)
val breadcrumb = event.breadcrumbs[0]
val breadcrumbTime = Instant.ofEpochMilli(event.timestamp.time)
.atZone(ZoneId.systemDefault())
.atZone(fixture.utcTimeZone)
.toLocalDateTime()
assertTrue { breadcrumbTime.plusSeconds(1).isAfter(utcTime) }
assertTrue { breadcrumbTime.minusSeconds(1).isBefore(utcTime) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import io.sentry.util.CollectionUtils;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
Expand Down Expand Up @@ -68,8 +67,7 @@ protected void append(@NotNull ILoggingEvent eventObject) {
// for the Android compatibility we must use old Java Date class
@SuppressWarnings("JdkObsolete")
final @NotNull SentryEvent createEvent(@NotNull ILoggingEvent loggingEvent) {
final SentryEvent event =
new SentryEvent(DateUtils.getDateTime(new Date(loggingEvent.getTimeStamp())));
final SentryEvent event = new SentryEvent(DateUtils.getDateTime(loggingEvent.getTimeStamp()));
final Message message = new Message();
message.setMessage(loggingEvent.getMessage());
message.setFormatted(loggingEvent.getFormattedMessage());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class SentryAppenderTest {
private class Fixture(minimumBreadcrumbLevel: Level? = null, minimumEventLevel: Level? = null, val transport: ITransport = mock<ITransport>()) {
val logger: Logger = LoggerFactory.getLogger(SentryAppenderTest::class.java)
val loggerContext = LoggerFactory.getILoggerFactory() as LoggerContext
val utcTimeZone: ZoneId = ZoneId.of("UTC")

init {
whenever(transport.send(any())).thenReturn(TransportResult.success())
Expand Down Expand Up @@ -99,14 +100,14 @@ class SentryAppenderTest {
@Test
fun `event date is in UTC`() {
fixture = Fixture(minimumEventLevel = Level.DEBUG)
val utcTime = LocalDateTime.now(ZoneId.of("UTC"))
val utcTime = LocalDateTime.now(fixture.utcTimeZone)

fixture.logger.debug("testing event date")

await.untilAsserted {
verify(fixture.transport).send(checkEvent { event ->
val eventTime = Instant.ofEpochMilli(event.timestamp.time)
.atZone(ZoneId.systemDefault())
.atZone(fixture.utcTimeZone)
.toLocalDateTime()

assertTrue { eventTime.plusSeconds(1).isAfter(utcTime) }
Expand Down Expand Up @@ -233,7 +234,7 @@ class SentryAppenderTest {
@Test
fun `attaches breadcrumbs with level higher than minimumBreadcrumbLevel`() {
fixture = Fixture(minimumBreadcrumbLevel = Level.DEBUG, minimumEventLevel = Level.WARN)
val utcTime = LocalDateTime.now(ZoneId.of("UTC"))
val utcTime = LocalDateTime.now(fixture.utcTimeZone)

fixture.logger.debug("this should be a breadcrumb #1")
fixture.logger.info("this should be a breadcrumb #2")
Expand All @@ -244,7 +245,7 @@ class SentryAppenderTest {
assertEquals(2, event.breadcrumbs.size)
val breadcrumb = event.breadcrumbs[0]
val breadcrumbTime = Instant.ofEpochMilli(event.timestamp.time)
.atZone(ZoneId.systemDefault())
.atZone(fixture.utcTimeZone)
.toLocalDateTime()
assertTrue { breadcrumbTime.plusSeconds(1).isAfter(utcTime) }
assertTrue { breadcrumbTime.minusSeconds(1).isBefore(utcTime) }
Expand Down
4 changes: 1 addition & 3 deletions sentry/api/sentry.api
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,10 @@ public final class io/sentry/CustomSamplingContext {

public final class io/sentry/DateUtils {
public static fun getCurrentDateTime ()Ljava/util/Date;
public static fun getCurrentDateTimeOrNull ()Ljava/util/Date;
public static fun getDateTime (J)Ljava/util/Date;
public static fun getDateTime (Ljava/lang/String;)Ljava/util/Date;
public static fun getDateTime (Ljava/util/Date;)Ljava/util/Date;
public static fun getDateTimeWithMillisPrecision (Ljava/lang/String;)Ljava/util/Date;
public static fun getTimestamp (Ljava/util/Date;)Ljava/lang/String;
public static fun getTimestampIsoFormat (Ljava/util/Date;)Ljava/lang/String;
}

public final class io/sentry/DiagnosticLogger : io/sentry/ILogger {
Expand Down
2 changes: 1 addition & 1 deletion sentry/src/main/java/io/sentry/Breadcrumb.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public Breadcrumb(final @Nullable Date timestamp) {

/** Breadcrumb ctor */
public Breadcrumb() {
this(DateUtils.getCurrentDateTimeOrNull());
this(DateUtils.getCurrentDateTime());
}

/**
Expand Down
100 changes: 38 additions & 62 deletions sentry/src/main/java/io/sentry/DateUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,96 +3,72 @@
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/** Utilities to deal with dates */
@ApiStatus.Internal
public final class DateUtils {
private static final String UTC = "UTC";
// ISO 8601
private static final String ISO_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
private static final String ISO_FORMAT_WITH_MILLIS = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
private static final ThreadLocal<SimpleDateFormat> SDF_ISO_FORMAT_WITH_MILLIS_UTC =

// if UTC is not found, it fallback to "GMT" which is UTC equivalent
private static final @NotNull TimeZone UTC_TIMEZONE = TimeZone.getTimeZone(UTC);

private static final @NotNull ThreadLocal<SimpleDateFormat> SDF_ISO_FORMAT_WITH_MILLIS_UTC =
new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
final TimeZone tz = TimeZone.getTimeZone(UTC);
final SimpleDateFormat simpleDateFormat =
new SimpleDateFormat(ISO_FORMAT_WITH_MILLIS, Locale.ROOT);
simpleDateFormat.setTimeZone(tz);
simpleDateFormat.setTimeZone(UTC_TIMEZONE);
return simpleDateFormat;
}
};
private static final ThreadLocal<SimpleDateFormat> SDF_ISO_FORMAT_WITH_MILLIS =
new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat(ISO_FORMAT_WITH_MILLIS, Locale.ROOT);
}
};
private static final ThreadLocal<SimpleDateFormat> SDF_ISO_FORMAT =

private static final @NotNull ThreadLocal<SimpleDateFormat> SDF_ISO_FORMAT_UTC =
new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat(ISO_FORMAT, Locale.ROOT);
final SimpleDateFormat simpleDateFormat = new SimpleDateFormat(ISO_FORMAT, Locale.ROOT);
simpleDateFormat.setTimeZone(UTC_TIMEZONE);
return simpleDateFormat;
}
};

private DateUtils() {}

/**
* Get date formatted as expected by Sentry.
*
* @param date the current date with local timezone
* @return the ISO formatted UTC date with millis precision.
*/
public static @NotNull String getTimestampIsoFormat(final @NotNull Date date) {
return SDF_ISO_FORMAT_WITH_MILLIS_UTC.get().format(date);
}

/**
* Get the current date and time as ISO UTC
* Get the current Date (UTC)
*
* @return the ISO UTC date and time
* @return the UTC Date
*/
@SuppressWarnings("JdkObsolete")
public static @NotNull Date getCurrentDateTime() throws IllegalArgumentException {
final String timestampIsoFormat = getTimestampIsoFormat(new Date());
return getDateTime(timestampIsoFormat);
public static @NotNull Date getCurrentDateTime() {
final Calendar calendar = Calendar.getInstance(UTC_TIMEZONE);
return calendar.getTime();
}

/**
* Get the current date and time as ISO UTC or null if not available
* Get the Date from UTC/ISO 8601 timestamp
*
* @return the ISO UTC date and time or null
*/
public static @Nullable Date getCurrentDateTimeOrNull() throws IllegalArgumentException {
try {
return getCurrentDateTime();
} catch (IllegalArgumentException ignored) {
// error getting current device's timestamp due to eg locale problems
}
return null;
}

/**
* Get Java Date from UTC timestamp format
*
* @param timestamp UTC format eg 2000-12-31T23:59:58Z or 2000-12-31T23:59:58.123Z
* @return the Date
* @param timestamp UTC/ISO 8601 format eg 2000-12-31T23:59:58Z or 2000-12-31T23:59:58.123Z
* @return the UTC Date
*/
public static @NotNull Date getDateTime(final @NotNull String timestamp)
throws IllegalArgumentException {
try {
return SDF_ISO_FORMAT_WITH_MILLIS.get().parse(timestamp);
return SDF_ISO_FORMAT_WITH_MILLIS_UTC.get().parse(timestamp);
} catch (ParseException e) {
try {
// to keep compatibility with older envelopes
return SDF_ISO_FORMAT.get().parse(timestamp);
return SDF_ISO_FORMAT_UTC.get().parse(timestamp);
} catch (ParseException ignored) {
// invalid timestamp format
}
Expand All @@ -101,10 +77,10 @@ private DateUtils() {}
}

/**
* Get Java Date from millis timestamp format
* Get the Date from millis timestamp
*
* @param timestamp millis format eg 1581410911.988 (1581410911 seconds and 988 millis)
* @return the Date UTC timezone
* @param timestamp millis eg 1581410911.988 (1581410911 seconds and 988 millis)
* @return the UTC Date
*/
@SuppressWarnings("JdkObsolete")
public static @NotNull Date getDateTimeWithMillisPrecision(final @NotNull String timestamp)
Expand All @@ -114,32 +90,32 @@ private DateUtils() {}
final long seconds = Long.parseLong(times[0]);
final long millis = times.length > 1 ? Long.parseLong(times[1]) : 0;

return getDateTime(new Date((seconds * 1000) + millis));
return getDateTime((seconds * 1000) + millis);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("timestamp is not millis format " + timestamp);
}
}

/**
* Get date formatted as expected by Sentry.
* Get the UTC/ISO 8601 timestamp from Date
*
* @param date already UTC format
* @return the ISO formatted date with millis precision.
* @param date the UTC Date
* @return the UTC/ISO 8601 timestamp
*/
public static @NotNull String getTimestamp(final @NotNull Date date) {
final DateFormat df = SDF_ISO_FORMAT_WITH_MILLIS.get();
final DateFormat df = SDF_ISO_FORMAT_WITH_MILLIS_UTC.get();
return df.format(date);
}

/**
* Converts the given Date and time to UTC timezone
* Get the Date from millis timestamp
*
* @param date the Date with local timezone
* @return the Date UTC timezone
* @param millis the UTC millis from the epoch
* @return the UTC Date
*/
public static @NotNull Date getDateTime(final @NotNull Date date)
throws IllegalArgumentException {
final String timestampIsoFormat = getTimestampIsoFormat(date);
return getDateTime(timestampIsoFormat);
public static @NotNull Date getDateTime(final long millis) {
final Calendar calendar = Calendar.getInstance(UTC_TIMEZONE);
calendar.setTimeInMillis(millis);
return calendar.getTime();
}
}
2 changes: 1 addition & 1 deletion sentry/src/main/java/io/sentry/SentryEvent.java
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ public SentryEvent(final @Nullable Throwable throwable) {
}

public SentryEvent() {
this(new SentryId(), DateUtils.getCurrentDateTimeOrNull());
this(new SentryId(), DateUtils.getCurrentDateTime());
}

@TestOnly
Expand Down
Loading

0 comments on commit 671f9e0

Please sign in to comment.