Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 315523 - bizarre DATE adjustment during binding for @Temporal(TemporalType.DATE) mapping
Summary: bizarre DATE adjustment during binding for @Temporal(TemporalType.DATE) mapping
Status: CLOSED NOT_ECLIPSE
Alias: None
Product: z_Archived
Classification: Eclipse Foundation
Component: Eclipselink (show other bugs)
Version: unspecified   Edit
Hardware: PC Windows XP
: P2 normal (vote)
Target Milestone: ---   Edit
Assignee: Nobody - feel free to take it CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2010-06-02 21:29 EDT by Ari Meyer CLA
Modified: 2022-06-09 10:31 EDT (History)
6 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Ari Meyer CLA 2010-06-02 21:29:54 EDT
Build Identifier: 2.0.2

Hi,

I'm doing a batch insert with Eclipselink 2.0.2, Oracle JDBC driver ojdbc5.jar 11.1.0.6.0, and running Oracle 10.2.0.4.0 DB.  I've annotated a java.util.Date with @Temporal(TemporalType.DATE):

    @Id
    @Column(name="PERIOD_START")
    @Temporal(TemporalType.DATE)
    private Date periodStart;

The date is part of the PK, and date values have no time component.  For about half of the inserts the binding is correct, but others are strangely bound to the day prior (ALL OF THE "PERIOD_START" DATES SHOULD BE THE FIRST OF THE MONTH):

[EL Fine]: 2010-04-28 16:15:09.406--ClientSession(26440262)--Connection(20153007)--Thread(Thread[main,5,main])--INSERT INTO BUDGET_RSRC_CURVE (PERIOD_START, TASKRSRC_ID, BUDGET_TYPE_ID, TASK_ID, PERIOD_UNITS, DELETE_DATE, AT_COMPLETION_UNITS, PROJ_ID, UPDATE_DATE, CREATE_USER, PERIOD_COST, AT_COMPLETION_COST, DELETE_USER, CREATE_DATE, UPDATE_USER) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
[EL Fine]: 2010-04-28 16:15:09.453--ClientSession(26440262)--Connection(20153007)--Thread(Thread[main,5,main])-- bind => [2004-08-31, 142719, 2, 35899, 1.517642240537477871242799665196798741817474365234375, null, 16.64731756756756198001312441192567348480224609375, 747, null, P2_USER, 2.435444011444863665616367143229581415653228759765625, 26.71486611486485429622916853986680507659912109375, null, 2010-04-28 15:12:53.515, null]
[EL Fine]: 2010-04-28 16:15:09.453--ClientSession(26440262)--Connection(20153007)--Thread(Thread[main,5,main])-- bind => [2009-05-31, 3357792, 2, 3042074, 0, null, 0, 747, null, P2_USER, 0, 0, null, 2010-04-28 15:14:01.515, null]
[EL Fine]: 2010-04-28 16:15:12.921--ClientSession(26440262)--Connection(20153007)--Thread(Thread[main,5,main])-- bind => [2008-01-01, 1118702, 2, 2335339, 0, null, 0, 747, null, P2_USER, 0, 0, null, 2010-04-28 15:12:54.656, null]
[EL Fine]: 2010-04-28 16:15:12.921--ClientSession(26440262)--Connection(20153007)--Thread(Thread[main,5,main])-- bind => [2005-02-01, 1118813, 2, 2335339, 0, null, 0.0481197860962566947851115628509433008730411529541015625, 747, null, P2_USER, 0, 4.81197860962566892339964397251605987548828125, null, 2010-04-28 15:12:57.375, null]
[EL Fine]: 2010-04-28 16:15:12.937--ClientSession(26440262)--Connection(20153007)--Thread(Thread[main,5,main])-- bind => [2007-10-31, 8048557, 2, 9036580, 0, null, 4.47829090909090954397697714739479124546051025390625, 747, null, P2_USER, 0, 447.8290909090909508449840359389781951904296875, null, 2010-04-28 15:14:34.968, null]
[EL Fine]: 2010-04-28 16:15:12.937--ClientSession(26440262)--Connection(20153007)--Thread(Thread[main,5,main])-- bind => [2008-12-01, 1727942, 2, 3042043, 0, null, 0, 747, null, P2_USER, 0, 0, null, 2010-04-28 15:13:31.015, null]
[EL Fine]: 2010-04-28 16:15:12.937--ClientSession(26440262)--Connection(20153007)--Thread(Thread[main,5,main])-- bind => [2008-07-31, 1118715, 2, 2335339, 0, null, 0, 747, null, P2_USER, 0, 0, null, 2010-04-28 15:12:54.796, null]
[EL Fine]: 2010-04-28 16:15:12.937--ClientSession(26440262)--Connection(20153007)--Thread(Thread[main,5,main])-- bind => [2007-10-31, 8922414, 2, 9713611, 0, null, 0, 747, null, P2_USER, 0, 0, null, 2010-04-28 15:14:39.093, null]
[EL Fine]: 2010-04-28 16:15:12.937--ClientSession(26440262)--Connection(20153007)--Thread(Thread[main,5,main])-- bind => [2009-01-01, 1727940, 2, 3042043, 0, null, 0, 747, null, P2_USER, 0, 0, null, 2010-04-28 15:13:29.031, null]
[EL Fine]: 2010-04-28 16:15:12.937--ClientSession(26440262)--Connection(20153007)--Thread(Thread[main,5,main])-- bind => [2006-03-01, 1118893, 2, 2334983, 14.838689732943915799978640279732644557952880859375, null, 13.87078839178081324234881321899592876434326171875, 747, null, P2_USER, 1558.062421959111134128761477768421173095703125, 1456.43278113698534070863388478755950927734375, null, 2010-04-28 15:13:13.64, null]
[EL Fine]: 2010-04-28 16:15:12.953--ClientSession(26440262)--Connection(20153007)--Thread(Thread[main,5,main])-- bind => [2004-12-01, 3260300, 2, 2335047, 0, null, 0, 747, null, P2_USER, 0, 0, null, 2010-04-28 15:13:48.093, null]
[EL Fine]: 2010-04-28 16:15:12.953--ClientSession(26440262)--Connection(20153007)--Thread(Thread[main,5,main])-- bind => [2005-02-01, 6659, 2, 35978, 0, null, 0, 747, null, P2_USER, 0, 0, null, 2010-04-28 15:12:47.843, null]
[EL Fine]: 2010-04-28 16:15:12.953--ClientSession(26440262)--Connection(20153007)--Thread(Thread[main,5,main])-- bind => [2007-09-30, 1118717, 2, 2335339, 0, null, 0, 747, null, P2_USER, 0, 0, null, 2010-04-28 15:12:55.078, null]
[EL Fine]: 2010-04-28 16:15:12.953--ClientSession(26440262)--Connection(20153007)--Thread(Thread[main,5,main])-- bind => [2007-09-30, 1118838, 2, 2334984, 0, null, 0, 747, null, P2_USER, 0, 0, null, 2010-04-28 15:13:02.765, null]
[EL Fine]: 2010-04-28 16:15:12.953--ClientSession(26440262)--Connection(20153007)--Thread(Thread[main,5,main])-- bind => [2008-06-30, 1118814, 2, 2335339, 0, null, 0, 747, null, P2_USER, 0, 0, null, 2010-04-28 15:12:57.625, null]
[EL Fine]: 2010-04-28 16:15:12.953--ClientSession(26440262)--Connection(20153007)--Thread(Thread[main,5,main])-- bind => [2009-02-01, 8166315, 2, 2334983, 0.49032192161031940003113049897365272045135498046875, null, 0, 747, null, P2_USER, 49.0321921610319435558267286978662014007568359375, 0, null, 2010-04-28 15:14:37.031, null]
....
....


However, when I changed the mapping to use TemporalType.TIMESTAMP, it binds correctly always:

[EL Fine]: 2010-04-28 17:11:03.506--ClientSession(16843259)--Connection(19478135)--Thread(Thread[main,5,main])--INSERT INTO BUDGET_RSRC_CURVE (PERIOD_START, TASKRSRC_ID, BUDGET_TYPE_ID, TASK_ID, PERIOD_UNITS, DELETE_DATE, AT_COMPLETION_UNITS, PROJ_ID, UPDATE_DATE, CREATE_USER, PERIOD_COST, AT_COMPLETION_COST, DELETE_USER, CREATE_DATE, UPDATE_USER) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
[EL Fine]: 2010-04-28 17:11:05.069--ClientSession(16843259)--Connection(19478135)--Thread(Thread[main,5,main])-- bind => [2008-01-01 00:00:00.0, 6192516, 2, 7509227, 4.6675985656858554051495957537554204463958740234375, null, 0, 747, null, P2_USER, 583.449820710731955841765739023685455322265625, 0, null, 2010-04-28 16:10:23.581, null]
[EL Fine]: 2010-04-28 17:11:05.069--ClientSession(16843259)--Connection(19478135)--Thread(Thread[main,5,main])-- bind => [2006-01-01 00:00:00.0, 3260294, 2, 2335047, 0, null, 0, 747, null, P2_USER, 0, 0, null, 2010-04-28 16:07:30.036, null]
[EL Fine]: 2010-04-28 17:11:05.069--ClientSession(16843259)--Connection(19478135)--Thread(Thread[main,5,main])-- bind => [2007-02-01 00:00:00.0, 4234621, 2, 2334985, 0.12251197917296341444171048351563513278961181640625, null, 0, 747, null, P2_USER, 12.251197917296341444171048351563513278961181640625, 0, null, 2010-04-28 16:09:28.921, null]
[EL Fine]: 2010-04-28 17:11:05.069--ClientSession(16843259)--Connection(19478135)--Thread(Thread[main,5,main])-- bind => [2010-10-01 00:00:00.0, 8166315, 2, 2334983, 0.5161283385371806531338734203018248081207275390625, null, 0, 747, null, P2_USER, 51.61283385371806531338734203018248081207275390625, 0, null, 2010-04-28 16:10:50.271, null]
[EL Fine]: 2010-04-28 17:11:05.069--ClientSession(16843259)--Connection(19478135)--Thread(Thread[main,5,main])-- bind => [2007-06-01 00:00:00.0, 1118717, 2, 2335339, 0, null, 0, 747, null, P2_USER, 0, 0, null, 2010-04-28 16:05:36.171, null]
[EL Fine]: 2010-04-28 17:11:05.069--ClientSession(16843259)--Connection(19478135)--Thread(Thread[main,5,main])-- bind => [2005-09-01 00:00:00.0, 6652, 2, 35900, 0, null, 0, 747, null, P2_USER, 0, 0, null, 2010-04-28 16:05:09.593, null]
[EL Fine]: 2010-04-28 17:11:05.069--ClientSession(16843259)--Connection(19478135)--Thread(Thread[main,5,main])-- bind => [2005-01-01 00:00:00.0, 1118644, 2, 2335336, 0.002591999092800317990026304215689378906972706317901611328125, null, 0, 747, null, P2_USER, 15.55199455680190823159136925823986530303955078125, 0, null, 2010-04-28 16:05:25.828, null]
[EL Fine]: 2010-04-28 17:11:05.069--ClientSession(16843259)--Connection(19478135)--Thread(Thread[main,5,main])-- bind => [2007-05-01 00:00:00.0, 1118815, 2, 2335339, 0, null, 0, 747, null, P2_USER, 0, 0, null, 2010-04-28 16:05:40.75, null]
[EL Fine]: 2010-04-28 17:11:05.069--ClientSession(16843259)--Connection(19478135)--Thread(Thread[main,5,main])-- bind => [2007-04-01 00:00:00.0, 3260301, 2, 2335047, 0, null, 0, 747, null, P2_USER, 0, 0, null, 2010-04-28 16:07:43.881, null]
[EL Fine]: 2010-04-28 17:11:05.069--ClientSession(16843259)--Connection(19478135)--Thread(Thread[main,5,main])-- bind => [2007-11-01 00:00:00.0, 3447331, 2, 4886278, 0, null, 0, 747, null, P2_USER, 0, 0, null, 2010-04-28 16:09:12.279, null]
[EL Fine]: 2010-04-28 17:11:05.069--ClientSession(16843259)--Connection(19478135)--Thread(Thread[main,5,main])-- bind => [2007-12-01 00:00:00.0, 5776433, 2, 2335985, 0, null, 0, 747, null, P2_USER, 0, 0, null, 2010-04-28 16:09:50.61, null]
[EL Fine]: 2010-04-28 17:11:05.069--ClientSession(16843259)--Connection(19478135)--Thread(Thread[main,5,main])-- bind => [2007-07-01 00:00:00.0, 6642, 2, 35899, 0, null, 0, 747, null, P2_USER, 0, 0, null, 2010-04-28 16:05:06.453, null]
[EL Fine]: 2010-04-28 17:11:05.069--ClientSession(16843259)--Connection(19478135)--Thread(Thread[main,5,main])-- bind => [2005-10-01 00:00:00.0, 2677691, 2, 2335033, 0, null, 0, 747, null, P2_USER, 0, 0, null, 2010-04-28 16:06:53.534, null]
[EL Fine]: 2010-04-28 17:11:05.084--ClientSession(16843259)--Connection(19478135)--Thread(Thread[main,5,main])-- bind => [2005-11-01 00:00:00.0, 3260294, 2, 2335047, 0, null, 0, 747, null, P2_USER, 0, 0, null, 2010-04-28 16:07:30.036, null]
[EL Fine]: 2010-04-28 17:11:05.084--ClientSession(16843259)--Connection(19478135)--Thread(Thread[main,5,main])-- bind => [2006-05-01 00:00:00.0, 1118847, 2, 2334984, 0, null, 0, 747, null, P2_USER, 0, 0, null, 2010-04-28 16:06:03.546, null]
[EL Fine]: 2010-04-28 17:11:05.084--ClientSession(16843259)--Connection(19478135)--Thread(Thread[main,5,main])-- bind => [2008-02-01 00:00:00.0, 1814180, 2, 3129496, 0, null, 0, 747, null, P2_USER, 0, 0, null, 2010-04-28 16:06:43.142, null]


I thought it might be a problem with the Oracle JDBC driver, but I tried with the latest ojdbc6.jar and got the same results.

Note that I've also set the following properties, as recommended for batch inserts according to http://forums.oracle.com/forums/thread.jspa?messageID=3829565 (though I got the same results without eclipselink.jdbc.batch-writing set):

<property name="eclipselink.jdbc.batch-writing" value="JDBC"/>
<property name="eclipselink.cache.shared.default" value="false"/>


Chris Delahunt replied to my corresponding forum question with:
Which JDK are you using?  I noticed a strange issue with calendar conversions when using an early JDK 1.6.0 build, causing problems inserting dates since it would shift milliseconds by one or two.  They went away when I switched to using jdk 1.6.0_17.  You might not be getting the problem when you use a timestamp temporal type if you are using a timestamp in your object since no conversion is neccessary.  Try using a timestamp temporal type on a calendar so you can see if the milliseconds are off or not.

REGARDING JDK VERSION: I'm running the latest 1.6.0_20, so unless there's been a regression, the JDK version is not the issue.

REGARDING USING A Calendar INSTEAD OF Date: Unfortunately, using Calendar with @Temporal(TemporalType.DATE) gives the same inconsistencies as java.util.Date does.

I also ran the test using Calendar with @Temporal(TemporalType.TIMESTAMP), and I don't see any milliseconds off -- everything looks fine.  On @PrePersist, the logs show data like:

periodStart=java.util.GregorianCalendar[time=1214899200000,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="GMT-08:00",offset=-28800000,dstSavings=0,useDaylight=false,transitions=0,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2008,MONTH=6,WEEK_OF_YEAR=27,WEEK_OF_MONTH=1,DAY_OF_MONTH=1,DAY_OF_YEAR=183,DAY_OF_WEEK=3,DAY_OF_WEEK_IN_MONTH=1,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=0,SECOND=0,MILLISECOND=0,ZONE_OFFSET=-28800000,DST_OFFSET=0]

all records show: MILLISECOND=0

The output of the binding for Calendar with @Temporal(TemporalType.TIMESTAMP) looks fine as well, with all the time components showing 00:00:00.0:

[EL Fine]: 2010-05-04 18:45:22.328--ClientSession(10130611)--Connection(9995652)--Thread(Thread[main,5,main])--INSERT INTO BUDGET_RSRC_CURVE (PERIOD_START, TASKRSRC_ID, BUDGET_TYPE_ID, TASK_ID, PERIOD_UNITS, AT_COMPLETION_UNITS, PROJ_ID, UPDATE_DATE, CREATE_USER, PERIOD_COST, AT_COMPLETION_COST, CREATE_DATE, UPDATE_USER) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
[EL Fine]: 2010-05-04 18:45:22.343--ClientSession(10130611)--Connection(9995652)--Thread(Thread[main,5,main])-- bind => [2009-07-01 00:00:00.0, 1727948, 2, 3042045, 0, 0, 747, null, P2_USER, 0, 0, 2010-05-04 17:45:09.046, null]
[EL Fine]: 2010-05-04 18:45:22.343--ClientSession(10130611)--Connection(9995652)--Thread(Thread[main,5,main])-- bind => [2008-06-01 00:00:00.0, 1727947, 2, 3042045, 0, 0, 747, null, P2_USER, 0, 0, 2010-05-04 17:45:08.984, null]
[EL Fine]: 2010-05-04 18:45:22.343--ClientSession(10130611)--Connection(9995652)--Thread(Thread[main,5,main])-- bind => [2008-06-01 00:00:00.0, 1727936, 2, 3042043, 0, 0, 747, null, P2_USER, 0, 0, 2010-05-04 17:45:07.343, null]
[EL Fine]: 2010-05-04 18:45:22.375--ClientSession(10130611)--Connection(9995652)--Thread(Thread[main,5,main])-- bind => [2007-03-01 00:00:00.0, 3758174, 2, 2335032, 0, 0, 747, null, P2_USER, 0, 0, 2010-05-04 17:45:17.765, null]
[EL Fine]: 2010-05-04 18:45:22.375--ClientSession(10130611)--Connection(9995652)--Thread(Thread[main,5,main])-- bind => [2006-10-01 00:00:00.0, 5776433, 2, 2335985, 0, 0, 747, null, P2_USER, 0, 0, 2010-05-04 17:45:19.296, null]
[EL Fine]: 2010-05-04 18:45:22.375--ClientSession(10130611)--Connection(9995652)--Thread(Thread[main,5,main])-- bind => [2004-11-01 00:00:00.0, 1118653, 2, 2335337, 0.0054400027200013599537609110257108113728463649749755859375, 0, 747, null, P2_USER, 32.6400163200081578906974755227565765380859375, 0, 2010-05-04 17:45:03.25, null]

So bottom line, using TemporalType.TIMESTAMP (no rounding performed) seems to be consistently safe, while TemporalType.DATE is not.  When Date/Calendar values have no time component and a mapping of TemporalType.DATE is used, they are frequently (but not always) rounded down to the previous date.  This inconsistent behavior is particularly troubling.

Workaround: stick to TemporalType.TIMESTAMP, even when the values have no time component.

Thanks,
Ari


Reproducible: Sometimes

Steps to Reproduce:
1. Create an entity with a Date or Calendar field, and map to a DATE column on Oracle 10.2.0.4.0 DB, e.g.:

    @Column(name="TEST_DATE")
    @Temporal(TemporalType.DATE)
    private Date testDate;

2.  Create entities with testDate having a value with no time component (we used date values where they were always the first of the month (ex: 01-JUN-2010).

3.  Persist the entities using a Oracle JDBC driver (I have tested with ojdbc5 and ojdbc6).  Some, but not all, of the TEST_DATE values will be found to have rounded down to the prior day (ex: if the original Date value was 01-JUN-2010, the value in the DB will be 31-MAY-2010).

4.  Try the same test with the mapping changed to use TemporalType.TIMESTAMP, and you will find all the values, without exception, are persisted correctly as the date specified, with no rounding.
Comment 1 Tom Ware CLA 2010-06-14 14:27:38 EDT
Requires triage for 2.1.1
Comment 2 Peter Krogh CLA 2010-07-26 08:54:59 EDT
Defferring to 2.2.0 for 2.1.1 closedown.
Comment 3 Andrei Ilitchev CLA 2010-11-10 17:24:24 EST
So far I could not reproduce the issue.

Convertion from util.Date to sql.Date is performed before executing sql, so it's not a driver problem.

Could you please run the following simple test and see if it can reproduce the problem and if so debug into sqlDateFromUtilDate to see what's going on:
            java.util.Date utilDate = ...
            System.out.println("utilDate = " + utilDate);
            java.sql.Date sqlDate = org.eclipse.persistence.internal.helper.Helper.sqlDateFromUtilDate(utilDate);
            System.out.println("sqlDate = " + sqlDate);
Comment 4 Andrei Ilitchev CLA 2010-11-16 14:37:29 EST
I can't reproduce the issue.
Comment 5 Ari Meyer CLA 2010-11-23 05:42:43 EST
Hi Andrei,

Apologies for the delay -- I don't check this email acct often, and I've changed the acct now.  I ran my test (setting the field to @Temporal(TemporalType.DATE)) with your debugging statements (printing both before and after em.persist() to see if there was any diff), and got the same incorrect results as before.  The date values stored in Oracle were, likewise, incorrect.  Code:

java.util.Date utilDate = aBudgetRsrcCurve.getPeriodStart();
System.out.println("utilDate (BEFORE PERSIST) = " + utilDate);
java.sql.Date sqlDate = org.eclipse.persistence.internal.helper.Helper.sqlDateFromUtilDate(utilDate);
System.out.println("sqlDate (BEFORE PERSIST) = " + sqlDate);

_entityManager.persist(aBudgetRsrcCurve);
//	_logger.debug("BudgetRsrcCurve = " + aBudgetRsrcCurve);

utilDate = aBudgetRsrcCurve.getPeriodStart();
System.out.println("utilDate (AFTER PERSIST) = " + utilDate);
sqlDate = org.eclipse.persistence.internal.helper.Helper.sqlDateFromUtilDate(utilDate);
System.out.println("sqlDate (AFTER PERSIST) = " + sqlDate);


sample output:

utilDate (BEFORE PERSIST) = Wed Oct 01 00:00:00 GMT-08:00 2003
sqlDate (BEFORE PERSIST) = 2003-09-30
[2010-11-23 02:11:41,515] DEBUG mil.army.usace.p2.util.Utils user ID = u4iesabm
[2010-11-23 02:11:41,530] DEBUG mil.army.usace.p2.entity.AuditListener Auditing model on prepersist: mil.army.usace.p2.entity.BudgetRsrcCurve@10a6723[
  taskrsrcId=11362
  periodStart=Wed Oct 01 00:00:00 GMT-08:00 2003
  budgetType=DRAFT
  projectId=748
  taskId=105758
  periodUnits=0
  periodCost=0
  atCompletionUnits=0
  atCompletionCost=0
  mil.army.usace.p2.entity.BudgetRsrcCurve@10a6723[
  createUser=u4iesabm
  createDate=Tue Nov 23 02:11:41 GMT-08:00 2010
  updateUser=<null>
  updateDate=<null>
]
]
utilDate (AFTER PERSIST) = Wed Oct 01 00:00:00 GMT-08:00 2003
sqlDate (AFTER PERSIST) = 2003-09-30


When I set the field back to @Temporal(TemporalType.TIMESTAMP), the output was exactly the same:

utilDate (BEFORE PERSIST) = Wed Oct 01 00:00:00 GMT-08:00 2003
sqlDate (BEFORE PERSIST) = 2003-09-30
[2010-11-23 02:29:15,513] DEBUG mil.army.usace.p2.util.Utils user ID = u4iesabm
[2010-11-23 02:29:15,528] DEBUG mil.army.usace.p2.entity.AuditListener Auditing model on prepersist: mil.army.usace.p2.entity.BudgetRsrcCurve@1a66c87[
  taskrsrcId=11362
  periodStart=Wed Oct 01 00:00:00 GMT-08:00 2003
  budgetType=DRAFT
  projectId=748
  taskId=105758
  periodUnits=0
  periodCost=0
  atCompletionUnits=0
  atCompletionCost=0
  mil.army.usace.p2.entity.BudgetRsrcCurve@1a66c87[
  createUser=u4iesabm
  createDate=Tue Nov 23 02:29:15 GMT-08:00 2010
  updateUser=<null>
  updateDate=<null>
]
]
utilDate (AFTER PERSIST) = Wed Oct 01 00:00:00 GMT-08:00 2003
sqlDate (AFTER PERSIST) = 2003-09-30


... but the date values stored in Oracle were correct this time -- all first of the month and no rounding down.  It appears that Helper.sqlDateFromUtilDate(utilDate) is rounding the date down consistently, at least in my tests.

Thanks,
Ari
Comment 6 Ari Meyer CLA 2010-11-23 06:07:40 EST
Looking deeper, it appears to be an issue of the timezone being converted from GMT to local.  Should the internally used Helper.dateFromCalendar(Calendar calendar) be converting it from its original timezone?  I can understand arguments both for and against this.  Unfortunately, in my case, my source date was not really meant to be Wed Oct 01 00:00:00 GMT-08:00 2003, but just Oct 1, 2003, in whatever the local timezone happens to be.

Given all this, do I really need to convert such dates to my local timezone and adjust them accordingly before setting them the JPA entity in order to avoid seeing these apparent inconsistencies?

Thanks,
Ari
Comment 7 Andrei Ilitchev CLA 2010-11-23 13:35:39 EST
I believe it's not a bug.

Helper.sqlDateFromUtilDate is method used to convert util.Date to sql.Date. The result sql.Date should not have hours/mins/secs (otherwise two sql.Dates corresponding to the same date won't be equal - comparison is done based on longTime). So the only way to get rid of hours/mins/secs is to use Calendar in default time zone (no time zone could be extracted from util.Date - the one printed out is the default time zone).

You should be ok if you either:
always create and process util.Dates in default time zone;
or:
always set default time zone to the same zone - no matter where the app runs.

If that is not acceptible then I would use Temporal.TIMESTAMP (there is no conversion (created Timestamp has the same long time as the source util.Date).

Finally you can define your own converter:
   @Id
    @Column(name="PERIOD_START")
    @Convert("DateConverter")
    @Converter(
        name="DateConverter",
        converterClass=myPackage.MyDateConverter.class
    )
    private Date periodStart;

where converter class looks like:
public class MyDateConverter implements org.eclipse.persistence.mappings.converters.Converter {
    public MyDateConverter() {}
    
    public Object convertDataValueToObjectValue(Object dataValue, Session session) {
        if (dataValue != null) {
	  return new java.util.Date(((java.sql.Date)dataValue).getTime());
        }        
        return null;
    }
    
    public Object convertObjectValueToDataValue(Object objectValue, Session session) {
        if (objectValue != null) {
	  return new java.sql.Date(((java.util.Date)dataValue).getTime());
        }        
        return null;
    }
Comment 8 Ari Meyer CLA 2010-11-26 21:15:41 EST
Thanks, Andrei -- that's what I figured.  I'll just leave it as a TIMESTAMP.  I'm just glad the mystery has been resolved.  Feel free to close this.

Best,
Ari
Comment 9 Ari Meyer CLA 2010-11-26 21:51:22 EST
Minor clarification: the date that I was persisting was midnight in the local timezone, which was then converted to GMT (default timezone) before being truncated, not the other way around as I first stated.  In any case, all good now -- thanks!
Comment 10 Ari Meyer CLA 2010-11-27 05:36:44 EST
Sorry for belaboring this, but I revisited the results, and now I'm more confused. ;-)  Just to be certain I wasn't misreading the output, I added a line to print utilDate.toGMTString():

utilDate (BEFORE PERSIST) = Wed Oct 01 00:00:00 GMT-08:00 2003
utilDate GMT (BEFORE PERSIST) = 1 Oct 2003 08:00:00 GMT
sqlDate (BEFORE PERSIST) = 2003-09-30

So sure enough, the original utilDate was already in my local ("default") timezone, PST.  Converting to GMT could only adjust the time forward (to 8:00AM GMT), not backward.  Why, then, is any conversion made at all when truncating it to a java.sql.Date, since the timezone should not have changed?  And how could any conversion have moved the time backward so that the resulting sqlDate is 09-30 instead of 10-01?  Please explain.

Thanks,
Ari
Comment 11 Chris Delahunt CLA 2010-11-29 12:08:08 EST
Hello Ari,

The Date toString calls seem to indicate that the Calendar's being used are in the GMT-08:00, while the Helper.sqlDateFromUtilDate return value indicates that it is using a Calendar without a timezone set (ie 1 Oct 2003 GMT converted into PST is 2003-09-30).  

To confirm, can you print off:
  System.out.println("calendar in helper: "+org.eclipse.persistence.internal.helper.Helper.allocateCalendar());
        System.out.println("timezone in helper: "+org.eclipse.persistence.internal.helper.Helper.getDefaultTimeZone());
  System.out.println(" JVM default Calendar is: "+java.util.Calendar.getInstance());
  System.out.println(" JVM default timezone is: "+TimeZone.getDefault());

Are you changing the default Timezone anywhere in your application after the EclipseLink classes might have been loaded? Ie calling TimeZone.setDefault?
Comment 12 Ari Meyer CLA 2010-12-01 03:33:21 EST
Hi Chris,

I'm definitely not changing the default timezone explicitly, and PST is what my timezone is set to on my PC.  Just to confirm, looking in the registry at HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\TimeZoneInformation (where the JVM apparently reads it from), it says:
StandardName = Pacific Standard Time

Here's the additional output:
calendar in helper: java.util.GregorianCalendar[time=1291185381903,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2010,MONTH=10,WEEK_OF_YEAR=49,WEEK_OF_MONTH=5,DAY_OF_MONTH=30,DAY_OF_YEAR=334,DAY_OF_WEEK=3,DAY_OF_WEEK_IN_MONTH=5,AM_PM=1,HOUR=10,HOUR_OF_DAY=22,MINUTE=36,SECOND=21,MILLISECOND=903,ZONE_OFFSET=-28800000,DST_OFFSET=0]
timezone in helper: sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]]
 JVM default Calendar is: java.util.GregorianCalendar[time=1291185571684,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="GMT-08:00",offset=-28800000,dstSavings=0,useDaylight=false,transitions=0,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2010,MONTH=10,WEEK_OF_YEAR=49,WEEK_OF_MONTH=5,DAY_OF_MONTH=30,DAY_OF_YEAR=334,DAY_OF_WEEK=3,DAY_OF_WEEK_IN_MONTH=5,AM_PM=1,HOUR=10,HOUR_OF_DAY=22,MINUTE=39,SECOND=31,MILLISECOND=684,ZONE_OFFSET=-28800000,DST_OFFSET=0]
 JVM default timezone is: sun.util.calendar.ZoneInfo[id="GMT-08:00",offset=-28800000,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]

utilDate = Wed Oct 01 00:00:00 GMT-08:00 2003
utilDate GMT = 1 Oct 2003 08:00:00 GMT
sqlDate = 2003-09-30


The other strange thing is that approx. 2/3 of the sqlDate conversions are correct, like below:

calendar in helper: java.util.GregorianCalendar[time=1291185397809,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2010,MONTH=10,WEEK_OF_YEAR=49,WEEK_OF_MONTH=5,DAY_OF_MONTH=30,DAY_OF_YEAR=334,DAY_OF_WEEK=3,DAY_OF_WEEK_IN_MONTH=5,AM_PM=1,HOUR=10,HOUR_OF_DAY=22,MINUTE=36,SECOND=37,MILLISECOND=809,ZONE_OFFSET=-28800000,DST_OFFSET=0]
timezone in helper: sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]]
 JVM default Calendar is: java.util.GregorianCalendar[time=1291185571715,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="GMT-08:00",offset=-28800000,dstSavings=0,useDaylight=false,transitions=0,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2010,MONTH=10,WEEK_OF_YEAR=49,WEEK_OF_MONTH=5,DAY_OF_MONTH=30,DAY_OF_YEAR=334,DAY_OF_WEEK=3,DAY_OF_WEEK_IN_MONTH=5,AM_PM=1,HOUR=10,HOUR_OF_DAY=22,MINUTE=39,SECOND=31,MILLISECOND=715,ZONE_OFFSET=-28800000,DST_OFFSET=0]
 JVM default timezone is: sun.util.calendar.ZoneInfo[id="GMT-08:00",offset=-28800000,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]
 
utilDate = Sat Nov 01 00:00:00 GMT-08:00 2003
utilDate GMT = 1 Nov 2003 08:00:00 GMT
sqlDate = 2003-11-01

I don't see anything obviously different between the raw values for utilDate for Oct. and Nov. that would round one of them down and leave the other at the first of the month.  However, I did notice that 'timezone in helper' has "dstSavings=3600000,useDaylight=true", while 'JVM default timezone' shows "dstSavings=0,useDaylight=false", but this is the same for both cases.  In any case, the DST attributes for 'timezone in helper' are incorrect.

Do you see anything here that could be causing these inconsistent results?

Thanks,
Ari
Comment 13 Chris Delahunt CLA 2010-12-01 09:43:14 EST
The difference between the two timezones is that one is using daylight savings while the other is not, which explains why it will work for most times and only have problems on 2 days a year, though only going back an hour will make a difference to dates.  

EclipseLink Calendar and TimeZone is obtained from the JVM statically when the class gets loaded, so at some point after start up the JVM's timezone is being changed.  Because EclipseLink cached the timezone/calendar, this change is not reflected into EclipseLink conversions.  I would like to close this bug with the original status and open a new one to get api added to allow changing the Timezone/Calendars to use for conversions since there doesn't seem to be a way to do that now.

As to why the JVM's timezone is changing - I cannot say other than it is.  EclipseLink calls 
protected static TimeZone defaultTimeZone = TimeZone.getDefault();  so at some point the default is being changed to the GMT-8 instead of PST.  Possibly a webserver change is overriding the system default?
Comment 14 Ari Meyer CLA 2010-12-02 07:44:37 EST
Hi Chris,

I'm running these tests locally from the command line, so I'm not sure what is causing the default timezone to change, but I see that you're right -- somehow it is changing.  I am now printing out the info at the beginning:
calendar in helper: java.util.GregorianCalendar[time=1291292678738,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2010,MONTH=11,WEEK_OF_YEAR=49,WEEK_OF_MONTH=1,DAY_OF_MONTH=2,DAY_OF_YEAR=336,DAY_OF_WEEK=5,DAY_OF_WEEK_IN_MONTH=1,AM_PM=0,HOUR=4,HOUR_OF_DAY=4,MINUTE=24,SECOND=38,MILLISECOND=738,ZONE_OFFSET=-28800000,DST_OFFSET=0]
timezone in helper: sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]]
 JVM default Calendar is: java.util.GregorianCalendar[time=1291292698551,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2010,MONTH=11,WEEK_OF_YEAR=49,WEEK_OF_MONTH=1,DAY_OF_MONTH=2,DAY_OF_YEAR=336,DAY_OF_WEEK=5,DAY_OF_WEEK_IN_MONTH=1,AM_PM=0,HOUR=4,HOUR_OF_DAY=4,MINUTE=24,SECOND=58,MILLISECOND=551,ZONE_OFFSET=-28800000,DST_OFFSET=0]
 JVM default timezone is: sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]]

But after the first call to the Primavera (project mgmt software) API, the test output changes:
calendar in helper: java.util.GregorianCalendar[time=1291292678738,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2010,MONTH=11,WEEK_OF_YEAR=49,WEEK_OF_MONTH=1,DAY_OF_MONTH=2,DAY_OF_YEAR=336,DAY_OF_WEEK=5,DAY_OF_WEEK_IN_MONTH=1,AM_PM=0,HOUR=4,HOUR_OF_DAY=4,MINUTE=24,SECOND=38,MILLISECOND=738,ZONE_OFFSET=-28800000,DST_OFFSET=0]
timezone in helper: sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]]
 JVM default Calendar is: java.util.GregorianCalendar[time=1291292869707,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="GMT-08:00",offset=-28800000,dstSavings=0,useDaylight=false,transitions=0,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2010,MONTH=11,WEEK_OF_YEAR=49,WEEK_OF_MONTH=1,DAY_OF_MONTH=2,DAY_OF_YEAR=336,DAY_OF_WEEK=5,DAY_OF_WEEK_IN_MONTH=1,AM_PM=0,HOUR=4,HOUR_OF_DAY=4,MINUTE=27,SECOND=49,MILLISECOND=707,ZONE_OFFSET=-28800000,DST_OFFSET=0]
 JVM default timezone is: sun.util.calendar.ZoneInfo[id="GMT-08:00",offset=-28800000,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]

I guess Primavera must be changing the default timezone internally, but I can't see their source code to verify this.  In any case, thanks for solving the puzzle.  I'll just leave the mapping as TIMESTAMP for now.  You can close this bug.

Thanks,
Ari
Comment 15 Eclipse Webmaster CLA 2022-06-09 10:31:47 EDT
The Eclipselink project has moved to Github: https://github.com/eclipse-ee4j/eclipselink