Repository: wangguanquan/eec
Pull request #29 (2019-06-06 14:42:49)

Fix#27


Patch:
diff --git a/src/main/java/cn/ttzero/excel/annotation/DisplayName.java b/src/main/java/cn/ttzero/excel/annotation/DisplayName.java
index 304445ba..5fae6fdd 100644
--- a/src/main/java/cn/ttzero/excel/annotation/DisplayName.java
+++ b/src/main/java/cn/ttzero/excel/annotation/DisplayName.java
@@ -24,6 +24,7 @@ import java.lang.annotation.*;
  */
 @Target({ElementType.FIELD})
 @Retention(RetentionPolicy.RUNTIME)
+@Inherited
 @Documented
 public @interface DisplayName {
     /**
diff --git a/src/main/java/cn/ttzero/excel/entity/Sheet.java b/src/main/java/cn/ttzero/excel/entity/Sheet.java
index ff14ecd9..30ecf7e8 100644
--- a/src/main/java/cn/ttzero/excel/entity/Sheet.java
+++ b/src/main/java/cn/ttzero/excel/entity/Sheet.java
@@ -77,29 +77,29 @@ public abstract class Sheet implements Cloneable, Storageable {
     protected RelManager relManager;
     protected int id;
     /**
-     * 自动列宽
+     * To mark the cell auto-width
      */
     protected int autoSize;
     /**
-     * 默认固定宽度
+     * The default cell width
      */
     protected double width = 20;
     /**
-     * 记录已写入行数，每写一行该数加1
+     * The row number
      */
     protected int rows;
     /**
-     * 设置单元格默认隐藏
+     * Mark the cell is hidden
      */
     protected boolean hidden;
 
     protected int headStyle;
     /**
-     * 自动隔行变色
+     * Automatic interlacing color
      */
     protected int autoOdd = -1;
     /**
-     * odd row's background color
+     * Odd row's background color
      */
     protected int oddFill;
     /**
@@ -170,27 +170,27 @@ public abstract class Sheet implements Cloneable, Storageable {
     }
 
     /**
-     * 伴生于Sheet用于控制头部样式和缓存列数据类型和转换
+     * 伴生于Sheet用于控制头部样式和缓存列数据the cell type和转换
      */
     public static class Column {
         /**
-         * Map的主键,object的属性名
+         * The key of Map or field name of entry
          */
         public String key;
         /**
-         * 列头名
+         * The header name
          */
         public String name;
         /**
-         * 列类型
+         * The cell type
          */
         public Class<?> clazz;
         /**
-         * 字符串是否共享
+         * The string value is shared
          */
         public boolean share = true;
         /**
-         * 0: 正常显示 1:显示百分比 2:显示人民币
+         * 0: standard 1:percentage 2:RMB
          */
         public int type;
         /**
@@ -213,19 +213,19 @@ public abstract class Sheet implements Cloneable, Storageable {
         public Styles styles;
 
         /**
-         * 指定列名和类型
+         * 指定the column name和the cell type
          *
-         * @param name  列名
-         * @param clazz 类型
+         * @param name  the column name
+         * @param clazz the cell type
          */
         public Column(String name, Class<?> clazz) {
             this(name, clazz, true);
         }
 
         /**
-         * 指定列名和对应对象中的field
+         * 指定the column name和对应对象中的field
          *
-         * @param name 列名
+         * @param name the column name
          * @param key  field
          */
         public Column(String name, String key) {
@@ -233,11 +233,11 @@ public abstract class Sheet implements Cloneable, Storageable {
         }
 
         /**
-         * 指定列名对应对象中的field和类型，不指定类型时默认取field类型
+         * 指定the column name对应对象中的field和the cell type，不指定类型时默认取field类型
          *
-         * @param name  列名
+         * @param name  the column name
          * @param key   field
-         * @param clazz 类型
+         * @param clazz the cell type
          */
         public Column(String name, String key, Class<?> clazz) {
             this(name, key, true);
@@ -245,10 +245,10 @@ public abstract class Sheet implements Cloneable, Storageable {
         }
 
         /**
-         * 指定列名，类型和值转换
+         * 指定the column name，the cell type和值转换
          *
-         * @param name      列名
-         * @param clazz     类型
+         * @param name      the column name
+         * @param clazz     the cell type
          * @param processor 转换
          */
         public Column(String name, Class<?> clazz, IntConversionProcessor processor) {
@@ -256,9 +256,9 @@ public abstract class Sheet implements Cloneable, Storageable {
         }
 
         /**
-         * 指定列名，对象field和值转换
+         * 指定the column name，对象field和值转换
          *
-         * @param name      列名
+         * @param name      the column name
          * @param key       field
          * @param processor 转换
          */
@@ -267,12 +267,12 @@ public abstract class Sheet implements Cloneable, Storageable {
         }
 
         /**
-         * 指定列名，类型和是否设定值共享
-         * 共享仅对字符串有效，转换后的类型为字符串也同样有效
+         * 指定the column name，the cell type和是否设定值共享
+         * 共享仅对字符串有效，转换后的the cell type为字符串也同样有效
          * 默认非共享以innerStr方式设值
          *
-         * @param name  列名
-         * @param clazz 类型
+         * @param name  the column name
+         * @param clazz the cell type
          * @param share true:共享 false:非共享
          */
         public Column(String name, Class<?> clazz, boolean share) {
@@ -282,11 +282,11 @@ public abstract class Sheet implements Cloneable, Storageable {
         }
 
         /**
-         * 指定列名，field和是否共享字串
-         * 共享仅对字符串有效，转换后的类型为字符串也同样有效
+         * 指定the column name，field和是否共享字串
+         * 共享仅对字符串有效，转换后的the cell type为字符串也同样有效
          * 默认非共享以innerStr方式设值
          *
-         * @param name  列名
+         * @param name  the column name
          * @param key   filed
          * @param share true:共享 false:非共享
          */
@@ -297,12 +297,12 @@ public abstract class Sheet implements Cloneable, Storageable {
         }
 
         /**
-         * 指定列名，类型，转换和是否共享字串
-         * 共享仅对字符串有效，转换后的类型为字符串也同样有效
+         * 指定the column name，the cell type，转换和是否共享字串
+         * 共享仅对字符串有效，转换后的the cell type为字符串也同样有效
          * 默认非共享以innerStr方式设值
          *
-         * @param name      列名
-         * @param clazz     类型
+         * @param name      the column name
+         * @param clazz     the cell type
          * @param processor 转换
          * @param share     true:共享 false:非共享
          */
@@ -312,11 +312,11 @@ public abstract class Sheet implements Cloneable, Storageable {
         }
 
         /**
-         * 指定列名，field，转换和是否共享字串
-         * 共享仅对字符串有效，转换后的类型为字符串也同样有效
+         * 指定the column name，field，转换和是否共享字串
+         * 共享仅对字符串有效，转换后的the cell type为字符串也同样有效
          * 默认非共享以innerStr方式设值
          *
-         * @param name      列名
+         * @param name      the column name
          * @param key       field
          * @param clazz     type of cell
          * @param processor 转换
@@ -327,11 +327,11 @@ public abstract class Sheet implements Cloneable, Storageable {
         }
 
         /**
-         * 指定列名，field，转换和是否共享字串
-         * 共享仅对字符串有效，转换后的类型为字符串也同样有效
+         * 指定the column name，field，转换和是否共享字串
+         * 共享仅对字符串有效，转换后的the cell type为字符串也同样有效
          * 默认非共享以innerStr方式设值
          *
-         * @param name      列名
+         * @param name      the column name
          * @param key       field
          * @param processor 转换
          * @param share     true:共享 false:非共享
@@ -342,10 +342,10 @@ public abstract class Sheet implements Cloneable, Storageable {
         }
 
         /**
-         * 指定列名，类型和单元样式
+         * 指定the column name，the cell type和单元样式
          *
-         * @param name      列名
-         * @param clazz     类型
+         * @param name      the column name
+         * @param clazz     the cell type
          * @param cellStyle 样式
          */
         public Column(String name, Class<?> clazz, int cellStyle) {
@@ -355,7 +355,7 @@ public abstract class Sheet implements Cloneable, Storageable {
         /**
          * 指定列，field 和单元样式
          *
-         * @param name      列名
+         * @param name      the column name
          * @param key       field
          * @param cellStyle 样式
          */
@@ -364,12 +364,12 @@ public abstract class Sheet implements Cloneable, Storageable {
         }
 
         /**
-         * 指事实上列名，类型，样式以及是否共享
-         * 共享仅对字符串有效，转换后的类型为字符串也同样有效
+         * 指事实上the column name，the cell type，样式以及是否共享
+         * 共享仅对字符串有效，转换后的the cell type为字符串也同样有效
          * 默认非共享以innerStr方式设值
          *
-         * @param name      列名
-         * @param clazz     类型
+         * @param name      the column name
+         * @param clazz     the cell type
          * @param cellStyle 样式
          * @param share     true:共享 false:非共享
          */
@@ -379,11 +379,11 @@ public abstract class Sheet implements Cloneable, Storageable {
         }
 
         /**
-         * 指事实上列名，field，样式以及是否共享
-         * 共享仅对字符串有效，转换后的类型为字符串也同样有效
+         * 指事实上the column name，field，样式以及是否共享
+         * 共享仅对字符串有效，转换后的the cell type为字符串也同样有效
          * 默认非共享以innerStr方式设值
          *
-         * @param name      列名
+         * @param name      the column name
          * @param key       field
          * @param cellStyle 样式
          * @param share     true:共享 false:非共享
@@ -414,9 +414,9 @@ public abstract class Sheet implements Cloneable, Storageable {
         }
 
         /**
-         * 设置单元格类型
+         * 设置单元格the cell type
          *
-         * @param type 类型
+         * @param type the cell type
          * @return Column实例
          * @see Const.ColumnType
          */
@@ -428,16 +428,16 @@ public abstract class Sheet implements Cloneable, Storageable {
         /**
          * 获取列头名
          *
-         * @return 列名
+         * @return the column name
          */
         public String getName() {
             return name;
         }
 
         /**
-         * 设置列名
+         * 设置the column name
          *
-         * @param name 列名
+         * @param name the column name
          * @return Column实例
          */
         public Column setName(String name) {
@@ -446,18 +446,18 @@ public abstract class Sheet implements Cloneable, Storageable {
         }
 
         /**
-         * 获取列类型
+         * 获取列the cell type
          *
-         * @return 类型
+         * @return the cell type
          */
         public Class<?> getClazz() {
             return clazz;
         }
 
         /**
-         * 设置列类型
+         * 设置列the cell type
          *
-         * @param clazz 类型
+         * @param clazz the cell type
          * @return Column实例
          */
         public Column setClazz(Class<?> clazz) {
@@ -677,7 +677,7 @@ public abstract class Sheet implements Cloneable, Storageable {
 
         /**
          * 设置共享
-         * 共享仅对字符串有效，转换后的类型为字符串也同样有效
+         * 共享仅对字符串有效，转换后的the cell type为字符串也同样有效
          * 默认非共享以innerStr方式设值
          *
          * @param share true:共享 false:非共享
@@ -696,9 +696,9 @@ public abstract class Sheet implements Cloneable, Storageable {
             ;
 
         /**
-         * 根据类型获取默认样式
+         * 根据the cell type获取默认样式
          *
-         * @param clazz 数据类型
+         * @param clazz the cell type
          * @return 样式
          */
         public int getCellStyle(Class<?> clazz) {
diff --git a/src/main/java/cn/ttzero/excel/entity/style/DefaultNumFmt.java b/src/main/java/cn/ttzero/excel/entity/style/BuiltInNumFmt.java
similarity index 91%
rename from src/main/java/cn/ttzero/excel/entity/style/DefaultNumFmt.java
rename to src/main/java/cn/ttzero/excel/entity/style/BuiltInNumFmt.java
index 66f9dce9..8df2545e 100644
--- a/src/main/java/cn/ttzero/excel/entity/style/DefaultNumFmt.java
+++ b/src/main/java/cn/ttzero/excel/entity/style/BuiltInNumFmt.java
@@ -31,11 +31,11 @@ import java.util.Locale;
  * load numFmt as default formats
  * Created by guanquan.wang at 2018-02-12 10:11
  */
-public final class DefaultNumFmt {
+public final class BuiltInNumFmt {
     private static NumFmt[][] data;
 
     static {
-        InputStream is = DefaultNumFmt.class.getClassLoader().getResourceAsStream("numFmt");
+        InputStream is = BuiltInNumFmt.class.getClassLoader().getResourceAsStream("numFmt");
         if (is != null) {
             List<NumFmt> list = new ArrayList<>();
             int maxLen = 0;
@@ -89,7 +89,7 @@ public final class DefaultNumFmt {
                     }
                 }
             } catch (IOException e) {
-                ; // Empty
+                // Empty
             }
 
             data = new NumFmt[maxLen + 1][]; // accept zero size
@@ -114,6 +114,12 @@ public final class DefaultNumFmt {
         return v != null ? v.id : -1;
     }
 
+    /**
+     * Getting the build-in Number format by code
+     *
+     * @param code the format code
+     * @return the {@link NumFmt}
+     */
     public static NumFmt get(String code) {
         int index = code.length();
         if (index >= data.length) return null;
@@ -129,6 +135,17 @@ public final class DefaultNumFmt {
         return v;
     }
 
+    /**
+     * Getting the build-in Number format by id
+     *
+     * @param id the built-in number formats id
+     * @return the {@link NumFmt}
+     */
+    public static NumFmt get(int id) {
+        // TODO
+        return null;
+    }
+
     public static class NumFmt {
         private String code;
         private int id;
diff --git a/src/main/java/cn/ttzero/excel/entity/style/NumFmt.java b/src/main/java/cn/ttzero/excel/entity/style/NumFmt.java
index c10b17b3..a6a05bcd 100644
--- a/src/main/java/cn/ttzero/excel/entity/style/NumFmt.java
+++ b/src/main/java/cn/ttzero/excel/entity/style/NumFmt.java
@@ -39,11 +39,12 @@ import org.dom4j.Element;
  * If you want to skip a code section and include a code section that follows it,
  * you must include the ending semicolon for the section that you skip.
  * <p>
- * https://support.office.com/en-us/article/create-or-delete-a-custom-number-format-78f2a361-936b-4c03-8772-09fab54be7f4
- * https://docs.microsoft.com/en-us/previous-versions/office/developer/office-2010/ee857658(v=office.14)
+ * {@see https://support.office.com/en-us/article/create-or-delete-a-custom-number-format-78f2a361-936b-4c03-8772-09fab54be7f4}
+ * {@see https://support.office.com/en-us/article/Number-format-codes-5026bbd6-04bc-48cd-bf33-80f18b4eae68?ui=en-US&rs=en-US&ad=US}
+ * {@see https://docs.microsoft.com/en-us/previous-versions/office/developer/office-2010/ee857658(v=office.14)}
  * Created by guanquan.wang at 2018-02-06 08:51
  */
-public class NumFmt {
+public class NumFmt implements Comparable<NumFmt> {
     private String code;
     private int id = -1;
 
@@ -110,4 +111,9 @@ public class NumFmt {
             .addAttribute("formatCode", code)
             .addAttribute("numFmtId", String.valueOf(id));
     }
+
+    @Override
+    public int compareTo(NumFmt o) {
+        return id - o.id;
+    }
 }
diff --git a/src/main/java/cn/ttzero/excel/entity/style/Styles.java b/src/main/java/cn/ttzero/excel/entity/style/Styles.java
index 9bec8607..29f3ca9f 100644
--- a/src/main/java/cn/ttzero/excel/entity/style/Styles.java
+++ b/src/main/java/cn/ttzero/excel/entity/style/Styles.java
@@ -18,12 +18,15 @@ package cn.ttzero.excel.entity.style;
 
 import cn.ttzero.excel.annotation.TopNS;
 import cn.ttzero.excel.entity.I18N;
+import cn.ttzero.excel.entity.Storageable;
 import cn.ttzero.excel.manager.Const;
+import cn.ttzero.excel.reader.ExcelReadException;
 import cn.ttzero.excel.util.FileUtil;
-import cn.ttzero.excel.util.StringUtil;
 import org.dom4j.Document;
+import org.dom4j.DocumentException;
 import org.dom4j.DocumentFactory;
 import org.dom4j.Element;
+import org.dom4j.io.SAXReader;
 
 import java.awt.Color;
 import java.io.IOException;
@@ -31,6 +34,9 @@ import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.*;
 
+import static cn.ttzero.excel.util.StringUtil.isEmpty;
+import static cn.ttzero.excel.util.StringUtil.isNotEmpty;
+
 /**
  * 每个style由一个int值组成
  * 0~ 8位 numFmt
@@ -42,7 +48,7 @@ import java.util.*;
  * Created by guanquan.wang on 2017/10/13.
  */
 @TopNS(prefix = "", uri = Const.SCHEMA_MAIN, value = "styleSheet")
-public class Styles {
+public class Styles implements Storageable {
 
     private Map<Integer, Integer> map;
     private Document document;
@@ -52,6 +58,12 @@ public class Styles {
     private List<Fill> fills;
     private List<Border> borders;
 
+    /**
+     * Cache the data/time format style index.
+     * It's use for fast test the cell value is a data or time value
+     */
+    private Set<Integer> dateFmtCache;
+
     private Styles() {
         map = new HashMap<>();
     }
@@ -71,6 +83,15 @@ public class Styles {
         return n;
     }
 
+    /**
+     * Returns the number of styles
+     *
+     * @return the total styles
+     */
+    public int size() {
+        return map.size();
+    }
+
     static final int INDEX_NUMBER_FORMAT = 24;
     static final int INDEX_FONT = 18;
     static final int INDEX_FILL = 12;
@@ -159,18 +180,74 @@ public class Styles {
     }
 
     /**
-     * add number format
+     * Load the style file from disk
      *
-     * @param numFmt
-     * @return
+     * @param path the style file path
+     * @return the {@link Styles} Object
+     */
+    @SuppressWarnings("unchecked")
+    public static Styles load(Path path) {
+        // load workbook.xml
+        SAXReader reader = new SAXReader();
+        Document document;
+        try {
+            document = reader.read(Files.newInputStream(path));
+        } catch (DocumentException | IOException e) {
+            throw new ExcelReadException(e);
+        }
+
+        Styles self = new Styles();
+        Element root = document.getRootElement();
+        // Number format
+        Element numFmts = root.element("numFmts");
+        List<Element> sub = numFmts.elements();
+        self.numFmts = new ArrayList<>();
+        for (Element e : sub) {
+            String id = getAttr(e, "numFmtId"), code = getAttr(e, "formatCode");
+            self.numFmts.add(new NumFmt(Integer.parseInt(id), code));
+        }
+        // Sort by id
+        self.numFmts.sort(Comparator.comparingInt(NumFmt::getId));
+
+        /*
+        Ignore other styles, there only parse number format
+        It's use for Excel reader
+         */
+
+        // Cell xf
+        Element cellXfs = root.element("cellXfs");
+        sub = cellXfs.elements();
+        int i = 0;
+        for (Element e : sub) {
+            String applyNumberFormat = getAttr(e, "applyNumberFormat");
+            if (isNotEmpty(applyNumberFormat) && Integer.parseInt(applyNumberFormat) == 1) {
+                String numFmtId = getAttr(e, "numFmtId");
+                int style = Integer.parseInt(numFmtId) << INDEX_NUMBER_FORMAT;
+                self.map.put(i, style);
+            }
+            i++;
+        }
+        // Test number format
+        for (Integer styleIndex : self.map.keySet()) {
+            self.isDate(styleIndex);
+        }
+
+        return self;
+    }
+
+    /**
+     * Add number format
+     *
+     * @param numFmt the {@link NumFmt} entry
+     * @return the numFmt part value in style
      */
     public final int addNumFmt(NumFmt numFmt) {
         // check and search default code
         if (numFmt.getId() < 0) {
-            if (StringUtil.isEmpty(numFmt.getCode())) {
+            if (isEmpty(numFmt.getCode())) {
                 throw new NullPointerException("NumFmt code");
             }
-            int index = DefaultNumFmt.indexOf(numFmt.getCode());
+            int index = BuiltInNumFmt.indexOf(numFmt.getCode());
             if (index > -1) { // default code
                 numFmt.setId(index);
             } else {
@@ -197,13 +274,13 @@ public class Styles {
     }
 
     /**
-     * add font
+     * Add font
      *
-     * @param font
-     * @return
+     * @param font the {@link Font} entry
+     * @return the font part value in style
      */
     public final int addFont(Font font) {
-        if (StringUtil.isEmpty(font.getName())) {
+        if (isEmpty(font.getName())) {
             throw new FontParseException("Font name not support.");
         }
         int i = fonts.indexOf(font);
@@ -327,6 +404,13 @@ public class Styles {
         return n;
     }
 
+    /**
+     * Write style to disk
+     *
+     * @param styleFile the storage path
+     * @throws IOException if I/O error occur
+     */
+    @Override
     public void writeTo(Path styleFile) throws IOException {
         if (document != null) { // Not null
             FileUtil.writeToDiskNoFormat(document, styleFile);
@@ -463,7 +547,221 @@ public class Styles {
         return borders.get(style << 20 >>> (INDEX_BORDER + 20));
     }
     public int getVertical(int style) {
-        return style <<26>>>(INDEX_VERTICAL +26);
+        return style << 26 >>> (INDEX_VERTICAL + 26);
+    }
+
+    /**
+     * Returns the attribute value from Element
+     *
+     * @param element current element
+     * @param attr the attr name
+     * @return the attr value
+     */
+    public static String getAttr(Element element, String attr) {
+        return element != null ? element.attributeValue(attr) : null;
+    }
+
+    /**
+     * Test the style is data format
+     *
+     * @param styleIndex the style index
+     * @return true if the style content data format
+     */
+    public boolean isDate(int styleIndex) {
+        // Test from cache
+        if (fastTestDateFmt(styleIndex)) return true;
+
+        Integer style = map.get(styleIndex);
+        if (style == null) return false;
+        int nf = style >> INDEX_NUMBER_FORMAT & 0xFF;
+
+        boolean isDate = false;
+        NumFmt numFmt;
+        // All indexes from 0 to 163 are reserved for built-in formats.
+        // The first user-defined format starts at 164.
+        if (nf < 164) {
+            isDate = (nf >= 14 && nf <= 22
+                || nf >= 27 && nf <= 36
+                || nf >= 45 && nf <= 47
+                || nf >= 50 && nf <= 58
+                || nf == 81);
+            // Test by numFmt code
+        } else if ((numFmt = findFmtById(nf)) != null && (isNotEmpty(numFmt.getCode()))) {
+            isDate = testCodeIsDate(numFmt.getCode());
+        }
+
+        // Put into data/time format cache
+        // Ignore the style code, Uniform use of 'yyyy-mm-dd hh:mm:ss' format output
+        if (isDate) {
+            if (dateFmtCache == null) dateFmtCache = new HashSet<>();
+            dateFmtCache.add(styleIndex);
+        }
+        return isDate;
+    }
+
+    public static boolean testCodeIsDate(String code) {
+        char[] chars = code.toCharArray();
+
+        int score = 0;
+        byte[] byteScore = new byte[26];
+        for (int i = 0, size = chars.length; i < size; ) {
+            char c = chars[i];
+            // To lower case
+            if (c >= 65 && c <= 90) c += 32;
+
+            int a = ++i;
+
+            if (c == '[') {
+                // Found the end char ']'
+                for (; i < size && chars[i] != ']'; i++);
+                int len = i - a + 1;
+                // DBNum{n}
+                if (len == 6 && chars[a] == 'D' && chars[a + 1] == 'B' && chars[a + 2] == 'N'
+                    && chars[a + 3] == 'u' && chars[a + 4] == 'm') {
+                    int n = chars[a + 5] - '0';
+                    // If use "[DBNum{n}]" etc. as the Excel display format, you can use Chinese numerals.
+                    if (n != 1 && n != 2 && n != 3) break;
+                }
+                // Maybe is a LCID
+                // [$-xxyyzzzz]
+                // https://stackoverflow.com/questions/54134729/what-does-the-130000-in-excel-locale-code-130000-mean
+                else if (i - a > 2 && chars[a] == '$' && chars[a + 1] == '-') {
+                    // Language & Calendar Identifier
+//                    String lcid = new String(chars, a + 2, i - a - 2);
+                    // Maybe the format is a data
+                    score = 50;
+                }
+            } else {
+                switch (c) {
+                    case 'y':
+                    case 'm':
+                    case 'd':
+                    case 'h':
+                    case 's':
+                        for (; i < size && chars[i] == c; i++);
+                        byteScore[c - 'a'] += i - a + 1;
+                        break;
+                    case 'a':
+                    case 'p':
+                        if (a < size && chars[a] == 'm')
+                            byteScore[c - 'a'] += 1;
+                        break;
+                }
+            }
+        }
+
+        // Exclude case
+        // y > 4
+        // h > 4
+        // s > 4
+        // am > 1 || pm > 1
+        if (byteScore[24] > 4 || byteScore[7] > 4 || byteScore[18] > 4 || byteScore[0] > 1 || byteScore[15] > 1) {
+            return false;
+        }
+
+        // Calculating the score
+        // Plus 5 points for each keywords
+        score += byteScore[0]  * 5; // am
+        score += byteScore[3]  * 5; // d
+        score += byteScore[7]  * 5; // h
+        score += byteScore[12] * 5; // m
+        score += byteScore[15] * 5; // pm
+        score += byteScore[18] * 5; // s
+        score += byteScore[24] * 5; // y
+
+        // Addition calculation if consecutive keywords appear
+        // y + m + d
+        if (byteScore[24] > 0 && byteScore[12] > 0 && byteScore[3] > 0
+            && byteScore[24] + byteScore[12] + byteScore[3] >= 4) {
+            score += 70;
+            // y + m
+        } else if (byteScore[24] > 0 && byteScore[12] > 0 && byteScore[24] + byteScore[12] >= 3) {
+            score += 60;
+            // m + d
+        } else if (byteScore[12] > 0 && byteScore[3] > 0) {
+            score += 60;
+            // Code is yyyy or yy
+        } else if (byteScore[24] == chars.length) {
+            score += 60;
+        }
+
+        // h + m + s
+        if (byteScore[7] > 0 && byteScore[12] > 0 && byteScore[18] > 0
+            && byteScore[7] + byteScore[12] + byteScore[18] > 3) {
+            score += 70;
+            // h + m
+        } else if (byteScore[7] > 0 && byteScore[12] > 0) {
+            score += 60;
+            // m + s
+        } else if (byteScore[12] > 0 && byteScore[18] > 0) {
+            score += 60;
+        }
+
+        // am + pm
+        if (byteScore[0] + byteScore[15] == 2) {
+            score += 50;
+        }
+
+        return score >= 70;
+    }
+
+    // Find the number format in array
+    private NumFmt findFmtById(int id) {
+        if (numFmts == null || numFmts.isEmpty())
+            return null;
+        NumFmt fmt = numFmts.get(0);
+        // Found it
+        if (fmt.getId() == id)
+            return fmt;
+        if (fmt.getId() < id) {
+            int size = numFmts.size();
+            if (fmt.getId() < 164) {
+                int index = 1;
+                for (; index < size;) {
+                    if ((fmt = numFmts.get(index++)).getId() >= 164)
+                        break;
+                }
+            }
+            // The format is sorted and incremented
+            // So get it by index, the index equals id subtract the first id
+            int index = id - fmt.getId();
+            if (index >= size) {
+                fmt = null;
+            } else {
+                fmt = numFmts.get(id - fmt.getId());
+                if (fmt.getId() == id) return fmt;
+                else if (fmt.getId() < id) {
+                    // Loop check forward
+                    for (; index++ < size; ) {
+                        NumFmt nf = numFmts.get(index);
+                        if (nf.getId() == id) {
+                            fmt = nf;
+                            break;
+                        }
+                    }
+                } else {
+                    // Loop check backward
+                    for (; index-- > 0; ) {
+                        NumFmt nf = numFmts.get(index);
+                        if (nf.getId() == id) {
+                            fmt = nf;
+                            break;
+                        }
+                    }
+                }
+            }
+        } else fmt = null;
+        return fmt;
+    }
+
+    /**
+     * Fast test cell value is data/time value
+     *
+     * @param styleIndex the style index
+     * @return true if the style content data format
+     */
+    public boolean fastTestDateFmt(int styleIndex) {
+        return dateFmtCache != null && dateFmtCache.contains(styleIndex);
     }
 
 }
diff --git a/src/main/java/cn/ttzero/excel/manager/Const.java b/src/main/java/cn/ttzero/excel/manager/Const.java
index 61600fc1..fb90b1ae 100644
--- a/src/main/java/cn/ttzero/excel/manager/Const.java
+++ b/src/main/java/cn/ttzero/excel/manager/Const.java
@@ -17,40 +17,41 @@
 package cn.ttzero.excel.manager;
 
 /**
- * 常量类
+ * The Const class
+ *
  * Created by guanquan.wang on 2017/9/30.
  */
-public class Const {
+public interface Const {
     /**
-     * open xml schema
+     * Open xml schema
      */
-    public static final String SCHEMA_MAIN = "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
+    String SCHEMA_MAIN = "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
     /**
-     * xml declaration
+     * Xml declaration
      */
-    public static final String XML_DECLARATION = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
+    String XML_DECLARATION = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
     /**
-     * excel xml declatation
+     * Excel xml declatation
      */
-    public static final String EXCEL_XML_DECLARATION = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>";
+    String EXCEL_XML_DECLARATION = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>";
     /**
      * "\n" in UNIX systems, "\r\n" in Windows systems.
      */
-    public static final String lineSeparator = System.lineSeparator();
+    String lineSeparator = System.lineSeparator();
     /**
-     * prefix of eec project
+     * Prefix of eec project
      */
-    public static final String EEC_PREFIX = "eec+";
+    String EEC_PREFIX = "eec+";
     /**
      * Size of row-block
      */
-    public static final int ROW_BLOCK_SIZE = 32;
+    int ROW_BLOCK_SIZE = 32;
 
     /**
      * Relation
      */
-    public static final class Relationship {
-        public static final String
+    interface Relationship {
+        String
             IMAGE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"
             , APP = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties"
             , CORE = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"
@@ -64,10 +65,10 @@ public class Const {
     }
 
     /**
-     * content-type
+     * Content-type
      */
-    public static final class ContentType {
-        public static final String
+    interface ContentType {
+        String
             PNG = "image/png"
             , JPG = "image/jpeg"
             , JPEG = "image/jpeg"
@@ -87,72 +88,72 @@ public class Const {
     }
 
     /**
-     * Excel 限制
+     * Excel Limit
      */
-    public static final class Limit {
+    interface Limit {
         /**
-         * Excel07 每个worksheet页最大行 1 << 20
+         * Excel07's max rows on sheet
          */
-        public static final int MAX_ROWS_ON_SHEET = 1_048_576;
+        int MAX_ROWS_ON_SHEET = 1_048_576;
         /**
-         * 每个worksheet页最大列
+         * The max columns on sheet
          */
-        public static final int MAX_COLUMNS_ON_SHEET = 16_384;
+        int MAX_COLUMNS_ON_SHEET = 16_384;
         /**
-         * 单个cell最多包含多少字符
+         * The max characters per cell
          */
-        public static final int MAX_CHARACTERS_PER_CELL = 32_767;
+        int MAX_CHARACTERS_PER_CELL = 32_767;
         /**
-         * 单个cell最多包含多少行
+         * The max line feeds per cell
          */
-        public static final int MAX_LINE_FEEDS_PER_CELL = 253;
+        int MAX_LINE_FEEDS_PER_CELL = 253;
         /**
          * Column width
          */
-        public static final int COLUMN_WIDTH = 255;
+        int COLUMN_WIDTH = 255;
     }
 
     /**
-     * 文件扩展名
+     * The file suffix
      */
-    public static final class Suffix {
+    interface Suffix {
         /**
          * Excel 07
          */
-        public static final String EXCEL_07 = ".xlsx";
+        String EXCEL_07 = ".xlsx";
         /**
          * Excel 03
          */
-        public static final String EXCEL_03 = ".xls";
+        String EXCEL_03 = ".xls";
         /**
-         * xml
+         * Xml
          */
-        public static final String XML = ".xml";
+        String XML = ".xml";
         /**
-         * relation
+         * Relation
          */
-        public static final String RELATION = ".rels";
+        String RELATION = ".rels";
         /**
-         * png
+         * Png
          */
-        public static final String PNG = ".png";
+        String PNG = ".png";
     }
 
     /**
-     * 单元格类型
+     * The cell type
      */
-    public static final class ColumnType {
+    interface ColumnType {
         /**
-         * 普通类型
+         * Standard
          */
-        public static final int NORMAL = 0;
+        int NORMAL = 0;
         /**
-         * 百分比类型
+         * Percentage
          */
-        public static final int PARENTAGE = 1;
+        int PARENTAGE = 1;
         /**
-         * 人民币类型
+         * RMB
          */
-        public static final int RMB = 2;
+        int RMB = 2;
     }
 }
diff --git a/src/main/java/cn/ttzero/excel/reader/Cell.java b/src/main/java/cn/ttzero/excel/reader/Cell.java
index 6bdc1284..441109a2 100644
--- a/src/main/java/cn/ttzero/excel/reader/Cell.java
+++ b/src/main/java/cn/ttzero/excel/reader/Cell.java
@@ -54,6 +54,11 @@ public class Cell {
     public BigDecimal mv;
     public int xf;
 
+    /**
+     * The style index
+     */
+    public short s;
+
     public void setT(char t) {
         this.t = t;
     }
@@ -122,5 +127,6 @@ public class Cell {
         this.cv = '\0';
         this.mv = null;
         this.xf = 0;
+        this.s = 0;
     }
 }
diff --git a/src/main/java/cn/ttzero/excel/reader/CellType.java b/src/main/java/cn/ttzero/excel/reader/CellType.java
new file mode 100644
index 00000000..e6724d0c
--- /dev/null
+++ b/src/main/java/cn/ttzero/excel/reader/CellType.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2019, guanquan.wang@yandex.com All Rights Reserved.
+ *
+ * Licensed 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 cn.ttzero.excel.reader;
+
+/**
+ * Create by guanquan.wang at 2019-06-06 21:17
+ */
+public enum CellType {
+    /*
+    String
+     */
+    STRING
+    /*
+    int, short, character
+     */
+    , INTEGER
+    /*
+    long
+     */
+    , LONG
+    /*
+    float, double
+     */
+    , DOUBLE
+    /*
+    boolean
+     */
+    , BOOLEAN
+    /*
+    date, time or date-time
+     */
+    , DATE
+    /*
+    Null
+     */
+    , BLANK
+}
diff --git a/src/main/java/cn/ttzero/excel/reader/ExcelReader.java b/src/main/java/cn/ttzero/excel/reader/ExcelReader.java
index 189aaaff..f9710fba 100644
--- a/src/main/java/cn/ttzero/excel/reader/ExcelReader.java
+++ b/src/main/java/cn/ttzero/excel/reader/ExcelReader.java
@@ -19,6 +19,7 @@ package cn.ttzero.excel.reader;
 import cn.ttzero.excel.annotation.NS;
 import cn.ttzero.excel.annotation.TopNS;
 import cn.ttzero.excel.entity.Relationship;
+import cn.ttzero.excel.entity.style.Styles;
 import cn.ttzero.excel.manager.Const;
 import cn.ttzero.excel.manager.ExcelType;
 import cn.ttzero.excel.manager.RelManager;
@@ -75,6 +76,11 @@ public class ExcelReader implements AutoCloseable {
      */
     private SharedStrings sst;
 
+    /**
+     * The {@link Styles}
+     */
+    private Styles styles;
+
     /**
      * 实例化Reader
      *
@@ -324,6 +330,9 @@ public class ExcelReader implements AutoCloseable {
         // Load SharedString
         this.sst = new SharedStrings(temp.resolve("xl/sharedStrings.xml"), cacheSize, hotSize).load();
 
+        // Load Styles
+        this.styles = Styles.load(temp.resolve("xl/styles.xml"));
+
         List<Sheet> sheets = new ArrayList<>();
         @SuppressWarnings("unchecked")
         Iterator<Element> sheetIter = root.element("sheets").elementIterator();
@@ -343,6 +352,8 @@ public class ExcelReader implements AutoCloseable {
             sheet.setPath(temp.resolve("xl").resolve(r.getTarget()));
             // put shared string
             sheet.setSst(sst);
+            // Setting styles
+            sheet.setStyles(styles);
             sheets.add(sheet);
         }
 
@@ -421,19 +432,20 @@ public class ExcelReader implements AutoCloseable {
         int length = Math.min(bytes.length, size);
         if (length < 4)
             return excelType;
-        int type = bytes[0] & 0xFF;
+        int type;
+        type  = bytes[0]  & 0xFF;
         type += (bytes[1] & 0xFF) << 8;
         type += (bytes[2] & 0xFF) << 16;
         type += (bytes[3] & 0xFF) << 24;
 
         int zip = 0x04034B50;
-        int b1 = 0xE011CFD0;
-        int b2 = 0xE11AB1A1;
+        int b1  = 0xE011CFD0;
+        int b2  = 0xE11AB1A1;
 
         if (type == zip) {
             excelType = ExcelType.XLSX;
         } else if (type == b1 && length >= 8) {
-            type = bytes[4] & 0xFF;
+            type  = bytes[4]  & 0xFF;
             type += (bytes[5] & 0xFF) << 8;
             type += (bytes[6] & 0xFF) << 16;
             type += (bytes[7] & 0xFF) << 24;
diff --git a/src/main/java/cn/ttzero/excel/reader/HeaderRow.java b/src/main/java/cn/ttzero/excel/reader/HeaderRow.java
index 272c9754..bece11c5 100644
--- a/src/main/java/cn/ttzero/excel/reader/HeaderRow.java
+++ b/src/main/java/cn/ttzero/excel/reader/HeaderRow.java
@@ -157,6 +157,11 @@ class HeaderRow extends Row {
         return clazz;
     }
 
+    @Override
+    public CellType getCellType(int columnIndex) {
+        return CellType.STRING;
+    }
+
     /**
      * Get T value by column index
      *
diff --git a/src/main/java/cn/ttzero/excel/reader/Row.java b/src/main/java/cn/ttzero/excel/reader/Row.java
index 11470e5c..778c3922 100644
--- a/src/main/java/cn/ttzero/excel/reader/Row.java
+++ b/src/main/java/cn/ttzero/excel/reader/Row.java
@@ -16,6 +16,7 @@
 
 package cn.ttzero.excel.reader;
 
+import cn.ttzero.excel.entity.style.Styles;
 import cn.ttzero.excel.util.DateUtil;
 import cn.ttzero.excel.util.StringUtil;
 import org.apache.logging.log4j.LogManager;
@@ -26,14 +27,9 @@ import java.sql.Timestamp;
 import java.util.Date;
 import java.util.StringJoiner;
 
-import static cn.ttzero.excel.reader.Cell.BOOL;
-import static cn.ttzero.excel.reader.Cell.NUMERIC;
-import static cn.ttzero.excel.reader.Cell.FUNCTION;
-import static cn.ttzero.excel.reader.Cell.SST;
-import static cn.ttzero.excel.reader.Cell.INLINESTR;
-import static cn.ttzero.excel.reader.Cell.LONG;
-import static cn.ttzero.excel.reader.Cell.DOUBLE;
-import static cn.ttzero.excel.reader.Cell.BLANK;
+import static cn.ttzero.excel.reader.Cell.*;
+import static cn.ttzero.excel.util.DateUtil.toLocalDate;
+import static cn.ttzero.excel.util.DateUtil.toTimestamp;
 
 /**
  * Create by guanquan.wang at 2019-04-17 11:08
@@ -48,11 +44,19 @@ public abstract class Row {
     int lc = -1;
     // Share cell
     Cell[] cells;
+    /**
+     * The Shared String Table
+     */
     SharedStrings sst;
     // The header row
     private HeaderRow hr;
     boolean unknownLength;
 
+    /**
+     * The global styles
+     */
+    Styles styles;
+
     /**
      * The number of row. (zero base)
      *
@@ -485,19 +489,19 @@ public abstract class Row {
         Timestamp ts;
         switch (c.t) {
             case NUMERIC:
-                ts = DateUtil.toTimestamp(c.nv);
+                ts = toTimestamp(c.nv);
                 break;
             case DOUBLE:
-                ts = DateUtil.toTimestamp(c.dv);
+                ts = toTimestamp(c.dv);
                 break;
             case SST:
                 if (c.sv == null) {
                     c.setSv(sst.get(c.nv));
                 }
-                ts = DateUtil.toTimestamp(c.sv);
+                ts = toTimestamp(c.sv);
                 break;
             case INLINESTR:
-                ts = DateUtil.toTimestamp(c.sv);
+                ts = toTimestamp(c.sv);
                 break;
             default: throw new UncheckedTypeException("");
         }
@@ -518,6 +522,47 @@ public abstract class Row {
         throw new UncheckedTypeException("can't convert to java.sql.Time");
     }
 
+    /**
+     * Returns the type of cell
+     *
+     * @param columnIndex the cell index from zero
+     * @return the {@link CellType}
+     */
+    public CellType getCellType(int columnIndex) {
+        Cell c = getCell(columnIndex);
+        CellType type;
+        switch (c.t) {
+            case SST:
+            case INLINESTR:
+                type = CellType.STRING;
+                break;
+            case NUMERIC:
+            case CHARACTER:
+                type = !styles.fastTestDateFmt(c.s) ? CellType.INTEGER : CellType.DATE;
+                break;
+            case LONG:
+                type = CellType.LONG;
+                break;
+            case DOUBLE:
+                type = !styles.fastTestDateFmt(c.s) ? CellType.DOUBLE : CellType.DATE;
+                break;
+            case BOOL:
+                type = CellType.BOOLEAN;
+                break;
+            case DATETIME:
+            case DATE:
+            case TIME:
+                type = CellType.DATE;
+                break;
+            case BLANK:
+                type = CellType.BLANK;
+                break;
+
+                default: type = CellType.STRING;
+        }
+        return type;
+    }
+
     /**
      * Returns the binding type if is bound, otherwise returns Row
      *
@@ -637,13 +682,15 @@ public abstract class Row {
                     joiner.add("<function>");
                     break;
                 case NUMERIC:
-                    joiner.add(String.valueOf(c.nv));
+                    if (!styles.fastTestDateFmt(c.s)) joiner.add(String.valueOf(c.nv));
+                    else joiner.add(toLocalDate(c.nv).toString());
                     break;
                 case LONG:
                     joiner.add(String.valueOf(c.lv));
                     break;
                 case DOUBLE:
-                    joiner.add(String.valueOf(c.dv));
+                    if (!styles.fastTestDateFmt(c.s)) joiner.add(String.valueOf(c.dv));
+                    else joiner.add(toTimestamp(c.dv).toString());
                     break;
                 case BLANK:
                     joiner.add(StringUtil.EMPTY);
diff --git a/src/main/java/cn/ttzero/excel/reader/XMLRow.java b/src/main/java/cn/ttzero/excel/reader/XMLRow.java
index aaca05b5..5fd087cd 100644
--- a/src/main/java/cn/ttzero/excel/reader/XMLRow.java
+++ b/src/main/java/cn/ttzero/excel/reader/XMLRow.java
@@ -16,6 +16,8 @@
 
 package cn.ttzero.excel.reader;
 
+import cn.ttzero.excel.entity.style.Styles;
+
 import static cn.ttzero.excel.reader.Cell.BOOL;
 import static cn.ttzero.excel.reader.Cell.NUMERIC;
 import static cn.ttzero.excel.reader.Cell.FUNCTION;
@@ -53,8 +55,9 @@ class XMLRow extends Row {
 
     private XMLRow() { }
 
-    XMLRow(SharedStrings sst, int startRow) {
+    XMLRow(SharedStrings sst, Styles styles, int startRow) {
         this.sst = sst;
+        this.styles = styles;
         this.startRow = startRow;
         buf = new StringBuilder();
     }
@@ -135,7 +138,7 @@ class XMLRow extends Row {
     }
 
     /**
-     * 解析每行数据
+     * Loop parse cell
      */
     private void parseCells() {
         int index = 0;
@@ -150,12 +153,13 @@ class XMLRow extends Row {
     }
 
     /**
-     * 迭代每列数据
+     * Loop parse cell
      *
-     * @return Cell
+     * @return the {@link Cell}
      */
     protected Cell nextCell() {
-        for (; cursor < to && (cb[cursor] != '<' || cb[cursor + 1] != 'c' || cb[cursor + 2] != ' '); cursor++) ;
+        for (; cursor < to && (cb[cursor] != '<' || cb[cursor + 1] != 'c'
+            || cb[cursor + 2] != ' '); cursor++) ;
         // end of row
         if (cursor >= to) return null;
         cursor += 2;
@@ -167,14 +171,16 @@ class XMLRow extends Row {
         // find type
         // n=numeric (default), s=string, b=boolean, str=function string
         char t = NUMERIC; // default
+        // The style index
+        short s = 0;
         for (; cb[cursor] != '>'; cursor++) {
-            // cell index
+            // Cell index
             if (cb[cursor] == ' ' && cb[cursor + 1] == 'r' && cb[cursor + 2] == '=') {
                 int a = cursor += 4;
                 for (; cb[cursor] != '"'; cursor++) ;
                 cell = cells[unknownLength ? (lc = toCellIndex(a, cursor)) - 1 : toCellIndex(a, cursor) - 1];
             }
-            // cell type
+            // Cell type
             if (cb[cursor] == ' ' && cb[cursor + 1] == 't' && cb[cursor + 2] == '=') {
                 int a = cursor += 4, n;
                 for (; cb[cursor] != '"'; cursor++) ;
@@ -182,16 +188,24 @@ class XMLRow extends Row {
                     t = cb[a]; // s, n, b
                 } else if (n == 3 && cb[a] == 's' && cb[a + 1] == 't' && cb[a + 2] == 'r') {
                     t = FUNCTION; // function string
-                } else if (n == 9 && cb[a] == 'i' && cb[a + 1] == 'n' && cb[a + 2] == 'l' && cb[a + 6] == 'S' && cb[a + 8] == 'r') {
+                } else if (n == 9 && cb[a] == 'i' && cb[a + 1] == 'n'
+                    && cb[a + 2] == 'l' && cb[a + 6] == 'S' && cb[a + 8] == 'r') {
                     t = INLINESTR; // inlineStr
                 }
                 // -> other unknown case
             }
+            // Cell style
+            if (cb[cursor] == ' ' && cb[cursor + 1] == 's' && cb[cursor + 2] == '=') {
+                int a = cursor += 4;
+                for (; cb[cursor] != '"'; cursor++) ;
+                s = (short) (toInt(cb, a, cursor) & 0xFFFF);
+            }
         }
 
         if (cell == null) return null;
 
-//        cell.setT(t);
+        // The style index
+        cell.s = s;
 
         // get value
         int a;
@@ -199,7 +213,6 @@ class XMLRow extends Row {
             case INLINESTR: // inner string
                 a = getT(e);
                 if (a == cursor) { // null value
-//                    cell.setSv(null);
                     cell.setT(BLANK); // Reset type to BLANK if null value
                 } else {
                     cell.setSv(unescape(buf, cb, a, cursor));
@@ -293,37 +306,40 @@ class XMLRow extends Row {
      * inner string
      * <is><t>cell value</t></is>
      *
-     * @param e
-     * @return
+     * @param e the last index in char buffer
+     * @return the end index of string value
      */
     private int getT(int e) {
-        for (; cursor < e && (cb[cursor] != '<' || cb[cursor + 1] != 't' || cb[cursor + 2] != '>'); cursor++) ;
+        for (; cursor < e && (cb[cursor] != '<' || cb[cursor + 1] != 't'
+            || cb[cursor + 2] != '>'); cursor++) ;
         if (cursor == e) return cursor;
         int a = cursor += 3;
-        for (; cursor < e && (cb[cursor] != '<' || cb[cursor + 1] != '/' || cb[cursor + 2] != 't' || cb[cursor + 3] != '>'); cursor++) ;
+        for (; cursor < e && (cb[cursor] != '<' || cb[cursor + 1] != '/'
+            || cb[cursor + 2] != 't' || cb[cursor + 3] != '>'); cursor++) ;
         return a;
     }
 
     /**
-     * shared string
-     * <v>1</v>
+     * The string index in shared string table
      *
-     * @param e
-     * @return
+     * @param e the last index in char buffer
+     * @return the end index of int value
      */
     private int getV(int e) {
-        for (; cursor < e && (cb[cursor] != '<' || cb[cursor + 1] != 'v' || cb[cursor + 2] != '>'); cursor++) ;
+        for (; cursor < e && (cb[cursor] != '<' || cb[cursor + 1] != 'v'
+            || cb[cursor + 2] != '>'); cursor++) ;
         if (cursor == e) return cursor;
         int a = cursor += 3;
-        for (; cursor < e && (cb[cursor] != '<' || cb[cursor + 1] != '/' || cb[cursor + 2] != 'v' || cb[cursor + 3] != '>'); cursor++) ;
+        for (; cursor < e && (cb[cursor] != '<' || cb[cursor + 1] != '/'
+            || cb[cursor + 2] != 'v' || cb[cursor + 3] != '>'); cursor++) ;
         return a;
     }
 
     /**
      * function string
      *
-     * @param e
-     * @return
+     * @param e the last index in char buffer
+     * @return the end index of function value
      */
     @SuppressWarnings("unused")
     private int getF(int e) {
@@ -333,11 +349,11 @@ class XMLRow extends Row {
     }
 
     /**
-     * 26进制转10进制
+     * Convert to column index
      *
-     * @param a
-     * @param b
-     * @return
+     * @param a the start index
+     * @param b the end index
+     * @return the cell index
      */
     private int toCellIndex(int a, int b) {
         int n = 0;
diff --git a/src/main/java/cn/ttzero/excel/reader/XMLSheet.java b/src/main/java/cn/ttzero/excel/reader/XMLSheet.java
index 3527b330..816ae797 100644
--- a/src/main/java/cn/ttzero/excel/reader/XMLSheet.java
+++ b/src/main/java/cn/ttzero/excel/reader/XMLSheet.java
@@ -17,6 +17,7 @@
 package cn.ttzero.excel.reader;
 
 import cn.ttzero.excel.entity.TooManyColumnsException;
+import cn.ttzero.excel.entity.style.Styles;
 import cn.ttzero.excel.manager.Const;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
@@ -43,23 +44,54 @@ class XMLSheet implements Sheet {
     private int index // per sheet index of workbook
         , size = -1; // size of rows per sheet
     private Path path;
+    /**
+     * The Shared String Table
+     */
     private SharedStrings sst;
+    /**
+     * The {@link Styles}
+     */
+    private Styles styles;
     private int startRow = -1; // row index of data
     private HeaderRow header;
     private boolean hidden; // state hidden
 
+    /**
+     * Setting the worksheet name
+     *
+     * @param name the worksheet name
+     */
     void setName(String name) {
         this.name = name;
     }
 
+    /**
+     * Setting the worksheet xml path
+     *
+     * @param path the temp path
+     */
     void setPath(Path path) {
         this.path = path;
     }
 
+    /**
+     * Setting the Shared String Table
+     *
+     * @param sst the {@link SharedStrings}
+     */
     void setSst(SharedStrings sst) {
         this.sst = sst;
     }
 
+    /**
+     * Setting {@link Styles}
+     *
+     * @param styles the {@link Styles}
+     */
+    void setStyles(Styles styles) {
+        this.styles = styles;
+    }
+
     /**
      * The worksheet name
      *
@@ -229,7 +261,7 @@ class XMLSheet implements Sheet {
             eof = true;
         } else {
             eof = false;
-            sRow = new XMLRow(sst, this.startRow > 0 ? this.startRow : 1); // share row space
+            sRow = new XMLRow(sst, styles, this.startRow > 0 ? this.startRow : 1); // share row space
         }
 
         return this;
@@ -313,7 +345,7 @@ class XMLSheet implements Sheet {
         }
         boolean eof = cb[nChar] == '>';
         if (eof) {
-            this.heof = eof;
+            this.heof = true;
             return null;
         }
 
@@ -343,7 +375,7 @@ class XMLSheet implements Sheet {
         }
 
         // row
-        return new XMLRow(sst, this.startRow > 0 ? this.startRow : 1).with(cb, start, nChar - start);
+        return new XMLRow(sst, styles, this.startRow > 0 ? this.startRow : 1).with(cb, start, nChar - start);
     }
 
     /**
diff --git a/src/main/java/cn/ttzero/excel/util/DateUtil.java b/src/main/java/cn/ttzero/excel/util/DateUtil.java
index f69b283f..70bdd065 100644
--- a/src/main/java/cn/ttzero/excel/util/DateUtil.java
+++ b/src/main/java/cn/ttzero/excel/util/DateUtil.java
@@ -213,6 +213,13 @@ public class DateUtil {
         return Timestamp.from(Instant.ofEpochSecond((n - DAYS_1900_TO_1970) * 86400L).minusMillis(tz));
     }
 
+    public static LocalDate toLocalDate(int n) {
+        if (n < DAYS_1900_TO_1970) {
+            throw new UncheckedTypeException("ConstantNumber " + n + " can't convert to java.util.Date");
+        }
+        return LocalDate.ofEpochDay(n - DAYS_1900_TO_1970);
+    }
+
     public static java.sql.Timestamp toTimestamp(String dateStr) {
         // check format string
         if (dateStr.indexOf('/') == 4) {


Test patch:
diff --git a/src/test/java/cn/ttzero/excel/entity/WorkbookTest.java b/src/test/java/cn/ttzero/excel/entity/WorkbookTest.java
index a3b9aefc..21e8cf24 100644
--- a/src/test/java/cn/ttzero/excel/entity/WorkbookTest.java
+++ b/src/test/java/cn/ttzero/excel/entity/WorkbookTest.java
@@ -16,6 +16,10 @@
 
 package cn.ttzero.excel.entity;
 
+import cn.ttzero.excel.util.FileUtil;
+
+import java.io.IOException;
+import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.Random;
@@ -41,7 +45,10 @@ public class WorkbookTest {
         return new String(cache, 0, n);
     }
 
-    public static Path getOutputTestPath() {
+    public static Path getOutputTestPath() throws IOException {
+        if (!Files.exists(defaultTestPath)) {
+            FileUtil.mkdir(defaultTestPath);
+        }
         return defaultTestPath;
     }
 
diff --git a/src/test/java/cn/ttzero/excel/entity/style/StylesTest.java b/src/test/java/cn/ttzero/excel/entity/style/StylesTest.java
new file mode 100644
index 00000000..2352c06a
--- /dev/null
+++ b/src/test/java/cn/ttzero/excel/entity/style/StylesTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2019, guanquan.wang@yandex.com All Rights Reserved.
+ *
+ * Licensed 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 cn.ttzero.excel.entity.style;
+
+import cn.ttzero.excel.entity.I18N;
+import cn.ttzero.excel.entity.WorkbookTest;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.nio.file.Path;
+
+import static cn.ttzero.excel.Print.println;
+import static cn.ttzero.excel.entity.style.Styles.INDEX_NUMBER_FORMAT;
+import static cn.ttzero.excel.entity.style.Styles.testCodeIsDate;
+
+/**
+ * Create by guanquan.wang at 2019-06-06 16:00
+ */
+public class StylesTest {
+
+    private Styles styles;
+
+    @Before public void before() {
+        styles = Styles.create(new I18N());
+
+        // Built-In number format
+        styles.of(16 << INDEX_NUMBER_FORMAT);
+        styles.of(20 << INDEX_NUMBER_FORMAT);
+        styles.of(30 << INDEX_NUMBER_FORMAT);
+        styles.of(46 << INDEX_NUMBER_FORMAT);
+        styles.of(7 << INDEX_NUMBER_FORMAT); // Not data-time
+        styles.of(14 << INDEX_NUMBER_FORMAT);
+        styles.of(10 << INDEX_NUMBER_FORMAT); // not data-time
+        styles.of(13 << INDEX_NUMBER_FORMAT); // not data-time
+
+        // Customize
+        styles.of(styles.addNumFmt(new NumFmt("\"¥\"#,##0.00;\"¥\"\\-#,##0.00")));
+        styles.of(styles.addNumFmt(new NumFmt("[$-F800]dddd\\,\\ mmmm\\ dd\\,\\ yyyy")));
+        styles.of(styles.addNumFmt(new NumFmt("[DBNum1][$-804]yyyy\"年\"m\"月\"d\"日\";@")));
+        styles.of(styles.addNumFmt(new NumFmt("[DBNum1][$-804]yyyy\"年\"m\"月\";@")));
+        styles.of(styles.addNumFmt(new NumFmt("[DBNum1][$-804]m\"月\"d\"日\";@")));
+        styles.of(styles.addNumFmt(new NumFmt("yyyy\"年\"m\"月\"d\"日\";@")));
+        styles.of(styles.addNumFmt(new NumFmt("[$-409]yyyy/m/d\\ h:mm\\ AM/PM;@")));
+        styles.of(styles.addNumFmt(new NumFmt("yy/m/d;@")));
+        styles.of(styles.addNumFmt(new NumFmt("[$-409]mmmmm/yy;@")));
+        styles.of(styles.addNumFmt(new NumFmt("[$-409]d/mmm/yy;@")));
+        styles.of(styles.addNumFmt(new NumFmt("[$-409]dd/mmm/yy;@")));
+        styles.of(styles.addNumFmt(new NumFmt("[$-F400]h:mm:ss\\ AM/PM")));
+        styles.of(styles.addNumFmt(new NumFmt("[$-409]h:mm:ss\\ AM/PM;@")));
+        styles.of(styles.addNumFmt(new NumFmt("h\"时\"mm\"分\"ss\"秒\";@")));
+        styles.of(styles.addNumFmt(new NumFmt("上午/下午h\"时\"mm\"分\"ss\"秒\";@")));
+        styles.of(styles.addNumFmt(new NumFmt("[DBNum1][$-804]上午/下午h\"时\"mm\"分\";@")));
+        styles.of(styles.addNumFmt(new NumFmt("[$-2010000]yyyy/mm/dd;@")));
+        styles.of(styles.addNumFmt(new NumFmt("[$-C07]d\\.mmmm\\ yyyy;@")));
+        styles.of(styles.addNumFmt(new NumFmt("[$-FC19]dd\\ mmmm\\ yyyy\\ \\г\\.;@")));
+        styles.of(styles.addNumFmt(new NumFmt("[$-FC19]yyyy\\,\\ dd\\ mmmm;@")));
+        styles.of(styles.addNumFmt(new NumFmt("[$-80C]dddd\\ d\\ mmmm\\ yyyy;@")));
+        styles.of(styles.addNumFmt(new NumFmt("[$-44F]dd\\ mmmm\\ yyyy\\ dddd;@")));
+        styles.of(styles.addNumFmt(new NumFmt("[$-816]d\\ \"de\"\\ mmmm\\ \"de\"\\ yyyy;@")));
+        styles.of(styles.addNumFmt(new NumFmt("yyyy/mm/dd\\ hh:mm:ss")));
+        styles.of(styles.addNumFmt(new NumFmt("yyyy/mm/dd")));
+        styles.of(styles.addNumFmt(new NumFmt("m/d")));
+    }
+
+    @Test public void testTestCodeIsDate() {
+        assert !testCodeIsDate("\"¥\"#,##0.00;\"¥\"\\-#,##0.00");
+        assert testCodeIsDate("[$-F800]dddd\\,\\ mmmm\\ dd\\,\\ yyyy");
+        assert testCodeIsDate("[DBNum1][$-804]yyyy\"年\"m\"月\"d\"日\";@");
+        assert testCodeIsDate("[DBNum1][$-804]yyyy\"年\"m\"月\";@");
+        assert testCodeIsDate("[DBNum1][$-804]m\"月\"d\"日\";@");
+        assert testCodeIsDate("yyyy\"年\"m\"月\"d\"日\";@");
+        assert testCodeIsDate("[$-409]yyyy/m/d\\ h:mm\\ AM/PM;@");
+        assert testCodeIsDate("yy/m/d;@");
+        assert testCodeIsDate("[$-409]mmmmm/yy;@");
+        assert testCodeIsDate("[$-409]d/mmm/yy;@");
+        assert testCodeIsDate("[$-409]dd/mmm/yy;@");
+        assert testCodeIsDate("[$-F400]h:mm:ss\\ AM/PM");
+        assert testCodeIsDate("[$-409]h:mm:ss\\ AM/PM;@");
+        assert testCodeIsDate("h\"时\"mm\"分\"ss\"秒\";@");
+        assert testCodeIsDate("上午/下午h\"时\"mm\"分\"ss\"秒\";@");
+        assert testCodeIsDate("[DBNum1][$-804]上午/下午h\"时\"mm\"分\";@");
+        assert testCodeIsDate("[$-2010000]yyyy/mm/dd;@");
+        assert testCodeIsDate("[$-C07]d\\.mmmm\\ yyyy;@");
+        assert testCodeIsDate("[$-FC19]dd\\ mmmm\\ yyyy\\ \\г\\.;@");
+        assert testCodeIsDate("[$-FC19]yyyy\\,\\ dd\\ mmmm;@");
+        assert testCodeIsDate("[$-80C]dddd\\ d\\ mmmm\\ yyyy;@");
+        assert testCodeIsDate("[$-44F]dd\\ mmmm\\ yyyy\\ dddd;@");
+        assert testCodeIsDate("[$-816]d\\ \"de\"\\ mmmm\\ \"de\"\\ yyyy;@");
+        assert testCodeIsDate("yyyy/mm/dd\\ hh:mm:ss");
+        assert testCodeIsDate("yyyy/mm/dd");
+        assert testCodeIsDate("m/d");
+
+        assert testCodeIsDate("yyyy");
+        assert testCodeIsDate("m-d");
+        assert testCodeIsDate("yy/m");
+    }
+
+    @Test public void testFastTestDateFmt() throws IOException {
+        Path storagePath = WorkbookTest.getOutputTestPath().resolve("styles.xml");
+        styles.writeTo(storagePath);
+
+        Styles styles = Styles.load(storagePath);
+        for (int i = 0, size = styles.size(); i < size; i++) {
+           boolean isDate = styles.fastTestDateFmt(i);
+           if (i == 0 || i == 5 || i >= 7 && i <= 9) assert !isDate;
+           else assert isDate;
+        }
+    }
+}
diff --git a/src/test/java/cn/ttzero/excel/reader/SharedStringsTest.java b/src/test/java/cn/ttzero/excel/reader/SharedStringsTest.java
index 953610dc..39e16817 100644
--- a/src/test/java/cn/ttzero/excel/reader/SharedStringsTest.java
+++ b/src/test/java/cn/ttzero/excel/reader/SharedStringsTest.java
@@ -41,9 +41,6 @@ public class SharedStringsTest {
 
     @Before public void before() throws IOException {
         root = WorkbookTest.getOutputTestPath();
-        if (!Files.exists(root)) {
-            FileUtil.mkdir(root);
-        }
     }
 
     @After public void close() throws IOException {