Given a DateTime object at 31-March-2011 and this code:
DateTime temp1 = new DateTime(2011, 3, 31, 12, 0, 0, 0);
DateTime temp2 = temp1.plusMonths(1);
DateTime temp3 = temp2.plusMonths(1);
after the execution
temp1 = 2011-03-31T12:00:00.000+02:00
temp2 = 2011-04-30T12:00:00.000+02:00
temp3 = 2011-05-30T12:00:00.000+02:00
temp3 is wrong here.
Is that above correct. Am I doing a mistake?
No, there's no mistake here. You're adding one month twice, which means the second time you'll get the result of adding a month to the possibly truncated result of adding the first month.
April only has 30 days, which is why you're getting April 30th for temp2 - and adding one month to April 30th gets you to May 30th.
If you want May 31st, use:
DateTime temp3 = temp1.plusMonths(2);
Basically, date and time arithmetic gives "odd" results if you try to think of it in terms of associativity etc.
Related
I noticed a strange behaviour of the TimeUnit class, so I created this minimal example to reproduce it.
long differenceInDays;
Calendar c1 = Calendar.getInstance();
Calendar c2 = Calendar.getInstance();
c1.setTimeInMillis(1466062306000l); // Thu Jun 16 2016 09:31:46 GMT+0200
c2.setTimeInMillis(1466028000000l); // Thu Jun 16 2016 00:00:00 GMT+0200
differenceInDays = TimeUnit.DAYS.convert(c2.getTimeInMillis() - c1.getTimeInMillis(), TimeUnit.MILLISECONDS);
System.out.println(differenceInDays); // obviously zero
c2.add(Calendar.DATE, +1);
differenceInDays = TimeUnit.DAYS.convert(c2.getTimeInMillis() - c1.getTimeInMillis(), TimeUnit.MILLISECONDS);
System.out.println(differenceInDays); // why zero and not one?
c2.add(Calendar.DATE, +1);
differenceInDays = TimeUnit.DAYS.convert(c2.getTimeInMillis() - c1.getTimeInMillis(), TimeUnit.MILLISECONDS);
System.out.println(differenceInDays); // suddenly a 1, but not a 2 like expected
It is obvious that the first time the difference is calculated it is 0, because not a whole day lies between the dates.
But the second time a whole day is added, so how can the difference be still 0?
Output:
001
I don't think this problem is daylight saving time or leap year related because I only do calculations within the same year, even month.
Here is a date to milliseconds calculator for you to check.
You can see better what's going on here with simple math:
c1 = 1466062306000
c2 = 1466028000000
d = 86400000 // one day
c2 - c1 = -34306000 // negative, but less than one day in magnitude
c2 - c1 + d = 52094000 // less than one day
c2 - c1 + d + d = 138494000 // more than one day, less than two days
The correct way to handle this, assuming you're using Java 8, is as follows:
// Decide what time zone you want to work in
ZoneId tz = ZoneId.of("Europe/Berlin");
// If you wanted the local time zone of the system,
// Use this instead:
// ZoneId tz = ZoneId.systemDefault();
// Get instants from the timestamps
Instant i1 = Instant.ofEpochMilli(1466062306000l);
Instant i2 = Instant.ofEpochMilli(1466028000000l);
// Get the calendar date in the specified time zone for each value
LocalDate d1 = i1.atZone(tz).toLocalDate();
LocalDate d2 = i2.atZone(tz).toLocalDate();
// Get the difference in days
long daysBetween = ChronoUnit.DAYS.between(d2, d1);
If your inputs are truly Calendar objects instead of timestamps, I'd suggest Calendar.toInstant() as described in the Legacy Date-Time Code guidance.
If you're using Java 7 or earlier, you will find similar capabilities from the Joda Time library.
if you really don't want to use any of these, and still do things the old (hard) way, then see this example.
In Java I have the following test that passes fine
// 42 bits of time is good enough for the next 100 years.
// An IEEE double has 52 bits of mantissa, so our dates can be easily fit.
#Test
public void testMaxBits() throws ParseException {
// Maximum 42 bit integer
long millis = (1L << 42) - 1;
Date date = new Date(millis);
//DateTime maxDate = new DateTime(2109, 5, 15, 8, 35, 11, 103);
Date maxDate = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS").parse("2109-05-15T08:35:11.103");
Assert.assertEquals(maxDate, date);
}
Now, I want to do the same sort of thing in C#, so I have a test in LinqPAD that test the C# implementation for correctness
DateTime maxDate = new DateTime(2109, 5, 15, 8, 35, 11, 103);
long beginTicks = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).Ticks;
long l = (1L << 42) - 1;
DateTime date = new DateTime(beginTicks + l, DateTimeKind.Utc);
maxDate.Dump();
date.Dump();
The output don't match, the values outputted ToString() values are
maxDate = 15/05/2109 08:35:11
date = 06/01/1970 02:10:04
What am I missing here?
Edit. I have see a great answer below from #zmitrok, I have changed
DateTime date = new DateTime(beginTicks + l, DateTimeKind.Utc);
to
DateTime date = new DateTime(beginTicks +
l * TimeSpan.TicksPerMillisecond, DateTimeKind.Utc);
but now get
date = 15/05/2109 07:35:11
Where has the hour gone?
Your test is basically confusing ticks with milliseconds. If you only need to store a number of milliseconds since the unix epoch, then do so - but I'd recommend using something like this to perform the conversion:
public static readonly DateTime UnixEpoch
= new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
public DateTime FromMillisecondsSinceUnixEpoch(long milliseconds)
{
return UnixEpoch.AddMilliseconds(milliseconds);
}
(As a side-note, that method already exists in my Noda Time project... hint hint :)
Your test would then be:
[TestMethod]
public void TestMaxBits()
{
long maxMillis = (1L << 42) - 1;
DateTime maxDate = DateTimeHelper.FromMillisecondsSinceUnixEpoch(maxMillis);
Assert.Greater(maxDate, new DateTime(2100, 1, 1, 0, 0, 0));
}
Note that:
This code doesn't mention ticks at all, because you're not interested in ticks
This code doesn't assert that the maximum date is some very specific value, because that's not what you care about; you care that 42 bits of time will carry you until the end of the century. (The "next 100 years" comment is somewhat specious, as 2109 is less than 100 years away from now, so I'll assume it really means "until the end of the 21st century.")
That of course make your question of "Where has the hour gone?" irrelevant - but the answer to that is simply that SimpleDateFormat defaults to using the system time zone, so you're actually relying on the time zone of the system you're running the test on, which is a really bad idea. If you set the time zone of the SimpleDateFormat to UTC, you'll find that it's 07:35:11 in Java as well.
The constructor you are using takes ticks as the first argument, however you are passing a value that was added to milliseconds.
Ticks: A date and time expressed in the number of 100-nanosecond intervals that have elapsed since January 1, 0001 at 00:00:00.000 in the Gregorian calendar.
I think you need to multiply ticks by this constant: https://msdn.microsoft.com/en-us/library/system.timespan.tickspermillisecond%28v=vs.110%29.aspx
I am trying to write a Java method to shift a Calendar, using a time offset in milliseconds.
But when I try to shift for one month (30 days) it does not work:
Calendar c = new GregorianCalendar(2012, 5, 23, 13, 23, 10);
long original = c.getTimeInMillis();
System.out.println("Original Date "+c.getTime());
long oneMonth = 30*24*60*60*1000; //one month in milliseconds
Calendar c_1Month = new GregorianCalendar();
c_1Month.setTimeInMillis(original+oneMonth);
System.out.println("After 1-month "+c_1Month.getTime());
The output is
Original Date Sat Jun 23 13:23:10 PDT 2012
After 1-month Sun Jun 03 20:20:22 PDT 2012
You can see that it does not shift it correctly to July 23rd.
I understand there is a specific method add(field, amount) in Calendar and I can change month using that, but I wanna have one single method in my client to shift time, with providing shift amount in milliseconds (the amount of shift changes based on my tests, and I do not want to have several methods for that).
You think the variable oneMonth is a long, but you assign it an integer value. But the result of 30*24*60*60*1000 doesn't fit into an integer and therefore overflows to a negative value.
If you change your code to long oneMonth = 30*24*60*60*1000L; it would work.
But despite of that "bug" in your code the comment of azurefrog is correct, this is not the recommended way to add one month, it would only be valid in case you like to add 30 days which is something different.
Try to use something like this instead:
c_1Month.add( Calendar.DAY_OF_MONTH, 30);
or
c_1Month.add( Calendar.MONTH, 1);
The Calendar class is aware and able to handle all corner cases very good. I would suggest you rely on this. It reduces possible bugs and makes code easier to read and understand.
With Java 8 the code to use would be:
LocalDateTime.now().plusMonths(1);
or
LocalDateTime.now().plusDays(30);
Add an "L" at the end of 30*24*60*60*1000
The way you are calculating, you are transforming the whole thing into an INTEGER.
Don't believe me :), try printing the value before adding the L after the 1000;
System.out.println(oneMonth);
Your final code should be:
Calendar c = new GregorianCalendar(2012, 5, 23, 13, 23, 10);
long original = c.getTimeInMillis();
System.out.println("Original Date "+c.getTime());
long oneMonth = 30*24*60*60*1000L; //one month in milliseconds
Calendar c_1Month = new GregorianCalendar();
c_1Month.setTimeInMillis(original+oneMonth);
System.out.println("After 1-month "+c_1Month.getTime());
I need to populate JComboBox with days as follows:
April 1, 2014
April 2, 2014
...
April 10,2014
I am using JodaTime to define dates. However, I don't know how to create an iterater over days in JodaTime.
JComboBox<String> days = new JComboBox<String>();
DateTime startD = new DateTime(2014, 4, 1, 0, 0, 0);
for (int i=0; i<10; i++)
{
// DateTime nextD = ...
days.addItem(startD.toString(DateTimeFormat.forPattern("yyyyMMdd")));
}
DateTime currentDate = startD.plusDays(i);
You should have found that easily by reading the javadoc.
Note that unless you really want the items to represent a precise instant (i.e. the first april at midnight in your timezone), you should probably use a LocalDate instead of a DateTime.
I used Date() for getting the date of my birthday, but it was returned the mismatch of the month. My birthday is 04-March-87. so i gave an input as,
Date birthDay= new Date(87,03,04,8,30,00);
But it returns correct year, day and time. But month was problem. It displays April month.
What's wrong with that?
Months are set from 0 to 11, January =0, February = 1, ..., December = 11.
So, for April do this:
Date birthDate = new Date(87,02,04,8,30,00); //March = 2
Hope this helps;
EDIT
The Date class with this constructor public Date(int year, int month, int date, int hrs, int min) is deprecated (i.e. it has been declared #Deprecated).
Rather do this.
Calendar calendar = Calendar.getInstance();
Calendar.set(1987, 2, 4, 0, 0, 0);
Date birthDate = calendar.getTime();
(It will return the same thing as what you asked for)
The month in the Date class starts with 0 for January, so March is 2, not 3.
Also, the Date(int, int, int, int, int, int) constructor is deprecated, so you should consider using the Calendar class instead.
Finally, be careful with leading zeros in Java - they represent octal constants. The number 09 would not do what you expect.