init research
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
package tech.v3.dataset;
|
||||
|
||||
import clojure.lang.RT;
|
||||
import clojure.lang.IDeref;
|
||||
import java.util.function.LongConsumer;
|
||||
import ham_fisted.ArrayLists;
|
||||
import ham_fisted.IMutList;
|
||||
import org.roaringbitmap.RoaringBitmap;
|
||||
|
||||
|
||||
public class ByteValidity {
|
||||
public static int trimIndexes(int[] indexes, int nIndexes, long maxIdx) {
|
||||
while(nIndexes > 0) {
|
||||
if(Integer.toUnsignedLong(indexes[nIndexes-1]) >= maxIdx)
|
||||
--nIndexes;
|
||||
else
|
||||
break;
|
||||
}
|
||||
return nIndexes;
|
||||
}
|
||||
public static abstract class ValidityBase implements LongConsumer, IDeref {
|
||||
long nElems;
|
||||
int idx;
|
||||
int nIndexes;
|
||||
int[] indexes;
|
||||
public ValidityBase(long nElems, long maxIndexes) {
|
||||
this.nElems = nElems;
|
||||
indexes = new int[(int)maxIndexes];
|
||||
nIndexes = 0;
|
||||
idx = 0;
|
||||
}
|
||||
}
|
||||
public static class ValidityIndexReducer implements LongConsumer, IDeref {
|
||||
IMutList indexes;
|
||||
public final long maxIdx;
|
||||
int idx;
|
||||
public ValidityIndexReducer(IMutList indexes, long maxIdx) {
|
||||
this.indexes = indexes;
|
||||
this.maxIdx = maxIdx;
|
||||
this.idx = 0;
|
||||
}
|
||||
public int trimIndexes() {
|
||||
int nIndexes = indexes.size();
|
||||
//empty loop intentional
|
||||
for(;nIndexes > 0 && indexes.getLong(nIndexes-1) >= maxIdx; --nIndexes);
|
||||
|
||||
return nIndexes;
|
||||
}
|
||||
public void accept(long value) {
|
||||
if(value != 0) {
|
||||
int intVal = (int)value;
|
||||
int offset = idx * 8;
|
||||
if( (intVal & 1) == 1) indexes.addLong(offset);
|
||||
if( (intVal & 2) == 2) indexes.addLong(offset+1);
|
||||
if( (intVal & 4) == 4) indexes.addLong(offset+2);
|
||||
if( (intVal & 8) == 8) indexes.addLong(offset+3);
|
||||
if( (intVal & 16) == 16) indexes.addLong(offset+4);
|
||||
if( (intVal & 32) == 32) indexes.addLong(offset+5);
|
||||
if( (intVal & 64) == 64) indexes.addLong(offset+6);
|
||||
if( (intVal & 128) == 128) indexes.addLong(offset+7);
|
||||
}
|
||||
++idx;
|
||||
}
|
||||
public Object deref() {
|
||||
return indexes.subList(0, trimIndexes());
|
||||
}
|
||||
}
|
||||
public static class MissingIndexReducer extends ValidityBase {
|
||||
public MissingIndexReducer(long nElems, long maxIndexes) {
|
||||
super(nElems, maxIndexes);
|
||||
}
|
||||
public void accept(long value) {
|
||||
if(value != -1) {
|
||||
int intVal = (int)value;
|
||||
int offset = idx * 8;
|
||||
if( (intVal & 1) != 1) indexes[nIndexes++] = offset;
|
||||
if( (intVal & 2) != 2) indexes[nIndexes++] = offset+1;
|
||||
if( (intVal & 4) != 4) indexes[nIndexes++] = offset+2;
|
||||
if( (intVal & 8) != 8) indexes[nIndexes++] = offset+3;
|
||||
if( (intVal & 16) != 16) indexes[nIndexes++] = offset+4;
|
||||
if( (intVal & 32) != 32) indexes[nIndexes++] = offset+5;
|
||||
if( (intVal & 64) != 64) indexes[nIndexes++] = offset+6;
|
||||
if( (intVal & 128) != 128) indexes[nIndexes++] = offset+7;
|
||||
}
|
||||
++idx;
|
||||
}
|
||||
public Object deref() {
|
||||
RoaringBitmap rb = new RoaringBitmap();
|
||||
rb.addN(indexes, 0, trimIndexes(indexes, nIndexes, nElems));
|
||||
return rb;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package tech.v3.dataset;
|
||||
|
||||
|
||||
import tech.v3.datatype.IndexConsumer;
|
||||
import tech.v3.datatype.ECount;
|
||||
import ham_fisted.Reducible;
|
||||
import ham_fisted.IMutList;
|
||||
import org.roaringbitmap.RoaringBitmap;
|
||||
import clojure.lang.IDeref;
|
||||
import clojure.lang.IFn;
|
||||
import clojure.lang.Keyword;
|
||||
import clojure.lang.PersistentArrayMap;
|
||||
|
||||
public class IntColParser
|
||||
implements IDeref, PParser, ECount {
|
||||
|
||||
public final IndexConsumer data;
|
||||
public final RoaringBitmap missing;
|
||||
public final Object colname;
|
||||
long lastidx;
|
||||
|
||||
public IntColParser(IFn rangeFn, IMutList dlist, Object colname) {
|
||||
data = new IndexConsumer(rangeFn, dlist);
|
||||
missing = new RoaringBitmap();
|
||||
this.colname = colname;
|
||||
}
|
||||
|
||||
public long lsize() { return lastidx; }
|
||||
|
||||
public void addMissing(long idx) {
|
||||
if(lastidx < idx) {
|
||||
missing.add(lastidx, idx);
|
||||
}
|
||||
lastidx = idx+1;
|
||||
}
|
||||
|
||||
public void addValue(long idx, Object val) {
|
||||
addMissing(idx);
|
||||
|
||||
if (val instanceof Long)
|
||||
data.accept((Long)val);
|
||||
else if (val instanceof Integer)
|
||||
data.accept((Integer)val);
|
||||
else if (val instanceof Short)
|
||||
data.accept((Short)val);
|
||||
else if (val instanceof Byte)
|
||||
data.accept((Byte)val);
|
||||
else if (val == null)
|
||||
missing.add((int)idx);
|
||||
else
|
||||
throw new RuntimeException("Value " + String.valueOf(val) + " is not an integer value");
|
||||
}
|
||||
|
||||
public Object deref() {
|
||||
return new PersistentArrayMap(new Object[] {
|
||||
Keyword.intern("tech.v3.dataset", "data"), data.deref(),
|
||||
Keyword.intern("tech.v3.dataset", "missing"), missing,
|
||||
Keyword.intern("tech.v3.dataset", "name"), colname});
|
||||
}
|
||||
|
||||
public Object finalize(long rowcount) {
|
||||
addMissing(rowcount);
|
||||
return deref();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2020 Haifeng Li. All rights reserved.
|
||||
*
|
||||
* Smile is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* Smile is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Smile. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package tech.v3.dataset;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.Path;
|
||||
import org.apache.parquet.io.InputFile;
|
||||
import org.apache.parquet.io.SeekableInputStream;
|
||||
|
||||
/**
|
||||
* Parquet InputFile with a local java.nio.Path.
|
||||
* Adapted from https://github.com/tideworks/arvo2parquet
|
||||
*
|
||||
* @author Haifeng Li
|
||||
* @hacks Chris Nuernberger
|
||||
*/
|
||||
public class LocalInputFile implements InputFile {
|
||||
/** Local file object. */
|
||||
private final RandomAccessFile input;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param path the input file path.
|
||||
* @throws FileNotFoundException when file cannot be found.
|
||||
*/
|
||||
public LocalInputFile(Path path) throws FileNotFoundException {
|
||||
input = new RandomAccessFile(path.toFile(), "r");
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLength() throws IOException {
|
||||
return input.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SeekableInputStream newStream() throws IOException {
|
||||
return new SeekableInputStream() {
|
||||
private final byte[] page = new byte[8192];
|
||||
private long markPos = 0;
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
return input.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b) throws IOException {
|
||||
return input.read(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
return input.read(b, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(long n) throws IOException {
|
||||
final long savPos = input.getFilePointer();
|
||||
final long amtLeft = input.length() - savPos;
|
||||
n = Math.min(n, amtLeft);
|
||||
final long newPos = savPos + n;
|
||||
input.seek(newPos);
|
||||
final long curPos = input.getFilePointer();
|
||||
return curPos - savPos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int available() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
input.close();
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "unused", "UnusedReturnValue"})
|
||||
private <T extends Throwable, R> R uncheckedExceptionThrow(Throwable t) throws T {
|
||||
throw (T) t;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void mark(int readlimit) {
|
||||
try {
|
||||
markPos = input.getFilePointer();
|
||||
} catch (IOException e) {
|
||||
uncheckedExceptionThrow(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void reset() throws IOException {
|
||||
input.seek(markPos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean markSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getPos() throws IOException {
|
||||
return input.getFilePointer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seek(long l) throws IOException {
|
||||
input.seek(l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFully(byte[] bytes) throws IOException {
|
||||
input.readFully(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFully(byte[] bytes, int i, int i1) throws IOException {
|
||||
input.readFully(bytes, i, i1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(ByteBuffer byteBuffer) throws IOException {
|
||||
return readDirectBuffer(byteBuffer, page, input::read);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFully(ByteBuffer byteBuffer) throws IOException {
|
||||
readFullyDirectBuffer(byteBuffer, page, input::read);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private interface ByteBufferReader {
|
||||
int read(byte[] b, int off, int len) throws IOException;
|
||||
}
|
||||
|
||||
private int readDirectBuffer(ByteBuffer byteBuffer, byte[] page, ByteBufferReader reader) throws IOException {
|
||||
// copy all the bytes that return immediately, stopping at the first
|
||||
// read that doesn't return a full buffer.
|
||||
int nextReadLength = Math.min(byteBuffer.remaining(), page.length);
|
||||
int totalBytesRead = 0;
|
||||
int bytesRead;
|
||||
|
||||
while ((bytesRead = reader.read(page, 0, nextReadLength)) == page.length) {
|
||||
byteBuffer.put(page);
|
||||
totalBytesRead += bytesRead;
|
||||
nextReadLength = Math.min(byteBuffer.remaining(), page.length);
|
||||
}
|
||||
|
||||
if (bytesRead < 0) {
|
||||
// return -1 if nothing was read
|
||||
return totalBytesRead == 0 ? -1 : totalBytesRead;
|
||||
} else {
|
||||
// copy the last partial buffer
|
||||
byteBuffer.put(page, 0, bytesRead);
|
||||
totalBytesRead += bytesRead;
|
||||
return totalBytesRead;
|
||||
}
|
||||
}
|
||||
|
||||
private static void readFullyDirectBuffer(ByteBuffer byteBuffer, byte[] page, ByteBufferReader reader) throws IOException {
|
||||
int nextReadLength = Math.min(byteBuffer.remaining(), page.length);
|
||||
int bytesRead = 0;
|
||||
|
||||
while (nextReadLength > 0 && (bytesRead = reader.read(page, 0, nextReadLength)) >= 0) {
|
||||
byteBuffer.put(page, 0, bytesRead);
|
||||
nextReadLength = Math.min(byteBuffer.remaining(), page.length);
|
||||
}
|
||||
|
||||
if (bytesRead < 0 && byteBuffer.remaining() > 0) {
|
||||
throw new EOFException("Reached the end of stream with " + byteBuffer.remaining() + " bytes left to read");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package tech.v3.dataset;
|
||||
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class NoCloseInputStream extends InputStream {
|
||||
public final InputStream stream;
|
||||
public NoCloseInputStream(InputStream _stream) {
|
||||
stream = _stream;
|
||||
}
|
||||
public int available() throws IOException { return stream.available(); }
|
||||
//Explicitly do not forward close call
|
||||
public void close(){}
|
||||
public void mark(int maxBytes) { stream.mark(maxBytes); }
|
||||
public boolean markSupported() { return stream.markSupported(); }
|
||||
public int read() throws IOException { return stream.read(); }
|
||||
public int read(byte[] data) throws IOException { return stream.read(data); }
|
||||
public int read(byte[] data, int off, int len) throws IOException { return stream.read(data,off,len); }
|
||||
public void reset() throws IOException { stream.reset(); }
|
||||
public long skip(long n) throws IOException { return stream.skip(n); }
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package tech.v3.dataset;
|
||||
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
public class NoCloseOutputStream extends OutputStream {
|
||||
public final OutputStream stream;
|
||||
public NoCloseOutputStream(OutputStream os) {
|
||||
stream = os;
|
||||
}
|
||||
public void close() throws IOException {}
|
||||
public void fluse() throws IOException { stream.flush(); }
|
||||
public void write(byte[] b) throws IOException { stream.write(b); }
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
stream.write(b,off,len);
|
||||
}
|
||||
public void write(int b) throws IOException { stream.write(b); }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package tech.v3.dataset;
|
||||
|
||||
|
||||
public interface PParser {
|
||||
public void addValue(long idx, Object value);
|
||||
public Object finalize(long rowcount);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package tech.v3.dataset;
|
||||
|
||||
import org.apache.parquet.schema.MessageType;
|
||||
import org.apache.parquet.io.api.RecordConsumer;
|
||||
import org.apache.parquet.hadoop.ParquetWriter;
|
||||
import org.apache.parquet.hadoop.api.WriteSupport;
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.parquet.io.OutputFile;
|
||||
import clojure.lang.IFn;
|
||||
import java.util.Map;
|
||||
|
||||
public class ParquetRowWriter extends WriteSupport<Long>
|
||||
{
|
||||
public final IFn rowWriter;
|
||||
public final MessageType schema;
|
||||
public final Map<String,String> metadata;
|
||||
public RecordConsumer consumer;
|
||||
public Object dataset;
|
||||
public ParquetRowWriter(IFn _writer, MessageType _schema, Map<String,String> _meta) {
|
||||
rowWriter = _writer;
|
||||
schema = _schema;
|
||||
metadata = _meta;
|
||||
consumer = null;
|
||||
//Outside forces must set dataset
|
||||
dataset = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WriteContext init(Configuration configuration) {
|
||||
return new WriteContext( schema, metadata );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareForWrite(RecordConsumer recordConsumer) {
|
||||
consumer = recordConsumer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Long record) {
|
||||
rowWriter.invoke(dataset,record,consumer);
|
||||
}
|
||||
public static class WriterBuilder extends ParquetWriter.Builder<Long,WriterBuilder>
|
||||
{
|
||||
public final ParquetRowWriter writer;
|
||||
public WriterBuilder(OutputFile outf, ParquetRowWriter _writer) {
|
||||
super(outf);
|
||||
writer = _writer;
|
||||
}
|
||||
public WriterBuilder self() { return this; }
|
||||
protected WriteSupport<Long> getWriteSupport(Configuration conf) {
|
||||
return writer;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package tech.v3.dataset;
|
||||
|
||||
import clojure.lang.Keyword;
|
||||
import java.lang.Iterable;
|
||||
import java.lang.AutoCloseable;
|
||||
|
||||
public class Spreadsheet
|
||||
{
|
||||
public interface Workbook extends Iterable, AutoCloseable
|
||||
{
|
||||
}
|
||||
public interface Sheet extends Iterable
|
||||
{
|
||||
public String name();
|
||||
|
||||
public String id();
|
||||
|
||||
public String stableId();
|
||||
}
|
||||
public interface Row extends Iterable
|
||||
{
|
||||
public int getRowNum();
|
||||
}
|
||||
public interface Cell
|
||||
{
|
||||
boolean missing();
|
||||
int getColumnNum();
|
||||
Object value();
|
||||
double doubleValue();
|
||||
boolean boolValue();
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package tech.v3.dataset;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class Text implements Comparable
|
||||
{
|
||||
public final String text;
|
||||
public Text(String data) {
|
||||
Objects.requireNonNull(data);
|
||||
text = data;
|
||||
}
|
||||
public String toString() { return text; }
|
||||
public int hashCode() { return text.hashCode(); }
|
||||
public boolean equals(Object other) {
|
||||
String strData = null;
|
||||
if (other instanceof String) {
|
||||
strData = (String)other;
|
||||
} else if (other instanceof Text) {
|
||||
strData = ((Text)other).text;
|
||||
}
|
||||
return text.equals(strData);
|
||||
}
|
||||
public int compareTo(Object other) {
|
||||
String strData = null;
|
||||
if (other instanceof String) {
|
||||
strData = (String)other;
|
||||
} else if (other instanceof Text) {
|
||||
strData = ((Text)other).text;
|
||||
}
|
||||
return text.compareTo(strData);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,517 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/** This file comes mainly from the tablesaw java project:
|
||||
https://github.com/jtablesaw
|
||||
*/
|
||||
|
||||
package tech.v3.datatype;
|
||||
|
||||
import static java.time.DayOfWeek.FRIDAY;
|
||||
import static java.time.DayOfWeek.MONDAY;
|
||||
import static java.time.DayOfWeek.SATURDAY;
|
||||
import static java.time.DayOfWeek.SUNDAY;
|
||||
import static java.time.DayOfWeek.THURSDAY;
|
||||
import static java.time.DayOfWeek.TUESDAY;
|
||||
import static java.time.DayOfWeek.WEDNESDAY;
|
||||
import static java.time.Month.APRIL;
|
||||
import static java.time.Month.AUGUST;
|
||||
import static java.time.Month.DECEMBER;
|
||||
import static java.time.Month.FEBRUARY;
|
||||
import static java.time.Month.JANUARY;
|
||||
import static java.time.Month.JULY;
|
||||
import static java.time.Month.JUNE;
|
||||
import static java.time.Month.MARCH;
|
||||
import static java.time.Month.MAY;
|
||||
import static java.time.Month.NOVEMBER;
|
||||
import static java.time.Month.OCTOBER;
|
||||
import static java.time.Month.SEPTEMBER;
|
||||
import static java.time.temporal.ChronoField.EPOCH_DAY;
|
||||
import static java.time.temporal.ChronoField.YEAR;
|
||||
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.LocalDate;
|
||||
import java.time.Month;
|
||||
import java.time.chrono.IsoChronology;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.time.temporal.TemporalField;
|
||||
import java.time.temporal.WeekFields;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* A short localdate packed into a single int value. It uses a short for year so the range is about
|
||||
* +-30,000 years
|
||||
*
|
||||
* <p>The bytes are packed into the int as: First two bytes: short (year) next byte (month of year)
|
||||
* last byte (day of month)
|
||||
*/
|
||||
public class PackedLocalDate {
|
||||
|
||||
/** The number of days in a 400 year cycle. */
|
||||
public static final int DAYS_PER_CYCLE = 146097;
|
||||
/**
|
||||
* The number of days from year zero to year 1970. There are five 400 year cycles from year zero
|
||||
* to 2000. There are 7 leap years from 1970 to 2000.
|
||||
*/
|
||||
public static final long DAYS_0000_TO_1970 = (DAYS_PER_CYCLE * 5L) - (30L * 365L + 7L);
|
||||
|
||||
public static byte getDayOfMonth(int date) {
|
||||
return (byte) date; // last byte
|
||||
}
|
||||
|
||||
public static short getYear(int date) {
|
||||
// get first two bytes, then convert to a short
|
||||
byte byte1 = (byte) (date >> 24);
|
||||
byte byte2 = (byte) (date >> 16);
|
||||
return (short) ((byte1 << 8) + (byte2 & 0xFF));
|
||||
}
|
||||
|
||||
public static LocalDate asLocalDate(int date) {
|
||||
return LocalDate.of(getYear(date), getMonthValue(date), getDayOfMonth(date));
|
||||
}
|
||||
|
||||
public static byte getMonthValue(int date) {
|
||||
// get the third byte
|
||||
return (byte) (date >> 8);
|
||||
}
|
||||
|
||||
public static int pack(LocalDate date) {
|
||||
return pack((short) date.getYear(), (byte) date.getMonthValue(), (byte) date.getDayOfMonth());
|
||||
}
|
||||
|
||||
public static int pack(short yr, byte m, byte d) {
|
||||
byte byte1 = (byte) ((yr >> 8) & 0xff);
|
||||
byte byte2 = (byte) yr;
|
||||
return ByteConversions.intFromBytesBE(byte1, byte2, m, d);
|
||||
}
|
||||
|
||||
public static int pack(int yr, int m, int d) {
|
||||
byte byte1 = (byte) ((yr >> 8) & 0xff);
|
||||
byte byte2 = (byte) yr;
|
||||
return ByteConversions.intFromBytesBE(byte1, byte2, (byte) m, (byte) d);
|
||||
}
|
||||
|
||||
public static int getDayOfYear(int packedDate) {
|
||||
return getMonth(packedDate).firstDayOfYear(isLeapYear(packedDate))
|
||||
+ getDayOfMonth(packedDate)
|
||||
- 1;
|
||||
}
|
||||
|
||||
public static boolean isLeapYear(int packedDate) {
|
||||
return IsoChronology.INSTANCE.isLeapYear(getYear(packedDate));
|
||||
}
|
||||
|
||||
public static Month getMonth(int packedDate) {
|
||||
return Month.of(getMonthValue(packedDate));
|
||||
}
|
||||
|
||||
public static int lengthOfMonth(int packedDate) {
|
||||
switch (getMonthValue(packedDate)) {
|
||||
case 2:
|
||||
return (isLeapYear(packedDate) ? 29 : 28);
|
||||
case 4:
|
||||
case 6:
|
||||
case 9:
|
||||
case 11:
|
||||
return 30;
|
||||
default:
|
||||
return 31;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the epoch day in a form consistent with the java standard */
|
||||
public static long toEpochDay(int packedDate) {
|
||||
long y = getYear(packedDate);
|
||||
long m = getMonthValue(packedDate);
|
||||
long total = 0;
|
||||
total += 365 * y;
|
||||
if (y >= 0) {
|
||||
total += (y + 3) / 4 - (y + 99) / 100 + (y + 399) / 400;
|
||||
} else {
|
||||
total -= y / -4 - y / -100 + y / -400;
|
||||
}
|
||||
total += ((367 * m - 362) / 12);
|
||||
total += getDayOfMonth(packedDate) - 1;
|
||||
if (m > 2) {
|
||||
total--;
|
||||
if (!isLeapYear(packedDate)) {
|
||||
total--;
|
||||
}
|
||||
}
|
||||
return total - DAYS_0000_TO_1970;
|
||||
}
|
||||
|
||||
public static DayOfWeek getDayOfWeek(int packedDate) {
|
||||
int dow0 = Math.floorMod((int) toEpochDay(packedDate) + 3, 7);
|
||||
return DayOfWeek.of(dow0 + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the quarter of the year of the given date as an int from 1 to 4, or -1, if the argument
|
||||
* is the MISSING_VALUE for DateColumn
|
||||
*/
|
||||
public static int getQuarter(int packedDate) {
|
||||
Month month = getMonth(packedDate);
|
||||
switch (month) {
|
||||
case JANUARY:
|
||||
case FEBRUARY:
|
||||
case MARCH:
|
||||
return 1;
|
||||
case APRIL:
|
||||
case MAY:
|
||||
case JUNE:
|
||||
return 2;
|
||||
case JULY:
|
||||
case AUGUST:
|
||||
case SEPTEMBER:
|
||||
return 3;
|
||||
case OCTOBER:
|
||||
case NOVEMBER:
|
||||
default: // must be december
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isInQ1(int packedDate) {
|
||||
return getQuarter(packedDate) == 1;
|
||||
}
|
||||
|
||||
public static boolean isInQ2(int packedDate) {
|
||||
return getQuarter(packedDate) == 2;
|
||||
}
|
||||
|
||||
public static boolean isInQ3(int packedDate) {
|
||||
return getQuarter(packedDate) == 3;
|
||||
}
|
||||
|
||||
public static boolean isInQ4(int packedDate) {
|
||||
return getQuarter(packedDate) == 4;
|
||||
}
|
||||
|
||||
public static boolean isAfter(int packedDate, int value) {
|
||||
return packedDate > value;
|
||||
}
|
||||
|
||||
public static boolean isEqualTo(int packedDate, int value) {
|
||||
return packedDate == value;
|
||||
}
|
||||
|
||||
public static boolean isBefore(int packedDate, int value) {
|
||||
return packedDate < value;
|
||||
}
|
||||
|
||||
public static boolean isOnOrBefore(int packedDate, int value) {
|
||||
return packedDate <= value;
|
||||
}
|
||||
|
||||
public static boolean isOnOrAfter(int packedDate, int value) {
|
||||
return packedDate >= value;
|
||||
}
|
||||
|
||||
public static boolean isDayOfWeek(int packedDate, DayOfWeek dayOfWeek) {
|
||||
DayOfWeek dow = getDayOfWeek(packedDate);
|
||||
return dayOfWeek == dow;
|
||||
}
|
||||
|
||||
public static boolean isSunday(int packedDate) {
|
||||
return isDayOfWeek(packedDate, SUNDAY);
|
||||
}
|
||||
|
||||
public static boolean isMonday(int packedDate) {
|
||||
return isDayOfWeek(packedDate, MONDAY);
|
||||
}
|
||||
|
||||
public static boolean isTuesday(int packedDate) {
|
||||
return isDayOfWeek(packedDate, TUESDAY);
|
||||
}
|
||||
|
||||
public static boolean isWednesday(int packedDate) {
|
||||
return isDayOfWeek(packedDate, WEDNESDAY);
|
||||
}
|
||||
|
||||
public static boolean isThursday(int packedDate) {
|
||||
return isDayOfWeek(packedDate, THURSDAY);
|
||||
}
|
||||
|
||||
public static boolean isFriday(int packedDate) {
|
||||
return isDayOfWeek(packedDate, FRIDAY);
|
||||
}
|
||||
|
||||
public static boolean isSaturday(int packedDate) {
|
||||
return isDayOfWeek(packedDate, SATURDAY);
|
||||
}
|
||||
|
||||
public static boolean isFirstDayOfMonth(int packedDate) {
|
||||
return getDayOfMonth(packedDate) == 1;
|
||||
}
|
||||
|
||||
public static boolean isInJanuary(int packedDate) {
|
||||
return getMonth(packedDate) == JANUARY;
|
||||
}
|
||||
|
||||
public static boolean isInFebruary(int packedDate) {
|
||||
return getMonth(packedDate) == FEBRUARY;
|
||||
}
|
||||
|
||||
public static boolean isInMarch(int packedDate) {
|
||||
return getMonth(packedDate) == MARCH;
|
||||
}
|
||||
|
||||
public static boolean isInApril(int packedDate) {
|
||||
return getMonth(packedDate) == APRIL;
|
||||
}
|
||||
|
||||
public static boolean isInMay(int packedDate) {
|
||||
return getMonth(packedDate) == MAY;
|
||||
}
|
||||
|
||||
public static boolean isInJune(int packedDate) {
|
||||
return getMonth(packedDate) == JUNE;
|
||||
}
|
||||
|
||||
public static boolean isInJuly(int packedDate) {
|
||||
return getMonth(packedDate) == JULY;
|
||||
}
|
||||
|
||||
public static boolean isInAugust(int packedDate) {
|
||||
return getMonth(packedDate) == AUGUST;
|
||||
}
|
||||
|
||||
public static boolean isInSeptember(int packedDate) {
|
||||
return getMonth(packedDate) == SEPTEMBER;
|
||||
}
|
||||
|
||||
public static boolean isInOctober(int packedDate) {
|
||||
return getMonth(packedDate) == OCTOBER;
|
||||
}
|
||||
|
||||
public static boolean isInNovember(int packedDate) {
|
||||
return getMonth(packedDate) == NOVEMBER;
|
||||
}
|
||||
|
||||
public static boolean isInDecember(int packedDate) {
|
||||
return getMonth(packedDate) == DECEMBER;
|
||||
}
|
||||
|
||||
public static boolean isLastDayOfMonth(int packedDate) {
|
||||
return getDayOfMonth(packedDate) == lengthOfMonth(packedDate);
|
||||
}
|
||||
|
||||
public static int withDayOfMonth(int dayOfMonth, int packedDate) {
|
||||
byte d = (byte) dayOfMonth;
|
||||
byte m = getMonthValue(packedDate);
|
||||
short y = getYear(packedDate);
|
||||
return pack(y, m, d);
|
||||
}
|
||||
|
||||
public static int withMonth(int month, int packedDate) {
|
||||
byte day = getDayOfMonth(packedDate);
|
||||
byte _month = (byte) month;
|
||||
short year = getYear(packedDate);
|
||||
return pack(year, _month, day);
|
||||
}
|
||||
|
||||
public static int withYear(int year, int packedDate) {
|
||||
byte day = getDayOfMonth(packedDate);
|
||||
byte month = getMonthValue(packedDate);
|
||||
short _year = (short) year;
|
||||
return pack(_year, month, day);
|
||||
}
|
||||
|
||||
public static int plusYears(int yearsToAdd, int packedDate) {
|
||||
if (yearsToAdd == 0) {
|
||||
return packedDate;
|
||||
}
|
||||
byte d = getDayOfMonth(packedDate);
|
||||
byte m = getMonthValue(packedDate);
|
||||
short y = getYear(packedDate);
|
||||
|
||||
int newYear = YEAR.checkValidIntValue(yearsToAdd + y);
|
||||
return resolvePreviousValid(newYear, m, d);
|
||||
}
|
||||
|
||||
public static int minusYears(int years, int packedDate) {
|
||||
return plusYears(-years, packedDate);
|
||||
}
|
||||
|
||||
public static int plusMonths(int months, int packedDate) {
|
||||
if (months == 0) {
|
||||
return packedDate;
|
||||
}
|
||||
|
||||
byte d = getDayOfMonth(packedDate);
|
||||
byte m = getMonthValue(packedDate);
|
||||
short y = getYear(packedDate);
|
||||
|
||||
long monthCount = y * 12L + (m - 1);
|
||||
long calcMonths = monthCount + months;
|
||||
int newYear = YEAR.checkValidIntValue(Math.floorDiv((int) calcMonths, 12));
|
||||
int newMonth = Math.floorMod((int) calcMonths, 12) + 1;
|
||||
return resolvePreviousValid(newYear, newMonth, d);
|
||||
}
|
||||
|
||||
public static int minusMonths(int months, int packedDate) {
|
||||
return plusMonths(-months, packedDate);
|
||||
}
|
||||
|
||||
public static int plusWeeks(int valueToAdd, int packedDate) {
|
||||
return plusDays(valueToAdd * 7, packedDate);
|
||||
}
|
||||
|
||||
public static int minusWeeks(int valueToSubtract, int packedDate) {
|
||||
return minusDays(valueToSubtract * 7, packedDate);
|
||||
}
|
||||
|
||||
public static int plusDays(int days, int packedDate) {
|
||||
if (days == 0) {
|
||||
return packedDate;
|
||||
}
|
||||
|
||||
byte d = getDayOfMonth(packedDate);
|
||||
byte m = getMonthValue(packedDate);
|
||||
short y = getYear(packedDate);
|
||||
|
||||
int dom = days + d;
|
||||
if (dom > 0) {
|
||||
if (dom <= 28) {
|
||||
return pack(y, m, (byte) dom);
|
||||
} else if (dom <= 59) { // 59th Jan is 28th Feb, 59th Feb is 31st Mar
|
||||
long monthLen = lengthOfMonth(packedDate);
|
||||
if (dom <= monthLen) {
|
||||
return pack(y, m, (byte) dom);
|
||||
} else if (m < 12) {
|
||||
return pack(y, (byte) (m + 1), (byte) (dom - monthLen));
|
||||
} else {
|
||||
YEAR.checkValidValue((long) y + 1);
|
||||
return pack((short) (y + 1), (byte) 1, (byte) (dom - monthLen));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
long mjDay = Math.addExact(toEpochDay(packedDate), days);
|
||||
return ofEpochDay(mjDay);
|
||||
}
|
||||
|
||||
public static int minusDays(int days, int packedDate) {
|
||||
return plusDays(-days, packedDate);
|
||||
}
|
||||
|
||||
public static boolean isInYear(int next, int year) {
|
||||
return getYear(next) == year;
|
||||
}
|
||||
|
||||
public static int lengthOfYear(int packedDate) {
|
||||
return (isLeapYear(packedDate) ? 366 : 365);
|
||||
}
|
||||
|
||||
public static int resolvePreviousValid(int year, int month, int day) {
|
||||
int dayResult = day;
|
||||
switch (month) {
|
||||
case 2:
|
||||
dayResult = Math.min(day, IsoChronology.INSTANCE.isLeapYear(year) ? 29 : 28);
|
||||
break;
|
||||
case 4:
|
||||
case 6:
|
||||
case 9:
|
||||
case 11:
|
||||
dayResult = Math.min(day, 30);
|
||||
break;
|
||||
}
|
||||
return pack((short) year, (byte) month, (byte) dayResult);
|
||||
}
|
||||
|
||||
public static int getWeekOfYear(int packedDateTime) {
|
||||
LocalDate date = asLocalDate(packedDateTime);
|
||||
if (date == null) {
|
||||
throw new IllegalArgumentException("Cannot get week of year for missing value");
|
||||
}
|
||||
TemporalField woy = WeekFields.of(Locale.getDefault()).weekOfWeekBasedYear();
|
||||
return date.get(woy);
|
||||
}
|
||||
|
||||
public static int ofEpochDay(long epochDay) {
|
||||
EPOCH_DAY.checkValidValue(epochDay);
|
||||
long zeroDay = epochDay + DAYS_0000_TO_1970;
|
||||
// find the march-based year
|
||||
zeroDay -= 60; // adjust to 0000-03-01 so leap day is at end of four year cycle
|
||||
long adjust = 0;
|
||||
if (zeroDay < 0) {
|
||||
// adjust negative years to positive for calculation
|
||||
long adjustCycles = (zeroDay + 1) / DAYS_PER_CYCLE - 1;
|
||||
adjust = adjustCycles * 400;
|
||||
zeroDay += -adjustCycles * DAYS_PER_CYCLE;
|
||||
}
|
||||
long yearEst = (400 * zeroDay + 591) / DAYS_PER_CYCLE;
|
||||
long doyEst = zeroDay - (365 * yearEst + yearEst / 4 - yearEst / 100 + yearEst / 400);
|
||||
if (doyEst < 0) {
|
||||
// fix estimate
|
||||
yearEst--;
|
||||
doyEst = zeroDay - (365 * yearEst + yearEst / 4 - yearEst / 100 + yearEst / 400);
|
||||
}
|
||||
yearEst += adjust; // reset any negative year
|
||||
int marchDoy0 = (int) doyEst;
|
||||
|
||||
// convert march-based values back to january-based
|
||||
int marchMonth0 = (marchDoy0 * 5 + 2) / 153;
|
||||
int month = (marchMonth0 + 2) % 12 + 1;
|
||||
int dom = marchDoy0 - (marchMonth0 * 306 + 5) / 10 + 1;
|
||||
yearEst += marchMonth0 / 10;
|
||||
|
||||
// check year now we are certain it is correct
|
||||
int year = YEAR.checkValidIntValue(yearEst);
|
||||
return pack((short) year, (byte) month, (byte) dom);
|
||||
}
|
||||
|
||||
public static int plus(int valueToAdd, ChronoUnit unit, int packedDate) {
|
||||
switch (unit) {
|
||||
case YEARS:
|
||||
return plusYears(valueToAdd, packedDate);
|
||||
case MONTHS:
|
||||
return plusMonths(valueToAdd, packedDate);
|
||||
case WEEKS:
|
||||
return plusWeeks(valueToAdd, packedDate);
|
||||
case DAYS:
|
||||
return plusDays(valueToAdd, packedDate);
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported Temporal Unit");
|
||||
}
|
||||
}
|
||||
|
||||
public static int minus(int valueToAdd, ChronoUnit unit, int packedDate) {
|
||||
return plus(-valueToAdd, unit, packedDate);
|
||||
}
|
||||
|
||||
public static int daysUntil(int packedDateEnd, int packedDateStart) {
|
||||
return (int) (toEpochDay(packedDateEnd) - toEpochDay(packedDateStart));
|
||||
}
|
||||
|
||||
public static int weeksUntil(int packedDateEnd, int packedDateStart) {
|
||||
return (int) (toEpochDay(packedDateEnd) - toEpochDay(packedDateStart)) / 7;
|
||||
}
|
||||
|
||||
public static int monthsUntil(int packedDateEnd, int packedDateStart) {
|
||||
|
||||
int start = getMonthInternal(packedDateStart) * 32 + getDayOfMonth(packedDateStart);
|
||||
int end = getMonthInternal(packedDateEnd) * 32 + getDayOfMonth(packedDateEnd);
|
||||
return (end - start) / 32;
|
||||
}
|
||||
|
||||
public static int yearsUntil(int packedDateEnd, int packedDateStart) {
|
||||
return monthsUntil(packedDateEnd, packedDateStart) / 12;
|
||||
}
|
||||
|
||||
public static int getMonthInternal(int packedDate) {
|
||||
return (getYear(packedDate) * 12 + getMonthValue(packedDate) - 1);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user