Unexpected conversion from java.sql.Timestamp to joda's LocalDate - java

i'm having a problem while converting Timestamp objects to joda's LocalTime.
See example below:
public static void main(String[] args) {
Timestamp t = Timestamp.valueOf("1111-11-11 00:00:00");
System.out.println(t); //-- prints '1111-11-11 00:00:00.0'
System.out.println(new LocalDate(t)); //-- prints '1111-11-17'
Calendar calendar = Calendar.getInstance();
calendar.setTime(t);
System.out.println(LocalDate.fromCalendarFields(calendar)); //-- prints '1111-11-11'
}
I could not determine why 'new LocalDate(t)' results in '1111-11-17'. Can anyone help me on that?
I notice this "problem" while using joda-time-hibernate to populate my bean's property of type LocalDate.

This indeed has to do with the different types of calendars. java.sql.Timestamp, like java.util.Date, don't have any calendar information, as they are stored solely as a number, the number of milliseconds since 1970, with the implied assumption that there was a jump between October 4 and October 15 1582, when the Gregorian calendar was officially adopted, replacing the old Julian calendar. So, you can't possibly have a Date (or Timestamp) object representing October 7, 1582. If you try to create such a date, you'll automatically end up 10 days later. For example:
Calendar c = Calendar.getInstance();
c.setTimeZone(TimeZone.getTimeZone("UTC"));
c.set(1582, Calendar.OCTOBER, 7, 0, 0, 0);
c.set(Calendar.MILLISECOND, 0);
d = c.getTime();
System.out.println("Date: " + d);
// Prints:
// Date: Sun Oct 17 00:00:00 GMT 1582
In other words, Date objects have an implied Julian+Gregorian chronology, automatically switching between those two.
JodaTime is a bit smarter, it supports several Chronologies, including a continuing Julian, a proleptic Gregorian, a mixed Julian+Gregorian, and a standard ISO Chronology which is almost identical to the Gregorian one. If you read the JavaDoc of the LocalDate.fromCalendarFields method, you'll see that it mentions that:
This factory method ignores the type of the calendar and always creates a LocalDate with ISO chronology.
The mixed Julian+Gregorian chronology behaves like the implicit Java dates, with an automatic switch between the two different calendars. The pure chronologies assume that their calendar system is forever in use, so for example it assumes that the Gregorian calendar has been used since the start of time.
Let's see how each Chronology treats the 1111-11-11 date:
Calendar c = Calendar.getInstance();
c.setTimeZone(TimeZone.getTimeZone("UTC"));
c.set(1111, Calendar.NOVEMBER, 11, 0, 0, 0);
c.set(Calendar.MILLISECOND, 0);
Date d = c.getTime();
System.out.println("Date: " + d + " (" + d.getTime() + " milliseconds)");
System.out.println("ISO: " + new DateTime(d, ISOChronology.getInstance(DateTimeZone.forID("UTC"))));
System.out.println("Julian+Gregorian: " + new DateTime(d, GJChronology.getInstance(DateTimeZone.forID("UTC"))));
System.out.println("Julian: " + new DateTime(d, JulianChronology.getInstance(DateTimeZone.forID("UTC"))));
System.out.println("Gregorian: " + new DateTime(d, GregorianChronology.getInstance(DateTimeZone.forID("UTC"))));
Ends up as:
Date: Sat Nov 11 00:00:00 GMT 1111 (-27079747200000 milliseconds)
ISO: 1111-11-18T00:00:00.000Z
Julian+Gregorian: 1111-11-11T00:00:00.000Z
Julian: 1111-11-11T00:00:00.000Z
Gregorian: 1111-11-18T00:00:00.000Z
As you can see, the two modern chronologies (ISO and Gregorian) report the correct date if they would have been in use since from the start, while the two that use the Julian calendar report the date as it was known back then, although in hindsight we know it to be off by 7 days compared to the true equinox date.
Let's see what happened around the switch:
c.set(1582, Calendar.OCTOBER, 15, 0, 0, 0);
d = c.getTime();
System.out.println("Date: " + d + " (" + d.getTime() + " milliseconds)");
System.out.println("ISO: " + new DateTime(d, ISOChronology.getInstance(DateTimeZone.forID("UTC"))));
System.out.println("Julian+Gregorian: " + new DateTime(d, GJChronology.getInstance(DateTimeZone.forID("UTC"))));
System.out.println("Julian: " + new DateTime(d, JulianChronology.getInstance(DateTimeZone.forID("UTC"))));
System.out.println("Gregorian: " + new DateTime(d, GregorianChronology.getInstance(DateTimeZone.forID("UTC"))));
ends up as:
Date: Fri Oct 15 00:00:00 GMT 1582 (-12219292800000 milliseconds)
ISO: 1582-10-15T00:00:00.000Z
Julian+Gregorian: 1582-10-15T00:00:00.000Z
Julian: 1582-10-05T00:00:00.000Z
Gregorian: 1582-10-15T00:00:00.000Z
So the only one that's left behind is the Julian calendar. That was a valid date in all the countries that didn't accept the Gregorian calendar yet, which back then was a lot of countries. Greece made the switch in 1923...
One millisecond before that, the date was:
c.add(Calendar.MILLISECOND, -1);
d = c.getTime();
System.out.println("Date: " + d + " (" + d.getTime() + " milliseconds)");
System.out.println("ISO: " + new DateTime(d, ISOChronology.getInstance(DateTimeZone.forID("UTC"))));
System.out.println("Julian+Gregorian: " + new DateTime(d, GJChronology.getInstance(DateTimeZone.forID("UTC"))));
System.out.println("Julian: " + new DateTime(d, JulianChronology.getInstance(DateTimeZone.forID("UTC"))));
System.out.println("Gregorian: " + new DateTime(d, GregorianChronology.getInstance(DateTimeZone.forID("UTC"))));
Meaning:
Date: Thu Oct 04 23:59:59 GMT 1582 (-12219292800001 milliseconds)
ISO: 1582-10-14T23:59:59.999Z
Julian+Gregorian: 1582-10-04T23:59:59.999Z
Julian: 1582-10-04T23:59:59.999Z
Gregorian: 1582-10-14T23:59:59.999Z
The ISO and Gregorian chronologies report a date that didn't actually exist in the Gregorian calendar, since there was no Gregorian calendar before October 15, yet this date is valid in an extended, proleptic Gregorian calendar. It's like finding a BCE date inscribed on a BCE monument... Nobody knew that they were before Christ before Christ was even born.
So, the root of the problem is that a date string is ambiguous, since you don't know in which calendar you're measuring. Is the year 5772 a year in the future, or is it the current Hebrew year? Java assumes a mixed Julian+Gregorian calendar. JodaTime provides extensive support for different calendars, and by default it assumes the ISO8601 chronology. Your date is automatically converted from the Julian calendar in use in 1111 to the ISO chronology that we currently use. If you want your JodaTime-enhanced timestamps to use the same chronology as the java.sql.Timestamp class, then explicitly select the GJChronology when constructing JodaTime objects.

The reason this is happening for dates several centuries in the past has to do with the (worldwide) switch to the Gregorian Calendar from the Julian Calendar. Basically, as countries implemented the new calendar system in a piecemeal fashion, they would lose days or weeks (and in some cases, months) in the process. In Spain, for example, 4 October 1582 in the Julian calendar was followed immediately by 15 October 1582 in the Gregorian calendar. Wikipedia has a decent discussion of the topic.
As long as your application does not deal with dates too far in the past, you won't have to deal with discrepancies related to this. As you mentioned in your comment, System.out.println(new LocalDate(Timestamp.valueOf("1999-11-11 00:00:00"))) correctly outputted 1999-11-11. If you need to work with these older dates, I'd suggest looking into an alternative date for the Julian calendar.
(As an aside, I'd be curious to see what would happen if you asked for the local date, in Spain, of 5 October 1582 ... a day which never happened.)

Related

Java and converting displayed value into local time zone and daylight savings

I know this is an oft asked type of question, and I've been looking around the web for solutions, but to no avail. The problem is that we're fetching a date value from the database and want to display it in a PDF in local time. The raw formatted value is "2022/05/24 15:18:10" and using Costa Rica as an example client time zone (we're in LA), we'd want this "2022/05/24 16:18:10". However, we're getting this: "2022/05/24 17:18:10".
One of the odd things I'm seeing when I run the code below is that it seems that our server time zone is PST, according to Java, but is actually PDT.
creationDate from db=Tue May 24 15:18:10 PDT 2022
Desired creationDate output=2022/05/24 16:18:10
cal.getTimeZone.getDisplayName()=Pacific Standard Time
formatted creationDate=2022/05/24 17:18:10
So one question is: why are we seeing PST instead of PDT and the second question is how do I fix this?
Example code:
public static void main(
String[] args)
{
final String DATE_TIME_PATTERN = "yyyy/MM/dd HH:mm:ss";
final String LA_TIMEZONE_ID = "America/Los_Angeles";
DateFormat df = new SimpleDateFormat(DATE_TIME_PATTERN);
TimeZone serverTimeZone = TimeZone.getTimeZone(LA_TIMEZONE_ID);
Calendar cal = Calendar.getInstance(/* serverTimeZone */); // works same with or without param
TimeZone clientTimeZone = TimeZone.getTimeZone("CST"); // Costa Rica = Central Standard Time
Date creationDate;
df.setTimeZone(clientTimeZone);
// Values as fetched from database.
cal.set(Calendar.DAY_OF_MONTH, 24);
cal.set(Calendar.MONTH, Calendar.MAY);
cal.set(Calendar.YEAR, 2022);
cal.set(Calendar.HOUR_OF_DAY, 15);
cal.set(Calendar.MINUTE, 18);
cal.set(Calendar.SECOND, 10);
creationDate = cal.getTime();
System.out.println("creationDate from db=" + creationDate.toString());
System.out.println("Desired creationDate output=2022/05/24 16:18:10");
System.out.println("cal.getTimeZone.getDisplayName()=" + cal.getTimeZone().getDisplayName());
System.out.println("formatted creationDate=" + df.format(creationDate));
}
So, after a bit of hacking and googling, it would appear (at least to me), that "Central Standard Time" isn't what you want to use (nor should you use it generally).
From time-and-date/Central Standard Time (CST)
Caution: This is NOT the current local time in most locations in that time zone
North America: Only some locations are currently on CST because most places in this time zone are currently on summer time / daylight saving time and are observing CDT.
And then add in time-and-date/Time Zone in Costa Rica
Costa Rica observes Central Standard Time all year. There are no Daylight Saving Time clock changes.
That's not confusing at all 🙄. So, as I "understand" it, CST is normally -6 hours and CDT is -5 hours, but Costa Rica is always -6 hours.
So, based on the observations of your code, CST seems to be having the day light savings value applied to it, regardless of what you do.
So, what to do about it? Well, the simple answer is, don't use it, in fact, stop using the java.util.Date and related APIs altogether and instead, make use of the replacement java.time APIS and the America/Costa_Rica time zone directly, for example...
final String DATE_TIME_PATTERN = "yyyy/MM/dd HH:mm:ss";
final String LA_TIMEZONE_ID = "America/Los_Angeles";
final String dateStringValue = "2022/05/24 15:18:10";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
LocalDateTime ldt = LocalDateTime.parse(dateStringValue, formatter);
ZoneId zoneId = ZoneId.of(LA_TIMEZONE_ID);
ZonedDateTime zdt = ldt.atZone(zoneId);
System.out.println(" Local = " + ldt);
System.out.println(" zdt = " + zdt);
System.out.println("Costa_Rica = " + zdt.withZoneSameInstant(ZoneId.of("America/Costa_Rica")));
System.out.println("US/Central = " + zdt.withZoneSameInstant(ZoneId.of("US/Central")));
which prints...
Local = 2022-05-24T15:18:10
zdt = 2022-05-24T15:18:10-07:00[America/Los_Angeles]
Costa_Rica = 2022-05-24T16:18:10-06:00[America/Costa_Rica]
US/Central = 2022-05-24T17:18:10-05:00[US/Central]
This is probably also a good (and over due) reason to dump the old java.util.Date and related classes and update to the java.time APIs, see the date/time trail for more details
Handling Daylight Savings Time in Java might also be worth a read

Oracle Timestamp to BST time conversion

I know that there are tons of different tutorials on time conversion, but this one got me very confused. My task is to read UTC DATE from Oracle DB and convert it into BST time (in a more human readable format).
Facts:
Field in the DB is of DATE type.
When i perform SELECT query it returns 2011-07-12 15:26:07 result.
I'm located in Poland, hence in July the TimeZone here is UTC+2
What's happening:
On the Java side I'm using "classical" JDBC connection to the DB.
When I perform Timestamp timestampDate = resultSet.getTimestamp(COLUMN_NAME) I get the result ... but ...
System.out.println(timestampDate) prints to the console 2011-07-12 15:26:07.0 (which is similar to what I see in the DB tool.
System.out.println(timestampDate.getTime()); prints to the console 1310477167000 (which is wondering, because according to the ms to date converter i found online, it's basically 2011-07-12 13:26:07.0 (2h earlier - which somehow might be related to Polish timezone on that date)
When I perform conversion according to this code:
ukDateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
ukDateFormatter.setTimeZone(TimeZone.getTimeZone("BST"));
return ukDateFormatter.format(timestampDate.getTime());
I get 2011-07-12 19:26:07 which I can't really explain.
I was also trying this
GregorianCalendar calendar = new GregorianCalendar();
calendar.setTime(timestampDate);
calendar.setTimeZone(TimeZone.getTimeZone("BST"));
return ukDateFormatter.format(calendar.getTime());
with the same result.
Question
How to properly read DATE from Oracle DB in "timezone agnostic" format and convert it into BST?
Here's a way of doing it in the database side:
with dates as (select to_date('01/07/2016 10:39:29', 'dd/mm/yyyy hh24:mi:ss') dt from dual union all
select to_date('01/02/2016 09:18:41', 'dd/mm/yyyy hh24:mi:ss') dt from dual)
select dt,
cast(dt AS TIMESTAMP) utc_dt_ts,
from_tz(cast(dt AS TIMESTAMP), 'UTC') AT time zone 'Europe/London' dt_as_ts_bst,
cast(from_tz(cast(dt AS TIMESTAMP), 'UTC') AT time zone 'Europe/London' AS DATE) dt_at_bst
from dates;
DT UTC_DT_TS DT_AS_TS_BST DT_AT_BST
------------------- ------------------------------------------------- ------------------------------------------------- -------------------
01/07/2016 10:39:29 01-JUL-16 10.39.29.000000 01-JUL-16 11.39.29.000000 EUROPE/LONDON 01/07/2016 11:39:29
01/02/2016 09:18:41 01-FEB-16 09.18.41.000000 01-FEB-16 09.18.41.000000 EUROPE/LONDON 01/02/2016 09:18:41
The fourth column (dt_at_bst) is the one that shows how to take the date and turn it into another date at BST. It does this by first casting the date as a timestamp and then telling Oracle to treat it as a timestamp at UTC and to output the timestamp for the 'Europe/London' region. Specifying the region like this (rather than passing a specific +01:00 timezone) means that the resultant timestamp will be daylight savings aware. Specifying the region as a three letter shortcut is not advised since that may represent more than one region - e.g. BST could be British Summer Time or Bering Standard Time; both very different things!
I have assumed that by BST you mean British Summer Time, so I have specified the region for the timestamp to be moved to as Europe/London. You would need to adjust this as applicable, if you need a different timezone.
I have included a winter and a summer date in my sample data to show you the effects of casting it into BST - the summer time is expecting to be changed, and the winter time is not.
Actually it is not about Oracle, but more about Java.
First of all:
When you use
System.out.println(timestampDate)
in output you see already adjusted time to your computer time zone.
It is always adjusted when you use Date (i.e.
Calendar.getTime() or Timestamp.getTime())
Code to play with:
SimpleDateFormat dtFmt = new SimpleDateFormat("HH:mm:ss");
NumberFormat nFmt = NumberFormat.getIntegerInstance();
nFmt.setMinimumIntegerDigits(2);
long currentTimeMs = System.currentTimeMillis();
GregorianCalendar utcCalendar = new GregorianCalendar(
TimeZone.getTimeZone("UTC"));
GregorianCalendar bstCalendar = new GregorianCalendar(
TimeZone.getTimeZone("Europe/London"));
GregorianCalendar localCalendar = new GregorianCalendar();
utcCalendar.setTimeInMillis(currentTimeMs);
bstCalendar.setTimeInMillis(currentTimeMs);
localCalendar.setTimeInMillis(currentTimeMs);
System.out.println("---- milliseconds ----");
System.out.println("Current ms : " + currentTimeMs);
System.out.println("Local Calendar ms: " + localCalendar.getTimeInMillis());
System.out.println("UTC Calendar ms: " + utcCalendar.getTimeInMillis());
System.out.println("BST Calendar ms: " + bstCalendar.getTimeInMillis());
System.out.println("---- SimpleFormat Time ----");
System.out.println("Current Time: "
+ dtFmt.format(new Date(currentTimeMs)));
System.out.println("Local Time: " + dtFmt.format(localCalendar.getTime()));
System.out.println("UTC Time : " + dtFmt.format(utcCalendar.getTime()));
System.out.println("BST Time : " + dtFmt.format(bstCalendar.getTime()));
System.out.println("---- Calendar Zone Time ----");
System.out.println("Local Zone Time: "
+ nFmt.format(localCalendar.get(Calendar.HOUR_OF_DAY)) + ":"
+ nFmt.format(localCalendar.get(Calendar.MINUTE)) + ":"
+ nFmt.format(localCalendar.get(Calendar.SECOND)));
System.out.println("UTC Zone Time : "
+ nFmt.format(utcCalendar.get(Calendar.HOUR_OF_DAY)) + ":"
+ nFmt.format(utcCalendar.get(Calendar.MINUTE)) + ":"
+ nFmt.format(utcCalendar.get(Calendar.SECOND)));
System.out.println("BST Zone Time : "
+ nFmt.format(bstCalendar.get(Calendar.HOUR_OF_DAY)) + ":"
+ nFmt.format(bstCalendar.get(Calendar.MINUTE)) + ":"
+ nFmt.format(bstCalendar.get(Calendar.SECOND)));
}
As you will see each Calendar returns Time fields (HOUR_OF_DAY, MINUTE, SECOND) according to its TimeZone, not what you print or format from Calendar.getTime())
What I did, and it seems to be working for me:
ukDateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
ukDateFormatter.setTimeZone(TimeZone.getTimeZone("Europe/London"));
and performing:
Timestamp timestampDate = rs.getTimestamp(...);
DateTime dateTime = new
DateTime(timestampDate).withZoneRetainFields(DateTimeZone.UTC);
System.out.println(ukDateFormatter.format(dateTime.getMillis()));
prints:
2011-07-12 16:26:07 from the input 2011-07-12 15:26:07
Why happened here?
What was so problematic here, is that rs.getTimestamp(...) was returning the date from the database "as it is" (since DATE column type doesn't preserve the timezone) implicitly but was adding some information about my local timezone - which I didn't wanted.
Easiest solution was to use joda and create new object, retaining values, but changing timezone to UTC. From that point conversion with SimpleDateFormat is quite straightforward.

How to calculate next week?

I want to precisely calculate the time one week from a given date, but the output I get back is one hour early.
code:
long DURATION = 7 * 24 * 60 * 60 * 1000;
System.out.println(" now: " + new Date(System.currentTimeMillis()));
System.out.println("next week: " + new Date(System.currentTimeMillis() + DURATION));
output:
now: Wed Sep 16 09:52:36 IRDT 2015
next week: Wed Sep 23 08:52:36 IRST 2015
How can I calculate this correctly?
Never, ever rely on millisecond arithmetic, there are too many rules and gotchas to make it of any worth (even over a small span of time), instead use a dedicated library, like Java 8's Time API, JodaTime or even Calendar
Java 8
LocalDateTime now = LocalDateTime.now();
LocalDateTime then = now.plusDays(7);
System.out.println(now);
System.out.println(then);
Which outputs
2015-09-16T15:34:14.771
2015-09-23T15:34:14.771
JodaTime
LocalDateTime now = LocalDateTime.now();
LocalDateTime then = now.plusDays(7);
System.out.println(now);
System.out.println(then);
Which outputs
2015-09-16T15:35:19.954
2015-09-23T15:35:19.954
Calendar
When you can't use Java 8 or JodaTime
Calendar cal = Calendar.getInstance();
Date now = cal.getTime();
cal.add(Calendar.DATE, 7);
Date then = cal.getTime();
System.out.println(now);
System.out.println(then);
Which outputs
Wed Sep 16 15:36:39 EST 2015
Wed Sep 23 15:36:39 EST 2015
nb: The "problem" you seem to be having, isn't a problem at all, but simply the fact that over the period, your time zone seems to have entered/exited day light savings, so Date is displaying the time, with it's correct offset
Try this
Calendar cal = Calendar.getInstance();
System.out.println(cal.getTime());
cal.add(Calendar.DAY_OF_MONTH, 7);
System.out.println(cal.getTime());
The difference is because of the different timezone. IRDT is +0430 and IRST is +0330
To overcome this issue you can use the JodaTime.
LocalDateTime now = LocalDateTime.now();
LocalDateTime nextweek = now.plusDays(7);
System.out.println(now);
System.out.println(nextweek);
As other said. It would be better to use Calendar or JodaTime library. But the question is why you were not getting the desired result. It was because currentTimeMillis() calculates time between "computer time" and coordinated universal time (UTC). Now consider following case.
long DURATION = 7 * 24 * 60 * 60 * 1000;
Date now = new Date();
Date nextWeek = new Date(now.getTime() + DURATION);
System.out.println(" now: " + now);
System.out.println("next week: " + nextWeek);
here Date.getTime() calculate time from 00:00:00 GMT every time and then when converted to string will give time for your local time zone.
Edit :
I was wrong. The reason is as simon said.
The actual "why" is that IRDT (Iran Daylight Time) ends on September
22nd. That's why the first date (September 16th) in the OP's post is
displayed as IRDT and the second date (September 23rd) is displayed as
IRST. Because IRST (Iran Standard Time) is one hour earlier than IRDT
the time displayed is 08:52:36 instead of 09:52:36.

Java TimeUnit.MILLISECONDS.toDays() gives wrong result

I'm trying to calculate the difference between two days in amount of days. For some reason comparing 01-03-2013 and 01-04-2013 gives the result 30, as does comparing 01-03-2013 and 31-03-2013
Calendar cal = Calendar.getInstance();
cal.clear();
cal.set(2013, Calendar.MARCH, 1);
Date start = cal.getTime();
cal.set(2013, Calendar.APRIL, 1);
Date end = cal.getTime();
long days = TimeUnit.MILLISECONDS.toDays(end.getTime() - start.getTime());
System.out.println("!!! Amount of days : " + String.valueOf(days));
>> 30
cal.set(2013, Calendar.MARCH, 1);
start = cal.getTime();
cal.set(2013, Calendar.MARCH, 31);
end = cal.getTime();
days = TimeUnit.MILLISECONDS.toDays(end.getTime() - start.getTime());
System.out.println("!!! Amount of days : " + String.valueOf(days));
>> 30
Why is this?
You will get those results if daylight savings started in your time zone on 31 March.
Between 1 March and 1 April, you have 30 24-hour days and one 23-hour day, because of the start of daylight savings. If you divide the total number of milliseconds by 24 x 60 x 60 x 1000, then you will get 30 plus 23/24. This gets rounded down to 30.
Time Zone
The correct answer by David Wallace explains that Daylight Saving Time or other anomalies affects the results of your code. Relying on default time zones (or outright ignoring time zones) will get you into this kind of trouble.
Make Span Inclusive-Exclusive
Also, the proper way to define a span of time is to make the beginning inclusive and the ending exclusive. So if you want the month of March, you need to go from first moment of first day to first moment of first day after March (April 1).
For lengthy discussion of this idea, see my other answers such as this one and this one.
Here's a diagram of mine lifted from other answers:
Joda-Time
The java.util.Date/Calendar classes bundled with Java are notoriously troublesome. Avoid them. Use either Joda-Time, or in Java 8, the new java.time.* package (inspired by Joda-Time).
The Joda-Time 2.3 library provides classes dedicated to spans of time: Period, Duration, and Interval. That library also has some handy static utility methods, such as Days.daysBetween.
Joda-Time's DateTime objects do know their own time zone, unlike java.util.Date/Calendar which seem to have a time zone but do not.
// Specify a timezone rather than rely on default.
DateTimeZone timeZone = DateTimeZone.forID( "Europe/Paris" );
DateTime marchFirst = new DateTime( 2013, DateTimeConstants.MARCH, 1, 0, 0, 0, timeZone );
DateTime aprilFirst = new DateTime( 2013, DateTimeConstants.APRIL, 1, 0, 0, 0, timeZone );
int days = Days.daysBetween( marchFirst, aprilFirst).getDays();
Dump to console…
System.out.println( "marchFirst: " + marchFirst );
System.out.println( "aprilFirst: " + aprilFirst ); // Note the change in time zone offset in the output.
System.out.println( "days: " + days );
When run, notice:
The correct answer: 31
The difference in time zone offset because of Daylight Saving Time in France.
marchFirst: 2013-03-01T00:00:00.000+01:00
aprilFirst: 2013-04-01T00:00:00.000+02:00
days: 31
When I run this version of your code here in United States west coast time zone:
java.util.Calendar cal = java.util.Calendar.getInstance();
cal.clear();
cal.set( 2013, java.util.Calendar.MARCH, 1 );
java.util.Date start = cal.getTime();
cal.set( 2013, java.util.Calendar.APRIL, 1 );
java.util.Date end = cal.getTime();
long days = java.util.concurrent.TimeUnit.MILLISECONDS.toDays( end.getTime() - start.getTime() );
System.out.println( "!!! Amount of days : " + String.valueOf( days ) );
cal.set( 2013, java.util.Calendar.MARCH, 1 );
start = cal.getTime();
cal.set( 2013, java.util.Calendar.MARCH, 31 );
end = cal.getTime();
days = java.util.concurrent.TimeUnit.MILLISECONDS.toDays( end.getTime() - start.getTime() );
System.out.println( "!!! Amount of days : " + String.valueOf( days ) );
I get:
!!! Amount of days : 30
!!! Amount of days : 29
For explanation, see comment by David Wallace on this answer.
Daylight Saving Time (United States) 2013 began at 2:00 AM on Sunday, March 10.
I executed same code in my system it is giving me output as :
!!! Amount of days : 31
please check your code again.

Java.util.Calendar - milliseconds since Jan 1, 1970

Program followed by output. Someone please explain to me why 10,000,000 milliseconds from Jan 1, 1970 is November 31, 1969. Well, someone please explain what's wrong with my assumption that the first test should produce a time 10,000,000 milliseconds from Jan 1, 1970. Numbers smaller than 10,000,000 produce the same result.
public static void main(String[] args) {
String x = "10000000";
long l = new Long(x).longValue();
System.out.println("Long value: " + l);
Calendar c = new GregorianCalendar();
c.setTimeInMillis(l);
System.out.println("Calendar time in Millis: " + c.getTimeInMillis());
String origDate = c.get(Calendar.YEAR) + "-" + c.get(Calendar.MONTH) + "-" + c.get(Calendar.DAY_OF_MONTH);
System.out.println("Date in YYYY-MM-DD format: " + origDate);
x = "1000000000000";
l = new Long(x).longValue();
System.out.println("\nLong value: " + l);
c.setTimeInMillis(l);
System.out.println("Calendar time in Millis: " + c.getTimeInMillis());
origDate = c.get(Calendar.YEAR) + "-" + c.get(Calendar.MONTH) + "-" + c.get(Calendar.DAY_OF_MONTH);
System.out.println("Date in YYYY-MM-DD format: " + origDate);
}
Long value: 10000000
Calendar time in Millis: 10000000
Date in YYYY-MM-DD format: 1969-11-31
Long value: 1000000000000
Calendar time in Millis: 1000000000000
Date in YYYY-MM-DD format: 2001-8-8
The dates you print from Calendar are local to your timezone, whereas the epoch is defined to be midnight of 1970-01-01 in UTC. So if you live in a timezone west of UTC, then your date will show up as 1969-12-31, even though (in UTC) it's still 1970-01-01.
First, c.get(Calendar.MONTH) returns 0 for Jan, 1 for Feb, etc.
Second, use DateFormat to output dates.
Third, your problems are a great example of how awkward Java's Date API is. Use Joda Time API if you can. It will make your life somewhat easier.
Here's a better example of your code, which indicates the timezone:
public static void main(String[] args) {
final DateFormat dateFormat = SimpleDateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL);
long l = 10000000L;
System.out.println("Long value: " + l);
Calendar c = new GregorianCalendar();
c.setTimeInMillis(l);
System.out.println("Date: " + dateFormat.format(c.getTime()));
l = 1000000000000L;
System.out.println("\nLong value: " + l);
c.setTimeInMillis(l);
System.out.println("Date: " + dateFormat.format(c.getTime()));
}
Calendar#setTimeInMillis() sets the calendar's time to the number of milliseconds after Jan 1, 1970 GMT.
Calendar#get() returns the requested field adjusted for the calendar's timezone which, by default, is your machine's local timezone.
This should work as you expect if you specify "GMT" timezone when you construct the calendar:
Calendar c = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
Sadly, java.util.Date and java.util.Calendar were poorly designed leading to this sort of confusion.
Your timezone is most likely lagging behind GMT (e.g., GMT-5), therefore 10,000,000ms from epoch is December 31 1969 in your timezone, but since months are zero-based in java.util.Calendar your Calendar-to-text conversion is flawed and you get 1969-11-31 instead of the expected 1969-12-31.
You can figure out yourself if you change your first c.setTimeInMillis(l); in c.clear();

Categories