/*-------------------------------------------------------------------------
*
* Copyright (c) 2003-2011, PostgreSQL Global Development Group
*
*
*-------------------------------------------------------------------------
*/
package org.teiid.transport.pg;
import java.sql.Date;
import java.util.TimeZone;
/**
* Slimmed down from org.postgresql.jdbc2.TimestampUtils
*/
public class TimestampUtils {
public static final long DATE_POSITIVE_INFINITY = 9223372036825200000l;
public static final long DATE_NEGATIVE_INFINITY = -9223372036832400000l;
public static final long DATE_POSITIVE_SMALLER_INFINITY = 185543533774800000l;
public static final long DATE_NEGATIVE_SMALLER_INFINITY = -185543533774800000l;
/**
* Returns the SQL Date object matching the given bytes with
* {@link Oid#DATE}.
*
* @param tz The timezone used.
* @param bytes The binary encoded date value.
* @return The parsed date object.
*/
public static Date toDate(TimeZone tz, int days) {
long secs = toJavaSecs(days * 86400L);
long millis = secs * 1000L;
int offset = tz.getOffset(millis);
if (millis <= DATE_NEGATIVE_SMALLER_INFINITY) {
millis = DATE_NEGATIVE_INFINITY;
offset = 0;
} else if (millis >= DATE_POSITIVE_SMALLER_INFINITY) {
millis = DATE_POSITIVE_INFINITY;
offset = 0;
}
return new Date(millis - offset);
}
/**
* Converts the given postgresql seconds to java seconds.
* Reverse engineered by inserting varying dates to postgresql
* and tuning the formula until the java dates matched.
* See {@link #toPgSecs} for the reverse operation.
*
* @param secs Postgresql seconds.
* @return Java seconds.
*/
private static long toJavaSecs(long secs) {
// postgres epoc to java epoc
secs += 946684800L;
// Julian/Gregorian calendar cutoff point
if (secs < -12219292800L) { // October 4, 1582 -> October 15, 1582
secs += 86400 * 10;
if (secs < -14825808000L) { // 1500-02-28 -> 1500-03-01
int extraLeaps = (int) ((secs + 14825808000L) / 3155760000L);
extraLeaps--;
extraLeaps -= extraLeaps / 4;
secs += extraLeaps * 86400L;
}
}
return secs;
}
/**
* Converts the given java seconds to postgresql seconds.
* See {@link #toJavaSecs} for the reverse operation.
* The conversion is valid for any year 100 BC onwards.
*
* @param secs Postgresql seconds.
* @return Java seconds.
*/
public static long toPgSecs(long secs) {
// java epoc to postgres epoc
secs -= 946684800L;
// Julian/Greagorian calendar cutoff point
if (secs < -13165977600L) { // October 15, 1582 -> October 4, 1582
secs -= 86400 * 10;
if (secs < -15773356800L) { // 1500-03-01 -> 1500-02-28
int years = (int) ((secs + 15773356800L) / -3155823050L);
years++;
years -= years/4;
secs += years * 86400;
}
}
return secs;
}
}