개발을 하다보면 문자열에 들어있는 날짜를 Date 형으로 변환해야 하는 경우가 있습니다.
날짜의 포맷이 조금씩 다르면 그 때마다 그에 맞춰 Date로 변환하는 작업을 해야하고, 특히 문자열에 timezone이 들어 있으면 은근히 귀찮습니다.
문자열로 날짜를 나타내는 규격은 크게 2개가 있습니다.
하나는 RFC 822이고 다른 하나는 ISO 8601 입니다. 그리고, 규격이 다르기 때문에 timezone을 나타내는 방법도 약간 다릅니다.
여기서는 두 규격 모두를 만족하는 static method를 만들어 보겠습니다.
1. SimpleDateFormat 및 단순 Format 문자열
자바에서 문자열을 Date 형으로 변환해야 할 때는 보통 SimpleDateFormat 클래스를 사용해서 변환합니다. 간단한 사용예를 보면 다음과 같습니다.
1 2 3 4 |
|
SimpleDateFormat에 사용되는 Format 문자열 (위에서 "yyyy-MM-dd HH:mm:ss.SSS")에 대한 설명은 여기에 자세히 나와있습니다.
2. RFC 822 & ISO 8601의 Timezone
Format 문자열에 timezone을 추가할 수 있습니다. Timezone을 나타내는 방법은 다음과 같습니다.
규격 | 표기법 |
설명 | SimpleDateFormat의 Format 문자열 |
RFC 822 | +0900 | UTC 기준으로 9시간 늦음 (한국 표준시) | Z |
KST, EST, IST |
Korea Standard Time (+0900) East Standard Time (-0500) India Standard Time (+0530) 과 같이 3자리 문자열 |
||
ISO 8601 | Z | 그리니치 표준시 (+00 과 동일) | X |
+09 |
|
||
+0900 |
|
||
+05:30 | 인도처럼 분단위 시간을 사용하는 경우 | XXX |
ISO 8601의 표기법의 Z (UTC+0을 나타냄)와 SimpleDateFormat의 Format 문자열의 Z(RFC 822의 timezone을 나타냄) 가 동일하여 헷갈릴 수 있으나 사용 예를 보면 쉽게 이해할 수 있습니다.
즉, 문자열 "2017-10-17 23:20:00.123KST"는 new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ"); 를 사용하여 Date로 변환할 수 있습니다.
또, 문자열 "2017-10-17 14:20:00.123Z"를 Date로 변환하려면 new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSX"); 를 사용하면 됩니다.
참고로, ISO 8601에서는 "2017-10-17T14:20:00.123Z"와 같이 날짜와 시간 사이에 T문자가 있는 것이 규격이고 이를 변환하기 위해서는 new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX"); 와 같이 'T'(single quotation으로 감싸야 합니다.)를 넣어줘야 합니다.
하지만, 여기 예제에서는 규격에 상관없이 T가 있거나 또는 없어도 정상 처리하도록 하겠습니다.
3. 문자열을 Date로 변환하는 static method
기본적으로 년-월-일 시:분:초 까지는 있어야 하며 밀리초는 있거나 없어도 정상적으로 처리되도록 했습니다.
그 외 timezone은 위에서 언급한 모든 종류의 timezone을 처리하도록 했습니다.
package net.zeany.date; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.regex.Pattern; public class DateUtils { // 문자열 (날짜) => SimpleDateFormat // yyyy-MM-dd HH:mm:ss => yyyy-MM-dd HH:mm:ss // yyyy-MM-dd HH:mm:ss.SSS => yyyy-MM-dd HH:mm:ss.SSS // // yyyy-MM-dd HH:mm:ssZ => yyyy-MM-dd HH:mm:ssX // yyyy-MM-dd HH:mm:ss+09 => yyyy-MM-dd HH:mm:ssX // yyyy-MM-dd HH:mm:ss+0900 => yyyy-MM-dd HH:mm:ssX // yyyy-MM-dd HH:mm:ss+09:00 => yyyy-MM-dd HH:mm:ssXXX // yyyy-MM-dd HH:mm:ssKST => yyyy-MM-dd HH:mm:ssZ // // yyyy-MM-dd HH:mm:ss.SSSZ => yyyy-MM-dd HH:mm:ss.SSSX // yyyy-MM-dd HH:mm:ss.SSS+09 => yyyy-MM-dd HH:mm:ss.SSSX // yyyy-MM-dd HH:mm:ss.SSS+0900 => yyyy-MM-dd HH:mm:ss.SSSX // yyyy-MM-dd HH:mm:ss.SSS+09:00 => yyyy-MM-dd HH:mm:ss.SSSXXX // yyyy-MM-dd HH:mm:ss.SSSKST => yyyy-MM-dd HH:mm:ss.SSSZ // // yyyy-MM-ddTHH:mm:ssZ => yyyy-MM-dd'T'HH:mm:ssX // yyyy-MM-ddTHH:mm:ss+09 => yyyy-MM-dd'T'HH:mm:ssX // yyyy-MM-ddTHH:mm:ss+0900 => yyyy-MM-dd'T'HH:mm:ssX // yyyy-MM-ddTHH:mm:ss+09:00 => yyyy-MM-dd'T'HH:mm:ssX // yyyy-MM-ddTHH:mm:ssKST => yyyy-MM-dd'T'HH:mm:ssZ // // yyyy-MM-ddTHH:mm:ss.SSSZ => yyyy-MM-dd'T'HH:mm:ss.SSSX // yyyy-MM-ddTHH:mm:ss.SSS+09 => yyyy-MM-dd'T'HH:mm:ss.SSSX // yyyy-MM-ddTHH:mm:ss.SSS+0900 => yyyy-MM-dd'T'HH:mm:ss.SSSX // yyyy-MM-ddTHH:mm:ss.SSS+09:00 => yyyy-MM-dd'T'HH:mm:ss.SSSXXX // yyyy-MM-ddTHH:mm:ss.SSSKST => yyyy-MM-dd'T'HH:mm:ss.SSSZ public static Date parse(String strDate) throws ParseException { if (strDate == null || strDate.isEmpty()) { throw new ParseException("Empty string", 0); } StringBuilder sdfSb = new StringBuilder("yyyy-MM-dd"); if (strDate.length() < 19) { // "yyyy-MM-dd HH:mm:ss".length == 19 throw new ParseException("Time is needed.", 11); } if (strDate.charAt(10) == 'T') { sdfSb.append("'T'HH:mm:ss"); } else if (strDate.charAt(10) == ' ') { sdfSb.append(" HH:mm:ss"); } else { throw new ParseException("Wrong separator", 10); } int timezoneIndex; // .SSS는 있을 수도 있고 없을 수도 있음, 없는 경우에는 19번째부터 timezone이고
// 있는 경우는 23번째부터 timezone
if (strDate.substring(19).length() >= 4
&& Pattern.matches("[.]\\d{3}", strDate.substring(19, 23))) {
sdfSb.append(".SSS"); timezoneIndex = 23; } else { timezoneIndex = 19; } // Timezone을 요약해보면 Z, +09, +0900은 X로, +09:00은 XXX로, KST는 Z로 String timezone = strDate.substring(timezoneIndex); if (timezone.equals("")) { ; } else if (timezone.equals("Z")) { sdfSb.append("X"); } else if (Pattern.matches("[+|-]\\d{2}", timezone)) { sdfSb.append("X"); } else if (Pattern.matches("[+|-]\\d{4}", timezone)) { sdfSb.append("X"); } else if (Pattern.matches("[+|-]\\d{2}[:]\\d{2}", timezone)) { sdfSb.append("XXX"); } else if (Pattern.matches("[A-Z]{3}", timezone)) { sdfSb.append("Z"); } else { throw new ParseException("Wrong timezone", timezoneIndex); } SimpleDateFormat sdf = new SimpleDateFormat(sdfSb.toString()); return sdf.parse(strDate); } }
이상으로 자바에서 문자열을 Date로 변환하는 방법에 대해 살펴봤습니다.