How to Shift Calendar in Java? - java

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

Related

Is the TimeUnit class broken?

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.

Long To XMLGregorianCalendar and back to Long

I am trying to convert from millisecond time stamp to XMLGregorianCalendar and back, but I seem to be getting wrong results. Am I doing something wrong? It seems I am gaining days.
// Time stamp 01-Jan-0001 00:00:00.000
Long ts = -62135740800000L;
System.out.println(ts);
System.out.println(new Date(ts)); // Sat Jan 01 00:00:00 PST 1 .. Cool!
// to Gregorian Calendar
GregorianCalendar gc = new GregorianCalendar();
gc.setTimeInMillis(ts);
// to XML Gregorian Calendar
XMLGregorianCalendar xc = DatatypeFactory.newInstance().newXMLGregorianCalendar(gc);
// back to GC
GregorianCalendar gc2 = xc.toGregorianCalendar();
// to Timestamp
Long newTs = gc2.getTimeInMillis();
System.out.println(newTs); // -62135568000000 .. uh?
System.out.println(new Date(newTs)); // Mon Jan 03 00:00:00 PST 1 .. where did the extra days come from?
Interesting - it works fine for values down to (about) -10000000000000L (and positive values) but larger negative values become inconsistent.
If you print out gc, xc, and gc2, you can see where the problem arises (the conversion from XMLGregorianCalendar to GregorianCalendar
gc: java.util.GregorianCalendar[time=-62135740800000 ... DAY_OF_WEEK=7
xc: 0001-01-01T08:00:00.000Z
gc2: java.util.GregorianCalendar[time=? ... DAY_OF_WEEK=5
If you print out the fields of xc, you get 1,1,1.
System.out.println(xc.getYear());
System.out.println(xc.getMonth());
System.out.println(xc.getDay());
For gc2, you get 1,0,1 (which matches xc, because months are zero-based in GregorianCalendar)
System.out.println(gc2.get(gc2.YEAR));
System.out.println(gc2.get(gc2.MONTH));
System.out.println(gc2.get(gc2.DAY_OF_MONTH));
However, adding these 3 println calls changes the output from printing out gc2! - the time=? output from gc2 changes to time=-62135568000000 - so some calculation has been triggered by querying the GregorianCalendar object; the areFieldsSet property also changes from false to true.
The timezones of the two GregorianCalendars are different, but this does not account for the error, which persists even if you set explicit TimeZone and Locale.
I believe here is the problem. Per documentation, toGregorianCalendar() relies on the GregorianCalendar corresponding defaults for conversion when there is any field missing.
If you try:
Date date = new Date();
long ts = date.getTime(); //in place of your input
and run your code, you should find, both to and from conversion working fine.
If you want your toGregorianCalendar() with custom provide inputs as in your example, please use toGregorianCalendar(TimeZone,Locale,Defaults) and supply the updated defaults to be used in conversion.

Java Convert unixtime to date , giving wrong results

Im trying to convert unixtime to date but the results im getting are wrong :
for example i have this unixtime : 1354312800 accurding to this site :
enter link description here
the result is :
Fri, 30 Nov 2012 22:00:00 GMT
but when i do :
long timestamp = 1354312800;
java.util.Date time=new java.util.Date((long)timestamp*1000);
int d = time.getDay();
int m = time.getMonth();
im getting :
d= 6 << this is wrong should be 30.
and m - 11
You have mistaken getDay()for getDate().
getDay Javadoc:
Returns the day of the week represented by this date. The returned value (0 = Sunday, 1 = Monday, 2 = Tuesday, 3 = Wednesday, 4 = Thursday, 5 = Friday, 6 = Saturday) represents the day of the week that contains or begins with the instant in time represented by this Date object, as interpreted in the local time zone.
So just use getDate() instead of getDay()
For more info, check the javadoc: http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Date.html
If your time still differs from the Unix time you tried, you are probably living not in the GMT Timezone, so you need to find out the date for the corresponding timezone:
SimpleDateFormat df = new SimpleDateFormat();
df.setTimeZone(TimeZone.getTimeZone("GMT"));
System.out.println(df.format(time));
This should give you the expected output, even though your local tiemzone differs from GMT
The method Date.getDay() gives the day of the week (0 = Sunday, ..., 6 = Saturday).
Change it to Date.getDate() and you will get 30 as the result.
Some side-notes:
The Date class is pretty much deprecated. Use Calendar instead, or even better, the Joda time library.
Your conversion is sort of funny. (long)timestamp*1000 converts timestamp to a long value (which is already a long value) and then automatically widens 1000 to a long value to carry out the multiplication.
I would skip the conversion, (long) , altogether, and if you want to explicitly say that the factors are long values, use 1000L instead, which is a long-literal.

Joda DateTime not giving the expected results

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.

adding days to a date

I have a program that needs to start on 1/1/09 and when I start a new day, my program will show the next day.
This is what I have so far:
GregorianCalendar startDate = new GregorianCalendar(2009, Calendar.JANUARY, 1);
SimpleDateFormat sdf = new SimpleDateFormat("d/M/yyyy");
public void setStart()
{
startDate.setLenient(false);
System.out.println(sdf.format(startDate.getTime()));
}
public void today()
{
newDay = startDate.add(5, 1);
System.out.println(newDay);
//I want to add a day to the start day and when I start another new day, I want to add another day to that.
}
I am getting the error found void but expected int, in 'newDay = startDate.add(5, 1);'
What should I do?
The Calendar object has an add method which allows one to add or subtract values of a specified field.
For example,
Calendar c = new GregorianCalendar(2009, Calendar.JANUARY, 1);
c.add(Calendar.DAY_OF_MONTH, 1);
The constants for specifying the field can be found in the "Field Summary" of the Calendar class.
Just for future reference, The Java API Specification contains a lot of helpful information about how to use the classes which are part of the Java API.
Update:
I am getting the error found void but
expected int, in 'newDay =
startDate.add(5, 1);' What should I
do?
The add method does not return anything, therefore, trying to assign the result of calling Calendar.add is not valid.
The compiler error indicates that one is trying to assign a void to a variable with the type of int. This is not valid, as one cannot assign "nothing" to an int variable.
Just a guess, but perhaps this may be what is trying to be achieved:
// Get a calendar which is set to a specified date.
Calendar calendar = new GregorianCalendar(2009, Calendar.JANUARY, 1);
// Get the current date representation of the calendar.
Date startDate = calendar.getTime();
// Increment the calendar's date by 1 day.
calendar.add(Calendar.DAY_OF_MONTH, 1);
// Get the current date representation of the calendar.
Date endDate = calendar.getTime();
System.out.println(startDate);
System.out.println(endDate);
Output:
Thu Jan 01 00:00:00 PST 2009
Fri Jan 02 00:00:00 PST 2009
What needs to be considered is what Calendar actually is.
A Calendar is not a representation of a date. It is a representation of a calendar, and where it is currently pointing at. In order to get a representation of where the calendar is pointing at at the moment, one should obtain a Date from the Calendar using the getTime method.
If you can swing it requirement wise, move all your date/time needs to JODA, which is a much better library, with the added bonus that almost everything is immutable, meaning multithreading comes in for free.

Categories