Found weird behavior with Joda API - java

I started looking at joda API for handling timezone related issues for my web application. I found very weird behavior while doing so. I hope there must be some workaround. here is my snap-code.
DateTime utcDateTime = new DateTime(System.currentTimeMillis(), DateTimeZone.UTC);
DateTime currentDateTime = DateTime.now();
int seconds = Seconds.secondsBetween(utcDateTime.toLocalDateTime(), currentDateTime.toLocalDateTime()).getSeconds();
System.out.println("hours::" + (((float)seconds)/(60*60)));
My local timezone is : kolkata, India (GMT + 5:30)
then I should get 5.5 as myn console output, but I am getting hours::5.499722
Don't know Why? Am I doing anything wrong?

(5.5 hours - 5.499722 hours) in ms
=
1000.8 ms
1 second, to the rounding/floating point precision. You call
DateTime utcDateTime = new DateTime(System.currentTimeMillis(), DateTimeZone.UTC);
DateTime currentDateTime = DateTime.now();
Seconds.secondsBetween().getSeconds() gives you an integer number of seconds. Your calls are successive meaning they can be off by even 1 millisecond, which can be rounded to 1 second when converted to int.

Given that you want hours and minutes (an Hours object can only hold an integer number of hours) you probably want:
Period period = new Period(utcDateTime.toLocalDateTime(),
currentDateTime.toLocalDateTime(),
PeriodType.time());
Or alternatively, and more cleanly IMO:
int offsetMillis = DateTimeZone.getDefault().getOffset(Instant.now());
Duration offset = Duration.millis(offsetMillis);
You should very rarely need to know the offset in this fashion though, in my experience.

Related

how to change the current date

my method accepts - hours, minutes, seconds and milliseconds separated by sign / as a string parameter
how can I add to the current date the parameters that come to the method.
Example 1: today, 02/10/2021, the method receives metnode data (10/10/10/10) - output - 02/10/2021 10:10:10
Example 2: today, 02/10/2021, the method receives metnode data (55/10/10/10) - output - 02/12/2021 07:10:10
That is, you need to add 55 hours 10 seconds 10 seconds and 10 milliseconds to the current date.
you cannot use the Calendar and StringTokenizer classes.
public void method(String s) {
s = s.replaceAll("/", "-");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss");
final LocalDateTime now = LocalDateTime.parse(s, formatter.withResolverStyle(ResolverStyle.LENIENT));
System.out.println(now);
}
i found the withResolverStyle (ResolverStyle.LENIENT) method
but did not understand how to use it.
A lenient DateTimeFormatter is enough
I don’t know if it’s the best solution. That probably depends on taste. It does use the ResolverStyle.LENIENT that you mentioned and generally works along the lines of the code in your question, only fixed and slightly simplified.
My formatter includes both date and time. This is necessary for surplus hours to be converted to days.
private static final DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("uuuu-MM-dd H/m/s/")
.appendValue(ChronoField.MILLI_OF_SECOND)
.toFormatter()
.withResolverStyle(ResolverStyle.LENIENT);
Next thing we need a string that matches the formatter. So let’s prepend the date to the time string that we already have got:
String timeString = "55/10/10/10";
LocalDate today = LocalDate.now(ZoneId.of("America/Regina"));
String dateTimeString = "" + today + ' ' + timeString;
LocalDateTime dateTime = LocalDateTime.parse(dateTimeString, formatter);
System.out.println(dateTime);
The output from my code when I ran it today (February 10) was:
2021-02-12T07:10:10.010
A different idea: use Duration
Edit: An alternative is to use the Duration class. A reason for doing that would be that it really appears that you are adding a duration rather than setting the time of day. A liability is that parsing your string into a Duration is a bit tricky. The Duration.parse method that we want to use only accepts ISO 8601 format. It goes like PT55H10M10.010S for a period of time of 55 hours 10 minutes 10.010 seconds. And yes, milliseconds need to be given as a fraction of the seconds.
String isoTimeString = timeString.replaceFirst("(/)(\\d+)$", "$100$2")
.replaceFirst("(\\d+)/(\\d+)/(\\d+)/0*(\\d{3})", "PT$1H$2M$3.$4S");
Duration dur = Duration.parse(isoTimeString);
LocalDateTime dateTime = LocalDate.now(ZoneId.of("Asia/Kathmandu"))
.atStartOfDay()
.plus(dur);
When I ran it just now — already February 11 in Kathmandu, Nepal — the output was:
2021-02-13T07:10:10.010
I am using two calls to replaceFirst(), each time using a regular expression. The first call simply adds some leading zeroes to the milliseconds. $1 and $2 in the replacement string give us what was matched by the first and the second group denoted with round brackets in the regular expression.
The second replaceFirst() call established the ISO 8601 format, which includes making sure that the milliseconds are exactly three digits so they work as a decimal fraction of the seconds.
Link: ISO 8601
Try this:
public void method(String s) {
String[] arr = s.split("/");
LocalDateTime now = LocalDateTime.of(
LocalDate.now(), LocalTime.of(0, 0))
.plusHours(Integer.parseInt(arr[0]))
.plusMinutes(Integer.parseInt(arr[1]))
.plusSeconds(Integer.parseInt(arr[2]))
.plusNanos(Integer.parseInt(arr[3]) * 1_000_000L);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy HH:mm:ss");
System.out.println(now.format(formatter));
}
Look into the LocalDateTime documentation. It offers various means for combining dates. Such as:
plus(amount, unit)
plusDays(days)
plusHours(hours)
plusMinutes(minutes)
just for simplicity , you can your LocalDateTime class. it is easy to understand. please refer to below code is used to add the hours, minuts, second and nanos to current Date Time.
this Date Time then can easy formatted by any format pattern as required.
public void addDateTime(int hours, int minuts, int seconds, int nanos) {
LocalDateTime adt = LocalDateTime.now();
System.out.println(adt);
adt = adt.plusHours(hours);
adt = adt.plusMinutes(minuts);
adt = adt.plusSeconds(seconds);
adt = adt.plusNanos(nanos);
System.out.println(adt);
}

Android converting the time in milliseconds

I have checked questions about converting time in milliseconds and I followed all answers, but I am still getting the wrong number of hours, although the minutes and seconds are ok.
In my application the user has to select a time from a TimePickerDialog and set the time in the textview. It will show for example 01:30 in a clock. It will show me 502125000222:30:00
My code TimePickerDialog:
TimePickerDialog timePickerDialog = new TimePickerDialog(this, new TimePickerDialog.OnTimeSetListener() {
#Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
Calendar mycalendar = Calendar.getInstance();
mycalendar.set(Calendar.HOUR_OF_DAY, hourOfDay);
mycalendar.set(Calendar.MINUTE, minute);
mycalendar.set(Calendar.SECOND, 0);
long times = mycalendar.getTimeInMillis();
// hmsTimeFormatter is a method return time format
String timerRemaind = hmsTimeFormatter(times);
//tv_remaindertime is TextView
tv_remaindertime.setText(timerRemaind);
............
And this hmsTimeFormatter method that converts milliseconds:
private String hmsTimeFormatter(long milliSeconds) {
String hms = String.format("%02d:%02d:%02d",
TimeUnit.MILLISECONDS.toHours(milliSeconds),
TimeUnit.MILLISECONDS.toMinutes(milliSeconds) - TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(milliSeconds)),
TimeUnit.MILLISECONDS.toSeconds(milliSeconds) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(milliSeconds)));
return hms;
}
To understand what's happening:
Calendar.getInstance() creates a Calendar with the current date/time in the JVM default timezone
You set the hour, minutes and seconds (to 01:30:00 I suppose)
You call getTimeInMillis(), that returns the number of milliseconds since unix epoch (1970-01-01T00:00Z)
Then you use this milliseconds value with TimeUnit, but that's not what you need. TimeUnit will treat the number of milliseconds as an amount of time, but what you need is a time of the day:
a time of the day is a specific moment in a day, such as "10 AM" or "05:30 PM".
an amount of time is just, well, an amount of elapsed time, such as "10 minutes and 45 seconds", or "2 years, 4 months, 5 days and 17 hours", but it's not attached to a specific point in time ("10 minutes relative to what?"); it's just the amount of time, by itself.
Although both concepts might use the same words ("hours", "minutes", "seconds", etc), they're not the same thing.
TimeUnit deals with amounts of time, so it does conversions like "1000 seconds is equivalent to how many minutes?".
Example: TimeUnit.MILLISECONDS.toHours(value) returns the number of hours equivalent to value milliseconds (not the time of the day). As the value you're using is the result of getTimeInMillis(), you're getting the total number of hours since January 1970.
By using the result of getTimeInMillis() (which represents a specific point in time) with TimeUnit, you're misusing the value, treating like it was an amount of time.
To format a date, you can use a SimpleDateFormat:
Calendar mycalendar = Calendar.getInstance();
mycalendar.set(Calendar.HOUR_OF_DAY, 1);
mycalendar.set(Calendar.MINUTE, 30);
mycalendar.set(Calendar.SECOND, 0);
// format: hour:minute:second
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
String hms = sdf.format(mycalendar.getTime()); // 01:30:00
With this, hms will have the value 01:30:00. Check the javadoc to know what the format HH:mm:ss means, so you can change it in case you need a different format (If you want just 01:30, for example, the format will be HH:mm).
Java new Date/Time API
The old classes (Date, Calendar and SimpleDateFormat) have lots of problems and design issues, and they're being replaced by the new APIs.
In Android you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. To make it work, you'll also need the ThreeTenABP (more on how to use it here).
First you create a org.threeten.bp.LocalTime (a class that represents a time of the day), then you format it with a org.threeten.bp.format.DateTimeFormatter:
// create time for 01:30
LocalTime time = LocalTime.of(1, 30);
// format: hour:minute:second
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("HH:mm:ss");
String hms = fmt.format(time);
System.out.println(hms); // 01:30:00
Also, check the javadoc for all available formats.
Hello how about using a powerful Library? its very easy and useful,
it`s saves you from many line
Joda-Time
Usage :
Gradle configuration:
compile 'joda-time:joda-time:2.9.9'
for more information and documentation :
https://github.com/JodaOrg/joda-time
http://www.joda.org/joda-time/
Happy Coding!
If I understand you right, you want to have millis converted to HH:mm:ss.
For that we use SimpleDateFormat
private String hmsTimeFormatter(long milliSeconds) {
Date date = new Date(milliSeconds);
SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
return format.format(date);
}
Just Try it and change the format Syntax to the one you like

Issues Converting String dates into Long values and performing calculations

I have a map of string values which represent down times for different components.
dependencyMap.put ("sut", "14:26:12,14:27:19,00:01:07;15:01:54,15:02:54,00:01:00;15:44:30,15:46:30,00:02:00;16:10:30,16:11:30,00:01:00");
dependencyMap.put ("jms", "14:26:12,14:28:12,00:02:00;15:10:50,15:12:55,00:02:05;15:42:30,15:43:30,00:01:00;16:25:30,16:27:30,00:02:00");
The strings represent the start, end and duration of down times.
(start)14:26:12,(end)14:27:19,(duration)00:01:07
I read the values in, then add them to a list of DependencyDownTime objects which hold the Long values startTime, endTime and duration.
jArray.forEach (dependency ->{
String downTimeValues = knownDowntimesMap.get(dependency);
final String[] downtime = downTimeValues.split (";");
for (final String str : downtime) {
final DependencyDownTime depDownTime = new DependencyDownTime ();
final String[] strings = str.split (",");
if (strings.length == 3) {
final DateFormat dateFormat = new SimpleDateFormat ("HH:mm:ss");
try {
depDownTime.setStartTime(dateFormat.parse (strings[0]).getTime ());
depDownTime.setEndTime (dateFormat.parse (strings[1]).getTime ());
depDownTime.setDuration (dateFormat.parse (strings[2]).getTime ());
downTimes.add (depDownTime);
} catch (final ParseException e) {
//logger.warn (e.getMessage (), e);
}
} else {
//logger.warn ("");
}
}
I then perform simple arithmetic on the values, which calculates the total down time for each component.
// sort the list by start time
Collections.sort(downTimes, Comparator.comparing (DependencyDownTime::getStartTime));
int i = 1;
Long duration = 0L;
for(DependencyDownTime dts: downTimes){
Long curStart = dts.getStartTime ();
Long curEnd = dts.getEndTime();
Long nextStart = downTimes.get(i).getStartTime ();
Long nextEnd = downTimes.get(i).getEndTime ();
if(duration == 0){
duration = dts.getDuration();
}
if(curStart.equals(nextStart) && curEnd < nextEnd){
duration += (nextEnd - curEnd);
}
else if(nextStart > curEnd){
duration += downTimes.get(i).getDuration();
}
else if( curStart < nextStart && curEnd > nextStart){
duration += (nextEnd - curEnd);
}
else if(curEnd == nextStart){
duration += downTimes.get(i).getDuration();
}
i++;
if(i == downTimes.size ()){
componentDTimeMap.put (application, duration);
return;
}
The expected values should be something like 1970-01-01T 00:14:35 .000+0100, a matter of minutes. The actual result is usually extremely high off by a matter of hours in the difference 1969-12-31T 15:13:35 .000+0100
I have 2 questions.
Am I parsing the values correctly?
If my calculations are a little off when adding and subtracting the long values. When I convert the values back to Date format will there be a drastic difference in the expected value?
As explained in your other question, don't mistake those 2 different concepts:
a time of the day: it represents a specific point of a day, such as 10 AM or 14:45:50
a duration: it represents an amount of time, such as "1 hour and 10 minutes" or "2 years, 3 months and 4 days". The duration doesn't tell you when it starts or ends ("1 hour and 10 minutes" relative to what?), it's not attached to a chronology, it doesn't correspond to a specific point in the timeline. It's just the amount of time, by itself.
In your input, you have:
(start)14:26:12,(end)14:27:19,(duration)00:01:07
The start and end represents times of the day, and the duration represents the amount of time. SimpleDateFormat is designed to work with dates and times of the day, but not with durations. Treating the duration as a time of the day might work, but it's a hack as explained in this answer.
Another problem is that when SimpleDateFormat parses only a time, it defaults the day to January 1st 1970 at the JVM default timezone, leading to all the strange results you see. Unfortunately there's no way to avoid that, as java.util.Date works with full timestamps. A better alternative is to use the new date/time API.
As in your other question you're using Java 8, I'm assuming you can also use it here (but if you're using Java <= 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. 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).
As you're working only with times, there's no need to consider date fields (day/month/year), we can use a LocalTime instead. You can parse the strings directly, because they are in ISO861 compliant format:
LocalTime start = LocalTime.parse("14:26:12");
LocalTime end = LocalTime.parse("14:27:19");
Unfortunately there are no built-in parsers for a duration, so you'll have to parse it manually:
// parse the duration manually
String[] parts = "00:01:07".split(":");
Duration d = Duration
// get hours
.ofHours(Long.parseLong(parts[0]))
// plus minutes
.plusMinutes(Long.parseLong(parts[1]))
// plus seconds
.plusSeconds(Long.parseLong(parts[2]));
Another alternative is to remove the durations from your input (or ignore them) and calculate it using the start and end:
Duration d = Duration.between(start, end);
Both will give you a duration of 1 minute and 7 seconds.
My suggestion is to change the DependencyDownTime to store start and end as LocalTime objects, and the duration as a Duration object. With this, your algorithm would be like this:
Duration total = Duration.ZERO;
for (...) {
LocalTime curStart = ...
LocalTime curEnd = ...
LocalTime nextStart = ...
LocalTime nextEnd = ...
if (total.toMillis() == 0) {
duration = dts.getDuration();
}
if (curStart.equals(nextStart) && curEnd.isBefore(nextEnd)) {
total = total.plus(Duration.between(curEnd, nextEnd));
} else if (nextStart.isAfter(curEnd)) {
total = total.plus(downTimes.get(i).getDuration());
} else if (curStart.isBefore(nextStart) && curEnd.isAfter(nextStart)) {
total = total.plus(Duration.between(curEnd, nextEnd));
} else if (curEnd.equals(nextStart)) {
total = total.plus(downTimes.get(i).getDuration());
}
i++;
if (i == downTimes.size()) {
// assuming you want the duration as a total of milliseconds
componentDTimeMap.put(application, total.toMillis());
return;
}
}
You can either store the Duration object, or the respective value of milliseconds. Don't try to transform it to a Date, because a date is not designed nor supposed to work with durations. You can adapt this code to format a duration if you want (unfortunately there are no native formatters for durations).
Limitations
The code above assumes that all start and end times are in the same day. But if you have start at 23:50 and end at 00:10, should the duration be 20 minutes?
If that's the case, it's a little bit trickier, because LocalTime is not aware of the date (so it considers 23:50 > 00:10 and the duration between them is "minus 23 hours and 40 minutes").
In this case, you could do a trick and assume the dates are all at the current date, but when start is greater than end, it means that end time is in the next day:
LocalTime start = LocalTime.parse("23:50");
LocalTime end = LocalTime.parse("00:10");
// calculate duration
Duration d;
if (start.isAfter(end)) {
// start is after end, it means end is in the next day
// current date
LocalDate now = LocalDate.now();
// start is at the current day
LocalDateTime startDt = now.atTime(start);
// end is at the next day
LocalDateTime endDt = now.plusDays(1).atTime(end);
d = Duration.between(startDt, endDt);
} else {
// both start and end are in the same day
// just calculate the duration in the usual way
d = Duration.between(start, end);
}
In the code above, the result will be a Duration of 20 minutes.
Don't format dates as durations
Here are some examples of why SimpleDateFormat and Date aren't good to handle durations of time.
Suppose I have a duration of 10 seconds. If I try to transform it to a java.util.Date using the value 10 to a date (AKA treating a duration as a date):
// a 10 second duration (10000 milliseconds), treated as a date
Date date = new Date(10 * 1000);
System.out.println(date);
This will get a date that corresponds to "10000 milliseconds after unix epoch (1970-01-01T00:00Z)", which is 1970-01-01T00:00:10Z. But when I print the date object, the toString() method is implicity called (as explained here). And this method converts this millis value to the JVM default timezone.
In the JVM I'm using, the default timezone is America/Sao_Paulo, so the code above outputs:
Wed Dec 31 21:00:10 BRT 1969
Which is not what is expected: the UTC instant 1970-01-01T00:00:10Z corresponds to December 31st 1969 at 9 PM in São Paulo timezone.
This happens because I'm erroneously treating the duration as a date (and the output will be different, depending on the default timezone configured in the JVM).
A java.util.Date can't (must not) be used to work with durations. Actually, now that we have better API's, it should be avoided whenever possible. There are too many problems and design issues with this, just don't use it if you can.
SimpleDateFormat also won't work properly if you handle the durations as dates. In this code:
SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
Date d = dateFormat.parse("10:00:00");
The input has only time fields (hour, minute and second), so SimpleDateFormat sets the date to January 1st 1970 at the JVM default timezone. If I System.out.println this date, the result will be:
Thu Jan 01 10:00:00 BRT 1970
That's January 1st 1970 at 10 AM in São Paulo timezone, which in UTC is equivalent to 1970-01-01T13:00:00Z - so d.getTime() returns 46800000.
If I change the JVM default timezone to Europe/London, it will create a date that corresponds to January 1st 1970 at 10 AM in London (or UTC 1970-01-01T09:00:00Z) - and d.getTime() now returns 32400000 (because 10 AM in London and 10 AM in São Paulo happened at different instants).
SimpleDateFormat isn't the right tool to work with durations - it isn't even the best tool to work with dates, actually.

Calculate time difference between two times represented as longs

I am trying to calculate the difference between two times, which are represented as longs in the Format HHmm 24 hour time. E.g 4:30pm is represented by the long 0430.
I am happy for the difference to be in minutes.
Is there a simple calculation that can be done to achieve this? I am aware of Java's Date class, however I want to avoid having to store dummy date information just for a calculation on time.
Thanks!
Putting aside the fact that this is a really, really bad way to store times, the easiest way to do this is to convert the HHMM time to minutes since the start of the day:
long strangeTimeFormatToMinutes(long time) {
long minutes = time % 100;
long hours = time / 100;
return minutes + 60 * hours;
}
Then just use plain old subtraction to get the difference.
You may also want to add validation that minutes and hours are in the ranges you expect, i.e. 0-59 and 0-23.
You mentioned that you didn't want to use the Date class because it required you to use a dummy date. The LocalTime class does not require that.
LocalTime start = LocalTime.of(6,15,30,200); // h, m, s, nanosecs
LocalTime end = LocalTime.of(6,30,30,320);
Duration d = Duration.between(start, end);
System.out.println(d.getSeconds()/60);
Pad zeros
First convert your integer to a 4-character string, padding with leading zeros.
For example, 430 becomes 0430 and parsed as 04:30. Or, 15 becomes 0015 and parsed as quarter past midnight, 00:15.
String input = String.format( "%04d", yourTimeAsInteger );
LocalDate
The LocalTime class represents a time-of-day value with no date and no time zone.
DateTimeFormatter f = DateTimeFormatter.ofPattern( "HHmm" );
LocalTime ld = LocalTime.parse( input , f ) ;

Joda-Time - Number of seconds between two dates

My english is not perfect, but I hope you can understand me.
I try to get the difference in seconds between two unix timestamps, but it's only return 0.
That's my code
unixOnline = Long.valueOf(online);
unixOffline = Long.valueOf(offline);
DateTimeZone BERLIN = DateTimeZone.forID("Europe/Berlin");
DateTime dateTimeOnline = new DateTime(unixOnline * 1000L, BERLIN);
DateTime dateTimeOffline = new DateTime(unixOffline * 1000L, BERLIN);
int seconds = Seconds.secondsBetween(new LocalDate(dateTimeOnline), new LocalDate(dateTimeOffline)).getSeconds();
System.out.println("Seconds: " + seconds);
Edit:
Online Timestamp: 1457536522
Offline Timestamp: 1457536642
LocalDate has no time component, so if the times are on the same day, they're effectively turned into the same time. Instead, just diff the DateTimes as they are;
int hours = Hours.hoursBetween(dateTimeOnline, dateTimeOffline).getHours();
(or in your case, since the difference is only 2 minutes, you'll only see the result with Minutes or Seconds)
EDIT: Since the question seems to have nothing to do with the time zone BERLIN which is in the code, this answer is a bit over complicated. Instead, use krzydyn's answer if it's just a time diff between UTC times.
Since you already have timestamps in seconds it can be simple calculated by formula:
int hours = (t2-t1)/3600;
Or if you need fractions:
float hours = (t2-t1)/3600f;
Update: (maybe I got suggested by the answer :)
So to get time diff in seconds is even simpler:
long seconds = t2-t1;

Categories

Resources