Trying to write code that will start at beginning of next day - java

I'm trying to write code to run at 1 am. The idea is to find the amount of time to next day in milliseconds, then do a sleep on that time
I do this by getting the reminder of the current time divided by to days time.
remainder= current time % 8640000
where current time is gotten from Time.getTimeInMillis();
I got 61175831
to get days by divide it by (606024)
witch gives me 16 hours
It's 1pm so 13+16=30 or 6am next day not 1 am in morning
delayTime=24*60*60*1000; // i day
Calendar Time = Calendar.getInstance();
long delay=TimeToNumber%delayTime;
ling days=delay/1000;
days=days/60;
days=days/60
Got 16 for days

Without knowing more about your usage scenario, I can't really comment on whether your suggested approach is the best way to do the actual scheduling. However, I can answer the question of how to determine the number of milliseconds until 1:00 AM.
ZonedDateTime now = ZonedDateTime.now();
LocalTime targetTime = LocalTime.parse("01:00");
ZonedDateTime targetDateTime;
if (now.toLocalTime().isBefore(targetTime)) {
targetDateTime = now.with(targetTime);
} else {
targetDateTime = now.plusDays(1).with(targetTime);
}
long millis = Duration.between(now, targetDateTime).toMillis();
Explanation
The Calendar API is a legacy API that comes with a bunch of challenges and gotchas with using it. Additionally, by attempting to do the calculations yourself mathematically, you're missing the various nuances that are automatically handled for you by the libraries (such as daylight saving time shifts and the like). For these reasons, I strongly suggest using the newer java.time API.
For this particular question, you need to determine the number of milliseconds until the next 1:00 AM. If the time is before 1:00 in the current date, you want to return the current date's 1:00. If it's after 1:00, you want to return tomorrow's 1:00.
ZonedDateTime is a good choice to represent 1:00 today/tomorrow, since in addition to the date & time, a time zone is needed in order to correlate the datetime with a specific instant in time.
Retrieving the next 1:00 can be achieved by comparing the current LocalTime with a LocalTime of 1:00 AM:
ZonedDateTime now = ZonedDateTime.now();
LocalTime targetTime = LocalTime.parse("01:00");
ZonedDateTime targetDateTime;
if (now.toLocalTime().isBefore(targetTime)) {
targetDateTime = now.with(targetTime);
} else {
targetDateTime = now.plusDays(1).with(targetTime);
}
This is assuming you're using the system default time zone. If you want to use a different time zone, this can be specified in the call to ZonedDateTime.now():
ZonedDateTime now = ZonedDateTime.now(ZoneId.of("America/New_York"));
With this ZonedDateTime representing the next 1:00 AM, you can use a Duration between the current time and that time, and then get the number of milliseconds in the duration:
long millis = Duration.between(now, targetDateTime).toMillis();

Related

What Date time format can be used to handle DST in Java

Our client sends us a start and end date-time in a text file as a String in the below format
2019-10-07 11:07 AM
All date-time is in one timezone. We calculate the difference between the start and end date-time to calculate the hours worked. The hours worked calculation goes wrong when the transition of daylight savings time happens. They are not sending enough information for us to calculate correctly.
I am about to recommend that they send us more information so that we can address this issue. What is a good solution here? What date-time format should we recommend to them that will help us address the DST change and calculate hours worked correctly.
We use Java.
Getting it right is not obvious
They are telling you their local time, and you can infer the time zone (because "all date is in one time zone").
The basic calculation looks like this:
ZoneId pacific = ZoneId.of("America/Los_Angeles");
DateTimeFormatter local = DateTimeFormatter.ofPattern("uuuu-MM-dd hh:mm a").withZone(pacific);
ZonedDateTime start = ZonedDateTime.parse("2022-11-06 01:30 AM", local);
ZonedDateTime until = ZonedDateTime.parse("2022-11-07 01:30 AM", local);
long hours = start.until(until, ChronoUnit.HOURS);
System.out.printf("%d hours elapsed%n", hours);
This prints "25 hours elapsed." In the Pacific time zone, November 6, 2022, is 25 hours long, because when daylight saving ends in the autumn, the clock is set back one hour. If someone tells you it's 1:00 AM, you don't know if midnight was one hour ago or two.
The default offset heuristic
What you really need is the offset, and you have to rely on some heuristic for that. By default, ZonedDateTime chooses one instant from multiple ambiguous local date-times by selecting the earliest offset (the "summer" offset).
Specifying the offset
If that's not what you want, you can override the offset explicitly. For example, maybe you process these time stamps close to real-time, and you can guess what the offset should be based on the current time. Or maybe you know that these local time stamps are always processed in chronological order; by tracking the latest time you've seen, and noting if an earlier time stamp follows, you can detect the clock set back and change the offset.
The ZonedDateTime.ofLocal() and ZonedDateTime.ofStrict() functions can be used to explicitly control the offset.
OffsetDateTime
Alternatively, you might request that they include the offset in the timestamp string. Usually this would be indicated with a signed number of hours and minutes: "-07:00" or "-0800". This will provide unambiguous interpretation of times during DST transitions.
Here is an example using OffsetDateTime. First, if the offset uses a colon, as in "2019-10-07T11:07:00+01:00", it is a standard format, and can be parsed like this:
OffsetDateTime start = OffsetDateTime.parse("2019-10-07T11:07:00+01:00");
If the colon is missing, you need a formatter to handle the non-standard input:
DateTimeFormatter odt = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
.appendOffsetId()
.toFormatter();
OffsetDateTime when = OffsetDateTime.parse("2019-10-07T11:07:00+01:00", odt);
From there, the calculation is the same as with ZonedDateTime:
OffsetDateTime start = OffsetDateTime.parse("2022-11-06T01:00:00-07:00", odt);
OffsetDateTime until = OffsetDateTime.parse("2022-11-07T01:54:00-08:00", odt);
long hours = start.until(until, ChronoUnit.HOURS);
System.out.printf("%d complete hours elapsed.%n", hours);
Duration duration = Duration.between(start, until);
System.out.println("Full duration: " + duration);
This is simple task. The DateTimeFormatter class gives you all the info you need. 2019-10-07 11:07 AM Your format would be 'yyyy-MM-dd hh:mm a' and you should use LocalDateTime class. But since you need to take into account daylight savings time then you might want to use classes ZonedDateTime or OffsetDateTime and provide your timezone. It might be an overkill, but I once worked on the project where I needed to parse Strings to Dates without knowing the format in advance. So, here is the article I wrote on how to do that: Java 8 java.time package: parsing any string to date

Hours+Minutes between two Java Date objects

I have found how to get the hours between two dates and the minutes . What I want is the exact difference between both of them, this is the code that I'm using:
private String getHours(Message punch) {
LocalTime out = Instant.ofEpochMilli(message.getOut().getTime()).atZone(ZoneId.of(message.getTimezone()))
.toLocalTime();
LocalTime in = Instant.ofEpochMilli(message.getIn().getTime()).atZone(ZoneId.of(message.getTimezone()))
.toLocalTime();
Duration duration = Duration.between(in, out);
Long hours = duration.toHours();
Long minutes = duration.toMinutes() - (hours * Constants.MINUTES_IN_AN_HOUR);
return String.format("%d:%d", hours, minutes);
}
It works fine for the major of the cases but I'm having an error in the following case:
message.getIn() returns: 12:59
message.getOut() returns: 22:00
Both are the same day, the difference that I'm expecting is 9:01, but I'm getting -14:-59
Debugging the code I realize that out is getting 04:00 and in is getting 18:59.
For almost all the cases it works well but It happens in some scenarios.
I believe your problem is that you are using LocalTime class but you should be using LocalDateTime class. It apears that your timezone is GMT+6, so in your example your in time and out time fall in different days - your in time in the evening of a previous day and out time on the morning of the next day. But because you are using LocalTime you are loosing the fact that those are times in different days. Change your LocalTime to LocalDateTime and see if this helps
You are working too hard, going through too many gyrations in your code.
Use Instant
You have a pair of Instant objects but throw them away. Use them. An instant is a specific moment on the timeline in UTC with a resolution of nanoseconds.
Instant start = … ;
Instant stop = … ;
Duration d = Duration.between( start , stop );
How you get your instants is a mystery. If you revise your Question to explain the exact nature of your inputs, I will provide more code here.
If you are being passed a pair of java.util.Date objects, convert them to Instant. Use new conversion methods added to the old date-time classes. No need for time zones at all for calculating elapsed time.
Instant start = utilDateX.toInstant() ;
Instant stop = utilDateY.toInstant() ;
Duration d = Duration.between( start , stop );
Calculating elapsed time with LocalTime is rarely appropriate because of crossing over into the next or previous days.

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();

Saving date time for all time zones in java

I have an application in which user creates questions and others can
see date time (when the question was created) with it. Now i get the
server date time and save it in db but the problem is app is used by
someone who live in a country with a 6-7 hour gap.
Well a small example would be that i live in some country and i create question at time 7:00pm but time in USA is 11am (just a guess). So user immediately retrieves question but for him question time should be 11am and not 7pm. So how can i save date time so it would be same for all time zones . I m kinda confused
so i need a little help . I know it's related to UTC date time but can
someone elaborate this a bit more :) . Thank u
Store into your database the difference, measured in milliseconds, between the current time and the epoch of midnight, January 1, 1970 UTC. that you can get by calling System.currentTimeMillis().
Once you have it you can provide the time in any Time Zone that you want, here is a simple code snippet that shows the time in all the time zones available on my machine.
This code uses the java.time framework built into Java 8 and later. Much of this functionality has also been back-ported to Java 6 & 7 and to Android.
long time = System.currentTimeMillis();
Instant instant = Instant.ofEpochMilli(time);
ZoneId.getAvailableZoneIds().stream().forEach(id -> {
ZoneId zId = ZoneId.of(id);
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zId);
System.out.printf(
"The current time in %s is %s%n",
zId, localDateTime.format(DateTimeFormatter.ISO_DATE_TIME)
);
}
);
Here is the equivalent for older versions of Java:
long time = System.currentTimeMillis();
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(time);
for (String id : TimeZone.getAvailableIDs()) {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
formatter.setTimeZone(TimeZone.getTimeZone(id));
System.out.printf(
"The current time in %s is %s%n", id, formatter.format(cal.getTime())
);
}
Response Update:
As you want to keep the original TimeZone, you will also have to store the time zone Id into your database in the pseudo standard format GMT+/-mm:ss.
For this, first you need to get the delta compared to UTC time (in the code snippet below tz is my current TimeZone):
int offsetFromUTC = tz.getOffset(time);
Then from this you can convert this delta in milliseconds into the expected time zone id which can be done like this:
String timeZoneId = String.format(
"GMT%+02d:%02d", offsetFromUTC / (60 * 60 * 1000), offsetFromUTC / (60 * 1000) % 60
);
The value of timeZoneId is the second value that you have to store into the database. With these two values you can display the time in any expected format, for example:
Calendar cal = Calendar.getInstance();
// Here I use the time retrieved from the DB
cal.setTimeInMillis(time);
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
// Here I use the time zone id retrieved from the DB
TimeZone tz = TimeZone.getTimeZone(timeZoneId);
formatter.setTimeZone(tz);
System.out.printf("The current time in %s is %s%n", id, formatter.format(cal.getTime()));
So how can i save date time so it would be same for all time zones
You should use UTC to store your dates, so if someone is using your app in +1 timezone, you need to convert it to UTC first. Timezone should only be used to display the time to the users.

Does this account for daylight savings?

// someTime is epoch in millis (UTC)
final long timeNow = new Date().getTime();
final long midnight = timeNow - timeNow % (3600 * 24 * 1000L);
final long yesterdayMidnight = midnight - (3600 * 24 * 1000L);
// check if same day.
if (someTime >= midnight)
// do something
// check if yesterday
if (someTime >= yesterdayMidnight)
Edited: My purpose is to check whether someTime is in the same day or in the previous day without doing too much heavyweight stuff.
Does this account for day light savings and why? If not, what's the simplest logic?
Your current code doesn't do anything with the local time zone - everything is in UTC, effectively (certainly in terms of your code, which is dealing in "milliseconds since the Unix epoch").
If you want to make your code time-zone-sensitive, you should use (in order of preference):
Java 8's java.time (look at ZonedDateTime and Clock for example)
Joda Time
java.util.Calendar with java.util.TimeZone
Use higher-level abstractions where possible - your code should do as little low-level manipulation of time as possible.
EDIT: Now that we know the purpose, here's an example implementation in Joda Time:
public void calculate(Instant now, Instant then, DateTimeZone zone) {
LocalDate today = new LocalDate(now, zone);
LocalDate otherDay = new LocalDate(then, zone);
if (otherDay.equals(today)) {
// Today day
} else if (otherDay.equals(today.minusDays(1)) {
// Yesterday
} else {
// Neither today nor yesterday
}
}
Note how there's nothing low level here - we're just working out which date each value (now and then) falls in within the given time zone, and then comparing those.
Your check will fail in some cases having in mind daylight savings. Let's assume it is now 5 o' clock on the day when daylight saving happens and at 3 o'clock we've switched the clock forward. Therefor only 4 hours have passed since midnight but it is in fact 5. So midnight in your code will be a time 5 hours ago. This means that if someTime is between 5 hours ago and 4 hours ago(e.g. 4 hours and a half ago) when it's in fact been yesterday your algorithm will report it has been today.
That does not seem to be correct from daylight savings point. First, what's the timezone of someTime date? What about the days/nights(in fact) when the daylight savings take place (one hour +/-)?
If you use Joda lib there is a convenient method DateTime.isBefore() that will return whether one date is before another.

Categories

Resources