Skip to content

Commit

Permalink
Change how we display temporal type units in the Charts
Browse files Browse the repository at this point in the history
  • Loading branch information
dzmipt committed Dec 6, 2024
1 parent d1917dd commit 243a8a6
Show file tree
Hide file tree
Showing 5 changed files with 273 additions and 113 deletions.
102 changes: 81 additions & 21 deletions src/studio/kdb/K.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import java.sql.Timestamp;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.time.Clock;
import java.time.*;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Stream;
Expand All @@ -17,11 +17,26 @@ public class K {
private final static SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy.MM.dd");
private final static SimpleDateFormat dateTimeFormatter = new SimpleDateFormat("yyyy.MM.dd'T'HH:mm:ss.SSS");
private final static SimpleDateFormat timestampFormatter = new SimpleDateFormat("yyyy.MM.dd'D'HH:mm:ss.");
private final static long NS_IN_SEC = 1_000_000_000;
private final static long SEC_IN_DAY = 24 * 60 * 60;
private final static long MS_IN_DAY = 1000 * SEC_IN_DAY;
private final static long NS_IN_DAY = NS_IN_SEC * SEC_IN_DAY;
private final static long NS_IN_MONTH = (long) (NS_IN_DAY*(365*4+1)/(12*4.0));
private static final java.text.DecimalFormat i2Formatter = new java.text.DecimalFormat("00");
private static final java.text.DecimalFormat i3Formatter = new java.text.DecimalFormat("000");

private static String i2(int i) {
return i2Formatter.format(i);
}
private static String i3(int i) {
return i3Formatter.format(i);
}
private static String l2(long i) {
return i2Formatter.format(i);
}

public final static long NS_IN_SEC = 1_000_000_000;
public final static long NS_IN_MLS = 1_000_000;
public final static long NS_IN_MIN = 60 * NS_IN_SEC;
public final static long SEC_IN_DAY = 24 * 60 * 60;
public final static long MS_IN_DAY = 1000 * SEC_IN_DAY;
public final static long NS_IN_DAY = NS_IN_SEC * SEC_IN_DAY;
public final static long NS_IN_MONTH = (long) (NS_IN_DAY*(365*4+1)/(12*4.0));

static {
TimeZone gmtTimeZone = java.util.TimeZone.getTimeZone("GMT");
Expand Down Expand Up @@ -934,6 +949,11 @@ public Date toDate() {
public KTimestamp toTimestamp() {
return new KTimestamp(value * NS_IN_DAY);
}

public KTimestamp toTimestamp(double fraction) {
return new KTimestamp((long) (value + fraction) * NS_IN_DAY);
}

}

public static class KGuid extends KBase {
Expand Down Expand Up @@ -1007,6 +1027,30 @@ public Time toTime() {
}
}

// Artificial class need to represent time in the charts.
public static class KTimeLong extends KLongBase {

public KTimeLong(long value) {
super(KType.TimeLong, value);
}

@Override
public StringBuilder format(StringBuilder builder, KFormatContext context) {
builder = super.format(builder, context);
long ns = value % NS_IN_SEC;
long v = value / NS_IN_SEC;
long sec = v % 60;
v = v / 60;
long min = v % 60;
long hh = v / 60;
builder.append(l2(hh))
.append(':').append(i2((int)min))
.append(':').append(i2((int)sec))
.append(".").append(nsFormatter.format(ns));
return builder;
}
}

public static class KDatetime extends KDoubleBase {

public KDatetime(double time) {
Expand Down Expand Up @@ -1040,17 +1084,34 @@ public static class KTimestamp extends KLongBase {
static KTimestamp now(Clock clock) {
long epoch = clock.instant().toEpochMilli();
long offset = TimeZone.getTimeZone(clock.getZone()).getOffset(epoch);
return new K.KTimestamp( (epoch + offset - MILLIS_OFFSET) * 1_000_000);
return new K.KTimestamp( (epoch + offset - MILLIS_OFFSET) * NS_IN_MLS);
}

public static KTimestamp of(LocalDateTime dateTime) {
Instant instant = dateTime.toInstant(ZoneOffset.UTC);
long ns = instant.getEpochSecond() * NS_IN_SEC + instant.getNano() - MILLIS_OFFSET * NS_IN_MLS;
return new KTimestamp(ns);
}

public static KTimestamp now() {
return now(systemClock);
}

/**
* Equivalent to t2 - this
*/
public KTimespan span(KTimestamp t2) {
return new KTimespan(t2.value - value);
}

public KTimestamp add(Duration duration) {
return new KTimestamp( value + duration.getNano() + NS_IN_SEC * duration.getSeconds() );
}

public KTimestamp add(K.KTimespan duration) {
return new KTimestamp( value + duration.value);
}

public KTimestamp(long time) {
super(KType.Timestamp, time);
}
Expand Down Expand Up @@ -1246,6 +1307,12 @@ public Date toDate() {
cal.set(y, m, 1);
return cal.getTime();
}

public LocalDateTime toLocalDateTime() {
int m = value + 24000, y = m / 12;
m %= 12;
return LocalDateTime.of(y, m+1, 1,0,0);
}
}

//@TODO: rename to Minute
Expand Down Expand Up @@ -1314,14 +1381,14 @@ public static class KTimespan extends KLongBase {
public final static KTimespan NULL = new KTimespan(NULL_VALUE);
public final static KTimespan ZERO = new KTimespan(0);

private final static Map<KType, Long> NS_IN_TYPES = Map.of(
final static Map<KType, Long> NS_IN_TYPES = Map.of(
KType.Timestamp, 1L,
KType.Timespan, 1L,
KType.Date, NS_IN_DAY,
KType.Month, NS_IN_MONTH,
KType.Minute, 60 * NS_IN_SEC,
KType.Minute, NS_IN_MIN,
KType.Second, NS_IN_SEC,
KType.Time, 1_000_000L,
KType.Time, NS_IN_MLS,
KType.Datetime, NS_IN_DAY
);

Expand Down Expand Up @@ -1373,6 +1440,10 @@ public double toUnitValue(ChronoUnit unit) {
return value / (double) NS_IN_UNITS.get(unit);
}

public static KTimespan of(Duration duration) {
return new KTimespan(duration.getSeconds()*NS_IN_SEC + duration.getNano());
}

public KTimespan(long x) {
super(KType.Timespan, x);
}
Expand Down Expand Up @@ -1409,17 +1480,6 @@ public Time toTime() {

}

private static final java.text.DecimalFormat i2Formatter = new java.text.DecimalFormat("00");
private static final java.text.DecimalFormat i3Formatter = new java.text.DecimalFormat("000");

private static String i2(int i) {
return i2Formatter.format(i);
}

private static String i3(int i) {
return i3Formatter.format(i);
}

public static abstract class KBaseVector<E extends KBase> extends KBase {
protected Object array;
private final int length;
Expand Down
115 changes: 115 additions & 0 deletions src/studio/kdb/KFormat.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package studio.kdb;

import java.text.DecimalFormat;
import java.text.FieldPosition;
import java.text.NumberFormat;
import java.text.ParsePosition;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;

public class KFormat extends NumberFormat {

private KType type;

private static final DecimalFormat fractionFormat = new DecimalFormat("+0.#####;-0.#####");

public KFormat(KType type) {
this.type = type;
}

@Override
public StringBuffer format(long number, StringBuffer toAppendTo, FieldPosition pos) {
return format((double)number, toAppendTo, pos);
}

@Override
public Number parse(String source, ParsePosition parsePosition) {
throw new UnsupportedOperationException();
}

@Override
public StringBuffer format(double value, StringBuffer toAppendTo, FieldPosition pos) {
K.KBase kValue;
if (type == KType.Int ||
type == KType.Double ||
type == KType.Float ||
type == KType.Short ||
type == KType.Long) {
kValue = new K.KDouble(value);
} else if (type == KType.Datetime) {
kValue = new K.KDatetime(value);
} else if (type == KType.Timestamp) {
kValue = new K.KTimestamp(getLong(value));
} else if (type == KType.Timespan) {
kValue = new K.KTimespan(getLong(value));
} else {
int intValue = getInt(value);
double fraction = value - intValue;
boolean hasFraction = Math.abs(fraction) >= 1e-5;

if (type == KType.Date) {
kValue = new K.KDate(intValue);
if (hasFraction) {
kValue = ((K.KDate)kValue).toTimestamp(fraction);
}
} else if (type == KType.Month) {
kValue = new K.Month(intValue);
if (hasFraction) {
LocalDateTime dateTime = ((K.Month)kValue).toLocalDateTime();

LocalDateTime next = dateTime.plus((int)Math.signum(fraction), ChronoUnit.MONTHS);
long days = Duration.between(dateTime, next).toDays();
long ns = (long) (Math.abs(fraction) * days * K.NS_IN_DAY);
kValue = K.KTimestamp.of(dateTime).add(new K.KTimespan(ns));
}

} else if (type == KType.Time) {
if (hasFraction) {
kValue = new K.KTimeLong((long) (value * K.NS_IN_MLS));
} else {
kValue = new K.KTime(intValue);
}
System.out.printf("KFormat KTime: value: %f, hasFraction: %b; kValue: %s\n", value, hasFraction, kValue);
} else if (type == KType.Second) {
if (hasFraction) {
kValue = new K.KTimeLong((long) (value * K.NS_IN_SEC));
} else {
kValue = new K.Second(intValue);
}
} else if (type == KType.Minute) {
if (hasFraction) {
kValue = new K.KTimeLong((long) (value * K.NS_IN_MIN));
} else {
kValue = new K.Minute(intValue);
}
} else {
throw new IllegalArgumentException("Unsupported type: " + type);
}
}

toAppendTo.append(kValue.toString(KFormatContext.NO_TYPE));

return toAppendTo;
}

private static long getLong(double value) {
return Math.round(value);
}

private static int getInt(double value) {
long longValue = getLong(value);
if (longValue >= Integer.MAX_VALUE) return Integer.MAX_VALUE;
if (longValue <= -Integer.MAX_VALUE) return -Integer.MAX_VALUE;
return (int) longValue;
}

public static double getValue(KType unit, double value) {
return value / K.KTimespan.NS_IN_TYPES.get(unit);
}

public static double getValue(KType unit, long value) {
return value / (double) K.KTimespan.NS_IN_TYPES.get(unit);
}

}
2 changes: 2 additions & 0 deletions src/studio/kdb/KType.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ public enum KType {
Second(-18, "second", 'v'),
Time(-19, "time", 't'),

TimeLong(-19000,"timeLong", 'l'),

List(0),
BooleanVector(Boolean, true),
GuidVector(Guid),
Expand Down
89 changes: 0 additions & 89 deletions src/studio/ui/chart/KFormat.java

This file was deleted.

Loading

0 comments on commit 243a8a6

Please sign in to comment.