Repository: wangguanquan/eec
Pull request #143 (2020-10-09 03:58:20)

Feature/fix#130
Support custome number format


Patch:
diff --git a/CHANGELOG b/CHANGELOG
index a9f48ae2..46398198 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,9 +1,3 @@
-Version 0.4.7 (2020-08-14)
--------------
-1. 安全更新，修复dom4j小于2.1.3版本可能启用XXE攻击。
-2. ExcelColumn注解增加comment属性，允许在Excel列头添加“批注”功能
-3. 修复一些已知BUG
-
 Version 0.4.6 (2020-04-20)
 -------------
 1. 什么也没做，0.4.4版本发布异常
diff --git a/README.md b/README.md
old mode 100755
new mode 100644
index ceeb4a80..40f07cd1
--- a/README.md
+++ b/README.md
@@ -436,11 +436,12 @@ pom.xml添加
 <dependency>
     <groupId>org.ttzero</groupId>
     <artifactId>eec-e3-support</artifactId>
-    <version>0.4.6</version>
+    <version>${eec.version}-SNAPSHOT</version>
 </dependency>
 ```
 
-你可以在 [search.maven.org](https://search.maven.org/artifact/org.ttzero/eec-e3-support) 查询eec-e3-support版本
+xls格式的读写目前处于开发中，只有发布SNAPSHOT版本，e3-support包含e3的依赖。
+
 
 #### 6. xls读取
 xls读取对方法式与xlsx完全一致
@@ -482,12 +483,6 @@ try (ExcelReader reader = ExcelReader.read(testResourceRoot().resolve("1.xlsx"))
 ```
 
 ## CHANGELOG
-Version 0.4.7 (2020-08-14)
--------------
-1. 安全更新，修复dom4j小于2.1.3版本可能启用XXE攻击。
-2. ExcelColumn注解增加comment属性，允许在Excel列头添加“批注”功能
-3. 修复一些已知BUG
-
 Version 0.4.6 (2020-04-20)
 -------------
 1. 什么也没做，0.4.4版本发布异常
@@ -506,13 +501,33 @@ Version 0.4.3 (2020-03-19)
 4. 解决打开EEC导出的空Worksheet时会弹出警告信息
 5. 缩小堆内存使用（最小6MB）
 
+Version 0.4.2 (2020-03-04)
+-------------
+1. 修复读poi生成的excel文件内容转对象时值为空的异常
+2. 修复读取某些空worksheet时抛IllegalArgumentException异常
+3. 使用slf4j统一日志接口，不强制用户使用log4j
+4. 字符串值默认使用inlineStr方式而非SharedString里从而提升一些写入速度
+5. 调整压缩等级提升一些压缩速度，最终文件比以前有所增加
+
+Version 0.4.1 (2020-03-03)
+-------------
+1. 修复ListObject导出文件时数据比实际数据少的BUG
+2. 修复ExcelColumn注解只有`share`属性时share值无效的BUG
+
+Version 0.4.0 (2020-02-24)
+-------------
+1. Worksheet增加`getDimension`方法返回单元格范围，替换原`getSize`方法
+2. 支持获取单元格公式
+3. 修复SNAPSHOT版导出excel文件无法正常打开的错误
+4. ExcelReader增加打开模式，可以指定读取单元格公式或者`合并单元格`的值。
+
 [更多...](./CHANGELOG)
 
 [travis]: https://travis-ci.org/wangguanquan/eec
 [travis-image]: https://travis-ci.org/wangguanquan/eec.png?branch=master
 
 [releases]: https://github.com/wangguanquan/eec/releases
-[release-image]: http://img.shields.io/badge/release-0.4.7-blue.svg?style=flat
+[release-image]: http://img.shields.io/badge/release-0.4.6-blue.svg?style=flat
 
 [license]: http://www.apache.org/licenses/LICENSE-2.0
 [license-image]: http://img.shields.io/badge/license-Apache--2-blue.svg?style=flat
diff --git a/pom.xml b/pom.xml
index dde59dfc..498fa5d0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
 
   <groupId>org.ttzero</groupId>
   <artifactId>eec</artifactId>
-  <version>0.4.8-SNAPSHOT</version>
+  <version>0.4.7-SNAPSHOT</version>
   <name>Excel Write/Read Framework</name>
   <packaging>jar</packaging>
   <url>https://www.ttzero.org</url>
@@ -20,23 +20,29 @@
     <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
     <maven.compiler.source>${java.version}</maven.compiler.source>
     <maven.compiler.target>${java.version}</maven.compiler.target>
-    <dom4j.version>2.1.3</dom4j.version>
+    <dom4j.version>1.6.1</dom4j.version>
+    <slf4j.version>1.7.30</slf4j.version>
   </properties>
 
   <dependencies>
     <dependency>
-      <groupId>org.dom4j</groupId>
+      <groupId>dom4j</groupId>
       <artifactId>dom4j</artifactId>
       <version>${dom4j.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+      <version>${slf4j.version}</version>
+    </dependency>
 
+    <!-- TEST -->
     <dependency>
       <groupId>ch.qos.logback</groupId>
       <artifactId>logback-classic</artifactId>
       <version>1.2.3</version>
+      <scope>test</scope>
     </dependency>
-
-    <!-- TEST -->
     <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
diff --git a/src/main/java/org/ttzero/excel/annotation/ExcelColumn.java b/src/main/java/org/ttzero/excel/annotation/ExcelColumn.java
index 6862382b..8139e578 100644
--- a/src/main/java/org/ttzero/excel/annotation/ExcelColumn.java
+++ b/src/main/java/org/ttzero/excel/annotation/ExcelColumn.java
@@ -59,4 +59,20 @@ public @interface ExcelColumn {
      * @return a header {@link HeaderComment}
      */
     HeaderComment comment() default @HeaderComment;
+
+    /**
+     * Specify the cell format.
+     * <p>
+     * It only supports the format specified by Office excel, please refer
+     * to {@link org.ttzero.excel.entity.style.NumFmt}.
+     * <p>
+     * If you are not sure whether the format is correct, please open
+     * Office excel{@code >} Format cell{@code >} Custom to debug the
+     * custom format here.
+     * <p>
+     * Note: It only used on Number or Date(include Timestamp, Time and java.time.*) field.
+     *
+     * @return the data format string
+     */
+    String format() default "";
 }
diff --git a/src/main/java/org/ttzero/excel/entity/AbstractTemplate.java b/src/main/java/org/ttzero/excel/entity/AbstractTemplate.java
index 9376485d..71613024 100644
--- a/src/main/java/org/ttzero/excel/entity/AbstractTemplate.java
+++ b/src/main/java/org/ttzero/excel/entity/AbstractTemplate.java
@@ -186,8 +186,9 @@ public abstract class AbstractTemplate {
         while (iterator.hasNext()) {
             Element si = iterator.next(), t = si.element("t");
             String txt = t.getText();
-            if (isPlaceholder(txt)) { // 判断是否是占位符
-                // 如果是占位符则对值进行替换
+            // Determine whether it is a placeholder text
+            if (isPlaceholder(txt)) {
+                // Replace if it is a placeholder text
                 t.setText(getValue(txt));
                 n++;
             }
@@ -257,8 +258,9 @@ public abstract class AbstractTemplate {
                 if (t != null && inlineStr.equals(t.getValue())) {
                     Element tv = cell.element("is").element("t");
                     String txt = tv.getText();
-                    if (isPlaceholder(txt)) { // 判断是否是占位符
-                        // 如果是占位符则对值进行替换
+                    // Determine whether it is a placeholder text
+                    if (isPlaceholder(txt)) {
+                        // Replace if it is a placeholder text
                         tv.setText(getValue(txt));
                         n++;
                     }
diff --git a/src/main/java/org/ttzero/excel/entity/Comments.java b/src/main/java/org/ttzero/excel/entity/Comments.java
index 73126037..09d9997c 100644
--- a/src/main/java/org/ttzero/excel/entity/Comments.java
+++ b/src/main/java/org/ttzero/excel/entity/Comments.java
@@ -42,12 +42,12 @@ import static org.ttzero.excel.util.StringUtil.isNotEmpty;
 public class Comments implements Storable, Closeable {
 
     /** Comments Cache*/
-    private List<C> commentList;
-    private int id;
-    private String author;
+    private final List<C> commentList;
+    private final int id;
+    private final String author;
 //    private final static int CACHE_SIZE = 20;
 
-    public Comments(int id, String author) {
+    Comments(int id, String author) {
         this.id = id;
         this.author = author;
         commentList = new ArrayList<>();
diff --git a/src/main/java/org/ttzero/excel/entity/EmptySheet.java b/src/main/java/org/ttzero/excel/entity/EmptySheet.java
index dacab4e4..05186753 100644
--- a/src/main/java/org/ttzero/excel/entity/EmptySheet.java
+++ b/src/main/java/org/ttzero/excel/entity/EmptySheet.java
@@ -69,6 +69,7 @@ public class EmptySheet extends Sheet {
      *
      * @return 0
      */
+    @Override
     public int size() {
         return 0;
     }
diff --git a/src/main/java/org/ttzero/excel/entity/I18N.java b/src/main/java/org/ttzero/excel/entity/I18N.java
index be17d65e..949dfa0c 100644
--- a/src/main/java/org/ttzero/excel/entity/I18N.java
+++ b/src/main/java/org/ttzero/excel/entity/I18N.java
@@ -20,6 +20,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
+import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.Locale;
 import java.util.Properties;
@@ -28,7 +29,7 @@ import java.util.Properties;
  * @author guanquan.wang on 2018-10-13
  */
 public class I18N {
-    private Properties pro;
+    private final Properties pro;
 
     public I18N() {
         Locale locale = Locale.getDefault();
@@ -40,7 +41,7 @@ public class I18N {
                 is = I18N.class.getClassLoader().getResourceAsStream("I18N/" + fn.replace("_", "zh-CN"));
             }
             if (is != null) {
-                try (Reader reader = new InputStreamReader(is, "UTF-8")) {
+                try (Reader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) {
                     pro.load(reader);
                 }
             }
@@ -81,7 +82,7 @@ public class I18N {
         String msg = pro.getProperty(code, code);
         char[] oldValue = msg.toCharArray();
         int[] indexs = search(oldValue);
-        int len = indexs.length >= args.length ? args.length : indexs.length, size = 0;
+        int len = Math.min(indexs.length, args.length), size = 0;
         for (int i = 0; i < len; size += args[i++].length()) ;
         StringBuilder buf = new StringBuilder(oldValue.length + size - (len << 1));
         buf.append(oldValue, 0, indexs[0]).append(args[0]);
diff --git a/src/main/java/org/ttzero/excel/entity/ICellValueAndStyle.java b/src/main/java/org/ttzero/excel/entity/ICellValueAndStyle.java
index e9044461..1958ce6d 100644
--- a/src/main/java/org/ttzero/excel/entity/ICellValueAndStyle.java
+++ b/src/main/java/org/ttzero/excel/entity/ICellValueAndStyle.java
@@ -87,19 +87,19 @@ public interface ICellValueAndStyle {
         if (isString(clazz)) {
             cell.setSv(e.toString());
         } else if (isDate(clazz)) {
-            cell.setAv(DateUtil.toDateValue((java.util.Date) e));
+            cell.setIv(DateUtil.toDateTimeValue((java.util.Date) e));
         } else if (isDateTime(clazz)) {
             cell.setIv(DateUtil.toDateTimeValue((Timestamp) e));
         } else if (isChar(clazz)) {
-            Character c = (Character) e;
+            char c = (Character) e;
             if (hasIntProcessor) conversion(row, cell, c, hc);
             else cell.setCv(c);
         } else if (isShort(clazz)) {
-            Short t = (Short) e;
+            short t = (Short) e;
             if (hasIntProcessor) conversion(row, cell, t, hc);
             else cell.setNv(t);
         } else if (isInt(clazz)) {
-            Integer n = (Integer) e;
+            int n = (Integer) e;
             if (hasIntProcessor) conversion(row, cell, n, hc);
             else cell.setNv(n);
         } else if (isLong(clazz)) {
@@ -113,7 +113,7 @@ public interface ICellValueAndStyle {
         } else if (isBigDecimal(clazz)) {
             cell.setMv((BigDecimal) e);
         } else if (isLocalDate(clazz)) {
-            cell.setAv(DateUtil.toDateValue((java.time.LocalDate) e));
+            cell.setIv(DateUtil.toDateValue((java.time.LocalDate) e));
         } else if (isLocalDateTime(clazz)) {
             cell.setIv(DateUtil.toDateTimeValue((java.time.LocalDateTime) e));
         } else if (isTime(clazz)) {
diff --git a/src/main/java/org/ttzero/excel/entity/ListSheet.java b/src/main/java/org/ttzero/excel/entity/ListSheet.java
index c5a58ce7..0baa2a2e 100644
--- a/src/main/java/org/ttzero/excel/entity/ListSheet.java
+++ b/src/main/java/org/ttzero/excel/entity/ListSheet.java
@@ -20,26 +20,28 @@ import org.ttzero.excel.annotation.ExcelColumn;
 import org.ttzero.excel.annotation.HeaderComment;
 import org.ttzero.excel.reader.Cell;
 import org.ttzero.excel.annotation.IgnoreExport;
-import org.ttzero.excel.util.StringUtil;
 
 import java.beans.IntrospectionException;
 import java.beans.Introspector;
 import java.beans.PropertyDescriptor;
 import java.io.IOException;
+import java.lang.reflect.AccessibleObject;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.ParameterizedType;
 import java.util.ArrayList;
-import java.util.LinkedHashMap;
+import java.util.Collection;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-import static org.ttzero.excel.util.ReflectUtil.indexOf;
+
 import static org.ttzero.excel.util.ReflectUtil.listDeclaredFields;
 import static org.ttzero.excel.util.ReflectUtil.listReadMethods;
-import static org.ttzero.excel.util.ReflectUtil.mapping;
+import static org.ttzero.excel.util.StringUtil.EMPTY;
 import static org.ttzero.excel.util.StringUtil.isNotEmpty;
+import static org.ttzero.excel.util.StringUtil.isEmpty;
 
 /**
  * List is the most important data source, you can pass all
@@ -252,7 +254,9 @@ public class ListSheet<T> extends Sheet {
                     cell.clear();
 
                     Object e;
-                    if (methods[i] != null)
+                    if (columns[i].isIgnoreValue())
+                        e = null;
+                    else if (methods[i] != null)
                         e = methods[i].invoke(o);
                     else
                         e = fields[i].get(o);
@@ -310,12 +314,10 @@ public class ListSheet<T> extends Sheet {
         } else if (start > 0) data.clear();
     }
 
-    private static final String[] exclude = {"serialVersionUID", "this$0"};
-
     // Returns the reflect <T> type
     private Class<?> getTClass() {
         Class<?> clazz;
-        if (getClass().getGenericSuperclass() instanceof  ParameterizedType) {
+        if (getClass().getGenericSuperclass() instanceof ParameterizedType) {
             @SuppressWarnings({"unchecked", "retype"})
             Class<?> tClass = (Class<T>) ((ParameterizedType) getClass()
                 .getGenericSuperclass()).getActualTypeArguments()[0];
@@ -328,65 +330,50 @@ public class ListSheet<T> extends Sheet {
         return clazz;
     }
 
-    private int methodMapping(Class<?> clazz, Method[] readMethods, Map<String, Method> tmp) {
-        try {
-            PropertyDescriptor[] propertyDescriptors = Introspector.getBeanInfo(clazz)
-                .getPropertyDescriptors();
-            Method[] allMethods = clazz.getMethods()
-                , mergedMethods = new Method[propertyDescriptors.length];
-            for (int i = 0; i < propertyDescriptors.length; i++) {
-                Method method = propertyDescriptors[i].getReadMethod();
-                if (method == null) continue;
-                int index = indexOf(allMethods, method);
-                mergedMethods[i] = index >= 0 ? allMethods[index] : method;
-            }
-
-            return mapping(readMethods, tmp, propertyDescriptors, mergedMethods);
-        } catch (IntrospectionException e) {
-            what("Get " + clazz + " property descriptor failed.");
-        }
-        return 0;
-    }
-
     /**
      * Get the first object of the object array witch is not NULL,
      * reflect all declared fields, and then do the following steps
      * <p>
-     * step 1. If there is has {@link ExcelColumn} annotation, the value of
+     * step 1. If the method has {@link ExcelColumn} annotation, the value of
      * this annotation is used as the column name.
      * <p>
-     * step 2. If the {@link ExcelColumn} annotation has no value or no
-     * {@link ExcelColumn} annotation, the field name is used as the column name.
+     * step 2. If the {@link ExcelColumn} annotation has no value or empty value,
+     * the field name is used as the column name.
      * <p>
-     * step 3. Skip this Field if field has a {@link IgnoreExport} annotation,
-     * or the field which has not getter method and has not {@link ExcelColumn} annotation.
+     * step 3. If the field has {@link ExcelColumn} annotation, the value of
+     * this annotation is used as the column name.
+     * <p>
+     * step 4. Skip this Field if it has a {@link IgnoreExport} annotation,
+     * or the field which has not {@link ExcelColumn} annotation.
      * <p>
      * The column order is the same as the order in declared fields.
      *
-     * @return the field array
+     * @return the column array length
      */
     private int init() {
         Class<?> clazz = getTClass();
         if (clazz == null) return 0;
 
-        // The main source
-        Field[] declaredFields = listDeclaredFields(clazz);
-
-        Method[] readMethods = null;
+        Map<String, Method> tmp = new HashMap<>();
         try {
-            readMethods = listReadMethods(clazz, method -> method.getAnnotation(ExcelColumn.class) != null);
+            PropertyDescriptor[] propertyDescriptors = Introspector.getBeanInfo(clazz)
+                    .getPropertyDescriptors();
+            for (PropertyDescriptor pd : propertyDescriptors) {
+                Method method = pd.getReadMethod();
+                if (method == null) continue;
+                tmp.put(pd.getName(), method);
+            }
         } catch (IntrospectionException e) {
-            what("Get " + clazz + " read declared failed.");
+            LOGGER.warn("Get class {} methods failed.", clazz);
         }
 
-        Map<String, Method> tmp = new LinkedHashMap<>();
-
-        int readLength = methodMapping(clazz, readMethods, tmp);
+        Field[] declaredFields = listDeclaredFields(clazz);
 
         if (!hasHeaderColumns()) {
             // Get ExcelColumn annotation method
-            methods = new Method[declaredFields.length + readLength];
+            methods = new Method[declaredFields.length + tmp.size()];
             List<Column> list = new ArrayList<>(declaredFields.length);
+
             int i = 0;
             for (; i < declaredFields.length; i++) {
                 Field field = declaredFields[i];
@@ -401,56 +388,67 @@ public class ListSheet<T> extends Sheet {
                         continue;
                     }
 
-                    method.setAccessible(true);
-                    methods[i] = method;
-                    ExcelColumn mec = method.getAnnotation(ExcelColumn.class);
-                    if (mec != null && isNotEmpty(mec.value())) {
-                        Column column = new Column(mec.value(), field.getName(), method.getReturnType())
-                            .setShare(mec.share());
-                        column.headerComment = createComment(method.getAnnotation(HeaderComment.class), mec.comment());
+                    Column column = createColumn(method);
+                    if (column != null) {
+                        methods[i] = method;
+                        column.clazz = method.getReturnType();
+                        column.key = gs;
+                        if (isEmpty(column.name)) {
+                            column.name = gs;
+                        }
                         list.add(column);
                         continue;
                     }
                 }
 
-                // Ignore annotation on field
-                IgnoreExport notExport = field.getAnnotation(IgnoreExport.class);
-                if (notExport != null || StringUtil.indexOf(exclude, gs.substring(gs.lastIndexOf('.') + 1)) >= 0) {
-                    declaredFields[i] = null;
+                Column column = createColumn(field);
+                if (column != null) {
+                    list.add(column);
+                    column.key = gs;
+                    if (isEmpty(column.name)) {
+                        column.name = gs;
+                    }
+                    if (method != null) {
+                        column.clazz = method.getReturnType();
+                        methods[i] = method;
+                    } else column.clazz = field.getType();
                     continue;
                 }
 
-                ExcelColumn ec = field.getAnnotation(ExcelColumn.class);
-                HeaderComment hc = field.getAnnotation(HeaderComment.class);
-                if (ec != null && isNotEmpty(ec.value())) {
-                    Column column = new Column(ec.value(), field.getName(), field.getType())
-                        .setShare(ec.share());
-                    column.headerComment = createComment(hc, ec.comment());
-                    list.add(column);
-                } else if (method != null) {
-                    Column column = new Column(field.getName(), field.getName(), field.getType())
-                        .setShare(ec != null && ec.share());
-                    column.headerComment = createComment(hc, ec != null ? ec.comment() : null);
-                    list.add(column);
-                }
+                // Ignore others
+                declaredFields[i] = null;
             }
-            if (readLength > 0) {
-                for (int j = 0; j < readLength; j++) {
-                    Method readMethod = readMethods[j];
-                    readMethod.setAccessible(true);
-                    methods[i++] = readMethod;
-                    ExcelColumn mec = readMethod.getAnnotation(ExcelColumn.class);
-                    Column column = new Column(mec.value(), readMethod.getName(), readMethod.getReturnType())
-                        .setShare(mec.share());
-                    column.headerComment = createComment(readMethod.getAnnotation(HeaderComment.class), mec.comment());
-                    list.add(column);
+
+            // Collect the method which has ExcelColumn annotation
+            Method[] readMethods = null;
+            try {
+                Collection<Method> values = tmp.values();
+                readMethods = listReadMethods(clazz, method -> method.getAnnotation(ExcelColumn.class) != null
+                        && !values.contains(method));
+            } catch (IntrospectionException e) {
+                // Ignore
+            }
+
+            if (readMethods != null) {
+                for (Method method : readMethods) {
+                    Column column = createColumn(method);
+                    if (column != null) {
+                        methods[i++] = method;
+                        list.add(column);
+                        column.clazz = method.getReturnType();
+                        column.key = method.getName();
+                        if (isEmpty(column.name)) {
+                            column.name = method.getName();
+                        }
+                    }
                 }
             }
+
             // No column to write
             if (list.isEmpty()) {
                 headerReady = eof = shouldClose = true;
                 this.end = 0;
-                what("Class [" + clazz + "] do not contains getter method and ExcelColumn annotation.");
+                LOGGER.warn("Class [{}] do not contains properties to export.", clazz);
                 return 0;
             }
             columns = new Column[list.size()];
@@ -458,7 +456,8 @@ public class ListSheet<T> extends Sheet {
             for (i = 0; i < columns.length; i++) {
                 columns[i].styles = workbook.getStyles();
             }
-            // Clear not export fields
+
+            // Clean
             i = 0;
             fields = new Field[columns.length];
             for (int j = 0; j < declaredFields.length; j++) {
@@ -473,7 +472,7 @@ public class ListSheet<T> extends Sheet {
                 System.arraycopy(methods, declaredFields.length, methods, i, methods.length - declaredFields.length);
                 i += methods.length - declaredFields.length;
             }
-            return i + readLength;
+            return i;
         } else {
             fields = new Field[columns.length];
             methods = new Method[columns.length];
@@ -491,11 +490,10 @@ public class ListSheet<T> extends Sheet {
                 }
 
                 if (methods[i] == null && fields[i] == null) {
-                    throw new ExcelWriteException("Column [" + hc.getName() + "(" + hc.key + ")"
-                        + "] not declare in class " + clazz);
-                }
-
-                if (hc.getClazz() == null) {
+                    LOGGER.warn("Column [" + hc.getName() + "(" + hc.key + ")"
+                            + "] not declare in class " + clazz);
+                    hc.ignoreValue();
+                } else if (hc.getClazz() == null) {
                     hc.setClazz(methods[i] != null ? methods[i].getReturnType() : fields[i].getType());
                 }
             }
@@ -503,6 +501,23 @@ public class ListSheet<T> extends Sheet {
         }
     }
 
+    private Column createColumn(AccessibleObject ao) {
+        if (ao.getAnnotation(IgnoreExport.class) != null) return null;
+        ao.setAccessible(true);
+        ExcelColumn ec = ao.getAnnotation(ExcelColumn.class);
+        if (ec != null) {
+            Column column = new Column(ec.value(), EMPTY, ec.share());
+            // Comment
+            column.headerComment = createComment(ao.getAnnotation(HeaderComment.class), ec.comment());
+            // Number format
+            if (isNotEmpty(ec.format())) {
+                column.setNumFmt(ec.format());
+            }
+            return column;
+        }
+        return null;
+    }
+
     private Comment createComment(HeaderComment precedence, HeaderComment normal) {
         HeaderComment comment = precedence != null ? precedence : normal;
         if (comment != null && (isNotEmpty(comment.value()) || isNotEmpty(comment.title()))) {
@@ -546,7 +561,7 @@ public class ListSheet<T> extends Sheet {
             blockSize = rowLimit - rows;
         }
         int end = start + blockSize;
-        return end <= this.end ? end : this.end;
+        return Math.min(end, this.end);
     }
 
     /**
@@ -571,6 +586,7 @@ public class ListSheet<T> extends Sheet {
     /**
      * Split worksheet data
      */
+    @Override
     protected void paging() {
         int len = dataSize(), limit = sheetWriter.getRowLimit() - 1;
         // paging
@@ -586,7 +602,7 @@ public class ListSheet<T> extends Sheet {
                 @SuppressWarnings("unchecked")
                 ListSheet<T> copy = getClass().cast(clone());
                 copy.start = i;
-                copy.end = (i = i + limit < len ? i + limit : len);
+                copy.end = (i = Math.min(i + limit, len));
                 copy.size = copy.end - copy.start;
                 copy.eof = copy.size == limit;
                 workbook.insertSheet(n++, copy);
diff --git a/src/main/java/org/ttzero/excel/entity/RowBlock.java b/src/main/java/org/ttzero/excel/entity/RowBlock.java
index c5720ce6..20fb7958 100644
--- a/src/main/java/org/ttzero/excel/entity/RowBlock.java
+++ b/src/main/java/org/ttzero/excel/entity/RowBlock.java
@@ -35,7 +35,7 @@ public class RowBlock implements Iterator<Row> {
     private Row[] rows;
     private int i, n, total = 0;
     private boolean eof;
-    private int limit;
+    private final int limit;
 
     public RowBlock() {
         this(ROW_BLOCK_SIZE);
@@ -109,10 +109,12 @@ public class RowBlock implements Iterator<Row> {
         return this;
     }
 
+    @Override
     public boolean hasNext() {
         return i < n;
     }
 
+    @Override
     public Row next() {
         return rows[i++];
     }
diff --git a/src/main/java/org/ttzero/excel/entity/SharedStringTable.java b/src/main/java/org/ttzero/excel/entity/SharedStringTable.java
index 375f84d8..38b0aed3 100644
--- a/src/main/java/org/ttzero/excel/entity/SharedStringTable.java
+++ b/src/main/java/org/ttzero/excel/entity/SharedStringTable.java
@@ -40,14 +40,14 @@ public class SharedStringTable implements Closeable, Iterable<String> {
     /**
      * The temp path
      */
-    private Path temp;
+    private final Path temp;
 
     /**
      * The total unique word in workbook.
      */
     private int count;
 
-    private SeekableByteChannel channel;
+    private final SeekableByteChannel channel;
 
     /**
      * Byte array buffer
@@ -469,12 +469,12 @@ public class SharedStringTable implements Closeable, Iterable<String> {
     }
 
     private static class SSTIterator implements Iterator<String> {
-        private SeekableByteChannel channel;
-        private ByteBuffer buffer;
+        private final SeekableByteChannel channel;
+        private final ByteBuffer buffer;
         private byte[] bytes;
         @SuppressWarnings("unused")
         private int count; // ignore
-        private char[] chars;
+        private final char[] chars;
         private SSTIterator(Path temp) {
             try {
                 channel = Files.newByteChannel(temp, StandardOpenOption.READ);
diff --git a/src/main/java/org/ttzero/excel/entity/SharedStrings.java b/src/main/java/org/ttzero/excel/entity/SharedStrings.java
index bce617ac..c4d333f4 100644
--- a/src/main/java/org/ttzero/excel/entity/SharedStrings.java
+++ b/src/main/java/org/ttzero/excel/entity/SharedStrings.java
@@ -83,7 +83,7 @@ public class SharedStrings implements Storable, Closeable {
     /**
      * Cache ASCII value
      */
-    private int[] ascii;
+    private final int[] ascii;
     private Path temp;
     private ExtBufferedWriter writer;
 
@@ -105,7 +105,7 @@ public class SharedStrings implements Storable, Closeable {
     /**
      * The number of expected insertions to the constructed bloom
      */
-    private int expectedInsertions = 1 << 17;
+    private final int expectedInsertions = 1 << 17;
 
     SharedStrings() {
         hot = FixSizeLRUCache.create();
@@ -132,7 +132,7 @@ public class SharedStrings implements Storable, Closeable {
         }
     }
 
-    private ThreadLocal<char[]> charCache = ThreadLocal.withInitial(() -> new char[1]);
+    private final ThreadLocal<char[]> charCache = ThreadLocal.withInitial(() -> new char[1]);
 
     /**
      * Getting the character value index (zero base)
diff --git a/src/main/java/org/ttzero/excel/entity/Sheet.java b/src/main/java/org/ttzero/excel/entity/Sheet.java
index 95b3cb19..9f24fb9d 100644
--- a/src/main/java/org/ttzero/excel/entity/Sheet.java
+++ b/src/main/java/org/ttzero/excel/entity/Sheet.java
@@ -17,6 +17,8 @@
 package org.ttzero.excel.entity;
 
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.ttzero.excel.annotation.TopNS;
 import org.ttzero.excel.entity.e7.XMLWorksheetWriter;
 import org.ttzero.excel.entity.style.Border;
@@ -56,6 +58,7 @@ import static org.ttzero.excel.entity.IWorksheetWriter.isLocalTime;
 import static org.ttzero.excel.entity.IWorksheetWriter.isLong;
 import static org.ttzero.excel.entity.IWorksheetWriter.isString;
 import static org.ttzero.excel.entity.IWorksheetWriter.isTime;
+import static org.ttzero.excel.entity.style.Styles.INDEX_BORDER;
 import static org.ttzero.excel.manager.Const.ROW_BLOCK_SIZE;
 import static org.ttzero.excel.util.StringUtil.isEmpty;
 
@@ -91,8 +94,11 @@ import static org.ttzero.excel.util.StringUtil.isEmpty;
  *
  * @author guanquan.wang on 2017/9/26.
  */
-@TopNS(prefix = {"", "r"}, value = "worksheet", uri = {Const.SCHEMA_MAIN, Const.Relationship.RELATIONSHIP})
+@TopNS(prefix = {"", "r"}, value = "worksheet"
+        , uri = {Const.SCHEMA_MAIN, Const.Relationship.RELATIONSHIP})
 public abstract class Sheet implements Cloneable, Storable {
+    protected final Logger LOGGER = LoggerFactory.getLogger(getClass());
+
     protected Workbook workbook;
 
     protected String name;
@@ -247,6 +253,14 @@ public abstract class Sheet implements Cloneable, Storable {
         public int o;
         public Styles styles;
         public Comment headerComment, cellComment;
+        /**
+         * Specify the cell number format
+         */
+        private NumFmt numFmt;
+        /**
+         * Only export column name and ignore value
+         */
+        private boolean ignoreValue;
 
         /**
          * Constructor Column
@@ -709,12 +723,25 @@ public abstract class Sheet implements Cloneable, Storable {
             return this;
         }
 
-        private static final NumFmt ip = new NumFmt("0%_);[Red]\\(0%\\)") // 整数百分比
-            , ir = new NumFmt("¥0_);[Red]\\(¥0\\)") // 整数人民币
-            , fp = new NumFmt("0.00%_);[Red]\\(0.00%\\)") // 小数百分比
-            , fr = new NumFmt("¥0.00_);[Red]\\(¥0.00\\)") // 小数人民币
-            , tm = new NumFmt("hh:mm:ss") // 时分秒
-            ;
+        /**
+         * Setting a cell format of number or date type
+         *
+         * @param code the format string
+         * @return the {@link Sheet.Column}
+         */
+        public Column setNumFmt(String code) {
+            this.numFmt = new NumFmt(code);
+            return this;
+        }
+
+        /**
+         * Returns the column {@link NumFmt}
+         *
+         * @return number format
+         */
+        public NumFmt getNumFmt() {
+            return numFmt != null ? numFmt : styles.getNumFmt(cellStyle);
+        }
 
         /**
          * Returns default style based on cell type
@@ -727,42 +754,48 @@ public abstract class Sheet implements Cloneable, Storable {
             if (isString(clazz)) {
                 style = Styles.defaultStringBorderStyle();
             } else if (isDate(clazz) || isLocalDate(clazz)) {
-                style = Styles.defaultDateBorderStyle();
+                style = styles.addNumFmt(new NumFmt("yyyy\\-mm\\-dd")) | (1 << INDEX_BORDER) | Horizontals.CENTER;
             } else if (isDateTime(clazz) || isLocalDateTime(clazz)) {
-                style = Styles.defaultTimestampBorderStyle();
+                style = styles.addNumFmt(new NumFmt("yyyy\\-mm\\-dd\\ hh:mm:ss")) | (1 << INDEX_BORDER) | Horizontals.CENTER;
             } else if (isBool(clazz) || isChar(clazz)) {
                 style = Styles.clearHorizontal(Styles.defaultStringBorderStyle()) | Horizontals.CENTER;
             } else if (isInt(clazz) || isLong(clazz)) {
                 style = Styles.defaultIntBorderStyle();
                 switch (type) {
-                    case Const.ColumnType.NORMAL: // 正常显示数字
+                    case Const.ColumnType.NORMAL:
                         break;
-                    case Const.ColumnType.PARENTAGE: // 百分比显示
-                        style = Styles.clearNumfmt(style) | styles.addNumFmt(ip);
+                    case Const.ColumnType.PARENTAGE:
+                        style = Styles.clearNumFmt(style) | styles.addNumFmt(new NumFmt("0%_);[Red]-0% "));
                         break;
-                    case Const.ColumnType.RMB: // 显示人民币
-                        style = Styles.clearNumfmt(style) | styles.addNumFmt(ir);
+                    case Const.ColumnType.RMB:
+                        style = Styles.clearNumFmt(style) | styles.addNumFmt(new NumFmt("¥0_);[Red]-¥0 "));
                         break;
                     default:
                 }
             } else if (isFloat(clazz) || isDouble(clazz) || isBigDecimal(clazz)) {
                 style = Styles.defaultDoubleBorderStyle();
                 switch (type) {
-                    case Const.ColumnType.NORMAL: // 正常显示数字
+                    case Const.ColumnType.NORMAL:
                         break;
-                    case Const.ColumnType.PARENTAGE: // 百分比显示
-                        style = Styles.clearNumfmt(style) | styles.addNumFmt(fp);
+                    case Const.ColumnType.PARENTAGE:
+                        style = Styles.clearNumFmt(style) | styles.addNumFmt(new NumFmt("0.00%_);[Red]-0.00% "));
                         break;
-                    case Const.ColumnType.RMB: // 显示人民币
-                        style = Styles.clearNumfmt(style) | styles.addNumFmt(fr);
+                    case Const.ColumnType.RMB:
+                        style = Styles.clearNumFmt(style) | styles.addNumFmt(new NumFmt("¥0.00_);[Red]-¥0.00 "));
                         break;
                     default:
                 }
             } else if (isTime(clazz) || isLocalTime(clazz)) {
-                style = Styles.clearNumfmt(Styles.defaultDateBorderStyle()) | styles.addNumFmt(tm);
+                style =  styles.addNumFmt(new NumFmt("hh:mm:ss")) | (1 << INDEX_BORDER) | Horizontals.CENTER;
             } else {
-                style = 0; // Auto-style
+                style = (1 << Styles.INDEX_FONT) | (1 << INDEX_BORDER); // Auto-style
+            }
+
+            // Reset custom number format if specified.
+            if (numFmt != null) {
+                style = Styles.clearNumFmt(style) | styles.addNumFmt(numFmt);
             }
+
             return style;
         }
 
@@ -777,6 +810,21 @@ public abstract class Sheet implements Cloneable, Storable {
             }
             return cellStyle = getCellStyle(clazz);
         }
+
+        /**
+         * @return bool
+         */
+        public boolean isIgnoreValue() {
+            return ignoreValue;
+        }
+
+        /**
+         * Ignore value
+         */
+        Column ignoreValue() {
+            this.ignoreValue = true;
+            return this;
+        }
     }
 
     /**
@@ -1178,7 +1226,7 @@ public abstract class Sheet implements Cloneable, Storable {
         if (headStyle == 0) {
             Styles styles = workbook.getStyles();
             Font font = new Font(workbook.getI18N().getOrElse("local-font-family", "Arial")
-                , 11, Font.Style.bold, Color.white);
+                , 11, Font.Style.BOLD, Color.white);
             headStyle = styles.of(styles.addFont(font)
                 | styles.addFill(Fill.parse("solid #666699"))
                 | styles.addBorder(Border.parse("thin black"))
@@ -1371,7 +1419,7 @@ public abstract class Sheet implements Cloneable, Storable {
         return c;
     }
 
-    private static ThreadLocal<char[][]> cache
+    private static final ThreadLocal<char[][]> cache
         = ThreadLocal.withInitial(() -> new char[][]{ {65}, {65, 65}, {65, 65, 65} });
 
     /**
diff --git a/src/main/java/org/ttzero/excel/entity/WaterMark.java b/src/main/java/org/ttzero/excel/entity/WaterMark.java
index 9a44b3b2..10e2d536 100644
--- a/src/main/java/org/ttzero/excel/entity/WaterMark.java
+++ b/src/main/java/org/ttzero/excel/entity/WaterMark.java
@@ -39,7 +39,7 @@ import java.nio.file.StandardCopyOption;
  * @author guanquan.wang at 2018-01-26 15:23
  */
 public class WaterMark {
-    private Path imagePath;
+    private final Path imagePath;
     private boolean temp;
 
     public WaterMark(String word) { // 文字水印
@@ -99,12 +99,11 @@ public class WaterMark {
     private Path createWaterMark(String watermark) {
         try {
             Path temp = createTemp();
-            int width = 510; // 水印图片的宽度
-            int height = 300; // 水印图片的高度 因为设置其他的高度会有黑线，所以拉高高度
+            int width = 510;
+            int height = 300;
 
-            // 获取bufferedImage对象
             BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
-            // 处理背景色，设置为 白色
+            // Setting white background color
             int minx = bi.getMinX();
             int miny = bi.getMinY();
             for (int i = minx; i < width; i++) {
@@ -113,24 +112,17 @@ public class WaterMark {
                 }
             }
 
-            // 获取Graphics2d对象
             Graphics2D g2d = bi.createGraphics();
-            // 设置字体颜色为灰色
             g2d.setColor(new Color(200, 200, 200));
-            // 设置图片的属性
             g2d.setStroke(new BasicStroke(1));
-            // 设置字体
             g2d.setFont(new java.awt.Font("华文细黑", java.awt.Font.ITALIC, 50));
-            // 设置字体倾斜度
             g2d.rotate(Math.toRadians(-10));
 
-            // 写入水印文字 原定高度过小，所以累计写水印，增加高度
             for (int i = 1; i < 10; i++) {
                 g2d.drawString(watermark, 0, 60 * i);
             }
-            // 设置透明度
+            // Setting alpha
             g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
-            // 释放对象
             g2d.dispose();
             ImageIO.write(bi, "png", temp.toFile());
             return temp;
@@ -185,7 +177,7 @@ public class WaterMark {
                 try {
                     return f.get(null).toString();
                 } catch (IllegalAccessException e) {
-                    ; // Empty
+                    // Empty
                 }
             }
         }
diff --git a/src/main/java/org/ttzero/excel/entity/Workbook.java b/src/main/java/org/ttzero/excel/entity/Workbook.java
index 8214cb83..296266f1 100644
--- a/src/main/java/org/ttzero/excel/entity/Workbook.java
+++ b/src/main/java/org/ttzero/excel/entity/Workbook.java
@@ -119,7 +119,7 @@ public class Workbook implements Storable {
      * A windows to debug
      */
     private Watch watch;
-    private I18N i18N;
+    private final I18N i18N;
 
     private SharedStrings sst;
     private Styles styles;
diff --git a/src/main/java/org/ttzero/excel/entity/csv/CSVCellValueAndStyle.java b/src/main/java/org/ttzero/excel/entity/csv/CSVCellValueAndStyle.java
index f8f39170..af0a29fd 100644
--- a/src/main/java/org/ttzero/excel/entity/csv/CSVCellValueAndStyle.java
+++ b/src/main/java/org/ttzero/excel/entity/csv/CSVCellValueAndStyle.java
@@ -90,15 +90,15 @@ public class CSVCellValueAndStyle implements ICellValueAndStyle {
         } else if (isDateTime(clazz)) {
             cell.setSv(DateUtil.toString((Timestamp) e));
         } else if (isChar(clazz)) {
-            Character c = (Character) e;
+            char c = (char) e;
             if (hasIntProcessor) conversion(row, cell, c, hc);
             else cell.setCv(c);
         } else if (isShort(clazz)) {
-            Short t = (Short) e;
+            short t = (short) e;
             if (hasIntProcessor) conversion(row, cell, t, hc);
             else cell.setNv(t);
         } else if (isInt(clazz)) {
-            Integer n = (Integer) e;
+            int n = (int) e;
             if (hasIntProcessor) conversion(row, cell, n, hc);
             else cell.setNv(n);
         } else if (isLong(clazz)) {
@@ -131,6 +131,7 @@ public class CSVCellValueAndStyle implements ICellValueAndStyle {
      * @param o  the cell value
      * @return const zero (general style)
      */
+    @Override
     public int getStyleIndex(int rows, Sheet.Column hc, Object o) {
         return 0;
     }
diff --git a/src/main/java/org/ttzero/excel/entity/e7/ContentType.java b/src/main/java/org/ttzero/excel/entity/e7/ContentType.java
index 3688ce7f..7e9f5fe7 100644
--- a/src/main/java/org/ttzero/excel/entity/e7/ContentType.java
+++ b/src/main/java/org/ttzero/excel/entity/e7/ContentType.java
@@ -39,8 +39,8 @@ import java.util.Set;
  */
 @TopNS(prefix = "", value = "Types", uri = "http://schemas.openxmlformats.org/package/2006/content-types")
 public class ContentType implements Storable {
-    private Set<? super Type> set;
-    private RelManager relManager;
+    private final Set<? super Type> set;
+    private final RelManager relManager;
 
     public ContentType() {
         set = new HashSet<>();
diff --git a/src/main/java/org/ttzero/excel/entity/e7/XMLCellValueAndStyle.java b/src/main/java/org/ttzero/excel/entity/e7/XMLCellValueAndStyle.java
index 209541b1..a6299899 100644
--- a/src/main/java/org/ttzero/excel/entity/e7/XMLCellValueAndStyle.java
+++ b/src/main/java/org/ttzero/excel/entity/e7/XMLCellValueAndStyle.java
@@ -33,11 +33,11 @@ public class XMLCellValueAndStyle implements ICellValueAndStyle {
     /**
      * Automatic interlacing color
      */
-    private int autoOdd;
+    private final int autoOdd;
     /**
      * Odd row's background color
      */
-    private int oddFill;
+    private final int oddFill;
 
     public XMLCellValueAndStyle(int autoOdd, int oddFill) {
         this.autoOdd = autoOdd;
diff --git a/src/main/java/org/ttzero/excel/entity/e7/XMLWorkbookWriter.java b/src/main/java/org/ttzero/excel/entity/e7/XMLWorkbookWriter.java
index 270ba428..5521a1d4 100644
--- a/src/main/java/org/ttzero/excel/entity/e7/XMLWorkbookWriter.java
+++ b/src/main/java/org/ttzero/excel/entity/e7/XMLWorkbookWriter.java
@@ -63,7 +63,7 @@ import static org.ttzero.excel.util.FileUtil.exists;
 public class XMLWorkbookWriter implements IWorkbookWriter {
 
     private Workbook workbook;
-    private RelManager relManager;
+    private final RelManager relManager;
 
     public XMLWorkbookWriter() {
         relManager = new RelManager();
@@ -107,6 +107,7 @@ public class XMLWorkbookWriter implements IWorkbookWriter {
      *
      * @param path the storage path
      */
+    @Override
     public void writeTo(Path path) throws IOException {
         Path zip = workbook.getTemplate() == null ? createTemp() : template();
         reMarkPath(zip, path);
@@ -118,6 +119,7 @@ public class XMLWorkbookWriter implements IWorkbookWriter {
         Files.copy(zip, os);
     }
 
+    @Override
     public void writeTo(File file) throws IOException {
         Path zip = workbook.getTemplate() == null ? createTemp() : template();
         FileUtil.cp(zip, file);
diff --git a/src/main/java/org/ttzero/excel/entity/e7/XMLWorksheetWriter.java b/src/main/java/org/ttzero/excel/entity/e7/XMLWorksheetWriter.java
index 41bc3300..870f908f 100644
--- a/src/main/java/org/ttzero/excel/entity/e7/XMLWorksheetWriter.java
+++ b/src/main/java/org/ttzero/excel/entity/e7/XMLWorksheetWriter.java
@@ -89,7 +89,7 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
     private ExtBufferedWriter bw;
     private Sheet sheet;
     private Sheet.Column[] columns;
-    private SharedStrings sst;
+    private final SharedStrings sst;
     private Comments comments;
 
     public XMLWorksheetWriter(Sheet sheet) {
@@ -515,13 +515,13 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
                 case SST:
                     writeString(cell.sv, r, i, xf);
                     break;
-                case DATE:
                 case NUMERIC:
                     writeNumeric(cell.nv, r, i, xf);
                     break;
                 case LONG:
                     writeNumeric(cell.lv, r, i, xf);
                     break;
+                case DATE:
                 case DATETIME:
                 case DOUBLE:
                 case TIME:
@@ -564,13 +564,13 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
                 case SST:
                     writeStringAutoSize(cell.sv, r, i, xf);
                     break;
-                case DATE:
                 case NUMERIC:
                     writeNumericAutoSize(cell.nv, r, i, xf);
                     break;
                 case LONG:
                     writeNumericAutoSize(cell.lv, r, i, xf);
                     break;
+                case DATE:
                 case DATETIME:
                 case DOUBLE:
                 case TIME:
@@ -781,7 +781,7 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
         writeNumeric(l, row, column, xf);
         Sheet.Column hc = columns[column];
         int n;
-        if (hc.width == 0 && hc.o < (n = stringSize(l))) {
+        if (hc.width == 0 && hc.o < (n = (l < 0L ? stringSize(-l) + 1 : stringSize(l)))) {
             hc.o = n;
         }
     }
@@ -846,28 +846,36 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
                 if (isString(clazz)) {
                     len = hc.o;
                 }
-                else if (isDate(clazz) || isLocalDate(clazz)) {
-                    len = 10;
-                }
-                else if (isDateTime(clazz) || isLocalDateTime(clazz)) {
-                    len = 20;
+                else if (isDate(clazz) || isLocalDate(clazz) || isDateTime(clazz) || isLocalDateTime(clazz)) {
+//                    len = 10;
+//                }
+//                else if (isDateTime(clazz) || isLocalDateTime(clazz)) {
+                    if (hc.getNumFmt() != null) {
+                        len = hc.getNumFmt().calcNumWidth(0);
+                    } else len = 20;
                 }
                 else if (isChar(clazz)) {
                     len = 1;
                 }
                 else if (isInt(clazz) || isLong(clazz)) {
                     // TODO Calculate character width based on numFmt
-                    len = hc.o;
+                    if (hc.getNumFmt() != null) {
+                        len = hc.getNumFmt().calcNumWidth(hc.o);
+                    } else len = hc.o;
                 }
                 else if (isFloat(clazz) || isDouble(clazz)) {
                     // TODO Calculate character width based on numFmt
-                    len = hc.o;
+                    if (hc.getNumFmt() != null) {
+                        len = hc.getNumFmt().calcNumWidth(hc.o);
+                    } else len = hc.o;
                 }
                 else if (isBigDecimal(clazz)) {
                     len = hc.o;
                 }
                 else if (isTime(clazz) || isLocalTime(clazz)) {
-                    len = 8;
+                    if (hc.getNumFmt() != null) {
+                        len = hc.getNumFmt().calcNumWidth(0);
+                    } else len = 8;
                 }
                 else if (isBool(clazz)) {
                     len = 5;
diff --git a/src/main/java/org/ttzero/excel/entity/style/Border.java b/src/main/java/org/ttzero/excel/entity/style/Border.java
index 3eb1bf9b..1d741927 100644
--- a/src/main/java/org/ttzero/excel/entity/style/Border.java
+++ b/src/main/java/org/ttzero/excel/entity/style/Border.java
@@ -37,7 +37,7 @@ public class Border {
 
     private static final Color defaultColor = new Color(51, 51, 51); // #333333
 
-    private SubBorder[] borders;
+    private final SubBorder[] borders;
 
     public Border() {
         borders = new SubBorder[6]; // left-right-top-bottom-diagonalDown-diagonalUp
@@ -249,6 +249,7 @@ public class Border {
         return this;
     }
 
+    @Override
     public int hashCode() {
         int down = borders[4] != null ? 1 : 0
             , up = borders[5] != null ? 2 : 0;
@@ -259,6 +260,7 @@ public class Border {
         return hash;
     }
 
+    @Override
     public boolean equals(Object o) {
         if (o instanceof Border) {
             Border other = (Border) o;
@@ -282,11 +284,11 @@ public class Border {
      * orientation is not set, the last color will be complemented.
      * <p>
      * eq:
-     * thin red
-     * thin red thin dashed dashed
-     * medium black thick #cccccc double black hair green
-     * none none thin thin
-     * </p>
+     * <p>thin red</p>
+     * <p>thin red thin dashed dashed</p>
+     * <p>medium black thick #cccccc double black hair green</p>
+     * <p>none none thin thin</p>
+     *
      * @param text the border value
      * @return the parse value of {@link Border}
      */
@@ -333,19 +335,21 @@ public class Border {
     }
 
     private static class SubBorder {
-        private BorderStyle style;
-        private Color color;
+        private final BorderStyle style;
+        private final Color color;
 
         public SubBorder(BorderStyle style, Color color) {
             this.style = style;
             this.color = color;
         }
 
+        @Override
         public int hashCode() {
             int hash = color.hashCode();
             return (style.ordinal() << 24) | (hash << 8 >>> 8);
         }
 
+        @Override
         public boolean equals(Object o) {
             return (o instanceof SubBorder) && o.hashCode() == hashCode();
         }
diff --git a/src/main/java/org/ttzero/excel/entity/style/BorderStyle.java b/src/main/java/org/ttzero/excel/entity/style/BorderStyle.java
index cca5b99c..aa6f3ff9 100644
--- a/src/main/java/org/ttzero/excel/entity/style/BorderStyle.java
+++ b/src/main/java/org/ttzero/excel/entity/style/BorderStyle.java
@@ -23,12 +23,12 @@ package org.ttzero.excel.entity.style;
  */
 public enum BorderStyle {
     NONE("none"),
-    THIN("thin"),    // 细直线
+    THIN("thin"),
     MEDIUM("medium"),
-    DASHED("dashed"), // 虚线
-    DOTTED("dotted"), // 点线
-    THICK("thick"), // 粗直线
-    DOUBLE("double"), // 双线
+    DASHED("dashed"),
+    DOTTED("dotted"),
+    THICK("thick"),
+    DOUBLE("double"),
     HAIR("hair"),
     MEDIUM_DASHED("mediumDashed"),
     DASH_DOT("dashDot"),
@@ -37,7 +37,7 @@ public enum BorderStyle {
     MEDIUM_DASH_DOT_DOT("mediumDashDotDot"),
     SLANTED_DASH_DOT("slantDashDot");
 
-    private String name;
+    private final String name;
 
     BorderStyle(String name) {
         this.name = name;
diff --git a/src/main/java/org/ttzero/excel/entity/style/BuiltInNumFmt.java b/src/main/java/org/ttzero/excel/entity/style/BuiltInNumFmt.java
index 7d65be8c..71cab8da 100644
--- a/src/main/java/org/ttzero/excel/entity/style/BuiltInNumFmt.java
+++ b/src/main/java/org/ttzero/excel/entity/style/BuiltInNumFmt.java
@@ -34,7 +34,7 @@ import java.util.Locale;
  * @author guanquan.wang at 2018-02-12 10:11
  */
 public final class BuiltInNumFmt {
-    private static NumFmt[][] data;
+    private static final NumFmt[][] data;
 
     static {
         InputStream is = BuiltInNumFmt.class.getClassLoader().getResourceAsStream("numFmt");
diff --git a/src/main/java/org/ttzero/excel/entity/style/Fill.java b/src/main/java/org/ttzero/excel/entity/style/Fill.java
index 5388b6a2..6ed5746c 100644
--- a/src/main/java/org/ttzero/excel/entity/style/Fill.java
+++ b/src/main/java/org/ttzero/excel/entity/style/Fill.java
@@ -22,6 +22,7 @@ import org.ttzero.excel.util.StringUtil;
 
 import java.awt.Color;
 import java.lang.reflect.Field;
+import java.util.Objects;
 
 /**
  * @author guanquan.wang at 2018-02-06 08:55
@@ -79,6 +80,7 @@ public class Fill implements Cloneable {
         return this;
     }
 
+    @Override
     public int hashCode() {
         int hash = 0;
         if (patternType != null) {
@@ -94,12 +96,13 @@ public class Fill implements Cloneable {
         return hash + (c << 8 >>> 8);
     }
 
+    @Override
     public boolean equals(Object o) {
         if (o instanceof Fill) {
             Fill other = (Fill) o;
             return (other.patternType == this.patternType)
-                && (other.bgColor != null ? other.bgColor.equals(bgColor) : null == bgColor)
-                && (other.fgColor != null ? other.fgColor.equals(fgColor) : null == fgColor);
+                && (Objects.equals(other.bgColor, bgColor))
+                && (Objects.equals(other.fgColor, fgColor));
         }
         return false;
     }
diff --git a/src/main/java/org/ttzero/excel/entity/style/Font.java b/src/main/java/org/ttzero/excel/entity/style/Font.java
index 8a3f8698..30892388 100644
--- a/src/main/java/org/ttzero/excel/entity/style/Font.java
+++ b/src/main/java/org/ttzero/excel/entity/style/Font.java
@@ -22,6 +22,7 @@ import org.ttzero.excel.util.StringUtil;
 
 import java.awt.Color;
 import java.lang.reflect.Field;
+import java.util.Objects;
 
 /**
  * @author guanquan.wang at 2018-02-02 16:51
@@ -38,11 +39,11 @@ public class Font implements Cloneable {
     private Font() {}
 
     public Font(String name, int size) {
-        this(name, size, Style.normal, null);
+        this(name, size, Style.NORMAL, null);
     }
 
     public Font(String name, int size, Color color) {
-        this(name, size, Style.normal, color);
+        this(name, size, Style.NORMAL, color);
     }
 
     public Font(String name, int size, int style, Color color) {
@@ -95,6 +96,7 @@ public class Font implements Cloneable {
                 try {
                     size = Integer.valueOf(temp);
                 } catch (NumberFormatException e) {
+                    //
                 }
                 if (size == null) {
                     int n;
@@ -206,42 +208,42 @@ public class Font implements Cloneable {
     }
 
     public Font italic() {
-        style |= Style.italic;
+        style |= Style.ITALIC;
         return this;
     }
 
     public Font bold() {
-        style |= Style.bold;
+        style |= Style.BOLD;
         return this;
     }
 
     public Font underLine() {
-        style |= Style.underLine;
+        style |= Style.UNDERLINE;
         return this;
     }
 
     public boolean isItalic() {
-        return (style & Style.italic) == Style.italic;
+        return (style & Style.ITALIC) == Style.ITALIC;
     }
     public boolean isBold() {
-        return (style & Style.bold) == Style.bold;
+        return (style & Style.BOLD) == Style.BOLD;
     }
     public boolean isUnderLine() {
-        return (style & Style.underLine) == Style.underLine;
+        return (style & Style.UNDERLINE) == Style.UNDERLINE;
     }
 
     public Font delItalic() {
-        style &= (Style.underLine | Style.bold);
+        style &= (Style.UNDERLINE | Style.BOLD);
         return this;
     }
 
     public Font delBold() {
-        style &= (Style.underLine | Style.italic);
+        style &= (Style.UNDERLINE | Style.ITALIC);
         return this;
     }
 
     public Font delUnderLine() {
-        style &= (Style.bold | Style.italic);
+        style &= (Style.BOLD | Style.ITALIC);
         return this;
     }
 
@@ -262,9 +264,9 @@ public class Font implements Cloneable {
         // name
         buf.append("    <name val=\"").append(name).append("\"/>").append(Const.lineSeparator);
         // family
-//        DECORATIVE  装饰
-//        MODERN   现代
-//        NOT_APPLICABLE  不适用
+//        DECORATIVE
+//        MODERN
+//        NOT_APPLICABLE
 //        ROMAN
 //        SCRIPT
 //        SWISS
@@ -324,8 +326,8 @@ public class Font implements Cloneable {
             return other.family == family
                 && other.style == style
                 && other.size == size
-                && (other.color != null ? other.color.equals(color) : null == color)
-                && (other.name != null ? other.name.equals(name) : null == name)
+                && (Objects.equals(other.color, color))
+                && (Objects.equals(other.name, name))
                 ;
         }
         return false;
@@ -384,11 +386,10 @@ public class Font implements Cloneable {
     // ######################################Static inner class######################################
 
     public static class Style {
-        public static final int normal = 0 // 正常
-            , italic = 1 << 2 // 斜体
-            , bold = 1 << 1 // 加粗
-            , underLine = 1 // 下划线
-            ;
+        public static final int NORMAL = 0;
+        public static final int ITALIC = 1 << 2;
+        public static final int BOLD = 1 << 1;
+        public static final int UNDERLINE = 1;
 
         public static int valueOf(String name) throws NoSuchFieldException, IllegalAccessException {
             Field field = Style.class.getDeclaredField(name);
diff --git a/src/main/java/org/ttzero/excel/entity/style/Horizontals.java b/src/main/java/org/ttzero/excel/entity/style/Horizontals.java
index 0eede33b..5f2ae5a4 100644
--- a/src/main/java/org/ttzero/excel/entity/style/Horizontals.java
+++ b/src/main/java/org/ttzero/excel/entity/style/Horizontals.java
@@ -32,7 +32,16 @@ public class Horizontals {
         , DISTRIBUTED = 7 // Distributed Horizontal Alignment
         ;
 
-    private static final String[] _names = {"general","left","right","center","centerContinuous","fill","justify","distributed"};
+    private static final String[] _names = {
+            "general"
+            ,"left"
+            ,"right"
+            ,"center"
+            ,"centerContinuous"
+            ,"fill"
+            ,"justify"
+            ,"distributed"
+    };
 
     public static String of(int n) {
         return _names[n];
diff --git a/src/main/java/org/ttzero/excel/entity/style/NumFmt.java b/src/main/java/org/ttzero/excel/entity/style/NumFmt.java
index d06b2e8a..4ff75266 100644
--- a/src/main/java/org/ttzero/excel/entity/style/NumFmt.java
+++ b/src/main/java/org/ttzero/excel/entity/style/NumFmt.java
@@ -19,6 +19,9 @@ package org.ttzero.excel.entity.style;
 import org.dom4j.Element;
 import org.ttzero.excel.util.StringUtil;
 
+import java.nio.charset.StandardCharsets;
+import java.util.Objects;
+
 /**
  * To create a custom number format, you start by selecting one of the built-in number formats as a starting point.
  * You can then change any one of the code sections of that format to create your own custom number format.
@@ -58,7 +61,7 @@ public class NumFmt implements Comparable<NumFmt> {
     }
 
     public NumFmt(String code) {
-        this.code = code;
+        this.code = clean(code);
     }
 
     public String getCode() {
@@ -88,6 +91,78 @@ public class NumFmt implements Comparable<NumFmt> {
         return new NumFmt().setId(id);
     }
 
+    /**
+     * Create a NumFmt
+     *
+     * @param code the numFmt code string
+     * @return NumFmt
+     */
+    public static NumFmt of(String code) {
+        return new NumFmt(code);
+    }
+
+    // Clean the format code
+    private static String clean(String code) {
+        if (StringUtil.isEmpty(code))
+            throw new NumberFormatException("The format code must not be null or empty.");
+
+        // Replace '-' to '\-'
+        code = escape(code, '-');
+        // Replace ' ' to '\ '
+        code = escape(code, ' ');
+
+        return code;
+    }
+
+    private static String escape(String code, char c) {
+        int i = code.indexOf(c);
+        if (i > -1) {
+            int j = 0;
+            StringBuilder buf = new StringBuilder();
+            do {
+                if (i != j) {
+                    buf.append(code, j, i);
+                    j = i;
+                }
+                if (i == 0 || code.charAt(i - 1) != '\\') {
+                    buf.append('\\');
+                }
+            } while ((i = code.indexOf(c, i + 1)) > -1);
+            code = buf.append(code, j, code.length()).toString();
+        }
+        return code;
+    }
+
+    /**
+     * Roughly calculate the cell length
+     *
+     * @param base the cell value length
+     * @return cell length
+     */
+    public int calcNumWidth(int base) {
+        int n = 0;
+        boolean ignore = false, comma = false;
+        char[] cs = new char[1];
+        for (int i = 0; i < code.length(); i++) {
+            char c = code.charAt(i);
+            if (c == '"' || c == '\\') continue;
+            if (ignore) {
+                if (c == ']' || c == ')') {
+                    ignore = false;
+                }
+                continue;
+            }
+            if (c == '[' || c == '(') {
+                ignore = true;
+                continue;
+            }
+            if (c == ',') comma = true;
+            cs[0] = c;
+            n += (new String(cs).getBytes(StandardCharsets.UTF_8).length >> 1) + 1;
+        }
+        return comma ? base + base / 3 + n - 5 : n;
+    }
+
     @Override
     public int hashCode() {
         return code != null ? code.hashCode() : 0;
@@ -97,7 +172,7 @@ public class NumFmt implements Comparable<NumFmt> {
     public boolean equals(Object o) {
         if (o instanceof NumFmt) {
             NumFmt other = (NumFmt) o;
-            return other.code != null ? other.code.equals(code) : null == code;
+            return Objects.equals(other.code, code);
         }
         return false;
     }
@@ -108,7 +183,7 @@ public class NumFmt implements Comparable<NumFmt> {
     }
 
     public Element toDom4j(Element root) {
-        if (StringUtil.isEmpty(code)) return root; // 内置format不用重复输出
+        if (StringUtil.isEmpty(code)) return root; // Build in style
         return root.addElement(StringUtil.lowFirstKey(getClass().getSimpleName()))
             .addAttribute("formatCode", code)
             .addAttribute("numFmtId", String.valueOf(id));
diff --git a/src/main/java/org/ttzero/excel/entity/style/Styles.java b/src/main/java/org/ttzero/excel/entity/style/Styles.java
index 374d28c1..425b7415 100644
--- a/src/main/java/org/ttzero/excel/entity/style/Styles.java
+++ b/src/main/java/org/ttzero/excel/entity/style/Styles.java
@@ -30,6 +30,7 @@ import org.dom4j.io.SAXReader;
 
 import java.awt.Color;
 import java.io.IOException;
+import java.io.InputStream;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.ArrayList;
@@ -66,7 +67,7 @@ import static org.ttzero.excel.util.StringUtil.isNotEmpty;
 @TopNS(prefix = "", uri = Const.SCHEMA_MAIN, value = "styleSheet")
 public class Styles implements Storable {
 
-    private Map<Integer, Integer> map;
+    private final Map<Integer, Integer> map;
     private Document document;
 
     private List<Font> fonts;
@@ -162,8 +163,8 @@ public class Styles implements Storable {
         self.document = factory.createDocument(rootElement);
 
         self.numFmts = new ArrayList<>();
-        self.addNumFmt(new NumFmt("yyyy\\-mm\\-dd"));
-        self.addNumFmt(new NumFmt("yyyy\\-mm\\-dd\\ hh:mm:ss"));
+//        self.addNumFmt(new NumFmt("yyyy\\-mm\\-dd"));
+//        self.addNumFmt(new NumFmt("yyyy\\-mm\\-dd\\ hh:mm:ss"));
 
         self.fonts = new ArrayList<>();
         Font font1 = new Font(i18N.get("en-font-family"), 11, Color.black);  // en
@@ -172,7 +173,7 @@ public class Styles implements Storable {
         self.addFont(font1);
 
         String lang = Locale.getDefault().toLanguageTag();
-        // 添加中文默认字体
+        // Add chinese font
         Font font2 = new Font(i18N.get("local-font-family"), 11); // cn
         font2.setFamily(3);
         font2.setScheme("minor");
@@ -181,7 +182,7 @@ public class Styles implements Storable {
         } else if ("zh-TW".equals(lang)) {
             font2.setCharset(Charset.CHINESEBIG5);
         }
-        // TODO other charset
+        // Other charset
         self.addFont(font2);
 
         self.fills = new ArrayList<>();
@@ -274,8 +275,9 @@ public class Styles implements Storable {
      * @return the numFmt part value in style
      */
     public final int addNumFmt(NumFmt numFmt) {
-        // check and search default code
-        if (numFmt.getId() < 0) {
+        // All indexes from 0 to 175 are reserved for built-in formats.
+        // The first user-defined format starts at 176.
+        if (numFmt.getId() < 0 || numFmt.getId() >= 176) {
             if (isEmpty(numFmt.getCode())) {
                 throw new NullPointerException("NumFmt code");
             }
@@ -384,8 +386,19 @@ public class Styles implements Storable {
             ;
     }
 
-    static final String[] attrNames = {"numFmtId", "fontId", "fillId", "borderId", "vertical", "horizontal"
-        , "applyNumberFormat", "applyFont", "applyFill", "applyBorder", "applyAlignment"};
+    static final String[] attrNames = {
+            "numFmtId"
+            , "fontId"
+            , "fillId"
+            , "borderId"
+            , "vertical"
+            , "horizontal"
+            , "applyNumberFormat"
+            , "applyFont"
+            , "applyFill"
+            , "applyBorder"
+            , "applyAlignment"
+    };
 
     /**
      * add style in document
@@ -449,7 +462,9 @@ public class Styles implements Storable {
         if (document != null) { // Not null
             FileUtil.writeToDiskNoFormat(document, styleFile);
         } else {
-            Files.copy(getClass().getClassLoader().getResourceAsStream("template/styles.xml"), styleFile);
+            try (InputStream temp = getClass().getClassLoader().getResourceAsStream("template/styles.xml")) {
+                if (temp != null) Files.copy(temp, styleFile);
+            }
         }
     }
 
@@ -463,7 +478,8 @@ public class Styles implements Storable {
     }
 
     ////////////////////////clear style///////////////////////////////
-    public static int clearNumfmt(int style) {
+
+    public static int clearNumFmt(int style) {
         return style & (-1 >>> 32 - INDEX_NUMBER_FORMAT);
     }
 
@@ -511,13 +527,13 @@ public class Styles implements Storable {
         return (1 << INDEX_NUMBER_FORMAT) | (1 << INDEX_BORDER) | Horizontals.RIGHT;
     }
 
-    public static int defaultDateBorderStyle() {
-        return (176 << INDEX_NUMBER_FORMAT) | (1 << INDEX_BORDER) | Horizontals.CENTER;
-    }
-
-    public static int defaultTimestampBorderStyle() {
-        return (177 << INDEX_NUMBER_FORMAT) | (1 << INDEX_BORDER) | Horizontals.CENTER;
-    }
+//    public static int defaultDateBorderStyle() {
+//        return (176 << INDEX_NUMBER_FORMAT) | (1 << INDEX_BORDER) | Horizontals.CENTER;
+//    }
+//
+//    public static int defaultTimestampBorderStyle() {
+//        return (177 << INDEX_NUMBER_FORMAT) | (1 << INDEX_BORDER) | Horizontals.CENTER;
+//    }
 
     public static int defaultDoubleBorderStyle() {
         return (2 << INDEX_NUMBER_FORMAT) | (1 << INDEX_FONT) | (1 << INDEX_BORDER) | Horizontals.RIGHT;
@@ -536,13 +552,13 @@ public class Styles implements Storable {
         return (1 << INDEX_NUMBER_FORMAT) | Horizontals.RIGHT;
     }
 
-    public static int defaultDateStyle() {
-        return (176 << INDEX_NUMBER_FORMAT) | Horizontals.CENTER;
-    }
-
-    public static int defaultTimestampStyle() {
-        return (177 << INDEX_NUMBER_FORMAT) | Horizontals.CENTER;
-    }
+//    public static int defaultDateStyle() {
+//        return (176 << INDEX_NUMBER_FORMAT) | Horizontals.CENTER;
+//    }
+//
+//    public static int defaultTimestampStyle() {
+//        return (177 << INDEX_NUMBER_FORMAT) | Horizontals.CENTER;
+//    }
 
     public static int defaultDoubleStyle() {
         return (2 << INDEX_NUMBER_FORMAT) | (1 << INDEX_FONT) | Horizontals.RIGHT;
@@ -575,7 +591,8 @@ public class Styles implements Storable {
 
     ////////////////////////////////To object//////////////////////////////////
     public NumFmt getNumFmt(int style) {
-        return numFmts.get(style >>> INDEX_NUMBER_FORMAT);
+        int n = style >>> INDEX_NUMBER_FORMAT;
+        return n < numFmts.size() ? numFmts.get(n) : null;
     }
 
     public Fill getFill(int style) {
diff --git a/src/main/java/org/ttzero/excel/manager/RelManager.java b/src/main/java/org/ttzero/excel/manager/RelManager.java
index 9deea1b4..8ae6c4df 100644
--- a/src/main/java/org/ttzero/excel/manager/RelManager.java
+++ b/src/main/java/org/ttzero/excel/manager/RelManager.java
@@ -33,6 +33,7 @@ import java.io.Serializable;
 import java.lang.reflect.Field;
 import java.nio.file.Path;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 import static org.ttzero.excel.util.FileUtil.exists;
@@ -50,10 +51,7 @@ public class RelManager implements Serializable {
 
     public static RelManager of(Relationship... relationships) {
         RelManager relManager = new RelManager();
-        relManager.relationships = new ArrayList<>();
-        for (Relationship r : relationships) {
-            relManager.relationships.add(r);
-        }
+        relManager.relationships = new ArrayList<>(Arrays.asList(relationships));
         return relManager;
     }
 
@@ -62,10 +60,10 @@ public class RelManager implements Serializable {
             relationships = new ArrayList<>();
         }
         int n = indexOf(rel.getTarget());
-        if (n > -1) { // 替换
+        if (n > -1) { // Replace
             rel.setId("rId" + (n + 1));
             relationships.set(n, rel);
-        } else { // 追加
+        } else { // Append
             rel.setId("rId" + (relationships.size() + 1));
             relationships.add(rel);
         }
diff --git a/src/main/java/org/ttzero/excel/manager/docProps/Core.java b/src/main/java/org/ttzero/excel/manager/docProps/Core.java
index dcfabdfc..2e92b01e 100644
--- a/src/main/java/org/ttzero/excel/manager/docProps/Core.java
+++ b/src/main/java/org/ttzero/excel/manager/docProps/Core.java
@@ -80,13 +80,15 @@ public class Core extends XmlEntity {
      * Create time(notnull)
      */
     @NS("dcterms")
-    @Attr(name = "type", value = "dcterms:W3CDTF", namespace = @NS(value = "xsi", uri = "http://www.w3.org/2001/XMLSchema-instance"))
+    @Attr(name = "type", value = "dcterms:W3CDTF"
+            , namespace = @NS(value = "xsi", uri = "http://www.w3.org/2001/XMLSchema-instance"))
     private Date created;
     /**
      * The last modify time(notnull)
      */
     @NS("dcterms")
-    @Attr(name = "type", value = "dcterms:W3CDTF", namespace = @NS(value = "xsi", uri = "http://www.w3.org/2001/XMLSchema-instance"))
+    @Attr(name = "type", value = "dcterms:W3CDTF"
+            , namespace = @NS(value = "xsi", uri = "http://www.w3.org/2001/XMLSchema-instance"))
     private Date modified;
 
 
diff --git a/src/main/java/org/ttzero/excel/manager/docProps/XmlEntity.java b/src/main/java/org/ttzero/excel/manager/docProps/XmlEntity.java
index b2cd787a..1a5890ed 100644
--- a/src/main/java/org/ttzero/excel/manager/docProps/XmlEntity.java
+++ b/src/main/java/org/ttzero/excel/manager/docProps/XmlEntity.java
@@ -48,6 +48,7 @@ class XmlEntity implements Storable {
 
     private String[] prefixs, uris;
 
+    @Override
     public void writeTo(Path path) throws IOException {
         DocumentFactory factory = DocumentFactory.getInstance();
         //use the factory to create a root element
@@ -76,9 +77,7 @@ class XmlEntity implements Storable {
 
         if (hasTopNs) {
             for (int i = 0; i < prefixs.length; i++) {
-                if (prefixs.length > 0) {
-                    rootElement.add(Namespace.get(prefixs[i], uris[i]));
-                }
+                rootElement.add(Namespace.get(prefixs[i], uris[i]));
             }
         }
 
diff --git a/src/main/java/org/ttzero/excel/reader/ExcelReader.java b/src/main/java/org/ttzero/excel/reader/ExcelReader.java
index d07b5a1a..86ac36d2 100644
--- a/src/main/java/org/ttzero/excel/reader/ExcelReader.java
+++ b/src/main/java/org/ttzero/excel/reader/ExcelReader.java
@@ -335,6 +335,7 @@ public class ExcelReader implements Closeable {
      *
      * @throws IOException when fail close readers
      */
+    @Override
     public void close() throws IOException {
         // Close all opened sheet
         for (Sheet st : sheets) {
diff --git a/src/main/java/org/ttzero/excel/reader/FixSizeLRUCache.java b/src/main/java/org/ttzero/excel/reader/FixSizeLRUCache.java
index 30c5a5b0..057b477b 100644
--- a/src/main/java/org/ttzero/excel/reader/FixSizeLRUCache.java
+++ b/src/main/java/org/ttzero/excel/reader/FixSizeLRUCache.java
@@ -29,7 +29,7 @@ import java.util.function.Consumer;
  */
 public class FixSizeLRUCache<K, V> implements Cache<K, V> {
     private static class E<K, V> implements Cache.Entry<K, V> {
-        private K k;
+        private final K k;
         private V v;
 
         private E(K k, V v) {
@@ -66,14 +66,14 @@ public class FixSizeLRUCache<K, V> implements Cache<K, V> {
     /**
      * The elements limit
      */
-    private int limit;
+    private final int limit;
 
     /**
      * Size of elements
      */
     private int size;
 
-    private Map<K, Node<E<K, V>>> table;
+    private final Map<K, Node<E<K, V>>> table;
 
     public static <K, V> FixSizeLRUCache<K, V> create() {
         return new FixSizeLRUCache<>();
@@ -298,7 +298,6 @@ public class FixSizeLRUCache<K, V> implements Cache<K, V> {
      *
      * @param action The action to be performed for each element
      * @throws NullPointerException if the specified action is null
-     * @since 1.8
      */
     @Override
     public void forEach(Consumer<? super Entry<K, V>> action) {
diff --git a/src/main/java/org/ttzero/excel/reader/Grid.java b/src/main/java/org/ttzero/excel/reader/Grid.java
index 7f049330..b8c49d48 100644
--- a/src/main/java/org/ttzero/excel/reader/Grid.java
+++ b/src/main/java/org/ttzero/excel/reader/Grid.java
@@ -184,7 +184,7 @@ interface Grid {
             return joiner.toString();
         }
 
-        private char[] chars = new char[64];
+        private final char[] chars = new char[64];
 
         private String append(String s) {
             int n = s.length();
@@ -194,6 +194,7 @@ interface Grid {
         }
 
         private static class FastLinkedScanner extends LinkedScanner {
+            @Override
             public Entry get(int r, int c) {
                 if (size() == 1) return head.entry;
                 return super.get(r, c);
@@ -322,8 +323,8 @@ interface Grid {
     class LinkedScanner implements Scanner {
 
         final static class E implements Entry {
-            private Dimension dim;
-            private Cell cell;
+            private final Dimension dim;
+            private final Cell cell;
             private int n;
 
             E(Dimension dim, Cell cell) {
@@ -345,7 +346,7 @@ interface Grid {
 
         private static class Node {
             private Node next;
-            private E entry;
+            private final E entry;
 
             Node(E entry, Node next) {
                 this.entry = entry;
diff --git a/src/main/java/org/ttzero/excel/reader/HeaderRow.java b/src/main/java/org/ttzero/excel/reader/HeaderRow.java
index 80b23991..abaf8a63 100644
--- a/src/main/java/org/ttzero/excel/reader/HeaderRow.java
+++ b/src/main/java/org/ttzero/excel/reader/HeaderRow.java
@@ -221,7 +221,7 @@ class HeaderRow extends Row {
 
             return mapping(writeMethods, tmp, propertyDescriptors, mergedMethods);
         } catch (IntrospectionException e) {
-            LOGGER.warn("Get " + clazz + " property descriptor failed.");
+            LOGGER.warn("Get {} property descriptor failed.", clazz);
         }
         return 0;
     }
@@ -230,7 +230,7 @@ class HeaderRow extends Row {
         int n = getIndex(first);
         if (n == -1) n = getIndex(second);
         if (n == -1) {
-            LOGGER.warn(clazz + " field [" + first + "] can't find in header" + Arrays.toString(names));
+            LOGGER.warn("{} field [{}] can't find in header {}", clazz, first, Arrays.toString(names));
         }
         return n;
     }
diff --git a/src/main/java/org/ttzero/excel/reader/IndexSharedStringTable.java b/src/main/java/org/ttzero/excel/reader/IndexSharedStringTable.java
index 1871e898..d6b51020 100644
--- a/src/main/java/org/ttzero/excel/reader/IndexSharedStringTable.java
+++ b/src/main/java/org/ttzero/excel/reader/IndexSharedStringTable.java
@@ -39,9 +39,9 @@ public class IndexSharedStringTable extends SharedStringTable {
     /**
      * The index temp path
      */
-    private Path temp;
+    private final Path temp;
 
-    private SeekableByteChannel channel;
+    private final SeekableByteChannel channel;
 
     /**
      * Byte array buffer
@@ -67,7 +67,7 @@ public class IndexSharedStringTable extends SharedStringTable {
     /**
      * A fix length multiplexing char array
      */
-    private char[] chars = new char[1];
+    private final char[] chars = new char[1];
 
     /**
      * Cache the getting index
@@ -360,7 +360,7 @@ public class IndexSharedStringTable extends SharedStringTable {
     private String parse(ByteBuffer readBuffer) {
         int n = readBuffer.getInt();
         if (bytes == null || bytes.length < n) {
-            bytes = new byte[n < 128 ? 128 : n];
+            bytes = new byte[Math.max(n, 128)];
         }
         if (n < 0) {
             char c = (char) ~n;
diff --git a/src/main/java/org/ttzero/excel/reader/PreCalc.java b/src/main/java/org/ttzero/excel/reader/PreCalc.java
index 7c51bc6c..03c4793f 100644
--- a/src/main/java/org/ttzero/excel/reader/PreCalc.java
+++ b/src/main/java/org/ttzero/excel/reader/PreCalc.java
@@ -29,7 +29,7 @@ import static org.ttzero.excel.reader.SharedStrings.toInt;
  */
 class PreCalc {
     // Reference position
-    private long position;
+    private final long position;
     // calc string value
     private char[] cb;
     private Node head, tail;
diff --git a/src/main/java/org/ttzero/excel/reader/Row.java b/src/main/java/org/ttzero/excel/reader/Row.java
index f22763d4..b8f1269b 100644
--- a/src/main/java/org/ttzero/excel/reader/Row.java
+++ b/src/main/java/org/ttzero/excel/reader/Row.java
@@ -118,13 +118,9 @@ public abstract class Row {
         return lc - fc <= 0;
     }
 
-    private String outOfBoundsMsg(int index) {
-        return "Index: " + index + ", Size: " + lc;
-    }
-
     protected void rangeCheck(int index) {
         if (index >= lc || index < 0)
-            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
+            throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + lc);
     }
 
     /**
@@ -629,7 +625,7 @@ public abstract class Row {
                 }
                 // @Mark:=>There is no missing `break`, this is normal logic here
             case INLINESTR:
-                d = Double.valueOf(c.sv);
+                d = Double.parseDouble(c.sv);
                 break;
 
             default: throw new UncheckedTypeException("Can't convert cell value to double");
diff --git a/src/main/java/org/ttzero/excel/reader/RowSetIterator.java b/src/main/java/org/ttzero/excel/reader/RowSetIterator.java
index 1832cf76..fec27d2e 100644
--- a/src/main/java/org/ttzero/excel/reader/RowSetIterator.java
+++ b/src/main/java/org/ttzero/excel/reader/RowSetIterator.java
@@ -24,8 +24,8 @@ import java.util.function.Supplier;
  * @author guanquan.wang at 2019-04-17 19:03
  */
 class RowSetIterator implements Iterator<Row> {
-    private boolean onlyDataRow;
-    private Supplier<Row> supplier;
+    private final boolean onlyDataRow;
+    private final Supplier<Row> supplier;
     private Row nextRow = null;
 
     public RowSetIterator(Supplier<Row> supplier, boolean onlyDataRow) {
diff --git a/src/main/java/org/ttzero/excel/reader/SharedStrings.java b/src/main/java/org/ttzero/excel/reader/SharedStrings.java
index 76609103..dd5931c7 100644
--- a/src/main/java/org/ttzero/excel/reader/SharedStrings.java
+++ b/src/main/java/org/ttzero/excel/reader/SharedStrings.java
@@ -252,7 +252,7 @@ public class SharedStrings implements Closeable {
             backward = new String[page];
 
             // Cache 8KB binary, it will store 1^16 strings.
-            tester = new Tester.FixBinaryTester(max > 1 << 16 ? 1 << 16 : max);
+            tester = new Tester.FixBinaryTester(Math.min(max, 1 << 16));
 
             if (hotSize > 0) hot = FixSizeLRUCache.create(hotSize);
             else hot = FixSizeLRUCache.create();
@@ -560,7 +560,7 @@ public class SharedStrings implements Closeable {
         escapeBuf.delete(0, escapeBuf.length());
         do {
             escapeBuf.append(cb, from, idx_38 - from);
-            // ASCII值
+            // ASCII
             if (cb[idx_38 + 1] == '#') {
                 int n = toInt(cb, idx_38 + 2, idx_59);
                 // byte range
@@ -570,7 +570,7 @@ public class SharedStrings implements Closeable {
                 // Unicode char
                 escapeBuf.append((char) n);
             }
-            // 转义字符
+            // desc
             else {
                 String name = new String(cb, idx_38 + 1, idx_59 - idx_38 - 1);
                 switch (name) {
@@ -681,7 +681,9 @@ interface Tester {
     int analysis();
 
     class FixBinaryTester implements Tester {
-        private int start, limit, initial_size;
+        private int start;
+        private int limit;
+        private final int initial_size;
         private long[] marks;
 //        private static final int LIMIT = (1 << 25) - 1;
 
diff --git a/src/main/java/org/ttzero/excel/reader/Sheet.java b/src/main/java/org/ttzero/excel/reader/Sheet.java
index 499dbec1..426bf344 100644
--- a/src/main/java/org/ttzero/excel/reader/Sheet.java
+++ b/src/main/java/org/ttzero/excel/reader/Sheet.java
@@ -194,6 +194,7 @@ public interface Sheet extends Closeable {
      *
      * @throws IOException if I/O error occur
      */
+    @Override
     void close() throws IOException;
 
     /**
diff --git a/src/main/java/org/ttzero/excel/util/CSVUtil.java b/src/main/java/org/ttzero/excel/util/CSVUtil.java
index 3c846991..204f0f11 100644
--- a/src/main/java/org/ttzero/excel/util/CSVUtil.java
+++ b/src/main/java/org/ttzero/excel/util/CSVUtil.java
@@ -336,8 +336,8 @@ public class CSVUtil {
     public static class Reader implements Closeable {
 
         private RowsIterator iterator;
-        private Path path;
-        private Charset charset;
+        private final Path path;
+        private final Charset charset;
         private char separator;
 
         private Reader(Path path, Charset charset) {
@@ -468,7 +468,7 @@ public class CSVUtil {
         }
 
         // None item iterator
-        private Spliterator<String[]> emptySql = new Spliterator<String[]>() {
+        private final Spliterator<String[]> emptySql = new Spliterator<String[]>() {
             @Override
             public boolean tryAdvance(Consumer<? super String[]> action) {
                 return false;
@@ -951,7 +951,7 @@ public class CSVUtil {
      */
     public static class Writer implements Closeable {
 
-        private BufferedWriter writer;
+        private final BufferedWriter writer;
         // Comma separator character, default ','
         private char separator = COMMA;
         private int column;
@@ -965,7 +965,7 @@ public class CSVUtil {
          * Line separator string.  This is the value of the line.separator
          * property at the moment that the stream was created.
          */
-        private char[] lineSeparator = System.lineSeparator().toCharArray();
+        private final char[] lineSeparator = System.lineSeparator().toCharArray();
 
         /**
          * Create a CSV format writer
diff --git a/src/main/java/org/ttzero/excel/util/DateUtil.java b/src/main/java/org/ttzero/excel/util/DateUtil.java
index dd65b41e..ef4c4df0 100644
--- a/src/main/java/org/ttzero/excel/util/DateUtil.java
+++ b/src/main/java/org/ttzero/excel/util/DateUtil.java
@@ -94,6 +94,16 @@ public class DateUtil {
         return second / SECOND_OF_DAY + day + DAYS_1900_TO_1970;
     }
 
+    /**
+     * java.util.Date to Office open xml date
+     *
+     * @param date the java.util.Date value
+     * @return Office open xml date
+     */
+    public static double toDateTimeValue(Date date) {
+        return toDateTimeValue(new Timestamp(date.getTime()));
+    }
+
     /**
      * java.util.Date to Office open xml date
      *
diff --git a/src/main/java/org/ttzero/excel/util/ExtBufferedWriter.java b/src/main/java/org/ttzero/excel/util/ExtBufferedWriter.java
index abf298df..bf78f6a5 100644
--- a/src/main/java/org/ttzero/excel/util/ExtBufferedWriter.java
+++ b/src/main/java/org/ttzero/excel/util/ExtBufferedWriter.java
@@ -26,7 +26,7 @@ import java.io.Writer;
  * @author guanquan.wang on 2017/10/11.
  */
 public class ExtBufferedWriter extends BufferedWriter {
-    private static int defaultCharBufferSize = 8192;
+    private static final int defaultCharBufferSize = 8192;
 
     public ExtBufferedWriter(Writer out) {
         this(out, defaultCharBufferSize);
@@ -40,7 +40,7 @@ public class ExtBufferedWriter extends BufferedWriter {
         }
     }
 
-    private char[][] cache_char_array = new char[25][];
+    private final char[][] cache_char_array = new char[25][];
     static final char[] MIN_INTEGER_CHARS = {'-', '2', '1', '4', '7', '4', '8', '3', '6', '4', '8'};
     static final char[] MIN_LONG_CHARS = "-9223372036854775808".toCharArray();
     private static final char[][] ESCAPE_CHARS = new char[63][];
diff --git a/src/main/java/org/ttzero/excel/util/FileUtil.java b/src/main/java/org/ttzero/excel/util/FileUtil.java
index ea2ad479..3dc6c56b 100644
--- a/src/main/java/org/ttzero/excel/util/FileUtil.java
+++ b/src/main/java/org/ttzero/excel/util/FileUtil.java
@@ -218,6 +218,7 @@ public class FileUtil {
                     LOGGER.error("Copy file from [{}] to [{}] failed...", srcFile.getPath(), descFile.getPath());
                 return;
             } catch (IOException e) {
+                //
             }
         }
         try (FileChannel inChannel = new FileInputStream(srcFile).getChannel();
@@ -304,7 +305,7 @@ public class FileUtil {
             }
         }
 
-        String ss[] = src.list();
+        String[] ss = src.list();
         if (ss == null) return;
         List<File> files = new ArrayList<>();
         LinkedList<File> folders = new LinkedList<>();
diff --git a/src/main/java/org/ttzero/excel/util/ReflectUtil.java b/src/main/java/org/ttzero/excel/util/ReflectUtil.java
index fc2040d8..13bfbd4c 100644
--- a/src/main/java/org/ttzero/excel/util/ReflectUtil.java
+++ b/src/main/java/org/ttzero/excel/util/ReflectUtil.java
@@ -33,8 +33,7 @@ import java.util.function.Predicate;
  * @author guanquan.wang at 2019-08-15 21:02
  */
 public class ReflectUtil {
-    private ReflectUtil() {
-    }
+    private ReflectUtil() { }
 
     /**
      * List all declared fields that contains all supper class
@@ -79,11 +78,11 @@ public class ReflectUtil {
      * List all declared fields that contains all supper class
      *
      * @param beanClass The bean class to be analyzed.
-     * @param predicate A field filter
+     * @param filter A field filter
      * @return all declared fields
      */
-    public static Field[] listDeclaredFields(Class<?> beanClass, Predicate<Field> predicate) {
-        return listDeclaredFields(beanClass, Object.class, predicate);
+    public static Field[] listDeclaredFields(Class<?> beanClass, Predicate<Field> filter) {
+        return listDeclaredFields(beanClass, Object.class, filter);
     }
 
     /**
@@ -93,13 +92,13 @@ public class ReflectUtil {
      * @param stopClass The base class at which to stop the analysis.  Any
      *                  methods/properties/events in the stopClass or in its base classes
      *                  will be ignored in the analysis.
-     * @param predicate A field filter
+     * @param filter A field filter
      * @return all declared fields
      */
-    public static Field[] listDeclaredFields(Class<?> beanClass, Class<?> stopClass, Predicate<Field> predicate) {
+    public static Field[] listDeclaredFields(Class<?> beanClass, Class<?> stopClass, Predicate<Field> filter) {
         Field[] fields = listDeclaredFields(beanClass, stopClass);
 
-        return fieldFilter(fields, predicate);
+        return filter != null ? fieldFilter(fields, filter) : fields;
     }
 
     /**
@@ -118,15 +117,15 @@ public class ReflectUtil {
      * List all declared methods that contains all supper class
      *
      * @param beanClass The bean class to be analyzed.
-     * @param predicate A method filter
+     * @param filter A method filter
      * @return all declared method
      * @throws IntrospectionException happens during introspection error
      */
-    public static Method[] listDeclaredMethods(Class<?> beanClass, Predicate<Method> predicate)
+    public static Method[] listDeclaredMethods(Class<?> beanClass, Predicate<Method> filter)
         throws IntrospectionException {
         Method[] methods = listDeclaredMethods(beanClass);
 
-        return methodFilter(methods, predicate);
+        return filter != null ? methodFilter(methods, filter) : methods;
     }
 
     /**
@@ -162,15 +161,15 @@ public class ReflectUtil {
      * @param stopClass The base class at which to stop the analysis.  Any
      *                  methods/properties/events in the stopClass or in its base classes
      *                  will be ignored in the analysis.
-     * @param predicate A method filter
+     * @param filter A method filter
      * @return all declared method
      * @throws IntrospectionException happens during introspection error
      */
-    public static Method[] listDeclaredMethods(Class<?> beanClass, Class<?> stopClass, Predicate<Method> predicate)
+    public static Method[] listDeclaredMethods(Class<?> beanClass, Class<?> stopClass, Predicate<Method> filter)
         throws IntrospectionException {
         Method[] methods = listDeclaredMethods(beanClass, stopClass);
 
-        return methodFilter(methods, predicate);
+        return filter != null ? methodFilter(methods, filter) : methods;
     }
 
     /**
@@ -216,15 +215,15 @@ public class ReflectUtil {
      * List all declared read methods that contains all supper class
      *
      * @param beanClass The bean class to be analyzed.
-     * @param predicate A method filter
+     * @param filter A method filter
      * @return all declared method
      * @throws IntrospectionException happens during introspection error
      */
-    public static Method[] listReadMethods(Class<?> beanClass, Predicate<Method> predicate)
+    public static Method[] listReadMethods(Class<?> beanClass, Predicate<Method> filter)
         throws IntrospectionException {
         Method[] methods = listReadMethods(beanClass);
 
-        return methodFilter(methods, predicate);
+        return filter != null ? methodFilter(methods, filter) : methods;
     }
 
     /**
@@ -234,15 +233,15 @@ public class ReflectUtil {
      * @param stopClass The base class at which to stop the analysis.  Any
      *                  methods/properties/events in the stopClass or in its base classes
      *                  will be ignored in the analysis.
-     * @param predicate A method filter
+     * @param filter A method filter
      * @return all declared method
      * @throws IntrospectionException happens during introspection error
      */
-    public static Method[] listReadMethods(Class<?> beanClass, Class<?> stopClass, Predicate<Method> predicate)
+    public static Method[] listReadMethods(Class<?> beanClass, Class<?> stopClass, Predicate<Method> filter)
         throws IntrospectionException {
         Method[] methods = listReadMethods(beanClass, stopClass);
 
-        return methodFilter(methods, predicate);
+        return filter != null ? methodFilter(methods, filter) : methods;
     }
 
     /**
@@ -291,15 +290,15 @@ public class ReflectUtil {
      * List all declared methods that contains all supper class
      *
      * @param beanClass The bean class to be analyzed.
-     * @param predicate A method filter
+     * @param filter A method filter
      * @return all declared method
      * @throws IntrospectionException happens during introspection error
      */
-    public static Method[] listWriteMethods(Class<?> beanClass, Predicate<Method> predicate)
+    public static Method[] listWriteMethods(Class<?> beanClass, Predicate<Method> filter)
         throws IntrospectionException {
         Method[] methods = listWriteMethods(beanClass);
 
-        return methodFilter(methods, predicate);
+        return filter != null ? methodFilter(methods, filter) : methods;
     }
 
     /**
@@ -309,15 +308,15 @@ public class ReflectUtil {
      * @param stopClass The base class at which to stop the analysis.  Any
      *                  methods/properties/events in the stopClass or in its base classes
      *                  will be ignored in the analysis.
-     * @param predicate A method filter
+     * @param filter A method filter
      * @return all declared method
      * @throws IntrospectionException happens during introspection error
      */
-    public static Method[] listWriteMethods(Class<?> beanClass, Class<?> stopClass, Predicate<Method> predicate)
+    public static Method[] listWriteMethods(Class<?> beanClass, Class<?> stopClass, Predicate<Method> filter)
         throws IntrospectionException {
         Method[] methods = listWriteMethods(beanClass, stopClass);
 
-        return methodFilter(methods, predicate);
+        return filter != null ? methodFilter(methods, filter) : methods;
     }
 
     /**
@@ -377,11 +376,11 @@ public class ReflectUtil {
     }
 
     // Do Filter
-    private static Method[] methodFilter(Method[] methods, Predicate<Method> predicate) {
+    private static Method[] methodFilter(Method[] methods, Predicate<Method> filter) {
         int n = 0;
         for (int i = 0; i < methods.length; i++) {
             Method method = methods[i];
-            if (predicate.test(method)) {
+            if (filter.test(method)) {
                 if (i != n) methods[n] = method;
                 n++;
             }
@@ -390,12 +389,12 @@ public class ReflectUtil {
         return n < methods.length ? Arrays.copyOf(methods, n) : methods;
     }
 
-    private static Field[] fieldFilter(Field[] fields, Predicate<Field> predicate) {
+    private static Field[] fieldFilter(Field[] fields, Predicate<Field> filter) {
         int n = 0;
         for (int i = 0; i < fields.length; i++) {
             Field field = fields[i];
             field.setAccessible(true);
-            if (predicate.test(field)) {
+            if (filter.test(field)) {
                 if (i != n) fields[n] = field;
                 n++;
             }


Test patch:
diff --git a/src/test/java/org/ttzero/excel/entity/CustomerNumFmtTest.java b/src/test/java/org/ttzero/excel/entity/CustomerNumFmtTest.java
new file mode 100644
index 00000000..3bc7ca92
--- /dev/null
+++ b/src/test/java/org/ttzero/excel/entity/CustomerNumFmtTest.java
@@ -0,0 +1,106 @@
+package org.ttzero.excel.entity;
+
+import org.junit.Test;
+import org.ttzero.excel.annotation.ExcelColumn;
+
+import java.io.IOException;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+public class CustomerNumFmtTest extends WorkbookTest {
+
+    @Test public void testFmtInAnnotation() throws IOException {
+        new Workbook("customize_numfmt").addSheet(new ListSheet<>(Item.random())).writeTo(defaultTestPath);
+    }
+
+    @Test public void testFmtInAnnotationYmdHms() throws IOException {
+        new Workbook("customize_numfmt_full").addSheet(new ListSheet<>(ItemFull.randomFull())).writeTo(defaultTestPath);
+    }
+
+    @Test public void testDateFmt() throws IOException {
+        new Workbook("customize_numfmt_full")
+                .setAutoSize(true)
+                .addSheet(new ListSheet<>(ItemFull.randomFull()
+                , new Sheet.Column("编码", "code")
+                , new Sheet.Column("姓名", "name")
+                , new Sheet.Column("日期", "date").setNumFmt("yyyy年mm月dd日 hh日mm分")
+        )).writeTo(defaultTestPath);
+    }
+
+    @Test public void testNumFmt() throws IOException {
+        new Workbook("customize_numfmt_full")
+                .setAutoSize(true)
+                .addSheet(new ListSheet<>(ItemFull.randomFull()
+                        , new Sheet.Column("编码", "code")
+                        , new Sheet.Column("姓名", "name")
+                        , new Sheet.Column("日期", "date").setNumFmt("上午/下午hh\"時\"mm\"分\"")
+                        , new Sheet.Column("数字", "num").setNumFmt("#,##0 ;[Red]-#,##0 ")
+                )).writeTo(defaultTestPath);
+    }
+
+    @Test public void testNegativeNumFmt() throws IOException {
+        new Workbook("customize_negative")
+                .setAutoSize(true)
+                .addSheet(new ListSheet<>(Arrays.asList(new Num(12345678), new Num(0), new Num(-12345678))))
+                .writeTo(defaultTestPath);
+    }
+
+    static class Item {
+        @ExcelColumn
+        String code;
+        @ExcelColumn
+        String name;
+        @ExcelColumn(format = "yyyy-mm-dd")
+        Date date;
+
+        static List<Item> random() {
+            int n = random.nextInt(10) + 1;
+            List<Item> list = new ArrayList<>(n);
+            for (; n-- > 0; ) {
+                Item e = new Item();
+                e.code = "code" + n;
+                e.name = getRandomString();
+                e.date = new Timestamp(System.currentTimeMillis() - random.nextInt(9999999));
+                list.add(e);
+            }
+            return list;
+        }
+    }
+
+    static class ItemFull extends Item {
+
+        @ExcelColumn
+        long num;
+
+        @ExcelColumn(format = "yyyy-mm-dd hh:mm:ss")
+        public Date getDate() {
+            return date;
+        }
+
+        static List<ItemFull> randomFull() {
+            int n = random.nextInt(10) + 1;
+            List<ItemFull> list = new ArrayList<>(n);
+            for (; n-- > 0; ) {
+                ItemFull e = new ItemFull();
+                e.code = "code" + n;
+                e.name = getRandomString();
+                e.date = new Timestamp(System.currentTimeMillis() - random.nextInt(9999999));
+                e.num = random.nextLong();
+                list.add(e);
+            }
+            return list;
+        }
+    }
+
+    static class Num {
+        @ExcelColumn(format = "[Blue]#,##0.00_);[Red]-#,##0.00_);0_)")
+        int num;
+
+        Num(int num) {
+            this.num = num;
+        }
+    }
+}
diff --git a/src/test/java/org/ttzero/excel/entity/ListObjectSheetTest.java b/src/test/java/org/ttzero/excel/entity/ListObjectSheetTest.java
index 852dc866..678b79a0 100644
--- a/src/test/java/org/ttzero/excel/entity/ListObjectSheetTest.java
+++ b/src/test/java/org/ttzero/excel/entity/ListObjectSheetTest.java
@@ -525,7 +525,9 @@ public class ListObjectSheetTest extends WorkbookTest {
     }
 
     public static class Item {
+        @ExcelColumn
         private int id;
+        @ExcelColumn
         private String name;
 
         public Item(int id, String name) {
@@ -556,20 +558,35 @@ public class ListObjectSheetTest extends WorkbookTest {
     }
 
     public static class AllType {
+        @ExcelColumn
         private boolean bv;
+        @ExcelColumn
         private char cv;
+        @ExcelColumn
         private short sv;
+        @ExcelColumn
         private int nv;
+        @ExcelColumn
         private long lv;
+        @ExcelColumn
         private float fv;
+        @ExcelColumn
         private double dv;
+        @ExcelColumn
         private String s;
+        @ExcelColumn
         private BigDecimal mv;
+        @ExcelColumn
         private Date av;
+        @ExcelColumn
         private Timestamp iv;
+        @ExcelColumn
         private Time tv;
+        @ExcelColumn
         private LocalDate ldv;
+        @ExcelColumn
         private LocalDateTime ldtv;
+        @ExcelColumn
         private LocalTime ltv;
 
         public static List<AllType> randomTestData(int size) {
@@ -798,20 +815,35 @@ public class ListObjectSheetTest extends WorkbookTest {
     }
 
     public static class BoxAllType {
+        @ExcelColumn
         private Boolean bv;
+        @ExcelColumn
         private Character cv;
+        @ExcelColumn
         private Short sv;
+        @ExcelColumn
         private Integer nv;
+        @ExcelColumn
         private Long lv;
+        @ExcelColumn
         private Float fv;
+        @ExcelColumn
         private Double dv;
+        @ExcelColumn
         private String s;
+        @ExcelColumn
         private BigDecimal mv;
+        @ExcelColumn
         private Date av;
+        @ExcelColumn
         private Timestamp iv;
+        @ExcelColumn
         private Time tv;
+        @ExcelColumn
         private LocalDate ldv;
+        @ExcelColumn
         private LocalDateTime ldtv;
+        @ExcelColumn
         private LocalTime ltv;
 
         public static List<AllType> randomTestData(int size) {
@@ -972,6 +1004,7 @@ public class ListObjectSheetTest extends WorkbookTest {
     }
 
     public static class ExtItem extends Item {
+        @ExcelColumn
         private String nice;
 
         public ExtItem(int id, String name) {