Countdown adds one hour between April and October - java

I'm working on simple countdown app to tell me how long until certain GMT time.
example:
input: 01-05-2021-02-00-00
output: (if entered time is local) 72 days, 15 hours, 21 mins, 49 secs
output(if entered time is gmt): 72 days, 17 hours, 21 mins, 33 secs
...there should be 16 not 17 hours i believe.
Everything's working fine untill input is between 1. April and 31 October. I give it GMT time and it transforms it to my local (berlin +1) time. If entered time is from mentioned interval it shows +2 hours differencer between berlin and GMT. since it is like this only for certain period of year (any year) it looks weird I don't know what went wrong?
my code:
String result = "";
String date = input;
SimpleDateFormat date_format = new SimpleDateFormat("dd-MM-yyyy-HH-mm-ss");
Date target_date = null;
try {
target_date = date_format.parse(date);
} catch (ParseException e) {
result += e.getMessage();
}
Date now = new Date();
long secs = 0;
if(local_time_radio_btn.isSelected()) {
secs = (target_date.getTime() - now.getTime()) / 1000;
}else {
int offset = target_date.getTimezoneOffset();
long n = 1000 * offset * 60;
long gmt = target_date.getTime() -n;
long now_gmt = now.getTime();
secs = (gmt - now_gmt) / 1000;
}
long days = secs / (60*60*24);
secs = secs % (60*60*24);
long hours = secs / (60*60);
secs = secs % (60*60);
long mins = secs / 60;
secs = secs % 60;
result += days+" days, "+hours+" hours, "+mins+" mins, "+secs +" secs";

java.time
I strongly recommend that you use java.time, the modern Java date and time API, for your date and time work.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-uuuu-HH-mm-ss");
String input = "01-05-2021-02-00-00";
LocalDateTime targetLdt = LocalDateTime.parse(input, formatter);
// German time case
ZoneId zone = ZoneId.of("Europe/Berlin");
ZonedDateTime now = ZonedDateTime.now(zone);
ZonedDateTime target = targetLdt.atZone(zone);
long days = ChronoUnit.DAYS.between(now, target);
ZonedDateTime afterDays = now.plusDays(days);
Duration timeDiff = Duration.between(afterDays, target);
String result = days + " days, " + timeDiff.toHours() + " hours, "
+ timeDiff.toMinutesPart() + " mins, "
+ timeDiff.toSecondsPart() + " secs";
System.out.println(result);
When I ran the code just now (2021-02-18T17:23:35.47 in Berlin), the output was:
71 days, 8 hours, 36 mins, 24 secs
For the UTC (or GMT) case you only need to change one line:
// GMT case
ZoneId zone = ZoneOffset.UTC;
71 days, 9 hours, 36 mins, 24 secs
What went wrong in your code?
First, don’t use SimpleDateFormat and Date as they are poorly designed and long outdated. Even if you insisted on using Date, you should definitely stay away from its getTimezoneOffset method. It has been deprecated since 1997 because it works unreliably across time zones.
Why your code gives an incorrect result in the months you mention is that it counts a day as always 24 hours. On March 28 the EU will transit to summer time (DST), so that day is only 23 hours long, giving rise to an error of exactly 1 hour. On October 31 this year the opposite transisiton may happen (I don’t think it’s been decided yet, but Java thinks that it does), so that day is 25 hours, balancing out for dates after that. Until summer time next year.
Which leads to the next point: don’t do time math yourself. It’s so easy to get wrong. And even when you get it right, it’s hard for the reader to convince oneself that it’s right. You have got a reliable date and time library that is happy to do it for you.
Link
Oracle tutorial: Date Time explaining how to use java.time.

What's weird about your code is that you're first parsing the input string according to a SimpleDateFormat instance that's configured to use local timezone (so, the resulting Date is wrong if the input was meant to be UTC), and then put a lot of effort into re-adjusting the point in time to UTC if that's what the user requested.
Instead, it would be clearer and more robust to have two SimpleDateFormat instances, one with the local timezone, and one with UTC, like:
SimpleDateFormat dateFormatLocal = new SimpleDateFormat("dd-MM-yyyy-HH-mm-ss");
SimpleDateFormat dateFormatUTC = new SimpleDateFormat("dd-MM-yyyy-HH-mm-ss");
dateFormatUTC.setTimeZone(TimeZone.getTimeZone("UTC"));
and select the correct one already when parsing.
By the way, your re-adjusting code uses deprecated features of the Date class, like getTimezoneOffset(), something that should be avoided since Java 1.1.

Related

How to get number of days ago from a epoch time from current day? [duplicate]

This question already has answers here:
Java, Calculate the number of days between two dates [duplicate]
(10 answers)
Calculating the difference between two Java date instances
(45 answers)
The difference between 2 Instants in Calendar Days
(4 answers)
Closed 1 year ago.
I have a scenario where epoch time is in string . I need to convert this to number of days ago from current time .
Eg:
String epoch = "1600852773514";
Date expiry = new Date(Long.parseLong(epoch));
Expiry gives me Wed Sep 23 14:49:33 IST 2020 .But I want to get number of days from today to the time 'epoch' . Like epoch is 240 days ago from today.
If expiry > 30 days
pass
else
fail
You can do something like this:
Date expiry = /* ... */;
Date now = new Date();
long days = (now.getTime() - expiry.getTime()) / 86_400_000;
if (days > 30) /* ... */
So we take the difference of the time in milliseconds:
long diff = (now.getTime() - expiry.getTime());
If we divide by 86.000.000 (this is how many milliseconds a day has), we get the number of past days.
As an alternative, you could use java.time (since Java 8) and count the days between two dates:
public static void main(String[] args) {
// example String
String epoch = "1618991673000";
// parse it to a long
long epochMillis = Long.parseLong(epoch);
// then create an Instant from that long value
Instant instant = Instant.ofEpochMilli(epochMillis);
// and convert it to an OffsetDateTime at UTC (+00:00)
OffsetDateTime odt = OffsetDateTime.ofInstant(instant, ZoneOffset.UTC);
// get today's date (only, no time of day considered)
LocalDate today = LocalDate.now();
// and extract the date of the OffsetDateTime
LocalDate then = odt.toLocalDate();
// count the days between the two dates
long daysGone = ChronoUnit.DAYS.between(then, today);
// and print the result...
System.out.println("Days between " + then
+ " and today (" + today + "): " + daysGone);
}
This outputs (today, 21st of May 2021):
Days between 2021-04-21 and today (2021-05-21): 30
Afterwards, you can easily check if the amount of days is greater or less than allowed in your scenario.
Converting to Instance's and using Duration class:
String epoch = "1600852773514";
Instant start = Instant.ofEpochMilli(Long.parseLong(epoch));
Instant now = Instant.now();
Long diff = Duration.between(start,now).toDays();
System.out.println(diff);

What is the algorithm represented in Java to convert an 12 hour am/pm meridiem format hour to 24 hour format?

I needed to convert only the hour part of 12 hour time string to 24 hour format. I'm not interested in other than hour parts of a date string. SimpleDateFormatter is notoriously buggy and I in fact I'm more interested in the conversion algorithm itself. So what I want is to find the algorithm to convert the hour part. This is example of an uncleaned string containing hour part: "12:15PM". Sometimes there is spaces between minutes and meridiem sometimes not. Sometimes it has also character 160. So before conversion method I check and clean the string parts and then feed then meridiem(am or pm) and hour to the method which returns hour as 24 hour format int.
This is my naive style code for beginners to convert an hour in 12 hour format to an hour in 24 format.
public static int convert12to24(String meridiem, String hour) {
//meridiem is that am or pm,
meridiem = meridiem.toLowerCase();
int h_12 = 0;
int h_24 = 0;
try {
h_12 = Integer.parseInt(hour);
} catch (NumberFormatException e) {
e.printStackTrace();
}
if (h_12 == 12) {
//this is the midnight hour
if (meridiem.contains("am")) {//generally before noon
h_24 = 0;
} else {//this is the hour starting at noon
h_24 = 12;
}
} else if (h_12 >= 1)//all the rest
{
if (meridiem.contains("am")) {
//hour starting after first hour at midnight to 11 facing noon
h_24 = h_12;
} else {//pm hours starting right after first hour after noon
h_24 = h_12 + 12;
}
}
return h_24;
}
java.time
I recommend using java.time, the modern Java date and time API, rather than your own calendar implementation. It can be used in all locales.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("h:mma", Locale.ENGLISH);
String timeString12Hours = "12:15PM";
LocalTime time = LocalTime.parse(timeString12Hours, formatter);
System.out.println("Time: " + time);
int hour = time.getHour();
System.out.println("Hour of day in 24 hour format: " + hour);
Output is:
Time: 12:15
Hour of day in 24 hour format: 12
What we get for free from java.time is validation. This code will detect if meridiem is not either AM or PM, or the hour is outside the range 1 through 12 or not a number at all.
Under no circumstances use the SimpleDateFormat class mentioned in the question. It’s a notorious troublemaker of a class and long outdated.
Edit: If for compatibility with old code that I don’t know and that you haven’t got time to clean up at the moment you need the method signature from your own answer:
private static DateTimeFormatter meridiemHourFormatter
= DateTimeFormatter.ofPattern("ahh", Locale.ENGLISH);
public static int convert12to24(String meridiem, String hour) {
String meridiemHour = meridiem + hour;
return LocalTime.parse(meridiemHour, meridiemHourFormatter).getHour();
}
Trying it out:
System.out.println(convert12to24("PM", "12"));
12
Every reader can decide for himself or herself: Which method implementation takes less energy to understand if in a hurry, yours or mine?
Link: Oracle tutorial: Date Time explaining how to use java.time.

Calculate time difference in java

I want to calculate time difference in java. I am calculating successfully only in between 1 hour. When I enter the time above 1 hour calculation is getting Wrong.
String start= "12:00:00";
String end= "12:50:00";
SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
Date date1 = format.parse(start);
Date date2 = format.parse(end);
long difference = date2.getTime() - date1.getTime();
System.out.println(difference/1000);
double time = difference/1000;
double tot = time/60;
JOptionPane.showMessageDialog(this, tot);
result displayed 50mins successfully from above code
I modified the same code and it looks like this:
String start = "12:00:00";
String end = "01:50:00";
SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
Date date1 = format.parse(start);
Date date2 = format.parse(end);
long difference = date2.getTime() - date1.getTime();
System.out.println(difference/1000);
double time = difference/1000;
double tot = time/60;
JOptionPane.showMessageDialog(this, tot);
here result displayed
-610.0 I don't know why I need to display the output of 1.50
Because you input the time in the AM|PM form, but your code is supposed to work with the 24-hour time format.
Change the time from
String end = "01:50:00";
to
String end = "13:50:00";
and it will work like a charm :)
(Or you can change the code to work with AM-PM time model, but it will be more difficult.
Actually, I would use the new OffsetTime classes to solve this problem.
Here is some example code:
public static void distance(String strTime1, String strTime2) {
OffsetTime time1 = OffsetTime.parse(strTime1);
OffsetTime time2 = OffsetTime.parse(strTime2);
int time1Seconds = time1.get(ChronoField.MINUTE_OF_DAY);
int time2Seconds = time2.get(ChronoField.MINUTE_OF_DAY);
System.out.println("Time 1 in seconds: " + time1Seconds);
System.out.println("Time 1 in seconds: " + time2Seconds);
System.out.println("Distance: " + Math.abs(time1Seconds - time2Seconds));
}
What you are doing is to parse a String simliar to yours. As a difference, I would recommend you to always add timezone information. Since it probably doesn't matter much in your case, I'd just add +00:00.
Please not Maths.abs(). As long as you are interested in the distance between both times and not in which direction the distance is, you can use this to get rid of minus or plus prefixes and use the absolute value.
That way you can create OffsetTime by parsing this string. After that you can read the minute of the day.
Please try it with:
public static void main(String[] args) {
distance("10:00:00+00:00", "12:00:00+00:00");
distance("12:30:00+00:00", "12:31:00+00:00");
distance("12:31:00+00:00", "12:30:00+00:00");
}
The results would be:
Time 1 in seconds: 600
Time 1 in seconds: 720
Distance: 120
Time 1 in seconds: 750
Time 1 in seconds: 751
Distance: 1
Time 1 in seconds: 751
Time 1 in seconds: 750
Distance: 1
You mentioned that you need fractions of minutes, like 1.5. Fractions are always complicated and not precise. That's why people invented BigDecimal. However, in your case BigDecimal doesn't help. I would recommend you to use a smaller unit, like seconds.
Just change your code to:
int time1Seconds = time1.get(ChronoField.SECOND_OF_DAY);
int time2Seconds = time2.get(ChronoField.SECOND_OF_DAY);
Here is some Javadocs:
https://docs.oracle.com/javase/8/docs/api/index.html?java/time/LocalDateTime.html
And a tutorial to the new classes (JDK8):
https://docs.oracle.com/javase/tutorial/datetime/iso/timezones.html
From SimpleDateFormat
Letter | Date or Time Component
H | Hour in day (0-23) .
h | Hour in am/pm (1-12)
So in your case you have "HH:mm:ss" which stands for 24Hr time format.
and its start from 00:00 till 23:59
So you can change this to like below for above pattern:
String start = "12:00:00";
String end = "13:50:00";
Or you can change your pattern like below:
SimpleDateFormat format = new SimpleDateFormat("hh:mm:ss");
String start = "12:00:00";
String end = "01:50:00";
See result: code output

Java calculating time with timestamps [duplicate]

This question already has answers here:
Calculating the difference between two Java date instances
(45 answers)
Closed 7 years ago.
Im trying to calculate the time difference between 2 Timestamps, this is the code:
Calendar calendar = Calendar.getInstance();
java.util.Date now = calendar.getTime();
Timestamp currentTimestamp = new Timestamp(now.getTime());
System.out.println("Current\n"+currentTimestamp);
DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
Date date = dateFormat.parse("28/02/2015");
Timestamp timestampBefore = new Timestamp(date.getTime());
System.out.println("Before\n"+timestampBefore);
Timestamp calculated = new Timestamp(currentTimestamp.getTime() - timestampBefore.getTime());
System.out.println("Calculated\n"+calculated);
Output:
Current
2015-02-28 12:12:40.975
Before
2015-02-28 00:00:00.0
Calculated
1970-01-01 13:12:40.975
I can understand why it returns 1970-01-01 but why does it return 13:12:40.975 ,1 hour more?
How to calculate the difference between 2 dates so the output is like this (based on this example):
Years:0, Months:0, Days:0, Hours:12, Minutes:12, Seconds:40 ?
Update: for java below 1.8 check out http://www.joda.org/joda-time/index.html
and for java 1.8 see answer.
Similar solution here: Java 8: Calculate difference between two LocalDateTime
(1) A timestamp is a point in time. If you calculate the difference between two timestamps, the result is not a timestamp (point in time), but a duration. So it is nonsense to convert the difference to a timestamp, hence it is useless to discuss the reason why the result is strange.
(2) You should probably use the new Java 8 time API (if you are able to use Java 8):
LocalTime now = LocalTime.now();
LocalTime previous = LocalTime.of(0, 0, 0, 0);
Duration duration = Duration.between(previous, now);
System.out.println(now);
System.out.println(previous);
System.out.println(duration);
Note that this just calculates the duration between two times of a day (hour-minute-second). If your want to include date information, use LocalDateTime instead:
LocalDateTime nextFirework = LocalDate.now()
.with(TemporalAdjusters.firstDayOfNextYear())
.atTime(LocalTime.MIDNIGHT);
LocalDateTime now = LocalDateTime.now();
// duration (in seconds and nanos)
Duration duration = Duration.between(now, nextFirework);
// duration in total hours
long hours = now.until(nextFirework, ChronoUnit.HOURS);
// equals to: duration.toHours();
If you want to have 'normalized' duration in years/months/days/hours/seconds, there is suprisingly no direct support. You could convert the duration to days, hours, minutes and seconds by yourself:
long d = duration.toDays();
long h = duration.toHours() - 24 * d;
long m = duration.toMinutes() - 60 * duration.toHours();
long s = duration.getSeconds() - 60 * duration.toMinutes();
System.out.println(d + "d " + h + "h " + m + "m " + s + "s ");
But note that you will have difficulties converting the days into months and years, as there is no unique number of days per month and a year can be a leap year with 366 days. For that, you can use Period, as in opposite to Duration, this class is associated with a timeline. Unfortunately, Period does only support dates, but no times:
// period in years/months/days (ignoring time information)
Period p = Period.between(now.toLocalDate(), nextFirework.toLocalDate());
System.out.println(p); // or use p.getYears(), p.getMonths(), p.getDays()
So probably you could combine both approaches - first, compute the Period from the dates and then the Duration using the times. Note that the duration can be negative, so you'll have to take care of that in case of:
Duration dur = Duration.between(start.toLocalTime(), end.toLocalTime());
LocalDate e = end.toLocalDate();
if (dur.isNegative()) {
dur = dur.plusDays(1);
e = e.minusDays(1);
}
Period per = Period.between(start.toLocalDate(), e);
System.out.println(per.toString() + ", " + dur.toString());

Calcul difference between two date for march month

I am trying to calculate the number of days between two dates.
First case :
String string = "01/03/2014";
Date dateFin = new SimpleDateFormat("dd/MM/yyyy", Locale.FRANCE).parse(string);
string = "31/03/2014";
Date dateDebut = new SimpleDateFormat("dd/MM/yyyy", Locale.FRANCE).parse(string);
long result = Math.abs(dateFin.getTime() - dateDebut.getTime());
System.out.println((int) (result / (long) (1000 * 3600 * 24)));
=> Result :
29
Second case :
String string = "01/03/2013";
Date dateFin = new SimpleDateFormat("dd/MM/yyyy", Locale.FRANCE).parse(string);
string = "31/03/2013";
Date dateDebut = new SimpleDateFormat("dd/MM/yyyy", Locale.FRANCE).parse(string);
long result = Math.abs(dateFin.getTime() - dateDebut.getTime());
System.out.println((int) (result / (long) (1000 * 3600 * 24)));
=> Result :
30
Question:
Why there is a difference between this two cases?
Thanks
The value in result is one hour less than the exact 24*30 hours. If you add 3600000 (that is 1 hour) to result, you will get the exact 24 hours (expressed in milliseconds). Apparently in France they change clocks from Winter to Summer time in the month of March. So in March there are 24*30 - 1 hours, not 24 hours. This explains why you don't have the same problem when you try the two May dates. This is my best explanation based on what I'm seeing.
See also:
http://timeanddate.com/time/change/france/paris?year=2013
http://timeanddate.com/time/change/france/paris?year=2014
You are parsing 31/03/2013 (i.e. 00:00 AM). The clock change didn't happen
until 31/03/2013, 02:00 AM. So in 2013 you have the exact 30*24 hours in March,
while in 2014 you have 1 hour less as the change happened on 3/30/2014.

Categories