From 6f616d0a517fa4a4883e7b2f7e0205c90d585fb0 Mon Sep 17 00:00:00 2001 From: Dominik Stadler Date: Fri, 30 Dec 2016 22:11:55 +0000 Subject: [PATCH] Bug 60369: Adjust pattern to better handle non-ASCII characters in Month-names which can appear since Java 8 git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1776646 13f79535-47bb-0310-9956-ffa450edef68 --- .../ss/usermodel/ExcelStyleDateFormatter.java | 16 +- .../TestExcelStyleDateFormatter.java | 160 ++++++++++++++++++ 2 files changed, 168 insertions(+), 8 deletions(-) create mode 100644 src/testcases/org/apache/poi/ss/usermodel/TestExcelStyleDateFormatter.java diff --git a/src/java/org/apache/poi/ss/usermodel/ExcelStyleDateFormatter.java b/src/java/org/apache/poi/ss/usermodel/ExcelStyleDateFormatter.java index 5f37bce45..203aabefa 100644 --- a/src/java/org/apache/poi/ss/usermodel/ExcelStyleDateFormatter.java +++ b/src/java/org/apache/poi/ss/usermodel/ExcelStyleDateFormatter.java @@ -65,7 +65,7 @@ public class ExcelStyleDateFormatter extends SimpleDateFormat { setTimeZone(LocaleUtil.getUserTimeZone()); } - private double dateToBeFormatted = 0.0; + private double dateToBeFormatted; // no-arg constructor is private because of undefined super call with locale @@ -88,12 +88,12 @@ public class ExcelStyleDateFormatter extends SimpleDateFormat { */ private static String processFormatPattern(String f) { String t = f.replaceAll("MMMMM", MMMMM_START_SYMBOL + "MMM" + MMMMM_TRUNCATE_SYMBOL); - t = t.replaceAll("\\[H\\]", String.valueOf(H_BRACKET_SYMBOL)); - t = t.replaceAll("\\[HH\\]", String.valueOf(HH_BRACKET_SYMBOL)); - t = t.replaceAll("\\[m\\]", String.valueOf(M_BRACKET_SYMBOL)); - t = t.replaceAll("\\[mm\\]", String.valueOf(MM_BRACKET_SYMBOL)); - t = t.replaceAll("\\[s\\]", String.valueOf(S_BRACKET_SYMBOL)); - t = t.replaceAll("\\[ss\\]", String.valueOf(SS_BRACKET_SYMBOL)); + t = t.replaceAll("\\[H]", String.valueOf(H_BRACKET_SYMBOL)); + t = t.replaceAll("\\[HH]", String.valueOf(HH_BRACKET_SYMBOL)); + t = t.replaceAll("\\[m]", String.valueOf(M_BRACKET_SYMBOL)); + t = t.replaceAll("\\[mm]", String.valueOf(MM_BRACKET_SYMBOL)); + t = t.replaceAll("\\[s]", String.valueOf(S_BRACKET_SYMBOL)); + t = t.replaceAll("\\[ss]", String.valueOf(SS_BRACKET_SYMBOL)); t = t.replaceAll("s.000", "s.SSS"); t = t.replaceAll("s.00", "s." + LL_BRACKET_SYMBOL); t = t.replaceAll("s.0", "s." + L_BRACKET_SYMBOL); @@ -119,7 +119,7 @@ public class ExcelStyleDateFormatter extends SimpleDateFormat { // Now handle our special cases if (s.indexOf(MMMMM_START_SYMBOL) != -1) { s = s.replaceAll( - MMMMM_START_SYMBOL + "(\\w)\\w+" + MMMMM_TRUNCATE_SYMBOL, + MMMMM_START_SYMBOL + "(\\p{L}|\\p{P})[\\p{L}\\p{P}]+" + MMMMM_TRUNCATE_SYMBOL, "$1" ); } diff --git a/src/testcases/org/apache/poi/ss/usermodel/TestExcelStyleDateFormatter.java b/src/testcases/org/apache/poi/ss/usermodel/TestExcelStyleDateFormatter.java new file mode 100644 index 000000000..af5baf2fb --- /dev/null +++ b/src/testcases/org/apache/poi/ss/usermodel/TestExcelStyleDateFormatter.java @@ -0,0 +1,160 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.ss.usermodel; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.text.DateFormatSymbols; +import java.text.FieldPosition; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.apache.poi.util.LocaleUtil; +import org.junit.Test; + +public class TestExcelStyleDateFormatter { + private static final String EXCEL_DATE_FORMAT = "MMMMM"; + + /** + * [Bug 60369] Month format 'MMMMM' issue with TEXT-formula and Java 8 + */ + @Test + public void test60369() throws ParseException { + // Setting up the locale to be tested together with a list of asserted unicode-formatted results and put them in a map. + Locale germanLocale = Locale.GERMAN; + List germanResultList = Arrays.asList("\u004a", "\u0046", "\u004d", "\u0041", "\u004d", + "\u004a", "\u004a", "\u0041", "\u0053", "\u004f", "\u004e", "\u0044"); + + Locale russianLocale = new Locale("ru", "RU"); + List russianResultList = Arrays.asList("\u044f", "\u0444", "\u043c", "\u0430", "\u043c", + "\u0438", "\u0438", "\u0430", "\u0441", "\u043e", "\u043d", "\u0434"); + + Locale austrianLocale = new Locale("de", "AT"); + List austrianResultList = Arrays.asList("\u004a", "\u0046", "\u004d", "\u0041", "\u004d", + "\u004a", "\u004a", "\u0041", "\u0053", "\u004f", "\u004e", "\u0044"); + + Locale englishLocale = Locale.UK; + List englishResultList = Arrays.asList("\u004a", "\u0046", "\u004d", "\u0041", "\u004d", + "\u004a", "\u004a", "\u0041", "\u0053", "\u004f", "\u004e", "\u0044"); + + Locale frenchLocale = Locale.FRENCH; + List frenchResultList = Arrays.asList("\u006a", "\u0066", "\u006d", "\u0061", "\u006d", + "\u006a", "\u006a", "\u0061", "\u0073", "\u006f", "\u006e", "\u0064"); + + Locale chineseLocale = Locale.CHINESE; + List chineseResultList = Arrays.asList("\u4e00", "\u4e8c", "\u4e09", "\u56db", "\u4e94", + "\u516d", "\u4e03", "\u516b", "\u4e5d", "\u5341", "\u5341", "\u5341"); + + Locale turkishLocale = new Locale("tr", "TR"); + List turkishResultList = Arrays.asList("\u004f", "\u015e", "\u004d", "\u004e", "\u004d", + "\u0048", "\u0054", "\u0041", "\u0045", "\u0045", "\u004b", "\u0041"); + + Locale hungarianLocale = new Locale("hu", "HU"); + List hungarianResultList = Arrays.asList("\u006a", "\u0066", "\u006d", "\u00e1", "\u006d", + "\u006a", "\u006a", "\u0061", "\u0073", "\u006f", "\u006e", "\u0064"); + + Locale indianLocale = new Locale("en", "IN"); + List indianResultList = Arrays.asList("\u004a", "\u0046", "\u004d", "\u0041", "\u004d", + "\u004a", "\u004a", "\u0041", "\u0053", "\u004f", "\u004e", "\u0044"); + + Locale indonesianLocale = new Locale("in", "ID"); + List indonesianResultList = Arrays.asList("\u004a", "\u0046", "\u004d", "\u0041", "\u004d", + "\u004a", "\u004a", "\u0041", "\u0053", "\u004f", "\u004e", "\u0044"); + + + Map> testMap = new HashMap>(); + testMap.put(germanLocale, germanResultList); + testMap.put(russianLocale, russianResultList); + testMap.put(austrianLocale, austrianResultList); + testMap.put(englishLocale, englishResultList); + testMap.put(frenchLocale, frenchResultList); + testMap.put(chineseLocale, chineseResultList); + testMap.put(turkishLocale, turkishResultList); + testMap.put(hungarianLocale, hungarianResultList); + testMap.put(indianLocale, indianResultList); + testMap.put(indonesianLocale, indonesianResultList); + + // We have to set up dates as well. + SimpleDateFormat testDateFormat = new SimpleDateFormat("dd.MM.yyyy", Locale.ROOT); + List testDates = Arrays.asList( + testDateFormat.parse("12.01.1980"), + testDateFormat.parse("11.02.1995"), + testDateFormat.parse("10.03.2045"), + testDateFormat.parse("09.04.2016"), + testDateFormat.parse("08.05.2017"), + testDateFormat.parse("07.06.1945"), + testDateFormat.parse("06.07.1998"), + testDateFormat.parse("05.08.2099"), + testDateFormat.parse("04.09.1988"), + testDateFormat.parse("03.10.2023"), + testDateFormat.parse("02.11.1978"), + testDateFormat.parse("01.12.1890")); + + // Let's iterate over the test setup. + for (Locale locale : testMap.keySet()) { + //System.err.println("Locale: " + locale); + ExcelStyleDateFormatter formatter = new ExcelStyleDateFormatter(EXCEL_DATE_FORMAT, new DateFormatSymbols(locale)); + for (int i = 0; i < 12; i++) { + // Call the method to be tested! + String result = + formatter.format(testDates.get(i), + new StringBuffer(), + new FieldPosition(java.text.DateFormat.MONTH_FIELD)).toString(); + //System.err.println(result + " - " + getUnicode(result.charAt(0))); + assertEquals(getUnicode(testMap.get(locale).get(i).charAt(0)), getUnicode(result.charAt(0))); + } + } + } + + private String getUnicode(char c) { + return "\\u" + Integer.toHexString(c | 0x10000).substring(1); + } + + @Test + public void testConstruct() { + assertNotNull(new ExcelStyleDateFormatter(EXCEL_DATE_FORMAT, LocaleUtil.getUserLocale())); + assertNotNull(new ExcelStyleDateFormatter(EXCEL_DATE_FORMAT)); + } + + @Test + public void testWithLocale() throws ParseException { + Locale before = LocaleUtil.getUserLocale(); + try { + LocaleUtil.setUserLocale(Locale.GERMAN); + String dateStr = new ExcelStyleDateFormatter(EXCEL_DATE_FORMAT).format( + new SimpleDateFormat("yyyy-MM-dd", Locale.ROOT).parse("2016-03-26")); + assertEquals("M", dateStr); + } finally { + LocaleUtil.setUserLocale(before); + } + } + + @Test + public void testWithPattern() throws ParseException { + String dateStr = new ExcelStyleDateFormatter("yyyy|" + EXCEL_DATE_FORMAT + "|").format( + new SimpleDateFormat("yyyy-MM-dd", Locale.ROOT).parse("2016-03-26")); + assertEquals("2016|M|", dateStr); + } +}