1 /*
2  * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 import java.io.ByteArrayInputStream;
25 import java.io.ByteArrayOutputStream;
26 import java.io.IOException;
27 import java.time.Month;
28 import java.time.ZoneId;
29 import java.time.ZoneOffset;
30 import java.time.ZonedDateTime;
31 import java.util.Locale;
32 import java.util.Properties;
33 import java.util.function.Supplier;
34 import java.util.logging.Level;
35 import java.util.logging.LogManager;
36 import java.util.logging.LogRecord;
37 import java.util.logging.XMLFormatter;
38 
39 /**
40  * @test
41  * @bug 8028185
42  * @summary XMLFormatter.format emits incorrect year (year + 1900)
43  * @author dfuchs
44  * @run main/othervm XMLFormatterDate
45  */
46 public class XMLFormatterDate {
47 
48     static final class TimeStamp {
49 
50         final ZonedDateTime zdt;
TimeStamp(ZoneId zoneId)51         TimeStamp(ZoneId zoneId) {
52             zdt = ZonedDateTime.now(zoneId);
53         }
getYear()54         int getYear() {
55             return zdt.getYear();
56         }
isJanuaryFirst()57         boolean isJanuaryFirst() {
58             return zdt.getMonth() == Month.JANUARY && zdt.getDayOfMonth() == 1;
59         }
60     }
61 
62 
63     /**
64      * Before the fix, JDK8 prints: {@code
65      * <record>
66      *   <date>3913-11-18T17:35:40</date>
67      *   <millis>1384792540403</millis>
68      *   <sequence>0</sequence>
69      *   <level>INFO</level>
70      *   <thread>1</thread>
71      *   <message>test</message>
72      * </record>
73      * }
74      * After the fix, it should print: {@code
75      * <record>
76      *   <date>2013-11-18T17:35:40</date>
77      *   <millis>1384792696519</millis>
78      *   <sequence>0</sequence>
79      *   <level>INFO</level>
80      *   <thread>1</thread>
81      *   <message>test</message>
82      * </record>
83      * }
84      * @param args the command line arguments
85      */
main(String[] args)86     public static void main(String[] args) {
87         Locale locale = Locale.getDefault();
88         try {
89             Locale.setDefault(Locale.ENGLISH);
90 
91             // Test with default format: by default date is in UTC.
92             System.out.println("Testing with UTC");
93             test(() -> new TimeStamp(ZoneOffset.UTC));
94 
95             // Change LogManager configuration so that new
96             // XMLFormatter prints date in the pre Java 9 local zone format
97             try {
98                 Properties props = new Properties();
99                 props.setProperty("java.util.logging.XMLFormatter.useInstant", "false");
100                 ByteArrayOutputStream baos = new ByteArrayOutputStream();
101                 props.store(baos, "");
102                 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
103                 LogManager.getLogManager().updateConfiguration(bais, (k) -> (o,n) -> n!=null?n:o);
104             } catch (IOException io) {
105                 throw new RuntimeException(io);
106             }
107 
108             // re test with the old format: date will be in the local time zone.
109             System.out.println("Testing with old format");
110             test(() -> new TimeStamp(ZoneId.systemDefault()));
111 
112         } finally {
113             Locale.setDefault(locale);
114         }
115     }
116 
test(Supplier<TimeStamp> timeStampSupplier)117     static void test(Supplier<TimeStamp> timeStampSupplier) {
118 
119         TimeStamp t1 = timeStampSupplier.get();
120         int year1 = t1.getYear();
121 
122         LogRecord record = new LogRecord(Level.INFO, "test");
123         XMLFormatter formatter = new XMLFormatter();
124         final String formatted = formatter.format(record);
125         System.out.println(formatted);
126 
127         final TimeStamp t2 = timeStampSupplier.get();
128         final int year2 = t2.getYear();
129         if (year2 < 1900) {
130             throw new Error("Invalid system year: " + year2);
131         }
132 
133         final StringBuilder buf2 = new StringBuilder()
134                 .append("<date>").append(year2).append("-");
135         if (!formatted.contains(buf2.toString())) {
136             StringBuilder buf1 = new StringBuilder()
137                     .append("<date>").append(year1).append("-");
138             if (formatted.contains(buf1) && year2 == year1 + 1
139                     && t2.isJanuaryFirst()) {
140                 // Oh! The year just switched in the midst of the test...
141                 System.out.println("Happy new year!");
142             } else {
143                 throw new Error("Expected year " + year2
144                         + " not found in log:\n" + formatted);
145             }
146         }
147     }
148 
149 }
150