Subtraction of 1ms leads to unexpected behaviour - java

What is wrong? I assume that if I subtract 1ms from 1 Jan 1980 0:0:0 then I've got 1979. But I must subtract about 500+ ms for this. Please, give me a hint.
val cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"))
cal.set(1980, 0, 1, 0, 0, 0)
val date = new Date
date.setTime(cal.getTimeInMillis()) // <- 1980 Jan 01 0:0:0
date.setTime(cal.getTimeInMillis() - 1) // <- 1980 Jan 01 0:0:0 too !!!
Updated.
The solution is
val cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"))
cal.setTimeInMillis(0)
cal.set(1980, 0, 1, 0, 0, 0)

With Calendar.set(year, month, day, hourOfDay, minute, second) no milliseconds are set. Consequently the Calendar implementation sets the milliseconds to "unknown" which is actually treated as the midpoint within the given second.
Subtracting 500ms means you just step over the midpoint. Same should happen if you add 500ms, which should bring you just over the second. Actually subtracting 500ms works and you must add 620ms to see the next second.

Related

Generate specific intervals between two time/date in Java

I want to generate intervals between two given date/time.
For instance, say for 24 hour format (HH:MM), I have these two endpoints, 00:00 and 11:51, and suppose I want to partition it in 24 pieces. So my calculation is like this:
(hour * 3600 + min * 60) / 24
If I use calendar.add(Calendar.SECOND, (hour * 3600 + min * 60) / 24), I am getting wrong dates/time. My calculation is double and I think calendar.add() does not support double. Like it is taking 28.883 as 29.
In essence I want something like this:
now : 15:57
today start : 00:00 (24hh)
output : 00:00, 00:47.85, …, 15:57
The actual problem with your code is that you are performing integer division. I assume both hour and min are defined as integer types. The formula (hour * 3600 + min * 60) / 24 always yields an integer type. If you change the code to (hour * 3600 + min * 60) / 24d the expression yields a floating point value at least.
The next problem is indeed that Calendar.add(int field, int amount) accepts only an integer as second argument. Of course, if you are passing Calendar.SECOND as first argument, then your precision is not higher than seconds. You can use Calendar.MILLISECOND to get a higher precision.
However, I suggest using the new Java Date and Time API, instead of the troublesome old API:
LocalTime startTime = LocalTime.of(0, 0);
LocalTime endTime = LocalTime.of(11, 51);
long span = Duration.between(startTime, endTime).toNanos();
final int n = 23; // Number of pieces
LongStream.rangeClosed(0, n)
.map(i -> i * span / n)
.mapToObj(i -> startTime.plusNanos(i))
.forEach(System.out::println);
You need to save your start date in a calendar object and then when you generate each division use the formula:
startCalendar.add(Calendar.Second, count * (hour * 3600 + min * 60) / 24))
That way the arithmetic errors that you get by dividing by 24 (or whatever) are not accumulated.
Here’s a variant of MC Emperor’s fine code. I wanted to leave the math to the library class. Duration has methods dividedBy and multipliedBy that we can use to our advantage.
LocalTime startTime = LocalTime.of(0, 0);
LocalTime endTime = LocalTime.of(11, 51);
final int n = 24; // Number of pieces
Duration piece = Duration.between(startTime, endTime).dividedBy(n);
LocalTime[] partitionTimes = IntStream.rangeClosed(0, n)
.mapToObj(i -> startTime.plus(piece.multipliedBy(i)))
.toArray(LocalTime[]::new);
System.out.println(Arrays.toString(partitionTimes));
Output:
[00:00, 00:29:37.500, 00:59:15, 01:28:52.500, 01:58:30, 02:28:07.500,
02:57:45, 03:27:22.500, 03:57, 04:26:37.500, 04:56:15, 05:25:52.500,
05:55:30, 06:25:07.500, 06:54:45, 07:24:22.500, 07:54, 08:23:37.500,
08:53:15, 09:22:52.500, 09:52:30, 10:22:07.500, 10:51:45,
11:21:22.500, 11:51]
Is there a rounding problem? With a start time in whole minutes and 24 pieces there won’t be since 24 divides evenly into the number of nanoseconds in a minute. With another number of pieces you may decide whether the slight inaccuracy is worth worrying about. If it is, for each partitioning time multiply before you divide.

Convert input second into Month

Months are represented by numbers ranging from 0 to 11. For example, 0 is January, 1 is February, etc.
The code would be
double mon = (sec/2592000);
But how do I control the range from 0-11?
You can use mod operation to control the range:
double mon = ((sec/2592000 - 1) % 12) // from 0 to 11

Joda time PeriodFormatterBuilder, can I omit weeks and use only days?

I have seen this, but is it possible to use only month and days, not weeks?
For example, instead of "1 month 2 weeks 2 days", I want "1 month 16 days".
You can use a solution similar to the answer you linked, but you'll also need to use a org.joda.time.PeriodType to normalized the period:
// 1 month, 2 weeks and 2 days
Period p = new Period(0, 1, 2, 2, 0, 0, 0, 0);
PeriodFormatter fmt = new PeriodFormatterBuilder()
// months
.appendMonths().appendSuffix(" month", " months").appendSeparator(" and ")
// days
.appendDays().appendSuffix(" day", " days")
.toFormatter();
System.out.println(fmt.print(p.normalizedStandard(PeriodType.yearMonthDayTime())));
This will print:
1 month and 16 days

Java date class interesting

End date was being computed to be earlier than the start date
Date startDate = new Date();
Date endDate = new Date(startDate.getTime() + (24 * 3600000 * 42));
System.out.println(startDate);
System.out.println(endDate);
output :
Tue Sep 17 01:46:31 EEST 2013
Mon Sep 09 08:43:43 EEST 2013
why the output is not correct ?
Your integer arithmetic has overflowed. The maximum possible value of an int is 2147483647 or Integer.MAX_VALUE (a little over 2 billion), but multiplying your integer literals would yield 3628800000 (about 3.6 billion). The result is a negative number (-666167296), and an earlier date.
Try casting one of your literals as a long to force long arithmetic (or use long literals):
( (long) 24 * 3600000 * 42)
or
(24L * 3600000 * 42)
This operation is well within the range of long values (max value 9223372036854775807, over 9 quintillion).
24 * 3600000 * 42 is 3,628,800,000 which does not fit into an int. Rollover occurs. Force the use of a long by casting one of the factors:
24L * 3600000 * 42
The number you're trying to add is 24 * 3600000 * 42 which is equal to 3,628,800,000. This is larger than 2,147,483,647 which is the maximum value that can be represented with the given data type. What you're experiencing is an overflow, meaning that after crossing the maximum value, the number loops back to its lowest value, which is in the negative. Therefore, you're adding a negative value to the date.

Permutation and combination required

I am trying to read the "day" value from native calendar of blackberry,the value is returned as a integer ,which is mapped to a value for each day of the week.The values are like :
Monday:32768
tue: 16384
wed :8192
thur :4096
fri :2048
sat :1024
sun :65536
I am able to see if the value is mon/tue/wed/thu/fri/sat/sun if the event is occuring for a single day using
if (rule.MONDAY == rule.getInt(rule.DAY_IN_WEEK)) {
System.out.println("occurs monday");
}
rule.getInt(rule.DAY_IN_WEEK)
value is also same as monday value.
Now the issue is, if the events is occuring on two/three or more number of days then
rule.getInt(rule.DAY_IN_WEEK)
returns me a sum of all days selected.
EXAMPLE: if the days are :wed,sat then i get the result as 9216 ,sum of wed+sat ,from this i am not getting to know which are the days the event occurs.
How can i do a permutation/combination of these numbers and get the exact result for 'n' number of days selected.
I assume the days are just bit flags in a number so you might change your check:
if ( (rule.getInt(rule.DAY_IN_WEEK) & rule.MONDAY) != 0 ) {
System.out.println("occurs monday");
}
Use binary and operator like this:
int day = rule.getInt(rule.DAY_IN_WEEK)
if(day & rule.MONDAY != 0) {
System.out.println("occurs monday");
}
if(day & rule.WEDNESDAY != 0) {
System.out.println("occurs wednesday");
} /* and so on */
Notice that:
0000 0100 0000 0000 = 1024
0000 1000 0000 0000 = 2048
.
.
.
check also bit mask
To know which days of the week the event occurs, you need to do something like this:
boolean occursOnMonday = (rule.getInt(rule.DAY_IN_WEEK) & rule.MONDAY) != 0;
where & is the bitwise AND operator. Why is this so?
Wednesday is 8192, which in binary it is 10000000000000 (2 times 13)
Saturday is 1024, which in binary it is 00010000000000 (2 times 9)
So an event that happens on Wed and Sat is 9216, which is 10010000000000.
Then using bit operations you are able to know what bits are in 1 and what bits are in 0, and with that you know what days the event happens.

Categories