I have a database that stores Dates and DateTimes (as INTEGERs and DOUBLEs, respectively) as Modified Julian Day Numbers (MJD). Modified Julian Day Numbers are a consecutive count of days from midnight UTC, 17 November 1858. By definition they are always reckoned in UTC, have an offset of +0:00 from GMT, and do not adjust for daylight savings. These properties simplify certain operations with DateTimes such as precedence and date arithmetic.
The downside is that MJDs must be relocalized from UTC and delocalized back to UTC before and after use, particularly for applications for which day boundaries are critically important (Medicare, for example, recognizes a billable date boundary as midnight in -local- time).
Consider the following static factory method whose purpose is to delocalize into an MJD (in UTC) a "regional day number" (basically, an MJD that has had the appropriate offset added to it so that it represents a local DateTime):
public static MJD ofDayNumberInZone(double regDN, ZoneId zone) {
:
:
}
It seems intuitively obvious that if you have a local date and time, and you know the local time zone, that you should have all the information you need in order to offset regDN
back to UTC (as required by an MJD).
In fact, this function is fairly simple to write using the previous Java Calendar API. The regDN
is easily converted to a Date
which is used to set a GregorianCalendar instance. Knowing the "local time zone" the calendar reports ZONE_OFFSET and DST_OFFSET values that can then be used to adjust the day number into an MJD.
This is my attempt to write a similar algorithm in the Java 8 DateTime API:
public static MJD ofDayNumberInZone(double zonedMJD, ZoneId zone) {
double epochSec = ((zonedMJD - MJD.POSIX_EPOCH_AS_MJD) * 86400.0);
LocalDateTime dt = LocalDateTime
.ofEpochSecond(
(long) epochSec,
(int) (epochSec - Math.floor(epochSec) * 1000000000.0),
---> zone.getRules().getOffset( <Instant> )
);
}
The problem is indicated at the arrow. Constructing a LocalDateTime instance using the ofEpochSecond method seems to require that you know the offsets in advance, which seems counterintuitive (I have the local time and the time zone already, it's the offset I want).
I haven't been successful in finding a simple way to obtain the offsets from local time back to UTC using the Java 8 API. While I could continue to use the old Calendar API, the new DateTime libraries offer compelling advantages ... so I'd like to try and figure this out. What am I missing?
EDIT: Here is an example, using the old Java Calendar API, of how a count of days and fractional days in an arbitrary time zone is "deregionalized" into UTC. This method takes a double which is the "regionalized day number" and a time zone object. It uses a GregorianCalendar to convert the parameters into a UTC count of milliseconds from the Epoch:
private static final Object lockCal = new Object();
private static final SimpleDateFormat SDF = new SimpleDateFormat();
private static final GregorianCalendar CAL = new
GregorianCalendar(TimeZone.getTimeZone(HECTOR_ZONE));
:
:
public static MJD ofDayNumberInZone(double rdn, TimeZone tz) {
Date dat = new Date((long) ((rdn - MJD.POSIX_EPOCH_AS_MJD) *
(86400.0 * 1000.0)));
return MJD.ofDateInZone(dat, tz);
}
public static MJD ofDateInZone(Date dat, TimeZone tz) {
long utcMillisFromEpoch;
synchronized(lockCal) {
CAL.setTimeZone(tz);
CAL.setTime(dat);
utcMillisFromEpoch = CAL.getTimeInMillis();
}
return MJD.ofEpochMillisInUTC(utcMillisFromEpoch);
}
public static MJD ofEpochMillisInUTC(long millis)
{ return new MJD((millis / (86400.0 * 1000.0)) + POSIX_EPOCH_AS_MJD); }