Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,12 @@
<version>${junit.version}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should not ship with any specific logging framework except for the facade provided by slf4j. It would be fine to include this as a test-scoped dependency if needed, but I would like to prevent that we make logging framework decisions for the end users of the library.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll scope it as 'test', thanks.

<version>1.7.5</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
12 changes: 11 additions & 1 deletion sas/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,15 @@
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,11 @@ public CountReaderCallback() {
}

@Override
public void column(int columnIndex, String columnName, String columnLabel, SasColumnType columnType,
int columnLength) {
public void column(int columnIndex,
String columnName,
String columnLabel,
SasColumnType columnType,
int columnLength) {
// do nothing
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.eobjects.metamodel.sas;

public class DateConversionException extends RuntimeException {

public DateConversionException(String msg, Object ... values) {
super(String.format(msg, values));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package org.eobjects.metamodel.sas;

import org.joda.time.DateTime;
import org.joda.time.Period;

public class DateTimeConverter {

private static final int MINYEAR= 1, MAXYEAR=9999, MINDAYS=-999999999, MAXDAYS=999999999;

public DateTime datetimeToJava(int dateSeconds) {
DateTime dt = new DateTime(1960, 1, 1, 0, 0).plusSeconds(dateSeconds);
return dt;
}

public DateTime date9ToJava(int date9) {

/*
* This bounding [MINDAYS, MAXDAYS] isn't actually part of the spec --
* it's a restriction that is inherited from the python implementation
* of the sas7bdat format (and the use of Python datetimes in that
* code), and it's here to make sure that the results of the two packages
* are mutually comparable.
*/
if(date9 < MINDAYS || date9 > MAXDAYS) {
throw new DateConversionException(
"date9=%d must fall within the range [%d, %d]",
date9, MINDAYS, MAXDAYS);
}

DateTime dt = new DateTime(1960, 1, 1, 0, 0).plusDays(date9);

/*
* Same as above -- this is a Python-ism, which seems reasonable
* (until we start collecting clinical study data in the year 10,000)
* and is here to make sure that the two code bases are as functionally
* equivalent as possible (which helps for testing).
*/
if(dt.getYear() < MINYEAR || dt.getYear() > MAXYEAR) {
throw new DateConversionException(
"year=%d must fall within the range [%d, %d]",
dt.getYear(), MINYEAR, MAXYEAR);
}

return dt;
}

public Period time5ToJavaPeriod(int time5) {
/*
* Carry over Python-determined time and timedelta bounds, see comment above.
*/
if(time5 < MINDAYS || time5 > MAXDAYS) {
throw new DateConversionException(
"time5=%d must fall within the range [%d, %d]",
time5, MINDAYS, MAXDAYS);
}

/*
* This is confusing to me -- I don't know why, if we're only
* measuring seconds-within-a-day for a TIME-formatted value,
* we are storing _the total number of seconds since Jan 1, 1960_
* BUT that appears to be what's going on here. <SIGH>
*/
DateTime dt = new DateTime(1960, 1, 1, 0, 0).plusSeconds(time5);

/*
* Carry over Python-determined time and timedelta bounds, see comment above.
*/
if(dt.getYear() < MINYEAR || dt.getYear() > MAXYEAR) {
throw new DateConversionException(
"year=%d must fall within the range [%d, %d]",
dt.getYear(), MINYEAR, MAXYEAR);
}

/*
* for TIME values, we only return the hours/minutes/seconds
* as a separate Period value.
*/
int hours = dt.getHourOfDay();
int minutes = dt.getMinuteOfHour();
int seconds = dt.getSecondOfMinute();
int millis = dt.getMillisOfSecond();

return new Period(hours, minutes, seconds, millis);
}
}
13 changes: 13 additions & 0 deletions sas/src/main/java/org/eobjects/metamodel/sas/IO.java
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,19 @@ public static double readDouble(byte[] buffer, int off) {
return bb.getDouble(off);
}

public static float readFloat(byte[] buffer, int off) {
ByteBuffer bb = ByteBuffer.wrap(buffer);
bb.order(ByteOrder.LITTLE_ENDIAN);
return bb.getFloat(off);
}

public static Long readLong(byte[] buffer, int off) {
return ByteBuffer
.wrap(buffer)
.order(ByteOrder.LITTLE_ENDIAN)
.getLong(off);
}

public static byte[] readBytes(byte[] data, int off, int len) {
if (data.length < off + len) {
throw new SasReaderException("readBytes failed! data.length: "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@
*/
public enum SasColumnType {

NUMERIC, CHARACTER;
NUMERIC, CHARACTER, DATE, TIME;
}
Loading