JDK-4482478 : SimpleDateFormat.format() mishandles larger negative times
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.text
  • Affected Version: 1.3.0
  • Priority: P5
  • Status: Closed
  • Resolution: Not an Issue
  • OS: solaris_2.6
  • CPU: sparc
  • Submitted: 2001-07-20
  • Updated: 2001-07-27
  • Resolved: 2001-07-27
Related Reports
Relates :  
Relates :  
Relates :  
Description
The following code shows that when negative time values get large, Date.toString() starts reporting (at least) the years incorrectly.  Since Date.toString() is written using SimpleDateFormat(), I presume the bug is there.

class t {
    public static void main(String[] args) {
        for (long val = -1; val < 0; val *= 2) {
            System.out.println("0x" + Long.toHexString(val) + ": " +
                val + ":\t" + new java.util.Date(val));
        }
    }
}

Given the output below, it clearly starts to fail at time 0xffff000000000000 when the year goes BC.  It looks (to me) like the sign is just not being put into the year output, but that's just a guess.

0xffffffffffffffff: -1: Wed Dec 31 18:59:59 EST 1969
0xfffffffffffffffe: -2: Wed Dec 31 18:59:59 EST 1969
0xfffffffffffffffc: -4: Wed Dec 31 18:59:59 EST 1969
0xfffffffffffffff8: -8: Wed Dec 31 18:59:59 EST 1969
0xfffffffffffffff0: -16:        Wed Dec 31 18:59:59 EST 1969
0xffffffffffffffe0: -32:        Wed Dec 31 18:59:59 EST 1969
0xffffffffffffffc0: -64:        Wed Dec 31 18:59:59 EST 1969
0xffffffffffffff80: -128:       Wed Dec 31 18:59:59 EST 1969
0xffffffffffffff00: -256:       Wed Dec 31 18:59:59 EST 1969
0xfffffffffffffe00: -512:       Wed Dec 31 18:59:59 EST 1969
0xfffffffffffffc00: -1024:      Wed Dec 31 18:59:58 EST 1969
0xfffffffffffff800: -2048:      Wed Dec 31 18:59:57 EST 1969
0xfffffffffffff000: -4096:      Wed Dec 31 18:59:55 EST 1969
0xffffffffffffe000: -8192:      Wed Dec 31 18:59:51 EST 1969
0xffffffffffffc000: -16384:     Wed Dec 31 18:59:43 EST 1969
0xffffffffffff8000: -32768:     Wed Dec 31 18:59:27 EST 1969
0xffffffffffff0000: -65536:     Wed Dec 31 18:58:54 EST 1969
0xfffffffffffe0000: -131072:    Wed Dec 31 18:57:48 EST 1969
0xfffffffffffc0000: -262144:    Wed Dec 31 18:55:37 EST 1969
0xfffffffffff80000: -524288:    Wed Dec 31 18:51:15 EST 1969
0xfffffffffff00000: -1048576:   Wed Dec 31 18:42:31 EST 1969
0xffffffffffe00000: -2097152:   Wed Dec 31 18:25:02 EST 1969
0xffffffffffc00000: -4194304:   Wed Dec 31 17:50:05 EST 1969
0xffffffffff800000: -8388608:   Wed Dec 31 16:40:11 EST 1969
0xffffffffff000000: -16777216:  Wed Dec 31 14:20:22 EST 1969
0xfffffffffe000000: -33554432:  Wed Dec 31 09:40:45 EST 1969
0xfffffffffc000000: -67108864:  Wed Dec 31 00:21:31 EST 1969
0xfffffffff8000000: -134217728: Tue Dec 30 05:43:02 EST 1969
0xfffffffff0000000: -268435456: Sun Dec 28 16:26:04 EST 1969
0xffffffffe0000000: -536870912: Thu Dec 25 13:52:09 EST 1969
0xffffffffc0000000: -1073741824:        Fri Dec 19 08:44:18 EST 1969
0xffffffff80000000: -2147483648:        Sat Dec 06 22:28:36 EST 1969
0xffffffff00000000: -4294967296:        Wed Nov 12 01:57:12 EST 1969
0xfffffffe00000000: -8589934592:        Tue Sep 23 09:54:25 EDT 1969
0xfffffffc00000000: -17179869184:       Sun Jun 15 23:48:50 EDT 1969
0xfffffff800000000: -34359738368:       Fri Nov 29 02:37:41 EST 1968
0xfffffff000000000: -68719476736:       Sat Oct 28 11:15:23 EDT 1967
0xffffffe000000000: -137438953472:      Tue Aug 24 02:30:46 EDT 1965
0xffffffc000000000: -274877906944:      Sun Apr 16 09:01:33 EDT 1961
0xffffff8000000000: -549755813888:      Wed Jul 30 22:03:06 EDT 1952
0xffffff0000000000: -1099511627776:     Wed Feb 27 23:06:12 EST 1935
0xfffffe0000000000: -2199023255552:     Thu Apr 26 04:12:24 EDT 1900
0xfffffc0000000000: -4398046511104:     Thu Aug 19 12:24:48 EDT 1830
0xfffff80000000000: -8796093022208:     Fri Apr 06 04:49:37 EDT 1691
0xfffff00000000000: -17592186044416:    Sat Jul 02 13:39:15 EDT 1412
0xffffe00000000000: -35184372088832:    Tue Jan 15 06:18:31 EST 0855
0xffffc00000000000: -70368744177664:    Sun Feb 11 17:37:02 EST 0261
0xffff800000000000: -140737488355328:   Thu Apr 06 16:14:04 EST 2491
0xffff000000000000: -281474976710656:   Fri Jul 24 13:28:09 EST 6951
0xfffe000000000000: -562949953421312:   Sun Feb 27 07:56:18 EST 15870
0xfffc000000000000: -1125899906842624:  Wed May 07 20:52:37 EST 33709
0xfff8000000000000: -2251799813685248:  Wed Sep 25 22:45:14 EST 69387
0xfff0000000000000: -4503599627370496:  Thu Jul 04 02:30:29 EST 140742
0xffe0000000000000: -9007199254740992:  Thu Jan 16 10:00:59 EST 283452
0xffc0000000000000: -18014398509481984: Fri Feb 15 01:01:58 EST 568873
0xff80000000000000: -36028797018963968: Sat Apr 13 07:03:56 EST 1139715
0xff00000000000000: -72057594037927936: Mon Aug 06 19:07:52 EST 2281399
0xfe00000000000000: -144115188075855872:        Sat Mar 25 19:15:44 EST 4564766
0xfc00000000000000: -288230376151711744:        Tue Jun 29 19:31:28 EST 9131501
0xf800000000000000: -576460752303423488:        Mon Jan 09 20:02:56 EST 18264970
0xf000000000000000: -1152921504606846976:       Sat Jan 31 21:05:53 EST 36531909
0xe000000000000000: -2305843009213693952:       Tue Mar 15 23:11:46 EST 73065787
0xc000000000000000: -4611686018427387904:       Tue Jun 11 03:23:32 EST 146133543
0x8000000000000000: -9223372036854775808:       Sun Dec 02 16:47:04 EST 292269055

Comments
EVALUATION SimpleDateFormat.format() itself can express a year in BC correctly. The problem which was mentioned in this bug report is caused by specicifcation of java.util.Date.toString(). As a book "The Java Programming Language" says, an output string of java.util.Date.toString() is the same as the ANSI C conversion for its ctime() function, and it doesn't have a means to express year in BC. Using a negative value as year to indicate that the year is in BC seems an incorrect way. And, adding an ERA string to output of toString() causes incompatibility problem. So, we decided not to fix this bug unless an RFE is submitted. Please create an instance of SimpleDateFormat in user's program as follows SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy G", Locale.US); and use sdf.format(date) to get a string from the Date in BC. yuka.kamiya@japan 2001-07-25 ------------------------------------------------------------ Okay, now someone has submitted an RFE. Glad to be of service. First, I did *not* specificy a negative (BC) year. This is fundamental. I specified a time, and the local formatter chose to *consider* it a BC year. As an everage application I have no *idea* what the local formatter would consider problematic -- I just (say) read the timestamp long value from a database and format it for the user's locale, which is what the *specification* for java.text.SimpleText.format() says it should do (and I filed the bug against that class). Having conferred heavily with the primary author of "The Java Programming Language", I must state that I think your analysis is flawed. First, the JPL isn't a spec, it is an (advanced) tutorial. Its statements, therefore, are not definitive. Even if they were, since ctime() never encounteres a BC situation, you have no *idea* what it's behavior would be. If ctime is the model, you have to figure out what is good behavior in areas that ctime never addressed. This is no excuse for doing whatever happens to be easy. The specification for java.util.Date.toString() does not say what happens to years that are BC. It cannot be referred to for an answer. It instead that it uses "yyyy", without saying a thing about what that means. So we have to infer it. I propose that it is most reasonable to infer it to not lose information and thereby masquerade as the wrong date. The closest we can understand as to what 'yyyy' means in date formatting is to look at SimpleDateFormat (although this, too, is an inference since it is not directly cited, but the reference implementation uses it, making this a reasonable starting supposition). What it says about *parsing* a 'yyyy' year string is this: If the year pattern has more than two 'y' characters, the year is interpreted literally, regardless of the number of digits. So using the pattern "MM/dd/yyyy", "01/11/12" parses to Jan 11, 12 A.D. Notice the "AD" thing in there. Obviously it is expected that AD vs. BC is understood even when not specified (as G) in the pattern. What I'm saying is that for the US locale, the format is wrong. It drops information and tells me that something dated in the past is current. I have know way of writing a general application to work around, except by making my code understand dates in the US locale, which is what the whole DataFormat stuff is supposed to relieve me of. (And besides, I bet this happens in the FR locale too -- what others should I special case?) This bug shows up in the formatter returned for the US locale in FULL, even, not to mention MEDIUM and LONG (SHORT is obviously only usable for nearby times, so should not be used in applications with arbitrary dates). ken.arnold@East 2001-07-25 An issue here is that the java.util.Date class doesn't specify which calendar systems to be used for handling calendar dates. I filed another bug report for this (4484483). The Date class implementation used to call the libc time functions. (My guess is that that'd be the reason why the JPL has the reference to the ANSI C ctime function.) I don't know if the Date class spec was very clear at that time about all the restrictions coming from the ANSI C time functions. In JDK1.1, the implementation was changed to use java.util.GregorianCalendar. As of this change, Date became to be able to support large negative time (i.e., a millisecond offset from the epoch) values and B.C. dates in the Julian calendar system. (Refer to "Calendrical Calculations" (ISBN 0-521-56474-3) 1.3 Negative Years for year numbering discussions.) However, the Date API doc didn't clarify all the assumptions, which is a minor issue for most applications. Now, to be able to handle B.C. dates (Julian) correctly, we do need to change the API spec. However, the Date class is almost deprecated. We didn't think it was worth spending engineering resources on the B.C. date support in the Date class. It can be done using GregorianCalendar and SimpleDateFormat. masayoshi.okutsu@Eng 2001-07-26 ------------------------------------------------------------ First, I don't agree with your synopsis shift, so I have changed it back. The problem is in SimpleDateFormat, as shown by the following code: import java.util.*; import java.text.*; class t { public static void main(String[] args) { DateFormat fmt = DateFormat.getDateInstance(DateFormat.FULL); for (long val = -1; val < 0; val *= 2) { System.out.println("0x" + Long.toHexString(val) + ": " + val + ":\t" + fmt.format(new java.util.Date(val))); } } } The only use of Date in this code is to hand a time into format(). And the output is still wrong, for example see the following output fragment: 0xfffff00000000000: -17592186044416: Saturday, July 2, 1412 0xffffe00000000000: -35184372088832: Tuesday, January 15, 0855 0xffffc00000000000: -70368744177664: Sunday, February 11, 0261 0xffff800000000000: -140737488355328: Thursday, April 6, 2491 0xffff000000000000: -281474976710656: Friday, July 24, 6951 Note how the date start moving forward in time in the middle here. I implied this in my last comment, but didn't include the code (sorry). As far as the outcome, I think a agree with what you say as far as it goes, which (if I understand properly) is that I *can* work around by creating my own SimpleDateFormat object with a G. But I *think* you also believe that this is a bug, and that in the future some indication of a BC year would come in the locale-specific date formatting for the US and related locales (if, of course, the kind of formatter is >= MEDIUM). Is this right? Your focus on Date instead of SimpleDateFormat is what's confusing me. I agree that the space for Data.toString() is inadequate and should be updated since you really can't deprecate it without great pain to the community. But that's not the whole story. (FWIW, the ctime reference was actually just an offhand quick thing to get the idea over to most of the readers, since most readers are familiar at some level with ctime() output. It wasn't a reference to the implementation, which I didn't consult on this point.) ken.arnold@East 2001-07-26 If you really mean that it is a SimpleDateFormat bug, it is not. The problems involved with your argument are: 1) The Date class implementation uses GregorianCalendar which handles dates before the Julian/Gregorian cutover date as Julian dates. That fact is not documented. In the Julian calendar system, the year numbering is ..., 3 B.C., 2 B.C. 1 B.C., 1 A.D., 2 A.D., ... Negative or 0 year numbers are incorrect in the Julian calendar system. Therefore, without designating the era of any given date, it's not possible for users to determine what the formatted date is. Note that the Date class doesn't intend to support any era designation with its API at all. 2) Date.toString() uses the format which does not distinguish B.C. dates from A.D. ones. 3) U.S. and other localizations use date/time formats which do not distinguish B.C. dates from A.D. ones. What format to be used for localization or another class does not mean any mishandling of large negative time values in SimpleDateFormat.format(). I can think of one enhancement in SimpleDateFormat which is: 4) Add new pattern letter which produces an era name only if the given date is in a different era from the current one. I filed separate bugs/RFEs for the three problems, 2) to 4). See the See also bugs. I'm closing this bug report as "not a bug" (of SimpleDateFormat.format), i.e., it's not mishandling of large negative time values. masayoshi.okutsu@Eng 2001-07-27
27-07-2001

WORK AROUND Only workaround I can see is to check for times that fall before BC and add a "-" to the year by hand. Use SimpleDateFormat.format() with the pattern letter 'G'. masayoshi.okutsu@Eng 2001-07-26
26-07-2001