2017年6月26日 星期一

Java 歷史特性 (5 ~ 7)

JAVA 5


  • 泛型
  • 自動封裝與解封裝
  •   例:
    1. long i = new Long(1);
  • 列舉(Enumerations)
  • 可變參數函式(Varargs)
  •   例:
    1. public void mehtod(String... args)
  • for each
  • Annotation
  • 改進多執行緒JAVA 程式的執行語義
  • 匯入靜態類別
  •   例:
    1. static import java.lang.System.*;
    2.  
    3. public class HelloWorld {
    4. public static void main(String args[]){
    5. out.println("Hello World.");
    6. }
    7. }

    JAVA 6

    建議直接參考原文:IBM - Java SE 6 新特性系列

  • Instrumentation 新功能
  •     動態載入/替換class
  • HTTP 增强
  • JMX 與系统管理
  • 編譯器 API
  • Java DB 和 JDBC 4.0
  •     Java DB (Derby) (Java內建DB)
        JDBC一些強化
  • 對腳本語言的支持
  • XML API 與 Web 服務
  • JAVA 7


  • 泛型寫法簡化
  •   例:
    1. HashMap<String, String> map = new HashMap<>()
    2.  
  • Multi-catch
  •   例:
    1. try{
    2.  
    3. }catch(NullPointerException | IllegalArgumentException e){
    4. e.printStackTrace();
    5. }
  • try-with-resources
  •   例:
    1. try(FileReader reader = new FileReader("file")){
    2.  
    3. }catch(IOException){
    4. e.printStackTrace();
    5. }
      在try中的資源,有實作java.lang.AutoCloseable的話
      於程式結束時會自動去call close方法

  • switch 支援字串
  • 數字可用下劃線「_」分隔
  •   例:123_456
  • byte,short,int,long 可用二進制表達
  •   例:int a = 0b01111;(數字前加0b)


    參考連結:
    wiki - Java_5.0
    IBM - Java SE 6 新特性系列

    2017年5月10日 星期三

    紅豆粽子

    材料:
    紅豆 1 斤 (600公克)
    6 兩 (250公克)
    1 斤 (600公克)
    糯米 2 斤 (1200公克)(1 斤約有10個粽子)

    糯米泡半天水

    準備餡料:
    1. 將紅豆煮爛(約1.5小時)
    2. 將紅豆加水放在漏盆中壓爛,下面用臉盆接
     目的有2點:a.去殼 b.取紅豆泥
    示意圖:

    3. 將第2步驟的紅豆泥水放到豆漿過濾袋脫水
    4. 將紅豆、糖、豬油一起下鍋去攪拌
     油的話推薦使用豬油 (比較香) (另外豬油最好的是板油)
     吃素的話可以用植物油
    (植物油的話要炒久一點,讓水分少一點比較好弄成一團一團的餡料)
     完成後約可以得到2斤的紅豆泥
    5. 在以1球約55公克的紅豆泥捏成長條狀

    6. 放冷凍
    到這邊為止餡料告一段落

    綁粽子:
    1. 取兩片粽葉
     粽葉有兩面,一面為光滑面,一面為粗糙面(梗突起的為粗糙面)
     光滑面朝上
    2. 將粽葉頭對尾重疊
    圖一 圖二
    3. 凹出容器的形狀(不要從中間凹,從1/4 or 3/4 處凹)
     看手順:
      凹右半邊要用第2點的圖一
      凹左半邊要用第2點的圖二

    4. 塞米塞紅豆餡
    5. 將粽葉閉合(待施工...)
    6. 用繩子綁起來

    大鍋水煮粽子約2小時

    完成!!!

    2017年4月26日 星期三

    WebSphere 使用xml設定部屬的專案

    有些應用程式在佈署到WebSphere的時候需要調整一些設定

    目前我碰到的是母類別最後(PARENT_LAST)


    是不是每次佈署都要改一次這個我沒有去驗證

    但這件事是可以靠xml去設定

    首先應用程式要弄成EAR檔的形式

    xml設定檔的位置如圖


    deployment.xml內容如下:
    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <appdeployment:Deployment xmi:version="2.0"
    3. xmlns:xmi="http://www.omg.org/XMI"
    4. xmlns:appdeployment="http://www.ibm.com/websphere/appserver/schemas/5.0/appdeployment.xmi"
    5. xmi:id="Deployment_1297859327856">
    6. <deployedObject xmi:type="appdeployment:ApplicationDeployment"
    7. xmi:id="ApplicationDeployment_1297859327856"
    8. startingWeight="10"
    9. warClassLoaderPolicy="SINGLE">
    10. <classloader xmi:id="Classloader_1297859327856" mode="PARENT_LAST"/>
    11. </deployedObject>
    12. </appdeployment:Deployment>

    Spring - CXF 小心得

    xml設定檔:
    1. <bean id="XxxBean" class="xxx.XxxBean">
    2.  
    3. <bean id="ServiceImpl" class="xxx.ServiceImpl" />
    4.  
    5. <jaxws:endpoint id="Service" implementorClass="xxx.Service" implementor="#ServiceImpl" address="/Service">
    6. <jaxws:properties>
    7. <entry key="mtom-enabled" value="true"/>
    8. </jaxws:properties>
    9. </jaxws:endpoint>


    Java:
    1. @MTOM
    2. public interface Service {
    3. @WebMethod
    4. public Response xxxMethod();
    5. }


    1. @MTOM
    2. public class ServiceImpl implements Service{
    3. @Autowired
    4. private ApplicationContext appContext;
    5.  
    6. // 讓bean不為單例改用此寫法
    7. private XxxBean getXxxBean(){
    8. return (XxxBean)appContext.getBean("XxxBean");
    9. }
    10.  
    11. @WebMethod
    12. public Response xxxMethod(){
    13. return new Response();
    14. }
    15. }


    1.不需要@WebService

    這邊要特別註明
    很多網站都會在 Java 的 Class 和 interface 上面加@WebService的標籤
    其實這個是很不必要的東西
    加了會造成這個 Service 有兩個接口

    一個是xml檔裡設定的address="/Service"
    另一個是@WebService

    @WebService有細項參數可以調整,若沒給的話會使用預設值
    而address就是其中一個

    所以如果沒有打算要開兩個接口的話就不要加@WebService

    (如果有讀者發現這段話是因為少設什麼所以才導致這種情形的話希望能回饋給我,感恩!!)

    2.scope prototype

    ServiceImpl在Spring-cxf的架構下每個request都是使用同一個ServiceImpl Object
    這就表示ServiceImpl中使用到@Autowired的Object也會被固定為同一個Object
    (無論@Autowired的Object有沒有做設定)

    假設有什麼寫法必須使用不同的Object,就只好使用appContext.getBean()的方式
    另外Bean要配合設定(擇其一)
    XML:scope="prototype"
    JAVA CLASS:@Scope("prototype")

    3.MTOM

    如果有用到DataHandler
    在上傳到service這時的inputStream用完的話要記得close
    不然會有殘檔在server上
    下載會自己關,不用擔心

    2017年4月25日 星期二

    zip4j 自定義OutputStream

    1. public class ZipUtil{
    2. /**
    3. * 由於有zipModel的原因
    4. * 故不建議使用static
    5. * */
    6.  
    7. private ZipModel zipModel = new ZipModel();
    8.  
    9. /*
    10. public static void main(String[] args) {
    11. ZipUtil util = new ZipUtil();
    12. try {
    13. util.doZipEnc();
    14. System.out.println("成功");
    15. } catch (Exception e) {
    16. System.out.println("失敗");
    17. e.printStackTrace();
    18. }
    19. }
    20.  
    21. public void doZipEnc() {
    22. try {
    23. // 將壓縮流寫到內存
    24. // ZipOutputStream saos = new ZipOutputStream(new ByteArrayOutputStream(), this.zipModel);
    25. // 本地測試
    26. FileOutputStream f = new FileOutputStream("C:/Users/Louis/Desktop/test1.zip" ,true);
    27. ZipOutputStream saos = new ZipOutputStream(f, this.zipModel);
    28.  
    29. for(int i=0;i<2;i++){
    30. ZipParameters parameters = createZipParameters("test" + i + ".txt", "" + i + i + i);
    31. addNewStreamToZip(saos, ("test" + i + ".txt").getBytes(), parameters);
    32. }
    33. // saos.writeTo(f);
    34.  
    35. saos.finish();
    36. saos.close();
    37. f.close();
    38. //文件大小
    39. // System.out.println("size:" +(saos.toByteArray().length/1024));
    40. } catch (Exception e) {
    41. e.printStackTrace();
    42. }
    43. }
    44. */
    45. public ZipOutputStream createZipOutputStream(OutputStream outputStream){
    46. return new ZipOutputStream(outputStream, zipModel);
    47. }
    48.  
    49. public void addNewStreamToZip(ZipOutputStream outputStream, InputStream data, ZipParameters parameters) throws ZipException {
    50. ByteArrayOutputStream tempOut = new ByteArrayOutputStream();
    51.  
    52. try{
    53. byte[] buffer = new byte[1024];
    54.  
    55. for(int i ; (i = data.read(buffer)) != -1 ; ){
    56. tempOut.write(buffer, 0, i);
    57. tempOut.flush();
    58. }
    59. }catch (Exception e) {
    60. throw new ZipException(e);
    61. }finally{
    62. try{
    63. data.close();
    64. tempOut.close();
    65. }catch (Exception e) {
    66. }
    67. }
    68.  
    69. addNewStreamToZip(outputStream, tempOut.toByteArray(), parameters);
    70. }
    71.  
    72. public void addNewStreamToZip(ZipOutputStream saos, byte[] data, ZipParameters parameters) throws ZipException {
    73. try {
    74. if (zipModel.getEndCentralDirRecord() == null) {
    75. throw new ZipException("invalid end of central directory record");
    76. }
    77. /*
    78. checkParameters(parameters);
    79.  
    80. saos.putNextEntry(null, parameters);
    81.  
    82. if (! parameters.getFileNameInZip().endsWith("/") &&
    83. ! parameters.getFileNameInZip().endsWith("\\")
    84. ) {
    85. saos.write(data);
    86. }
    87. */
    88. saos.putNextEntry(null, parameters);
    89.  
    90. saos.write(data);
    91.  
    92. saos.closeEntry();
    93. } catch (ZipException e) {
    94. throw e;
    95. } catch (Exception e) {
    96. throw new ZipException(e);
    97. }
    98. }
    99.  
    100. public ZipParameters createZipParameters(String fileNameInZip, String password){
    101. ZipParameters parameters = new ZipParameters();
    102.  
    103. parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE); // 壓縮方式
    104. parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL); // 壓縮等級
    105. parameters.setSourceExternalStream(true);
    106. parameters.setFileNameInZip(fileNameInZip);
    107.  
    108. // 若有多檔,password個別有效
    109. if(password != null && ! password.trim().isEmpty()) {
    110. parameters.setEncryptFiles(true);
    111. parameters.setEncryptionMethod(Zip4jConstants.ENC_METHOD_STANDARD); // 加密方式
    112. parameters.setPassword(password);
    113. }
    114.  
    115. return parameters;
    116. }
    117. /*
    118. private void checkParameters(ZipParameters parameters) throws ZipException {
    119.  
    120. if (parameters == null) {
    121. throw new ZipException("cannot validate zip parameters");
    122. }
    123.  
    124. if ((parameters.getCompressionMethod() != Zip4jConstants.COMP_STORE) &&
    125. parameters.getCompressionMethod() != Zip4jConstants.COMP_DEFLATE) {
    126. throw new ZipException("unsupported compression type");
    127. }
    128.  
    129. if (parameters.getCompressionMethod() == Zip4jConstants.COMP_DEFLATE) {
    130. if (parameters.getCompressionLevel() < 0 && parameters.getCompressionLevel() > 9) {
    131. throw new ZipException("invalid compression level. compression level dor deflate should be in the range of 0-9");
    132. }
    133. }
    134.  
    135. if (parameters.isEncryptFiles()) {
    136. if (parameters.getEncryptionMethod() != Zip4jConstants.ENC_METHOD_STANDARD &&
    137. parameters.getEncryptionMethod() != Zip4jConstants.ENC_METHOD_AES) {
    138. throw new ZipException("unsupported encryption method");
    139. }
    140.  
    141. if (parameters.getPassword() == null || parameters.getPassword().length <= 0) {
    142. throw new ZipException("input password is empty or null");
    143. }
    144. } else {
    145. parameters.setAesKeyStrength(-1);
    146. parameters.setEncryptionMethod(-1);
    147. }
    148. }
    149. */
    150. }


    用法:
    1. ZipUtil zipUtil = new ZipUtil();
    2.  
    3. ZipOutputStream zipOutputStream = zipUtil.createZipOutputStream(outputStream);
    4.  
    5. ZipParameters parameters = zipUtil.createZipParameters(fileNameInZip, zipPwd);
    6.  
    7. zipUtil.addNewStreamToZip(zipOutputStream, input, parameters);
    8.  
    9. zipOutputStream.finish();
    10. zipOutputStream.close();


    第3行這邊可以自由定義需要的OutputStream
    第5行
    fileNameInZip:在zip檔中檔案的名字
    (zip檔中允許相同名字的檔案,有問題的只會發生在解zip的時候一直會問你要不要覆蓋相同的檔案)
    zipPwd:密碼是個別有效的

    CompareCollectionUtil

    使用CompareCollectionUtil有個大前提

    Collection內的Class必須實作 hashCode 和 equal
    因為原理是使用equal這個方法在比較

    1. public class CompareCollectionUtil {
    2. /**
    3. * a = [1, 2, 3] b = [2, 3, 4]
    4.  
    5. * 比對兩個List 用 a 去跟 b 比較,回傳缺少的部分
    6.  
    7. * return [1]
    8. **/
    9. public static <T> List<T> deleted(Collection<T> a, Collection<T> b) {
    10. if(a == null){
    11. return new ArrayList<T>();
    12. }
    13.  
    14. List<T> result = new ArrayList<T>(a);
    15.  
    16. if(b != null){
    17. result.removeAll(b);
    18. }
    19.  
    20. return result;
    21. }
    22.  
    23. /**
    24. * a = [1, 2, 3] b = [2, 3, 4]
    25.  
    26. * 比對兩個List 用 a 去跟 b 比較,回傳相同的部分
    27.  
    28. * return [2, 3]
    29. **/
    30. public static <T> List<T> retained(Collection<T> a, Collection<T> b) {
    31. if(a == null || b == null){
    32. return new ArrayList<T>();
    33. }
    34.  
    35. List<T> result = new ArrayList<T>(a);
    36. result.retainAll(b);
    37.  
    38. return result;
    39. }
    40.  
    41. /**
    42. * a = [1, 2, 3] b = [2, 3, 4]
    43.  
    44. * 比對兩個List 用 a 去跟 b 比較,回傳多的部分
    45.  
    46. * return [4]
    47. **/
    48. public static <T> List<T> added(Collection<T> a, Collection<T> b) {
    49. return deleted(b, a);
    50. }
    51.  
    52. /**
    53. * a = [1, 2, 3] b = [2, 3, 4]
    54.  
    55. * 比對兩個List 用 a 去跟 b 比較,回傳不同的部分
    56.  
    57. * return [1, 4]
    58. **/
    59. public static <T> List<T> different(Collection<T> a, Collection<T> b){
    60. List<T> list = new ArrayList<T>();
    61.  
    62. list.addAll(added(a, b));
    63. list.addAll(deleted(a, b));
    64.  
    65. return list;
    66. }
    67. }

    2017年4月21日 星期五

    自定義Hibernate Type

    原始問題:
    某個老舊的table設定,其中一個欄位設char(10)
    結果裡面的值有長有短,不足10碼的就自動補空白
    導致在後端處理的時候處理很麻煩

    當然最簡單的方式是改DB欄位設定
    但總不是如人所願
    所以只好針對Hibernate轉Bean的時候下手

    先談失敗的改法:
    我修改Hibernate塞Bean的時候一定要透過set方法 (找不到怎麼設的了QQ)
    然後我在set方法中寫trim的動作
    承原始問題,那些char設定很多都用在Primary Key
    最後用Bean Update資料的時候就爆炸了
    Exception在Hibernate這邊,還沒到DB
    Exception的大意是Bean的Id被改過 (很久以前,也懶得還原當時情況了)

    後來就改為現在這邊要介紹的方法

    先自定義Hibernate Type
    1. public class CustomerTrimStringType implements UserType{
    2.  
    3. private int sqlType(){
    4. return Types.VARCHAR;
    5. }
    6.  
    7. @Override
    8. public Object assemble(Serializable arg0, Object arg1) throws HibernateException {
    9. return null;
    10. }
    11.  
    12. @Override
    13. public Object deepCopy(Object value) throws HibernateException {
    14. return value;
    15. }
    16.  
    17. @Override
    18. public Serializable disassemble(Object arg0) throws HibernateException {
    19. return null;
    20. }
    21.  
    22. @Override
    23. public boolean equals(Object value1, Object value2) throws HibernateException {
    24. if (value1 == null) {
    25. if (value2 != null) {
    26. return false;
    27. }
    28. return true;
    29. }
    30.  
    31. return value1.equals(value2);
    32. }
    33.  
    34. @Override
    35. public int hashCode(Object value) throws HibernateException {
    36. return value == null ? 0 : value.hashCode();
    37. }
    38.  
    39. @Override
    40. public boolean isMutable() {
    41. return false;
    42. }
    43.  
    44. @Override
    45. public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner)
    46. throws HibernateException, SQLException
    47. {
    48. Object value = rs.getString(names[0]);
    49. if ((value == null) || (rs.wasNull())){
    50. return null;
    51. }
    52.  
    53. return value.toString().trim();
    54. }
    55.  
    56. @Override
    57. public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor arg3)
    58. throws HibernateException, SQLException
    59. {
    60. if(value == null){
    61. st.setNull(index, sqlType());
    62. }else{
    63. st.setString(index, (String)value);
    64. }
    65. }
    66.  
    67. @Override
    68. public Object replace(Object arg0, Object arg1, Object arg2) throws HibernateException {
    69. return null;
    70. }
    71.  
    72. @Override
    73. public Class returnedClass() {
    74. return String.class;
    75. }
    76.  
    77. @Override
    78. public int[] sqlTypes() {
    79. return new int[]{ Types.VARCHAR };
    80. }
    81. }

    在45行nullSafeGet這個方法做修改
    另外,沒意外的話nullSafeSet這個方法是Hibernate -> DB的動作

    設定Hibernate Bean的欄位要使用自定義的Type
    1. public class TableBean {
    2.  
    3. private String column1;
    4.  
    5. @Column(name = "column1")
    6. @Type(type="packageName.CustomerTrimStringType")
    7. public String getColumn1() {
    8. return this.Column1;
    9. }
    10.  
    11. public void setColumn1(String column1){
    12. this.column1 = column1;
    13. }
    14. }

    2017年4月20日 星期四

    Struts Converter

    原始問題:
    Client端的時區是美國
    Server端時區是台灣
    當Client Submit資料時,Date就會有時區的差異沒辦法直接使用
    所以配置了針對Date格式的轉換器去固定時區

    1. 先建立轉換器的Class
    1. public class DateConvert extends DefaultTypeConverter{
    2. @Override
    3. @SuppressWarnings("rawtypes")
    4. public Object convertValue(Map context, Object value, Class toType) {
    5. if(Date.class.equals(toType)){
    6. DateFormat df = DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.TAIWAN);
    7. String[] str = (String[]) value;
    8. try {
    9. if(str != null && ! StringUtils.isBlank(str[0])){
    10. return df.parse(str[0]);
    11. }else{
    12. return null;
    13. }
    14. } catch (ParseException e) {
    15. e.printStackTrace();
    16. }
    17. }
    18. /*
    19. // 可以在同一個class針對不同的type做處理
    20. // 不過個人比較偏好一種寫一個class (有需求的話)
    21. else if(String.class.equals(toType)){
    22. do something
    23. }
    24. */
    25. return null;
    26. }
    27. }

    2. 建立struts2的配置文件xwork-conversion.properties,放在src路徑下
    java.util.Date=pageName.DateConvert



    參考連結:
    http://www.blogjava.net/max/archive/2013/04/10/79602.html
    http://www.cnblogs.com/lpshou/archive/2012/11/27/2791188.html

    2017年4月19日 星期三

    民國年選擇器 DatePicker

    要搭配Jquery Datepicker

    1. /**
    2. * Created by EIJI on 2014/1/3.
    3. */
    4. (function(){
    5. var dateNative = new Date();
    6.  
    7. // 補0函式
    8. var padLeft = function(str, len){
    9.  
    10. if(str.length >= len){
    11. return str;
    12. }else{
    13. return padLeft(("0" + str), len);
    14. }
    15. };
    16.  
    17. var funcColle = function(){
    18. this.onSelect = {
    19. basic: function(dateText, inst){
    20. /*
    21. var yearNative = inst.selectedYear < 1911 ? inst.selectedYear + 1911 : inst.selectedYear;
    22. */
    23. dateNative = new Date(inst.selectedYear, inst.selectedMonth, inst.selectedDay);
    24.  
    25. // 年分小於100會被補成19**, 要做例外處理
    26. var yearTW;
    27. if (twSettings.yearPadZero) {
    28. if(inst.selectedYear > 1911){
    29. yearTW = padLeft((inst.selectedYear - 1911).toString(), 3);
    30. }else{
    31. yearTW = padLeft(inst.selectedYear.toString(), 3)
    32. }
    33. }else{
    34. if(inst.selectedYear > 1911){
    35. yearTW = inst.selectedYear - 1911;
    36. }else{
    37. yearTW = inst.selectedYear;
    38. }
    39. }
    40. var monthTW = padLeft((inst.selectedMonth + 1).toString(), 2);
    41. var dayTW = padLeft(inst.selectedDay, 2);
    42.  
    43. return yearTW + twSettings.splitMark + monthTW + twSettings.splitMark + dayTW;
    44. }
    45. };
    46. };
    47.  
    48. var twSettings = {
    49. closeText: '關閉',
    50. prevText: '上個月',
    51. nextText: '下個月',
    52. currentText: '今天',
    53. changeYear: true, //手動修改年
    54. changeMonth: true, //手動修改月
    55. yearRange: '1912:' + dateNative.getFullYear(),
    56. monthNames: [
    57. '一月','二月','三月','四月','五月',
    58. '六月','七月','八月','九月','十月',
    59. '十一月','十二月'
    60. ],
    61. monthNamesShort: [
    62. '一月','二月','三月','四月','五月',
    63. '六月','七月','八月','九月','十月',
    64. '十一月','十二月'
    65. ],
    66. dayNames: ['星期日','星期一','星期二','星期三','星期四','星期五','星期六'],
    67. dayNamesShort: ['周日','周一','周二','周三','周四','周五','周六'],
    68. dayNamesMin: ['日','一','二','三','四','五','六'],
    69. weekHeader: '周',
    70. dateFormat: 'yy/mm/dd',
    71. splitMark: '/', // 分割年月日的標誌
    72. firstDay: 1,
    73. isRTL: false,
    74. showMonthAfterYear: false,
    75. yearSuffix: '',
    76. yearPadZero: false,
    77. beforeShow: function() {
    78. setTimeout(function(){
    79. $('.ui-datepicker').css('z-index', 1000);
    80. }, 0);
    81. }
    82. // ,defaultLastYear: false
    83. /*
    84. 當沒有設defaultDate時,先用yearRange做defaultDate
    85. 此時defaultLastYear設定預設為最後一年or第一年
    86. */
    87. };
    88.  
    89. // 把yearText換成民國
    90. var replaceYearText = function(){
    91. var $yearText = $('.ui-datepicker-year');
    92.  
    93. if(twSettings.changeYear !== true){
    94. $yearText.text('民國' + dateNative.getFullYear() - 1911);
    95. }else{
    96. // 下拉選單
    97. /*
    98. if($yearText.prev('span.datepickerTW-yearPrefix').length === 0){
    99. $yearText.before("民國 ");
    100. }
    101. */
    102. $yearText.children().each(function(){
    103. if(parseInt($(this).text(), 10) > 1911){
    104. $(this).text(parseInt($(this).text(), 10) - 1911);
    105. }
    106. });
    107. }
    108. };
    109.  
    110. $.fn.datepickerTW = function(options){
    111. if(typeof options === "undefined"){
    112. options = {};
    113. }
    114.  
    115. var fn = new funcColle();
    116. // setting on init,
    117. if(typeof options == 'object'){
    118. //onSelect例外處理, 避免覆蓋
    119. if(typeof options.onSelect == 'function'){
    120. fn.onSelect.newFunc = options.onSelect;
    121. }
    122.  
    123. options.onSelect = function(dateText, inst){
    124. var outputValue = fn.onSelect.basic(dateText, inst);
    125.  
    126. if(twSettings.yearPadZero){
    127. outputValue = padLeft(outputValue, 9);
    128. }
    129. $(this).val(outputValue);
    130.  
    131. if(typeof fn.onSelect.newFunc === 'function'){
    132. fn.onSelect.newFunc(outputValue, inst);
    133. }
    134. };
    135.  
    136. // year range正規化成西元, 小於1911的數字都會被當成民國年
    137. if(options.yearRange){
    138. var temp = options.yearRange.split(':');
    139. for(var i = 0; i < temp.length; i += 1){
    140. //民國前處理
    141. if(parseInt(temp[i], 10) < 1 ){
    142. temp[i] = parseInt(temp[i], 10) + 1911;
    143. }else{
    144. if(parseInt(temp[i], 10) < 1911){
    145. temp[i] = parseInt(temp[i], 10) + 1911;
    146. }else{
    147. temp[i] = temp[i];
    148. }
    149. }
    150. }
    151. options.yearRange = temp[0] + ':' + temp[1];
    152. }
    153.  
    154. // 預設default Date
    155. if(options.defaultDate){
    156.  
    157. }else if(options.yearRange){
    158. var temp = options.yearRange.split(':');
    159.  
    160. if(options.defaultLastYear){
    161. options.defaultDate = temp[1] - new Date().getFullYear() + 'y';
    162. }else{
    163. options.defaultDate = temp[0] - new Date().getFullYear() + 'y';
    164. }
    165. }else if($.trim($(this).val()) != '' && $(this).val() != undefined){
    166. var tempDate = $(this).val().split(twSettings.splitMark);
    167. var tempYear = tempDate[0];
    168.  
    169. var year;
    170. if(parseInt(tempYear, 10) < 1911){
    171. year = padLeft((parseInt(tempYear, 10) + 1911).toString(), 4);
    172. }else{
    173. year = parseInt(tempYear, 10);
    174. }
    175.  
    176. options.defaultDate = year - new Date().getFullYear() + 'y';
    177. }
    178. }
    179.  
    180. // setting after init
    181. if(arguments.length > 1){
    182. // 目前還沒想到正常的解法, 先用轉換成init setting obj的形式
    183. if(arguments[0] === 'option'){
    184. options = {};
    185. options[arguments[1]] = arguments[2];
    186. }
    187. }
    188.  
    189. // override settings
    190. $.extend(twSettings, options);
    191.  
    192. // init
    193. $(this).datepicker(twSettings);
    194.  
    195. // beforeRender
    196. $(this).click(function(){
    197. var isFirstTime = ($(this).val() == '');
    198. // year range and default date
    199. if((twSettings.defaultDate || twSettings.yearRange) && isFirstTime){
    200. /* 當有year range時, select初始化設成range的最末年
    201. 已調整到上面"預設default Date"中
    202. if(twSettings.defaultDate){
    203. $(this).datepicker('setDate', twSettings.defaultDate);
    204. }
    205.  
    206. // 這段處理不好,因為「年」只改了外觀,但javascript實際值還是原本那年
    207. // 當有year range時, select初始化設成range的最末年
    208. if(twSettings.yearRange){
    209. var $yearSelect = $('.ui-datepicker-year'),
    210. nowYear = twSettings.defaultDate
    211. ? $(this).datepicker('getDate').getFullYear()
    212. : dateNative.getFullYear();
    213.  
    214. $yearSelect.children(':selected').removeAttr('selected');
    215. if($yearSelect.children('[value=' + nowYear + ']').length > 0){
    216. $yearSelect.children('[value=' + nowYear + ']').attr('selected', 'selected');
    217. }else{
    218. $yearSelect.children().last().attr('selected', 'selected');
    219. }
    220. }
    221. */
    222. } else {
    223. var tempDate = $(this).val().split(twSettings.splitMark);
    224.  
    225. if(tempDate.length != 3){
    226. $(this).datepicker('setDate', new Date());
    227. }else{
    228. var tempYear = tempDate[0];
    229. var tempMonth = tempDate[1] - 1;
    230. var tempDay = padLeft(tempDate[2], 2);
    231.  
    232. var year;
    233. if(parseInt(tempYear, 10) < 1911){
    234. year = padLeft((parseInt(tempYear, 10) + 1911).toString(), 4);
    235. }else{
    236. year = parseInt(tempYear, 10);
    237. }
    238.  
    239. dateNative = new Date(year, tempMonth, tempDay);
    240.  
    241. $(this).datepicker('setDate', dateNative);
    242. }
    243. }
    244.  
    245. var yearTW;
    246. if (twSettings.yearPadZero) {
    247. if(dateNative.getFullYear() > 1911){
    248. yearTW = padLeft((dateNative.getFullYear() - 1911).toString(), 3);
    249. }else{
    250. yearTW = padLeft(dateNative.getFullYear().toString(), 3);
    251. }
    252. }else{
    253. if(dateNative.getFullYear() > 1911){
    254. yearTW = dateNative.getFullYear() - 1911;
    255. }else{
    256. yearTW = dateNative.getFullYear();
    257. }
    258. }
    259. var monthTW = padLeft((dateNative.getMonth() + 1).toString(), 2);
    260. var dayTW = padLeft(dateNative.getDate().toString(), 2);
    261.  
    262. $(this).val(yearTW + twSettings.splitMark + monthTW + twSettings.splitMark + dayTW);
    263.  
    264. replaceYearText();
    265.  
    266. if(isFirstTime){
    267. $(this).val('');
    268. }
    269. });
    270.  
    271. // afterRender
    272. $(this).focus(function(){
    273. replaceYearText();
    274. });
    275.  
    276. return this;
    277. };
    278. })();

    用法跟Jquery Datepicker一樣
        $element.datepickerTW();

    在這邊有客製小功能:

        splitMark: '/' // 分割年月日的標誌
    可自由設定年月日中間的分隔符號

        yearPadZero: false
    年的部分(民國年)補滿3碼