Comparing times and times operations in Java - java

Hello
I wanna create app that has different behaviors if it is morning, noon, afternoon, evening.
Now i wanna set some variables with the time for each
For example noon = 12:00
Now i wanna compare current time with them and see if it is morning for example, and to calculate how much till noon 12:00 - current time.
Now I've seen several examples with different dates, but i want to compare only by hour.

Calendar cal=GregorianCalendar.getInstance();
int hour = cal.get(Calendar.HOUR);
Then compare the hour.
This will work for your local time zone

You can use the joda time hoursBetween or you can use Java calendar class. I would recommend using JodaTime.
Using the Java Calendar class:
Calendar future = Calendar.getInstance(); //future time
future.set(Calendar.YEAR, 2011);
future.set(Calendar.MONTH, 0);
future.set(Calendar.DATE,27);
future.set(Calendar.HOUR_OF_DAY,17);
//get current time
Calendar now = Calendar.getInstance();
//time difference between now and future in hours
long hoursDiff = (future.getTimeInMillis() - now.getTimeInMillis())/(60 * 60 * 1000);
System.out.println("Difference in hours is ="+hoursDiff);//prints 2 since it's 3 pm here
This does not factor in day light savings and compares against your LOCAL time zone.
Using Joda Time hoursBetween:
DateTime futureDate = new DateTime(future.getTime());
DateTime current = new DateTime(now.getTime());
int difference = Hours.hoursBetween(current,futureDate).getHours();
System.out.println("Difference in hours is ="+difference);
Also look at this question and this question.

tl;dr
if (
LocalTime.now( ZoneId.of( "Africa/Tunis” ) )
.isBefore( LocalTime.of( 12 , 0 ) )
) {
… // Do morning stuff.
}
java.time
The other answers were correct but use outmoded classes. The java.util.Date/.Calendar classes have been supplanted by the java.time framework built into Java 8 and later.
The LocalTime class represents a time-of-day without a date and without a time zone.
Make some constants for your definition of "morning", "afternoon", and so on. In real work I would use an enum. But I'll use a simple variable here for the demo.
LocalTime noon = LocalTime.of( 12 , 0 );
Time zone is crucial to interpreting time-of-day. A time-of-day only has meaning in the context of a particular time zone. If not specified, your JVM’s current default time zone will automatically be applied silently. I strongly recommend you instead always explicitly specify the desired/expected time zone. You can think of a ZonedDateTime as being the combination of an Instant plus a time zone (ZoneId).
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime now = ZonedDateTime.now( zoneId );
We can extract a LocalTime from the ZonedDateTime according to its assigned time zone.
LocalTime nowLocalTime = now.toLocalTime();
Compare to the target time.
Boolean isMorning = nowLocalTime.isBefore( noon );
Use the Duration class to represent a span of time as a total number of seconds plus a fraction of a second in nanoseconds.
ZonedDateTime todayNoon = now.adjustInto( noon );
Duration untilNoon = Duration.between( now , todayNoon );
The default output of Duration::toString is a string in a format defined by ISO 8601. Example PT38M2S which thirty-eight minutes and two seconds. You can also ask for number of minutes, etc.
By getting a Duration from ZonedDateTime objects we will get an accurate result accounting for anomalies such as Daylight Saving Time (DST). If you would rather use a generic 24-hour day calculation, pass the LocalTime objects to Duration.between.

You can use GregorianCalendar to do this. Create a new GregorianCalendar and set the month, day, and year to some constant value. Set the hour to whatever time you're interested in, ie 12:00 noon. Now just getTimeInMillis() and store that value. Later, you can create another GregorianCalendar with the no-arg version to get the current time. Set the month, day, and year to the same constant value as your benchmark value, and then just compare the getTimeInMillis() again to see if it is before, equal to, or after the reference time of day.

Related

Java ZonedDateTime.toInstant() behavior

I'm running the below expressions on December 7th, 2018.
I'm seeing a discrepancy whereby this:
ZonedDateTime.now(ZoneId.of("America/New_York")).minusDays(30)
returns (correctly):
2018-11-07T22:44:11.242576-05:00[America/New_York]
whereas conversion to an instant:
ZonedDateTime.now(ZoneId.of("America/New_York")).minusDays(30).toInstant()
seems to mess up the result by adding an extra day to it:
2018-11-08T03:58:01.724728Z
I need an instant conversion to use its result in the following code as Date:
... = Date.from(t.toInstant())
An equivalent Python code (Django) works correctly:
datetime.datetime.now('America/New_York')+datetime.timedelta(days=-30)
evaluating to: datetime: 2018-11-07 20:13:55.063888-05:00
What's causing the discrepancy?
What should I use so that Java conversion to Date resulted in the November 7th being returned, just like in Python's case? Basically, I'm looking to an equivalent translation of that Python code into Java, or in pseudocode:
`datetime.X = datetime.now(deployment_zone) - (N_days)`,
where `deployment_zone` is configurable (i.e. `America/New_York`)
`N_days` is configurable (i.e. 30)
Update for #Basil Bourque:
When I formulated the original question, I (per SO rules) tried to simplify it to a digestible form which probably destroyed most of the necessary context making it vague. Let me try again.
As I explained in the comments, I'm converting the existing Python code (which is more actively maintained and which client wants to keep intact) to existing Java code (legacy that has not been properly maintained and strayed away from the Python's logic some time back). Both code bases need to be functionally on par with each other. Java needs to do what Python is already doing.
Python code is as follows (I'm lumping all into one place for succinctness, in reality it's distributed across a couple of files):
analytics.time_zone=America/New_York
TIME_ZONE = props.getProperty('analytics.time_zone', 'UTC')
TZ = pytz.timezone(TIME_ZONE)
def days_back(num_days=0):
adjusted_datetime = datetime.datetime.now(TZ)+datetime.timedelta(days=-num_days)
return DateRangeUtil.get_start_of_day(adjusted_datetime)
class DateRangeUtil():
#staticmethod
def get_start_of_day(date):
return date.astimezone(TZ).replace(hour=0, minute=0, second=0, microsecond=0)
which basically takes the configured time zone, in which it obtains the current instant, subtracts a specified number of days from it, converts it to the beginning of that date and thus receives the lower bound of the range to use while querying the DB, something like Start time: datetime: 2018-11-07 20:13:55.063888-05:00
When I started on the Java side, it had:
public final static DateRange parse(String dateRange) {
//.....
int days = ...
return new DateRange(toBeginningOfDay(daysBack(days)), toEndOfDay(daysBack(0)));
private final static Date daysBack(int days) {
return toDate(LocalDateTime.now().minusDays(days));
}
private final static Date toBeginningOfDay(Date d)
{
Calendar c=Calendar.getInstance();
c.setTime(d);
c.set(HOUR_OF_DAY,0);
c.set(MINUTE,0);
c.set(SECOND,0);
c.set(MILLISECOND, 0);
return c.getTime();
}
private final static Date toDate(LocalDateTime t) {
return Date.from(t.atZone(ZoneId.systemDefault()).toInstant());
}
That code didn't work and introduced the discrepancy which I describe in my original question. I started experimenting and introduced ZonedDateTime into the picture. While investigating, I found that it's the call to .toInstant() that seems to be a culprit and wanted to understand what's behind it in more depth.
In his answer, #ernest_k suggested a solution which seemed to have worked, but I still didn't quite understood which is clear from questions in the comments to his response.
The changes I made based on #ernest_k response are as follows:
private final static Date daysBack(int days) {
return toDate(ZonedDateTime.now(ZoneId.of("America/New_York")).minusDays(days).toLocalDateTime());
private final static Date toDate(LocalDateTime t) {
return Date.from(t.toInstant(ZoneOffset.UTC));
}
This seems to produce the desired outcome: However conversion from local to zoned and then back again seemed too much, so I experimented a bit more and found that simply the LocalDateTime does the trick as well:
private final static Date toDate(LocalDateTime t) {
return Date.from(t.toInstant(ZoneOffset.UTC));
}
private final static Date daysBack(int days) {
return toDate(LocalDateTime.now().minusDays(days));
}
I can see that LocalDate (and perhaps LocalDateTime) has a convenient atStartOfDay() which seems to be a fitting candidate for elimination of Dates out of the picture while replacing the legacy toBeginningOfDay(Date d) method above. Not sure it's doing the same thing - I haven't yet experimented with that idea, so the suggestions are most welcome.
So, with all of the tribulations above, my question started around toInstant() behavior, and when it's passed a zone id, whether it converts TO an instant in that zone, or FROM it, or what?
I guess for the situation I'm describing we only care that the lower time bound in the DB query is formed by comparing some consistent marker of current time (its upper bound) to what it was in the same place (time zone?) in the past N days, so comparing it with UTC should server the purpose.
Does that then make passing the zone in unnecessary?
Now, that a solution seems to have been found, the question revolves around the soundness of the approach described above and the solution that's been stumbled upon - is it the most optimal one, best practices around Java timing libs, etc. The code needs to work for any time zone in which the code bases will end up being deployed, that's why the zone is passed in via configuration.
Also, I wonder if things change when/if the DB itself is deployed off-premise from the rest of the codebase and is configured to persist data in some other time zone. But that might be another question.
tl;dr
ZonedDateTime.toInstant() adjusts a moment from a time zone to UTC. You end up with the same moment, different wall-clock time, and possibly a different date for the same simultaneous point on the timeline. What you are seeing is not a problem, not a discrepancy.
Your problem is not with subtracting 30 days. The real problems:
Not understanding that time zone affects the date
Conflating dates with days
Furthermore, your Question is vague. Saying “30 days ago” can mean at least three different things:
30 * 24 hours
A range from 22:44 thirty calendar days ago in New York time zone to 22:44 now in New York time
The entire day today as seen in New York and the entire days going back 30 days on the calendar as seen in New York.
All three possibilities are covered below, with example code, labeled with ➥.
⑦🕥 🇺🇸📞 ↔ 📞🇮🇸 ⑧🕞
On the 7th of December, shortly before midnight (22:44), Alice in her New York apartment decides to call her friend Bob in Reykjavík, Iceland. Bob can't believe his phone is ringing, and looking over at the clock on his bedside table sees the time is almost 4 AM (03:44). And Bob's fancy digital clock shows the date as the 8th of December, not the 7th. Same simultaneous moment, same point on the timeline, different wall-clock time, different date.
The people of Iceland use UTC as their time zone, year-round. New York is five hours behind UTC in December 2018, and so five hours behind Iceland. In New York it is “yesterday” the 7th while in Iceland it is “tomorrow” the 8th. Different dates, same moment.
So forget about subtracting the thirty days. Any time you take a moment in New York that is close to midnight, and then adjust to UTC, you will be moving the date forward.
No discrepancy, no extra day added. For any given moment, the date varies around the globe by time zone. With a range in time zones of about 26-27 hours, it is always “tomorrow” and “yesterday” somewhere.
Another Answer suggests involving LocalDateTime into this problem. That is ill-advised. That class purposely lacks any concept of time zone or offset-from-UTC. That means a LocalDateTime cannot represent a moment. A LocalDateTime represents potential moments along the range of 26-27 hours mentioned above. Makes no sense to involve that class here.
Instead, use OffsetDateTime for a moment viewed with an offset-from-UTC, versus [ZonedDateTime][2] which uses a time zone.
What is the difference between an offset and zone? An offset is merely a number of hours-minutes-seconds, nothing more, nothing less. A zone, in contrast, is much more. A zone is a history of the past, present, and future changes to the offset used by the people of particular region. So a time zone is always preferable to a mere offset, as it brings more information. If you want UTC specifically, you need only an offset, an offset of zero hours-minutes-seconds.
OffsetDateTime odt = zdt.toOffsetDateTime().withOffsetSameInstant( ZoneOffset.UTC ) ; // Adjust from a time zone to UTC.
The zdt and odt seen here both represent the same moment, the same point on the timeline, different wall-clock time, like Alice and Bob example above.
Days != Dates
If you want to query for a range of thirty days ago, you must define what you mean by “days”.
Days
➥ Do you mean 30 chunks of 24-hour long spans of time? If so, work with Instant. This class represents a moment in UTC, always in UTC.
ZoneId z = ZoneId.of( "America/New_York" ) ;
ZonedDateTime zdtNow = ZonedDateTime.now( z ) ;
Instant instantNow = zdt.toInstant() ; // Adjust from time zone to UTC. Same moment, different wall-clock time.
Instant instantThirtyDaysAgo = instantNow.minus( 30 , ChronoUnit.DAYS ) ; // Subtract ( 30 * 24 hours ) without regard for dates.
You may be able to exchange an Instant with your database via your JDBC driver. But Instant is optional, while support for OffsetDateTime is required by JDBC 4.2 and later. If that is the case, let's re-write that code.
ZoneId z = ZoneId.of( "America/New_York" ) ;
ZonedDateTime zdtNow = ZonedDateTime.now( z ) ;
OffsetDateTime odtNow = zdt.toOffsetDateTime().withOffsetSameInstant( ZoneOffset.UTC ) ; // Adjust from time zone to UTC. Same moment, different wall-clock time.
OffsetDateTime odtThirtyDaysAgo = odtNow.minusDays( 30 ) ;
Your SQL might be something like the following.
Note what we are using the Half-Open approach to defining a span-of-time, where the beginning is inclusive while the ending is exclusive. This is generally best practice, as it avoid the problem of finding the infinitely divisible last moment, and it provides for neatly abutting spans without gaps. So we do not use the SQL command BETWEEN, being fully-closed (inclusive on both ends).
SELECT * FROM event_ WHERE when_ >= ? AND when_ < ? ;
Set values for the placeholders in your prepared statement.
myPreparedStatement.setObject( 1 , odtThirtyDaysAgo ) ;
myPreparedStatement.setObject( 2 , odtNow ) ;
Dates
➥ If by “30 days ago” you meant 30 boxes on the calendar hanging on the wall in a New York office, that is a very different problem.
Same time-of-day
And if so, do you mean from the current moment and moving back 30 days to the same time-of-day?
ZoneId z = ZoneId.of( "America/New_York" ) ;
ZonedDateTime zdtNow = ZonedDateTime.now( z ) ;
ZonedDateTime zdtThirtyDaysAgo = zdtNow.minusDays( 30 ) ; // `ZonedDateTime` will try to keep the same time-of-day but will adjust if that time on that date in that zone is not valid.
With the code seen above, the ZonedDateTime class will try to use the same time-of-day on the earlier date. But that time may not be valid on that date in that zone, because of anomalies such as Daylight Saving Time (DST) cutover. In such an anomaly, the ZonedDateTime class adjusts to a valid time. Be sure to study the JavaDoc to understand the algorithm and to see if it suits your business rules.
Pass to your prepared statement.
myPreparedStatement.setObject( 1 , zdtThirtyDaysAgo ) ;
myPreparedStatement.setObject( 2 , zdtNow ) ;
Entire day
➥ Or by “30 days ago” do you mean dates, and by dates you mean all-day-long?
If so, we need to focus on the date-only value, by using LocalDate class, without a time-of-day and without a time zone.
ZoneId z = ZoneId.of( "America/New_York" ) ;
LocalDate today = LocalDate.now( z ) ;
LocalDate tomorrow = today.plusDays( 1 ) ;
LocalDate thirtyDaysAgo = tomorrow.minusDays( 30 ) ;
Now we need to go from the date to a specific moment by assigning a time-of-day and a time zone. We want the time to be the first moment of the day. Do not assume that means 00:00. Because of anomalies such as DST, the day may start at another time such as 01:00. Let java.time determine the first moment of the day on that date in that zone.
ZonedDateTime zdtStart = thirtyDaysAgo.atStartOfDay( z ) ;
ZonedDateTime zdtStop = tomorrow.atStartOfDay( z ) ;
Pass to your prepared statement.
myPreparedStatement.setObject( 1 , zdtStart ) ;
myPreparedStatement.setObject( 2 , zdtStop ) ;
That "extra day" is not really an extra day. 2018-11-07T22:44:11 in New York is equivalent to 2018-11-08T03:58:01 in UTC (it's the same point in time). The difference is just 5 hours, not a day (and when I google this, I see New York is GMT-5).
ZonedDateTime#toInstant returns an Instant instance representing the same point in time (in UTC):
Converts this date-time to an Instant.
This returns an Instant representing the same point on the time-line as this date-time. The calculation combines the local date-time and offset.
If you want to not use the offset when converting to instant, then you should perhaps use LocalDateTime:
ZonedDateTime.now(ZoneId.of("America/New_York"))
.toLocalDateTime()
.toInstant(ZoneOffset.UTC)
This tells it to convert as though it were already UTC time (but a warning is appropriate here: this changes the date/time value)
First, avoid the need for an old-fashioned Date if you can. java.time, the modern Java date and time API, gives you all the functionality you need.
Sometimes we do need a Date for a legacy API that we cannot change or don’t want to upgrade just now. Java is giving you what I think you want. Demonstration:
ZonedDateTime nov7 = ZonedDateTime.of(2018, 11, 7, 22, 44, 0, 0,
ZoneId.of("America/New_York"));
Instant inst = nov7.toInstant();
System.out.println("As Instant: " + inst);
Date oldFashionedDate = Date.from(inst);
System.out.println("As Date: " + oldFashionedDate);
Output from this was:
As Instant: 2018-11-08T03:44:00Z
As Date: Wed Nov 07 22:44:00 EST 2018
Admitted, to get this output I had to change my JVM’s default time zone to America/New_York first.
Date and Instant are roughly equivalent but print differently. Meaning their toString methods behave differently, which may be confusing. Each is a point in time, none of them is a date (despite the name of one of them). It is never the same date in all time zones.
Date.toString picks up your JVM’s time zone setting and uses it for generating the string it returns. Instant.toString on the other hand always uses UTC for this purpose. This is why the same point in time is printed with different date and time. Fortunately they both also print a bit of time zone information so the difference is at least visible. Date prints EST, which, albeit ambiguous, in this case means Eastern Standard Time. Instant prints Z for offset zero from UTC or “Zulu time”.

Number of days between two epoch days in scala

I have two epoch timestamps, I am trying to find the number of days between the two timestamps.
This is what I have now:
dateFrom = inputEntry.getValue(inputFields(0).get).asInstanceOf[String].toLong
dateTo =inputEntry.getValue(inputFields(1).get).asInstanceOf[String].toLong
Example:
dateFrom dateTo result
1501583232 1501641000 1
1501583232 1501986600 5
I am starting with two epoch dates here
tl;dr
ChronoUnit.DAYS.between( … , … )
Details
This has been covered many times already on Stack Overflow. So briefly here…
For date-time values, use date-time objects. Use java.time classes only, avoid the troublesome legacy date-time classes ( Date, Calendar, etc).
Do you mean a difference of dates or a difference of 24-hour chunks of time?
I'll go with dates here.
First, translate what appears to be number of whole seconds since an epoch reference date of 1970-01-01T00:00:00Z into a point in the timeline in UTC.
Note the L on the end of numeric literal to indicate a long rather than int.
Instant instant = Instant.ofEpochSecond( 1_501_583_232L ) ;
Assign time zone for which you want to consider dates.
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = instant.atZone( z );
Convert to a date-only.
LocalDate ld = zdt.toLocalDate() ;
Get difference.
long days = ChronoUnit.DAYS.between( ld , ld2 ) ;
To get the results you want, you must define how you're going to calculate the difference.
Taking your first example (difference between 1501583232 and 1501641000 should be 1 day):
The epochs 1501583232 and 1501641000 are the number of seconds since 1970-01-01T00:00Z, so they are equivalent to the following UTC dates:
1501583232: 2017-08-01T10:27:12Z
1501641000: 2017-08-02T02:30:00Z
Note that the difference between them is 16 hours, 2 minutes and 48 seconds (so, less than a day). If you get the difference in days, technically it will be zero.
But if you consider only the dates (2017-08-01 and 2017-08-02) and ignore the time (hour/minute/second), then the difference can be either zero or 1, depending on the timezone you are.
If you consider only the UTC dates (2017-08-01 and 2017-08-02), the difference is 1 day.
But if you take the same UTC dates above in America/Los_Angeles timezone, you'll get:
1501583232: 2017-08-01T03:27:12-07:00
1501641000: 2017-08-01T19:30-07:00
Now the difference is zero days, no matter if you consider only the date (both are 2017-08-01), or the date and time (the difference in hours will be 16, less than a day).
So, you must define how you're going to calculate the difference (consider only the date, or both date and time, and what timezone will be used).
In your case, it seems that you're considering only the date and ignoring the time, but it's not clear what timezone it's using. Anyway, you can use JDK's 8 new java.time API for that (for JDK <= 7 you can use the ThreeTen Backport - The code below works for both. The only difference is the package names (in Java 8 is java.time and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp), but the classes and methods names are the same).
The code is basically the same of #BasilBourque's answer, because it's very straighforward with the new API (I just wanted to add the insights above).
First you create the Instant's from the epoch values:
Instant instant1 = Instant.ofEpochSecond(1501583232L);
Instant instant2 = Instant.ofEpochSecond(1501641000L);
If you want the difference considering the date and time, you can use:
ChronoUnit.DAYS.between(instant1, instant2);
The result will be zero.
If you want to consider only the dates in UTC (and ignore the time), just do:
// convert to UTC and get just the date (day/month/year)
LocalDate d1 = instant1.atZone(ZoneOffset.UTC).toLocalDate();
LocalDate d2 = instant2.atZone(ZoneOffset.UTC).toLocalDate();
long days = ChronoUnit.DAYS.between(d1, d2);
The result will be 1.
To convert to a different timezone (instead of UTC), use the ZoneId class:
// use a specific timezone
ZoneId zone = ZoneId.of("Asia/Kolkata");
// convert the Instant to a timezone and get only the date
LocalDate d1 = instant1.atZone(zone).toLocalDate();
LocalDate d2 = instant2.atZone(zone).toLocalDate();
long days = ChronoUnit.DAYS.between(d1, d2);
In this case, the difference is 1, but as I said above, different timezones can produce different results (can be either zero or 1 - for example, changing the code above to ZoneId.of("America/Los_Angeles"), the result is zero).
Note that the API uses IANA timezones names (always in the format Region/City, like Asia/Kolkata or Europe/Berlin).
Avoid using the 3-letter abbreviations (like CST or IST) because they are ambiguous and not standard.
You can get a list of available timezones (and choose the one that fits best your system) by calling ZoneId.getAvailableZoneIds().
You can also use the system's default timezone with ZoneId.systemDefault(), but this can be changed without notice, even at runtime, so it's better to explicity use a specific one.

How to get a TimeZone ID from a TimeStamp Value

Is it possible to get a TimeZone ID from a certain TimeStamp ? If it is please explain by a simple code.
private String getDate(long timeStamp) {DateFormat objFormatter = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
TimeZone timeZone = TimeZone.getTimeZone("GMT+4:30");
//Instead of the Above code I want to get the TimeZone ID from my timeStamp objFormatter.setTimeZone(timeZone);
Calendar objCalendar =
Calendar.getInstance(timeZone);
objCalendar.setTimeInMillis(timeStamp * 1000);
String result = objFormatter.format(objCalendar.getTime());
objCalendar.clear();
return result;
}
tl;dr
Impossible to derive offset/zone from a count-from-epoch-in-UTC. But you can adjust into a zone.
Instant.ofEpochSecond( yourCount )
.atZone( ZoneId.of( "Pacific/Auckland" ) )
Avoid count-from-epoch
Firstly, avoid using a count-from-epoch number to track date-time values. Do you mean a count of whole seconds, milliseconds, microseconds, nanoseconds, or something else? Do you mean the Unix/Java epoch of 1970-01-01T00:00:00Z or one of the couple dozen other epochs in use by many computer systems?
Apparently you have whole seconds, and I'll assume the Unix/Java epoch.
Impossible to get zone from count-from-epoch
You cannot “ get a TimeZone ID from a certain TimeStamp”, that is impossible. Your count-from-epoch was made while accounting for a certain time zone, usually UTC. If must know that intended zone used in creating that count-from-epoch, it cannot be deduced.
Perhaps your goal is actually adjusting this count-from-epoch into a date-time for a particular region’s time zone. Read on.
java.time
Avoid the troublesome old date-time classes such as Date & Calendar now supplanted by the java.time classes.
Convert your count-from-epoch into a point on the timeline in UTC.
Instant instant = Instant.ofEpochSecond( yourCount ) ;
Assign your desired time zone.
Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).
ZoneId z = ZoneId.of( "Asia/Kabul" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
See this code run live at IdeOne.com.
Notice the 4.5 hour difference, changing from 02:40 to 07:10, appropriate for time in Kabul. This is the same moment, the same point on the time zone, but viewed through the lens of a different region’s wall-clock time.
input: 1500000000
instant: 2017-07-14T02:40:00Z
zdt: 2017-07-14T07:10+04:30[Asia/Kabul]
I would like to answer this question based on the definition of each terminology.
What is timestamp?
Timestamp or Unix Timestamp is the number of seconds that have elapsed since 00:00:00 Coordinated Universal Time (UTC), Thursday, 1 January 1970,minus the number of leap seconds that have taken place since then. Wikipedia
Wath is Time Zone?
A time zone is a region of the earth where the same standard time is used. Each time zone is described by an identifier and usually has the format region/city (Asia/Tokyo) and an offset from Greenwich/UTC time. For example, the offset for Tokyo is +09:00. Time Zone Oracle Doc
Regarding to both definitions there is no way to get a region of the earth based on a number of seconds (time), it is imperative to know from what region of the earth the time comes from.

Java - Calculating hour difference - specific scenario

I just want to calculate time difference in minutes, hopefully there is already a class which does it correctly.
So the values which I get from the website are only Hours and Minutes (for instance: event starts at: 20:30 and ends at 03:30).
When I use 'Duration.between' I get incorrect values - it is happening when the first value is greater than second or second is past midnight.
I think I would be able to do this with 'ifs' and 'elses' but I am sure that there is already a class or a method which would solve the issue in more elegant way, but I can't find it.
Here is my code: it works only when the second value is greater than first:
LocalTime eventStarts = LocalTime.now();
LocalTime eventEnds = LocalTime.now();
eventStarts = eventStarts.withHour(22).withMinute(00);
eventEnds = eventEnds.withHour(03).withMinute(00);
Duration durationBetweenEvents = Duration.between(eventStarts, eventEnds);
System.out.println(durationBetweenEvents.toMinutes());
in this case i get '-1140'
Just add the following to your code.
if (durationBetweenEvents.isNegative()) {
durationBetweenEvents = durationBetweenEvents.plusDays(1);
}
You have to be a little careful with daylight savings. Your calculation can be an hour out, if daylight savings time starts or ends between the events. But without knowing date or timezone information, there's no way to deal with that.
Need dates & time zone
LocalTime has no notion of dates or days. So it is limited to a single generic 24-hour day. Going from an evening time to a morning time is viewed as going backwards in time, not wrapping around to another day as no days exist.
To know the duration of an actual event you need dates and time zone.
Getting the current date requires a time zone. For any given moment, the date varies around the globe by zone.
Once you have dates, apply a ZoneId to get ZonedDateTime objects. From there you can get a Duration that takes into account anomalies such as Daylight Saving Time (DST).
ZoneId z = ZoneId.of( "America/Montreal" );
LocalDate today = LocalDate.now( z );
LocalDate tomorrow = today.plusDays( 1 );
ZonedDateTime zdtStart =
ZonedDateTime.of( today , LocalTime.parse( "20:30" ) , z ) ;
ZonedDateTime zdtStop =
ZonedDateTime.of( tomorrow , LocalTime.parse( "03:30" ) , z ) ;
Duration d = Duration.between( zdtStart , zdtStop ) ;
long totalMinutes = d.toMinutes() ;
zdtStart.toString(): 2017-04-13T20:30-04:00[America/Montreal]
zdtStop.toString(): 2017-04-14T03:30-04:00[America/Montreal]
d.toString(): PT7H
totalMinutes: 420
See this code run live at IdeOne.com.

Android date with added milliseconds not at the correct time

What I am trying to do is store a date, represented by a long of milliseconds, that is the next midnight from the current time. So, posting this at 10:11 PM, I would want a date representing 12:00 AM tomorrow morning. For this task, I wrote this line of code (knowing that there are 86400000 milliseconds in one day):
long time = System.currentTimeMillis() + (86400000 - (System.currentTimeMillis() % 86400000));
The line is designed to calculate the extra milliseconds from the last midnight, substract that from one whole day to find the time until the next midnight, then add that to the current time so that the new value is the value of the next midnight. For whatever reason though, the date object I am using to debug spits out "Wed Apr 20 20:00:00 EDT 2016" when calling the #toString() method. The current time is said to be "Tue Apr 19 22:08:34 EDT 2016" at the same time as the other date is being calculated.
This means that the long of milliseconds is actually representing 8:00 PM the next day, while I want it to represent 12:00 AM. Can anyone help me spot the flaw in my logic?
I most likely am missing something obvious, so bear with me.
NOTE: I also tried calculating the time like this:
long time = System.currentTimeMillis() - (System.currentTimeMillis() % 86400000) + 86400000;
But this resulted in the same Date object.
You are forgetting to adjust for your timezone. Here is a simple way to achieve this.
TimeZone tz = TimeZone.getDefault();
long time = System.currentTimeMillis() + (86400000 - (System.currentTimeMillis() % 86400000));
time -= tz.getOffset(time);
System.out.println(new Date(time));
java.time
You are using flawed troublesome date-time classes that have long been supplanted, first by the Joda-Time library and now it's successor, the java.time framework built into Java 8 and later. Much of java.time has been back-ported to Java 6 & 7 and then adapted to Android by the ThreeTenABP project.
An Instant is a moment on the timeline in UTC, with a resolution in nanoseconds.
Instant instant = Instant.now();
Apply a time zone to get a ZonedDateTime. Time zone is crucial to determining a date. It may already be “tomorrow” to the east of you or “yesterday” to the west.
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( zoneId );
To get the first moment of the day, do not assume that time is 00:00:00.0. Anomalies such as Daylight Saving Time may result in a different time. Let java.time determine that time-of-day. To do that we must go through the LocalDate class.
LocalDate today = zdt.toLocalDate();
Days are not always 24 hours long, which is an incorrect assumption in you question and code. Again, let java.time determine the following day with a call to plusDays.
LocalDate tomorrow = LocalDate.plusDays( 1 );
ZonedDateTime zdtTomorrowStart = tomorrow.atStartOfDay( zoneId );
I suggest you avoid tracking date-time values as a count from epoch. That is prone to error, and is terribly difficult to debug. And you will be losing data as you go from nanosecond resolution to milliseconds. But if you insist.
long millis = zdtTomorrowStart.toInstant().toEpochMilli();
Direct time calculation can be tricky, there are quite a few corner cases. I think the simplest solution to avoid it is :
// now, with current timezone and locale
Calendar calendar = new GregorianCalendar();
// tomorrow
calendar.add(Calendar.DAY_OF_MONTH, 1);
// midnight
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
// get the resulting date
Date date = calendar.getTime();

Categories

Resources