How to get offset from timestamp in JAVA - java

I have two timestamps 1498329000000 and 1485282600000 in Europe/Brussels-
In which first one exist in Day Light saving and second one is non day light saving
How to get offset in positive or negative (difference) from UTC? i.e either 1hr or 2 hr , based on DST.

With the java.time-API it's quite easy:
public static ZoneOffset getZoneOffset(long epochMillis, String zoneName) {
Instant instant = Instant.ofEpochMilli(epochMillis);
ZonedDateTime zonedDateTime = instant.atZone(ZoneId.of(zoneName));
return zonedDateTime.getOffset();
}
public static void main(String[] args) {
System.out.println(getZoneOffset(1498329000000l, "Europe/Brussels")); // +02:00
System.out.println(getZoneOffset(1485282600000l, "Europe/Brussels")); // +01:00
}
Also have a look at the class ZoneRules which have sone helpful methods for working with timezones and Daylight Savings Time (DST):
boolean isDaylightSavings(Instant instant)
Checks if the specified instant is in daylight savings.
This checks if the standard offset and the actual offset are the same for the specified instant. If they are not, it is assumed that daylight savings is in operation.
This default implementation compares the actual and standard offsets.
Parameters:
instant - the instant to find the offset information for, not null, but null may be ignored if the rules have a single offset for all instants
Returns:
the standard offset, not null
Duration getDaylightSavings(Instant instant)
Gets the amount of daylight savings in use for the specified instant in this zone.
This provides access to historic information on how the amount of daylight savings has changed over time. This is the difference between the standard offset and the actual offset. Typically the amount is zero during winter and one hour during summer. Time-zones are second-based, so the nanosecond part of the duration will be zero.
This default implementation calculates the duration from the actual and standard offsets.
Parameters:
instant - the instant to find the daylight savings for, not null, but null may be ignored if the rules have a single offset for all instants
Returns:
the difference between the standard and actual offset, not null
ZoneOffsetTransition nextTransition(Instant instant)
Gets the next transition after the specified instant.
This returns details of the next transition after the specified instant. For example, if the instant represents a point where "Summer" daylight savings time applies, then the method will return the transition to the next "Winter" time.
Parameters:
instant - the instant to get the next transition after, not null, but null may be ignored if the rules have a single offset for all instants
Returns:
the next transition after the specified instant, null if this is after the last transition
To get the the ZoneRules you can use:
ZoneRules rules = ZoneId.of("Europe/Brussels").getRules();

You can get the timezone offset from UTC in seconds in Brussels timezone of a particular timestamp, using this code snippet:
ZoneId zone = ZoneId.of("Europe/Brussels");
Instant instant = Instant.ofEpochMilli(timestamp);
OffsetDateTime dateTime = OffsetDateTime.ofInstant(instant, zone);
int offsetInSeconds = dateTime.get(ChronoField.OFFSET_SECONDS);
If you want to get it in hours you need to do some more work, and some countries have timezone offsets that are not whole hours.
public static void main(String[] args) {
System.out.println(utcOffsetInHoursInBrussels(1498329000000L));
System.out.println(utcOffsetInHoursInBrussels(1485282600000L));
}
public static int utcOffsetInHoursInBrussels(long timestamp) {
ZoneId zone = ZoneId.of("Europe/Brussels");
Instant instant = Instant.ofEpochMilli(timestamp);
OffsetDateTime dateTime = OffsetDateTime.ofInstant(instant, zone);
int offsetInSeconds = dateTime.get(ChronoField.OFFSET_SECONDS);
if (offsetInSeconds % 3600 == 0) {
return offsetInSeconds / 3600;
} else {
throw new IllegalArgumentException("Offset is not a whole hour");
}
}
Output:
2
1

You don’t necessarily need to go through a ZonedDateTime or OffsetDateTime as in the other answers.
ZoneOffset offset = ZoneId.of("Europe/Brussels")
.getRules()
.getOffset(Instant.ofEpochMilli(1_498_329_000_000L));
Which you prefer is probably a matter of taste.

Oles answer is the best because your simple problem to query the timezone offset for a given instant does not require to use complex types like ZonedDateTime at all.
Java-8 or later (see also Oles answer):
ZoneRules rules = ZoneId.of("Europe/Brussels").getRules();
ZoneOffset offsetDST =
rules.getOffset(Instant.ofEpochMilli(1498329000000L));
System.out.println(offsetDST); // +02:00
ZoneOffset offsetSTD =
rules.getOffset(Instant.ofEpochMilli(1485282600000L));
System.out.println(offsetSTD); // +01:00
Old API (which is surprisingly even the simplest approach):
TimeZone tz = TimeZone.getTimeZone("Europe/Brussels");
System.out.println(tz.getOffset(1498329000000L)); // 7200000 ms
System.out.println(tz.getOffset(1485282600000L)); // 3600000 ms

Related

Convert Instant to OffsetDateTime then Instant.parse() on the OffsetDateTime leads to DateTimeParseException for zero seconds cases

I am receiving instant from a service and below are the cases.
In first case, say instant = "2021-03-23T04:17:35Z" & In second case,
instant = "2021-07-15T05:27:00Z"
Then required to convert instant to offsetDateTime, doing like
OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.UTC)
Now I want to calculate hours gap between above offsetDateTime and instant.now
ChronoUnit.HOURS.between(Instant.parse(offsetDateTime), Instant.now())
Results
case 1 : it works fine
case 2 : ERROR : DateTimeParseException: Text '2021-07-15T05:27Z' could not be parsed at index 16
Figured out the reason :
in case 2, if it would have pass the same 2021-07-15T05:27:00Z. it would work but as instant.atOffset(ZoneOffset.UTC) internally will call to below method, where zero would be removed, basically minute part will be sorted out. so below fn will return 2021-07-15T05:27Z , this will lead to DateTimeParseException.
public static OffsetDateTime ofInstant(Instant instant, ZoneId zone) {
Objects.requireNonNull(instant, "instant");
Objects.requireNonNull(zone, "zone");
ZoneRules rules = zone.getRules();
ZoneOffset offset = rules.getOffset(instant);
LocalDateTime ldt = LocalDateTime.ofEpochSecond(instant.getEpochSecond(), instant.getNano(), offset);
return new OffsetDateTime(ldt, offset);
}
One solution I am assuming manually append the zero but that's might not be good practice.
You do not need any DateTimeFormatter
You do not need any DateTimeFormatter to parse your Date-Time strings. The modern Date-Time API is based on ISO 8601 and does not require using a DateTimeFormatter object explicitly as long as the Date-Time string conforms to the ISO 8601 standards.
import java.time.Instant;
import java.time.temporal.ChronoUnit;
public class Main {
public static void main(String[] args) {
Instant instant1 = Instant.parse("2021-03-23T04:17:35Z");
Instant instant2 = Instant.parse("2021-07-15T05:27:00Z");
System.out.println(instant1);
System.out.println(instant2);
System.out.println(ChronoUnit.HOURS.between(instant1, instant2));
}
}
Output:
2021-03-23T04:17:35Z
2021-07-15T05:27:00Z
2737
ONLINE DEMO
Learn more about the modern Date-Time API from Trail: Date Time.
The Answer by Avinash is correct.
In addition, let’s look at this code from the Question:
public static OffsetDateTime ofInstant(Instant instant, ZoneId zone)
{
Objects.requireNonNull(instant, "instant");
Objects.requireNonNull(zone, "zone");
ZoneRules rules = zone.getRules();
ZoneOffset offset = rules.getOffset(instant);
LocalDateTime ldt = LocalDateTime.ofEpochSecond(instant.getEpochSecond(), instant.getNano(), offset);
return new OffsetDateTime(ldt, offset);
}
Firstly, if you want to apply an offset to a moment, there is no need for LocalDateTime class. Simply do this:
OffsetDateTime odt = instant.atOffset( myZoneOffset ) ;
See the tutorial on naming conventions for at…, from…, with…, etc.
When you want to perceive a moment thought the wall-clock time used by the people of a particular region, apply a time zone (ZoneId) to an Instant to get a ZonedDateTime. Use ZonedDateTime rather than OffsetDateTime. A ZonedDateTime is preferable to a mere OffsetDateTime because it contains more information. This information may be critical if adding or subtracting to move to another moment. This information is also useful when producing text to represent the content of this date-time object.
Understand that an offset from UTC is simply a number of hours-minutes-seconds. A time zone is much more. A time zone is a history of the past, present, and future changes to the offset used by the people of a particular region as decided by politicians.
ZonedDateTime zdt = instant.atZone( myZoneId ) ;
So your method should be something like this.
public static ZonedDateTime ofInstant(Instant instant, ZoneId zone)
{
ZonedDateTime zdt =
Objects.requireNonNull( instant )
.atZone(
Objects.requireNonNull( zone )
)
;
return zdt ;
}
If you really need a OffsetDateTime, despite ZonedDateTime being preferred, extract one.
OffsetDateTime odt = zdt.toOffsetDateTime() ;

Java - auto generate current day + user input time

At the moment I got a class 'Flight' with Date datatypes; departure and arrival datetime.
The adding of flights happens by user input. The day should be the current date automatically and the time is the user's choice. Which means a person only has to input HH:mm.
At the moment it is a bit confusing with all the choices; Timestamp, Date, Localtime etc.
How can I make a simple user input with a scanner for this problem?
It should take todays date, add the user input containing the time and add it together to fit into my Date datatype.
Anyone has a clue how to do this or could provide some tips / best practices?
Since you said that you are developing a desktop app and you need the current date (on the pc) you can use a combination of LocalDate and LocalTime to achieve your goal.
Here is the code:
public static void main(String[] args) {
LocalDate currentDate = LocalDate.now();
LocalTime userInputTime = null;
Scanner sc = new Scanner(System.in);
String dateTimeLine = sc.nextLine();
sc.close();
DateTimeFormatter dt = DateTimeFormatter.ofPattern("HH:mm");
userInputTime = LocalTime.parse(dateTimeLine,dtf);
System.err.println(LocalDateTime.of(currentDate, userInputTime));
}
First, you use LocalDate.now() in order to generate the current date (only the date, without hours, minutes and seconds).
Next we use Scanner in order to read a string entered by the user.
In order to convert the string to a LocalTime (this class contains info only about time in a day, so it has values for hours,minutes,seconds and nanoseconds), we have to define a DateTimeFormatter. The DateTimeFormatter defines how the string will be converted into LocalTime instance.
In the code I just wrote, I said that the string input will be of type "hours:minutes". For example, possible values are:
"10:25" - 10 hours and 25 minutes,
"23:00" - 23 hours and 0 minutes,
"02:13" - 2 hours and 13 minutes.
After we create the LocalTime object, all we have to do is to join the date and time objects in order to create a LocalDateTime object which is done in this line:
LocalDateTime.of(currentDate, userInputTime)
So lets say that the date on your current PC is 2018-05-06. If you run the program and enter 10:50 in the console, the output should be a LocalDateTime object that has 2018-05-06 as a date and 10 hours and 50 minutes as time of the day.
It is important to note that this line:
userInputTime = LocalTime.parse(dateTimeLine,dtf);
will throw an java.time.format.DateTimeParseException if the entered string by the user does not satisfy the required format.
tl;dr
LocalDateTime.of( // A `LocalDateTime` represents a set of *potential* moments along a range of about 26-27 hours. Not an actual moment, not a point on the timeline.
LocalDate.systemDefault() , // Get the JVM’s current default time zone. Can change at any moment *during* runtime. When crucial, always confirm with the user.
LocalTime.parse( "14:57" ) // Parse a string in standard ISO 8601 format as a time-of-day without regard for time zone or offset-from-UTC.
) // Returns a `LocalDateTime` object.
.atZone( // Determine an actual moment, a point on the timeline.
ZoneId( "Africa/Tunis" ) // Specify a time zone as `Continent/Region`, never as 3-4 letter pseudo-zones such as `PST`, `CST`, or `IST`.
) // Returns a `ZonedDateTime` object.
.toInstant() // Extracts a `Instant` object from the `ZonedDateTime` object, always in UTC by default.
Details
At the moment it is a bit confusing
Date-time handling is very confusing work.
Tips:
Forget about your own time zone. Think in terms of UTC rather than your own parochial time zone.
Learn the difference between real moments (points on the timeline), and date-time approximations that are not on the timeline, often-called “local” values.
Be careful when reading Stack Overflow or other places on the internet about date-time. You will encounter poor advice and many wrong solutions.
with all the choices; Timestamp, Date, Localtime etc.
Never use the troublesome old legacy date-time classes bundled with the earliest versions of Java. Never use java.sql.Timestamp, java.util.Date, java.util.Calendar, and so on.
➡ Use only classes in the java.time package.
The java.time classes are an industry-leading date-time framework. Extremely well-designed and thought-through, with lessons learned from the Joda-Time project it succeeds.
Anyone has a clue how to do this or could provide some tips / best practices?
You might be sorry you asked. Read on.
At the moment I got a class 'Flight' with Date datatypes; departure and arrival datetime.
So define a Flight class.
In real-life, flights happen far enough out in the future that we risk politicians changing the definition of the time zone. Most commonly these changes are adopting/dropping/altering Daylight Saving Time (DST). But arbitrary changes are made periodically for all kinds of reasons. We could debate the wisdom/sanity of such changes, but the fact is they happen. They happen quite frequently as politicians seemly oddly prone to making these changes around the world in many countries. And nearly all of them do so with little forewarning, sometimes just weeks. Or even with no warning at all, as North Korea did this week.
I have no understanding of how airlines actually work, but from poking around airline schedules and various readings, it seems they try to maintain their schedules using the zoned time of the departing locality. So if a flight is scheduled to depart LAX at 6 AM, they keep that flight schedule on the day before, the day of, and the day after a DST change-over. If this is indeed the general intent, that means sitting-around killing time on one DST cut-over while trying to save an hour on the opposite DST cut-over. Apparently, Amtrak adopts this practice for its trains. Let’s proceed with this approach.
Using this “imaginary” schedule approach means we cannot determine for certain the exact moment when 6 AM will occur in the future. So we need to record our desire for that date and that time-of-day without applying a time zone. But we must record the desired time zone so we know in what context we can later determine the exact moment, when close enough in time that we needn’t worry about zone changes.
So we use LocalDate and LocalTime types, as they purposely lack any concept of time zone (a name in Continent/Region format) or offset-from-UTC (a number of hours-minutes-seconds).
The ZoneId class represents a time zone.
I am using the word Unzoned in the names to remind us that these values do not represent actual moments on the timeline. The word “local” tends to confuse beginners.
public class Flight {
private String flightNumber;
private LocalDate departureDateUnzoned;
private LocalTime departureTimeUnzoned;
private ZoneId departureZoneId ;
}
As for arrival, store the span-of-time expected for that flight rather than the arrival date-time. You can calculate the arrival, so no need to store it. The Duration class tracks a number of hours, minutes, seconds, and fractional second.
To calculate the arrival, let’s return a single value using the LocalDateTime class, which simply combines a LocalDate with a LocalTime. We could have used this type to make a single departureUnzoned member variable in our class definition. I went with separate LocalDate and LocalTime as building blocks so you would understand the pieces. So many programmers use their intuition rather than the documentation to assume that LocalDateTime means a specific moment in a locality when actually it means just the opposite. (You will find many incorrect Answers on Stack Overflow advising LocalDateTime when actually Instant or ZonedDateTime should be used.)
Let's add a method to calculate that arrival.
public class Flight {
private String flightNumber;
private LocalDate departureDateUnzoned;
private LocalTime departureTimeUnzoned;
private ZoneId departureZoneId;
private Duration duration;
public LocalDateTime arrivalDateTimeUnzoned () {
LocalDateTime departureUnzoned = LocalDateTime.of( this.departureDateUnzoned , this.departureTimeUnzoned );
LocalDateTime ldt = departureUnzoned.plus( this.duration );
return ldt;
}
}
But this returned LocalDateTime fails to account for time zone. Usually, airlines and train report to customers the expected arrival time adjusted into the time zone of that region. So we need an arrival time zone. And we can use that zone when calculating the arrival, thereby producing a ZonedDateTime. A ZonedDateTime is a specific moment, it is a point on the timeline, unlike LocalDateTime. But remember, if we are scheduling flights out into the future, the calculated ZonedDateTime will change if our code is run after politicians redefine the time zone.
public class Flight {
private String flightNumber;
private LocalDate departureDateUnzoned;
private LocalTime departureTimeUnzoned;
private ZoneId departureZoneId;
private Duration duration;
private ZoneId arrivalZoneId;
public ZonedDateTime arrivalDateTimeZoned () {
ZonedDateTime departureZoned = ZonedDateTime.of( this.departureDateUnzoned , this.departureTimeUnzoned , this.departureZoneId );
ZonedDateTime zdt = departureZoned.plus( this.duration );
return zdt;
}
}
Back to the part of your Question about determining the date automatically. That requires a time zone. For any given moment, the date varies around the globe. Think about that. A few minutes after midnight in Paris France is a new day, while still “yesterday” in Montréal Québec.
We can ask for the JVM’s current default time zone.
ZoneId userZoneId = ZoneId.systemDefault() ;
But when crucial, you must confirm with the user.
ZoneId userZoneId = ZoneId.of( "America/Montreal" ) ;
So now we can add the constructor you asked for, passing the time-of-day (a LocalTime, and guessing the time zone by using the JVM’s current default.
But we still need all the other pieces. So defaulting the date does not save us much.
public class Flight {
private String flightNumber;
private LocalDate departureDateUnzoned;
private LocalTime departureTimeUnzoned;
private ZoneId departureZoneId;
private Duration duration;
private ZoneId arrivalZoneId;
// Constructor
public Flight ( String flightNumber , LocalTime departureTimeUnzoned , ZoneId departureZoneId , Duration duration , ZoneId arrivalZoneId ) {
this.flightNumber = flightNumber;
this.departureTimeUnzoned = departureTimeUnzoned;
this.departureZoneId = departureZoneId;
this.duration = duration;
this.arrivalZoneId = arrivalZoneId;
// Determine today’s date using JVM’s current default time zone. Not advisable in many business scenarios, but specified by our Question at hand.
ZoneId z = ZoneId.systemDefault();
LocalDate today = LocalDate.now( z );
this.departureDateUnzoned = today;
}
public ZonedDateTime arrivalDateTimeZoned () {
ZonedDateTime departureZoned = ZonedDateTime.of( this.departureDateUnzoned , this.departureTimeUnzoned , this.departureZoneId );
ZonedDateTime zdt = departureZoned.plus( this.duration );
return zdt;
}
}
Let’s add a toString method for reporting.
We represent the date-time values as strings in standard ISO 8601 formats. The java.time classes use these standard formats when parsing/generating strings. The Z on the end is pronounced Zulu and means UTC.
While airlines and trains report date-times to their customers in the regions’ time zones, we can assume they use only UTC internally. The Instant class represents values in UTC specifically. So our toString extracts Instant objects from the ZonedDateTime objects.
And we add a main method for demonstration. Here is the complete class, with import etc.
package com.basilbourque.example;
import java.time.*;
public class Flight {
private String flightNumber;
private LocalDate departureDateUnzoned;
private LocalTime departureTimeUnzoned;
private ZoneId departureZoneId;
private Duration duration;
private ZoneId arrivalZoneId;
// Constructor
public Flight ( String flightNumber , LocalTime departureTimeUnzoned , ZoneId departureZoneId , Duration duration , ZoneId arrivalZoneId ) {
this.flightNumber = flightNumber;
this.departureTimeUnzoned = departureTimeUnzoned;
this.departureZoneId = departureZoneId;
this.duration = duration;
this.arrivalZoneId = arrivalZoneId;
// Determine today’s date using JVM’s current default time zone. Not advisable in many business scenarios, but specified by our Question at hand.
ZoneId z = ZoneId.systemDefault();
LocalDate today = LocalDate.now( z );
this.departureDateUnzoned = today;
}
public ZonedDateTime arrivalDateTimeZoned () {
ZonedDateTime departureZoned = ZonedDateTime.of( this.departureDateUnzoned , this.departureTimeUnzoned , this.departureZoneId );
ZonedDateTime zdt = departureZoned.plus( this.duration );
return zdt;
}
#Override
public String toString () {
ZonedDateTime departureZoned = ZonedDateTime.of( this.departureDateUnzoned , this.departureTimeUnzoned , this.departureZoneId );
String flightInUtc = departureZoned.toInstant().toString() + "/" + this.arrivalDateTimeZoned().toInstant().toString();
return "Flight{ " +
"flightNumber='" + this.flightNumber + '\'' +
" | departureDateUnzoned=" + this.departureDateUnzoned +
" | departureTimeUnzoned=" + this.departureTimeUnzoned +
" | departureZoneId=" + this.departureZoneId +
" | departureZoned=" + departureZoned +
" | duration=" + this.duration +
" | arrivalZoneId=" + this.arrivalZoneId +
" | calculatedArrival=" + this.arrivalDateTimeZoned() +
" | flightInUtc=" + flightInUtc +
" }";
}
public static void main ( String[] args ) {
LocalTime lt = LocalTime.of( 6 , 0 ); // 6 AM.
Flight f = new Flight( "A472" , lt , ZoneId.of( "America/Los_Angeles" ) , Duration.parse( "PT6H37M" ) , ZoneId.of( "America/Montreal" ) );
String output = f.toString();
System.out.println( output );
}
}
When run.
Flight{ flightNumber='A472' | departureDateUnzoned=2018-05-06 | departureTimeUnzoned=06:00 | departureZoneId=America/Los_Angeles | departureZoned=2018-05-06T06:00-07:00[America/Los_Angeles] | duration=PT6H37M | arrivalZoneId=America/Montreal | calculatedArrival=2018-05-06T12:37-07:00[America/Los_Angeles] | flightInUtc=2018-05-06T13:00:00Z/2018-05-06T19:37:00Z }
To use this from the console, ask the user for the time-of-day in 24-hour clock. Parse the input string.
String input = "14:56" ; // 24-hour clock.
LocalTime lt = LocalTime.parse( input ) ;
This is far from complete for real-world work. But hopefully it makes for an educational example.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

java 8 compare datetime, with zoned time

How do I check if request is in between this window? I have a time of request as 2011-12-03T15:15:30-05:00 and a time window which could be defined in any zone, example 09:00:00+00:00 and 17:00:00+00:00.
Now if i parse the datetime to LocalTime i loose the timezone.
public LocalTime time(String time){
return LocalTime.from(DateTimeFormatter.ISO_OFFSET_TIME.parse(time));
}
private ZonedDateTime dateTime(String dateTime){
return ZonedDateTime.from(DateTimeFormatter.ISO_DATE_TIME.parse(dateTime));
}
//Compare function
public boolean compare(ZonedDateTime dateTime, LocalTime localTime, LocalTime localTime2) {
LocalTime time = dateTime.withZoneSameInstant(utc).toLocalTime();
int start = time.compareTo(localTime);
int end = time.compareTo(localTime2);
return start >= 0 && end <= 0;
}
now i invoke the above compare function as:
service.compare(dateTime("2011-12-03T15:15:30-05:00"), time("09:00:00+00:00"), time("17:00:00+00:00"));
Here is example of code that will compare a ZonedDateTime value in any time zone, to see if it falls within a time-of-day window define in another (or same) time zone, correctly adjusting for the time zone difference.
Notice that time window values are now OffsetTime, not LocalTime, so the window time zone offset is known.
Also notice that this code was change to be upper-exclusive, which is generally what you want for range comparisons like this.
public static boolean inWindow(ZonedDateTime dateTime, OffsetTime windowStart, OffsetTime windowEnd) {
if (! windowStart.getOffset().equals(windowEnd.getOffset()))
throw new IllegalArgumentException("windowEnd must have same offset as windowStart");
OffsetTime time = dateTime.toOffsetDateTime()
.withOffsetSameInstant(windowStart.getOffset())
.toOffsetTime();
return (! time.isBefore(windowStart) && time.isBefore(windowEnd));
}

How to Convert Modified Julian Day Numbers with the Java 8 DateTime API

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); }
Per your comments, your core issue seems to be about the ambiguity of converting a date-time without time zone (a LocalDateTime) into a zoned moment (a ZonedDateTime). You explain that anomalies such as Daylight Saving Time (DST) can result in invalid values.
ZonedDateTime zdt = myLocalDateTime.atZone( myZoneId );
This is true. There is no perfect solution when landing in the DST “Spring-forward” or ”Fall-back” cutovers. However, the java.time classes do resolve the ambiguity by adopting a certain policy. You may or may not agree with that policy. But if you do agree, then you can rely on java.time to determine a result.
To quote the documentation for ZonedDateTime.ofLocal:
In the case of an overlap, where clocks are set back, there are two valid offsets. If the preferred offset is one of the valid offsets then it is used. Otherwise the earlier valid offset is used, typically corresponding to "summer".
In the case of a gap, where clocks jump forward, there is no valid offset. Instead, the local date-time is adjusted to be later by the length of the gap. For a typical one hour daylight savings change, the local date-time will be moved one hour later into the offset typically corresponding to "summer".
LocalDate modifiedJulianEpoch = LocalDate.of( 1858 , 11 , 17 );
LocalDate today = LocalDate.now( ZoneOffset.UTC );
long days = ChronoUnit.DAYS.between ( modifiedJulianEpoch , today );
today: 2017-03-19
days: 57831
I do not quite understand your issues. But it seems to me that the point of MJD (Modified Julian Days) is to have a way to track a “One True Time” to avoid all the confusion of time zones. In standard ISO 8601 calendar system, UTC plays than role of “One True Time”. So I suggest sticking to UTC.
When you need to consider a region’s wall-clock time, such as your Medicare example of the region’s end-of-day, determine the regional wall-clock time and then convert to UTC. The Instant class in java.time is always in UTC by definition.
ZoneId z = ZoneId.of( "America/Los_Angeles" );
LocalDate localDate = LocalDate.now( z );
ZonedDateTime firstMomentNextDay = localDate.plusDays( 1 ).atStartOfDay( z );
Instant medicareExpiration = firstMomentNextDay.toInstant(); // UTC
BigDecimal modJulDays = this.convertInstantToModifiedJulianDays( medicareExpiration ) ;
Use BigDecimal when working with fractional decimals where accuracy matters. Using double, Double, float, or Float means using Floating-Point technology that trades away accuracy for faster performance.
Here is a rough-cut at some code to do the conversion from BigDecimal (Modified Julian Days) to Instant. I suppose some clever person might find a leaner or meaner version of this code, but my code here seems to be working. Use at your own risk. I barely tested this code at all.
public Instant convertModifiedJulianDaysToInstant ( BigDecimal modJulDays ) {
Instant epoch = OffsetDateTime.of ( 1858, 11, 17, 0, 0, 0, 0, ZoneOffset.UTC ).toInstant ( ); // TODO: Make into a constant to optimize.
long days = modJulDays.toBigInteger ( ).longValue ( );
BigDecimal fractionOfADay = modJulDays.subtract ( new BigDecimal ( days ) ); // Extract the fractional number, separate from the integer number.
BigDecimal secondsFractional = new BigDecimal ( TimeUnit.DAYS.toSeconds ( 1 ) ).multiply ( fractionOfADay );
long secondsWhole = secondsFractional.longValue ( );
long nanos = secondsFractional.subtract ( new BigDecimal ( secondsWhole ) ).multiply ( new BigDecimal ( 1_000_000_000L ) ).longValue ( );
Duration duration = Duration.ofDays ( days ).plusSeconds ( secondsWhole ).plusNanos ( nanos );
Instant instant = epoch.plus ( duration );
return instant;
}
And going the other direction.
public BigDecimal convertInstantToModifiedJulianDays ( Instant instant ) {
Instant epoch = OffsetDateTime.of ( 1858, 11, 17, 0, 0, 0, 0, ZoneOffset.UTC ).toInstant ( ); // TODO: Make into a constant to optimize.
Duration duration = Duration.between ( epoch, instant );
long wholeDays = duration.toDays ( );
Duration durationRemainder = duration.minusDays ( wholeDays );
BigDecimal wholeDaysBd = new BigDecimal ( wholeDays );
BigDecimal partialDayInNanosBd = new BigDecimal ( durationRemainder.toNanos ( ) ); // Convert entire duration to a total number of nanoseconds.
BigDecimal nanosInADayBd = new BigDecimal ( TimeUnit.DAYS.toNanos ( 1 ) ); // How long is a standard day in nanoseconds?
int scale = 9; // Maximum number of digits to the right of the decimal point.
BigDecimal partialDayBd = partialDayInNanosBd.divide ( nanosInADayBd ); // Get a fraction by dividing a total number of nanos in a day by our partial day of nanos.
BigDecimal result = wholeDaysBd.add ( partialDayBd );
return result;
}
Calling those conversion methods.
BigDecimal input = new BigDecimal ( "57831.5" );
Instant instant = this.convertModifiedJulianDaysToInstant ( input );
BigDecimal output = this.convertInstantToModifiedJulianDays ( instant );
Dump to console.
System.out.println ( "input.toString(): " + input );
System.out.println ( "instant.toString(): " + instant );
System.out.println ( "output.toString(): " + output );
input.toString(): 57831.5
instant.toString(): 2017-03-19T12:00:00Z
output.toString(): 57831.5
See all that code running live at IdeOne.com.
Also, my Answer to a similar Question may be helpful.

How to get milliseconds from LocalDateTime in Java 8

I am wondering if there is a way to get current milliseconds since 1-1-1970 (epoch) using the new LocalDate, LocalTime or LocalDateTime classes of Java 8.
The known way is below:
long currentMilliseconds = new Date().getTime();
or
long currentMilliseconds = System.currentTimeMillis();
I'm not entirely sure what you mean by "current milliseconds" but I'll assume it's the number of milliseconds since the "epoch," namely midnight, January 1, 1970 UTC.
If you want to find the number of milliseconds since the epoch right now, then use System.currentTimeMillis() as Anubian Noob has pointed out. If so, there's no reason to use any of the new java.time APIs to do this.
However, maybe you already have a LocalDateTime or similar object from somewhere and you want to convert it to milliseconds since the epoch. It's not possible to do that directly, since the LocalDateTime family of objects has no notion of what time zone they're in. Thus time zone information needs to be supplied to find the time relative to the epoch, which is in UTC.
Suppose you have a LocalDateTime like this:
LocalDateTime ldt = LocalDateTime.of(2014, 5, 29, 18, 41, 16);
You need to apply the time zone information, giving a ZonedDateTime. I'm in the same time zone as Los Angeles, so I'd do something like this:
ZonedDateTime zdt = ldt.atZone(ZoneId.of("America/Los_Angeles"));
Of course, this makes assumptions about the time zone. And there are edge cases that can occur, for example, if the local time happens to name a time near the Daylight Saving Time (Summer Time) transition. Let's set these aside, but you should be aware that these cases exist.
Anyway, if you can get a valid ZonedDateTime, you can convert this to the number of milliseconds since the epoch, like so:
long millis = zdt.toInstant().toEpochMilli();
What I do so I don't specify a time zone is,
System.out.println("ldt " + LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
System.out.println("ctm " + System.currentTimeMillis());
gives
ldt 1424812121078
ctm 1424812121281
As you can see the numbers are the same except for a small execution time.
Just in case you don't like System.currentTimeMillis, use Instant.now().toEpochMilli()
Since Java 8 you can call java.time.Instant.toEpochMilli().
For example the call
final long currentTimeJava8 = Instant.now().toEpochMilli();
gives you the same results as
final long currentTimeJava1 = System.currentTimeMillis();
To avoid ZoneId you can do:
LocalDateTime date = LocalDateTime.of(1970, 1, 1, 0, 0);
System.out.println("Initial Epoch (TimeInMillis): " + date.toInstant(ZoneOffset.ofTotalSeconds(0)).toEpochMilli());
Getting 0 as value, that's right!
You can use java.sql.Timestamp also to get milliseconds.
LocalDateTime now = LocalDateTime.now();
long milliSeconds = Timestamp.valueOf(now).getTime();
System.out.println("MilliSeconds: "+milliSeconds);
To get the current time in milliseconds (since the epoch), use System.currentTimeMillis().
You can try this:
long diff = LocalDateTime.now().atZone(ZoneOffset.UTC).toInstant().toEpochMilli();
Why didn't anyone mentioned the method LocalDateTime.toEpochSecond():
LocalDateTime localDateTime = ... // whatever e.g. LocalDateTime.now()
long time2epoch = localDateTime.toEpochSecond(ZoneOffset.UTC);
This seems way shorter that many suggested answers above...
For LocalDateTime I do it this way:
LocalDateTime.of(2021,3,18,7,17,24,341000000)
.toInstant(OffsetDateTime.now().getOffset())
.toEpochMilli()
I think this is more simpler:
ZonedDateTime zdt = ZonedDateTime.of(LocalDateTime.now(), ZoneId.systemDefault());
Assert.assertEquals(System.currentTimeMillis(), zdt.toInstant().toEpochMilli());
get the millis like System.currentTimeMillis() (from UTC).
There are some methods available that no one has mentioned here. But I don't see a reason why they should not work.
In case of LocalDate, you can use the toEpochDay() method. It returns the number of days since 01/01/1970. That number then can be easily converted to milliseconds:
long dateInMillis = TimeUnit.DAYS.toMillis(myLocalDate.toEpochDays());
Documentation can be found here.
In case of LocalDateTime, you can use the toEpochSecond() method. It returns the number of seconds since 01/01/1970. That number then can be converted to milliseconds, too:
long dateTimeInMillis = TimeUnit.SECONDS.toMillis(myLocalDateTime.toEpochSeconds());
Documentation for that is here.
If you have a Java 8 Clock, then you can use clock.millis() (although it recommends you use clock.instant() to get a Java 8 Instant, as it's more accurate).
Why would you use a Java 8 clock? So in your DI framework you can create a Clock bean:
#Bean
public Clock getClock() {
return Clock.systemUTC();
}
and then in your tests you can easily Mock it:
#MockBean private Clock clock;
or you can have a different bean:
#Bean
public Clock getClock() {
return Clock.fixed(instant, zone);
}
which helps with tests that assert dates and times immeasurably.
Date and time as String to Long (millis):
String dateTimeString = "2020-12-12T14:34:18.000Z";
DateTimeFormatter formatter = DateTimeFormatter
.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ENGLISH);
LocalDateTime localDateTime = LocalDateTime
.parse(dateTimeString, formatter);
Long dateTimeMillis = localDateTime
.atZone(ZoneId.systemDefault())
.toInstant()
.toEpochMilli();
default LocalDateTime getDateFromLong(long timestamp) {
try {
return LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneOffset.UTC);
} catch (DateTimeException tdException) {
// throw new
}
}
default Long getLongFromDateTime(LocalDateTime dateTime) {
return dateTime.atOffset(ZoneOffset.UTC).toInstant().toEpochMilli();
}

Categories

Resources