1 
   2 /*
   3  * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
   4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   5  *
   6  * This code is free software; you can redistribute it and/or modify it
   7  * under the terms of the GNU General Public License version 2 only, as
   8  * published by the Free Software Foundation.
   9  *
  10  * This code is distributed in the hope that it will be useful, but WITHOUT
  11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  13  * version 2 for more details (a copy is included in the LICENSE file that
  14  * accompanied this code).
  15  *
  16  * You should have received a copy of the GNU General Public License version
  17  * 2 along with this work; if not, write to the Free Software Foundation,
  18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  19  *
  20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  21  * or visit www.oracle.com if you need additional information or have any
  22  * questions.
  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;
  51         TimeStamp(ZoneId zoneId) {
  52             zdt = ZonedDateTime.now(zoneId);
  53         }
  54         int getYear() {
  55             return zdt.getYear();
  56         }
  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      */
  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 
 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 }