[JAVA] 람다식 기본 예제 2 (sorted)

JDK 라이브러리의 코드에서 정렬, 리버싱은 많이 사용된다.
정렬을 위해 리스트에 대해 sort() 메서드를 사용한다. 이 메서드는 void메서드 이기 때문에 원본 리스트를 보존하기 위해서 복사본을 만들어 두어야 하고 원본이 아닌 복사복에 대해서 sort() 메서드는 호출해야한다.
이젠 stream 의 sorted()를 사용하면 번거러운 작업을 피할 수 있다.

예제소스1은 하나의 값으로 정렬.
예제소스2는 복수의 값을 비교하여 정렬

예제소스1

public class Person {

	  private final String name;
	  private final int age;

	  public Person(final String theName, final int theAge) {
	    name = theName;
	    age = theAge;
	  } 

	  public String getName() { return name; }
	  public int getAge() { return age; }

	  public String toString() {
	    return String.format("%s - %d", name, age);
	  }

} 


final List<Person> people = Arrays.asList( new Person("Zohn", 25), new Person("Sara", 21), new Person("Jane", 21), new Person("Greg", 35)); //java7 sort 사용 Collections.sort(people, new Comparator<Person>() { public int compare(Person o1, Person o2) { return o1.getName().compareTo(o2.getName()); } }); Collections.sort(people, new Comparator<Person>() { public int compare(Person o1, Person o2) { return o1.getName().compareTo(o2.getName()); } }); System.out.println("sort 이름순 정렬 "+people); //java8 sorted 사용 List<Person> people1= people.stream() .sorted(Comparator.comparing((Person person) -> person.getAge())).collect(Collectors.toList()); System.out.println("sorted 나이순 정렬 "+people1); List<Person> people2= people.stream() .sorted(Comparator.comparing((Person person) -> person.getName())).collect(Collectors.toList()); System.out.println("sotred 이름순 정렬 "+people2); }


출력

sort 이름순 정렬 [Greg - 35, Jane - 21, Sara - 21, Zohn - 25]

sorted 나이순 정렬 [Jane - 21, Sara - 21, Zohn - 25, Greg - 35]

sotred 이름순 정렬 [Greg - 35, Jane - 21, Sara - 21, Zohn - 25]


예제소스2
정렬 조건 : 우선노출여부가 Y인게 1순위, 영역코드 2순위, 노출순위 3순위로 정렬.
복수의 조건으로 정렬할때는 .thenComparing 을 사용한다.

public class Person {

	    public Person(String prioYn, String expsFdC, String prioOr)
	    {
	        this.prioYn = prioYn;				// 우선노출여부
	        this.expsFdC = expsFdC;				// 영역코드
	        this.prioOr = prioOr;				// 노출순위
	    }

	    /**
	     * The name of the person.
	     */
	    public String prioYn;
	    public String expsFdC;
	    public String prioOr;
		public String getPrioYn() {
			return prioYn;
		}
		public void setPrioYn(String prioYn) {
			this.prioYn = prioYn;
		}
		public String getExpsFdC() {
			return expsFdC;
		}
		public void setExpsFdC(String expsFdC) {
			this.expsFdC = expsFdC;
		}
		public String getPrioOr() {
			return prioOr;
		}
		public void setPrioOr(String prioOr) {
			this.prioOr = prioOr;
		}
		@Override
		public String toString() {
			return "Person [prioYn=" + prioYn + ", expsFdC=" + expsFdC + ", prioOr=" + prioOr + "]";
		}
	    
	}


	    List<Person> peopleList = new ArrayList<>();
	    peopleList.add(new Person("Y", "B", "1"));
	    peopleList.add(new Person("N", "B", "0"));
	    peopleList.add(new Person("Y", "B", "3"));
	    peopleList.add(new Person("Y", "B", "2"));
	    peopleList.add(new Person("", "A", ""));	    
	    peopleList.add(new Person("", "A", ""));
	    
	    for(Person personVO : peopleList) {
	    	System.out.println(personVO.toString());
	    }
	    System.out.println("---------------------------------------------------------");
	    
	    List<Person> peopleSortedList = new ArrayList<>();
	    peopleSortedList = peopleList.stream().map(p -> {        // expsFdC(영역코드)가 A인것은 우선노출여부가 없으므로 N으로 set
	    	Person obj = p;
	    	if("A".equals(obj.getExpsFdC())){
	    		obj.setPrioYn("N");
	    	}
	    	return obj;
	    }).sorted(Comparator.comparing(Person::getPrioYn).reversed()
				.thenComparing(Comparator.comparing(Person::getExpsFdC))
				.thenComparing(Comparator.comparing(Person::getPrioOr)))
	    		.collect(Collectors.toList());
	    
	    System.out.println("peopleSortedList");
	    for(Person personVO : peopleSortedList) {
	    	System.out.println(personVO.toString());
	    }
	}

출력

Person [prioYn=Y, expsFdC=B, prioOr=1]

Person [prioYn=N, expsFdC=B, prioOr=0]

Person [prioYn=Y, expsFdC=B, prioOr=3]

Person [prioYn=Y, expsFdC=B, prioOr=2]

Person [prioYn=, expsFdC=A, prioOr=]

Person [prioYn=, expsFdC=A, prioOr=]

---------------------------------------------------------

peopleSortedList

Person [prioYn=Y, expsFdC=B, prioOr=1]

Person [prioYn=Y, expsFdC=B, prioOr=2]

Person [prioYn=Y, expsFdC=B, prioOr=3]

Person [prioYn=N, expsFdC=A, prioOr=]

Person [prioYn=N, expsFdC=A, prioOr=]

Person [prioYn=N, expsFdC=B, prioOr=0]




[JAVA] 람다식 기본 예제 1 (map, filter, reduce, collect)

자바 8 에서 람다식이 나오면서 stream 인터페이스가 나왔습니다. stream 인터페이스를 사용하여 람다식을 기존 JAVA코드(명령형 스타일)와 비교해보겠습니다.

아래에 소개하는 4개의 메서드를 간단히 설명하면 
map()은 엘리먼트 변경, filter()는 엘리먼트 선택, reduce(), collect()는 엘리먼트를 하나로 리턴 이다.

1. map()
map 메서드는 입력 컬렉션을 출력 컬렉션으로 매핑하거나 변경할때 유용하다.

예제 코드
list의 엘리먼트 값을 모두 대문자로 변경하여 출력.

final List<String> names = Arrays.asList("Sehoon", "Songwoo", "Chan", "Youngsuk", "Dajung");
			//java 7
			System.out.println("java 7");
			for(String name : names) {
				System.out.println(name.toUpperCase());
			}

			System.out.println("");

			//java 8 Lambda
			System.out.println("java 8");
			names.stream()
				.map(name -> name.toUpperCase())
				.forEach(name -> System.out.println(name));


java 7
SEHOON
SONGWOO
CHAN
YOUNGSUK
DAJUNG

java 8
SEHOON
SONGWOO
CHAN
YOUNGSUK
DAJUNG

 

출력


2. filter()
filter 메서드는 컬렉션을 조건에 의한 선택을 할때 유용하다. filter 메서드는
boolean 결과를 리턴하는 람다표현식이 필요하다.
예제의 collection 메서드는 filter 표현식에 나온값을 list로 변경한다. 

예제 코드
'S' 로 시작하는 이름을 출력.

		final Listt<String> names = Arrays.asList("Sehoon", "Songwoo", "Chan", "Youngsuk", "Dajung");

		//java 7
		System.out.println("java 7");
		final List startsWithN1 = new ArrayList();
		for (String name : names) {
			if (name.startsWith("S")) {
				startsWithN1.add(name);
			}
		}

		System.out.println(startsWithN1);

		System.out.println("");

		//java 8 Lambda
		System.out.println("java 8");
		final List startsWithN2 =  
				names.stream().filter(name -> name.startsWith("S"))
								.collect(Collectors.toList());

		System.out.println(startsWithN2); 


출력

java 7
[Sehoon, Songwoo]

java 8
[Sehoon, Songwoo]



3. reduce()
reduce 메서드는 엘리먼트를 비교하고 컬렉션에서 하나의 값으로 연산한다.
람다 예제 소스를 보면 첫번째로 리스트에 있는 처음 두개 엘리먼트를 사용한다. 그리고 람다 표현식의 결과는 다음호출에 사용된다. 두번째 호출에서는 name1은 이전 호출의 결과이며 name2는 컬렉션의 세번째 엘리먼트 이다.

예제코드
특정 스트링값의 길이보다 크고, 리스트의 가장 긴이름을 가진 엘리먼트를 출력.

		final List<String> names = Arrays.asList("Sehoon", "Songwoo", "Chan", "Youngsuk", "Dajung");

		//java 7
		String LongerEliment1  = "";
		for (String name : names) {
			if(("hoone".length() <= name.length()) && (LongerEliment1.length() <= name.length())) {
				LongerEliment1 = name;
			}
		}
		
		System.out.println("java 7 "+LongerEliment1);

		//java 8 Lambda
		String LongerEliment2 = names.stream()
				.reduce("hoone", (name1, name2) -> 
					name1.length() >= name2.length() ? name1 : name2);
		System.out.println("java 8 "+LongerEliment2);

출력

java 7 Youngsuk
java 8 Youngsuk


4. collect()
collect 메서드는 reduce() 메서드와 동일하게 값을 하나로 모으는 다른형태인데, collect는 여러 convenience method를 제공한다.
아래 예제는 리스트의 엘리먼트를 콤마로 구분하여 출력하는데, 기존 for문으로는 마지막 엘리먼트에 콤마를 안붙이는게 쉽지는 않다. 하지만 collect 머서드를 사용하면 간단하게 만들 수 있다.

예제코드
리스트의 엘리먼트를 콤마로 구분하여 출력. 단 마지막 엘리먼트에 콤마가 없어야한다.

 		final List<String> names = Arrays.asList("Sehoon", "Songwoo", "Chan", "Youngsuk", "Dajung");

		System.out.println("java 7");
		//java 7
		for(int i = 0; i < names.size() - 1; i++) {
			System.out.print(names.get(i).toUpperCase() + ", ");
		}
		
		if(names.size() > 0) { 
			System.out.println(names.get(names.size() - 1).toUpperCase());
		}

		System.out.println("java 8");
		//java 8 Lambda
		System.out.println(names.stream()
					.map(String::toUpperCase)
					.collect(Collectors.joining(", ")));


출력

java 7
SEHOON, SONGWOO, CHAN, YOUNGSUK, DAJUNG

java 8
SEHOON, SONGWOO, CHAN, YOUNGSUK, DAJUNG


[JAVA] 람다식(lambda expression) 이란?

람다식(lambda expression) 이란?

JAVA 8 부터 람다식(lambda expression) 이 추가 되었다. 
람다식은 말그대로 표현방식 중 하나로 함수형 스타일이라고 할 수 있다.
함수형 스타일은 
함수의 입력만을 의존하여 출력을 만드는 구조로 외부에 상태를 변경하는 것을 지양하는 패러다임으로 부작용(Side-effect) 발생을 최소화 하는 방법론이다.
요약하면 기존의 불필요한 코드를 줄이고 가독성을 향상시키는것에 목적을 두고 있습니다.

함수형 스타일을 이해하기 위해 기존 JAVA 코드(명령형 스타일)와 로 비교를 해보겠습니다.
names 라는 리스트에서 이름이 'S' 로 시작하는사람수를 구하는 소스를 보겠습니다.

기존 JAVA 코드 (명령형 스타일)

    final List<String> names = Arrays.asList("Sehoon", "Songwoo", "Chan", "Youngsuk", "Dajung");

    int foundCnt = 0;

    for(String name : names) {

   if(name.startsWith("S")) {

    foundCnt += 1;

    }

    }

    

    System.out.println("이름이 S로 시작하는 사람은 몇명인가요? "+ foundCnt);


람다식 (함수형 스타일)

    final List<String> names = Arrays.asList("Sehoon", "Songwoo", "Chan", "Youngsuk", "Dajung");

    System.out.println("이름이 S로 시작하는 사람은 몇명인가요? " + 

    names.stream().filter(name -> name.startsWith("S")).count());

위의 두개의 소스 모두 아래와 같이 동일한 결과가 나옵니다

결과

이름이 S로 시작하는 사람은 몇명인가요? 2 

명령형, 함수형 스타일을 비교하면 소스가 휄씬 간결하다는것을 볼 수 있다. 
따라서 람다 표현식을 사용하면, 기존의 불필요한 코드를 줄여주고, 작성된 코드의 가독성을 높여줍니다.


람다식의 장점

1. 코드를 간결하게 구성할 수 있다.
함수형 버전는 간결하다. 몇줄의 코드만으로 명령형 버전과 같은결과를 낼 수 있다. 여기서 간결하다는 것은 단순의 코드의 줄 갯수 뿐만 아니라 오류가 없고 개발자의 의도를 효과적으로 잘 전달한다는 것을 의미한다.

2. 오류를 줄일 수 있다.
변수의 명시적인 변경이나 재할당의 문제는 버그의 원천이 되고, 이를 동시성을 갖도록 하기에는 무척 어려운 코드가 된다.
위에서 작성한 명령형 코드에서 반복적으로 
foundCnt 를 변경했다. 하지만 함수형 코드에서는 명시적인 변경이 없다. 
변수에 대한 변경이 적다는 것은 코드 안에서 발생할 수 있는 오류의 확률을 더 낮게 한다는 의미이다.

3. 원하는 부분을 병렬화 하기 쉽다.

병렬화를 구현하기는 매우 어려운 작업이다. 태스크를 다중 스레드로 나누고, 결과를  모은 뒤 순차적으로 옮겨야한다. 또한 다른 스레드가 데이터를 업데이트 하는 동안에 스레드간의 충돌이 안되게 해야한다.
말로 보는것 조차 어려워보이는데 람다식을 사용하면 간단하게 메소드를 변경하는것만 으로 구현할 수 있다.





[참조] 자바 8 람다의 힘




[JSP] JSP 커스텀태그(Coustom tag) 사용하기

JSTL(JSP Standard Tag Library) 태그를 사용하면 JSP에서 반복문, 상태제어 등 여러가지 기능을 할 수 있다.
하지만 때로는 JSTL에서 제공해주는 태크로는 충분하지 않을때가 있는데 이때 커스텀태그를 생성할 수 있다. 커스텀 태그라고 특별한것은 아니고 JSTL 또한 커스텀태그의 일종인데 자주 사용하는 라이브러리를 모아놓은게 JSTL이다

JSP 커프텀 태그(Coustom tag)

예를들어 숫자를 천단위로 콤마를 찍는다고 가정합니다.

전체소스코드는 github에 있습니다. 
https://github.com/sehoone/customJspTag

사용법

<mytags:monneyFormat number="123123.574" format="#,###.00"/>


구성



소스코드

JAVA로 커스텀태그 로직을 만듭니다.

NumberFormat.java

package customTag;


import java.io.IOException;

import java.text.DecimalFormat;


import javax.servlet.jsp.JspException;

import javax.servlet.jsp.SkipPageException;

import javax.servlet.jsp.tagext.SimpleTagSupport;


public class NumberFormat extends SimpleTagSupport {


private String format;

private String number;


public void setFormat(String format) {

this.format = format;

}


public void setNumber(String number) {

this.number = number;

}


@Override

public void doTag() throws JspException, IOException {

System.out.println("input Number :" + number);

System.out.println("input Format :" + format);

try {

//포맷팅할 숫자

double amount = Double.parseDouble(number);

//포맷 형식( EX. #,###.00 )

DecimalFormat formatter = new DecimalFormat(format);

String formattedNumber = formatter.format(amount);

//출력

getJspContext().getOut().write(formattedNumber);

} catch (Exception e) {

e.printStackTrace();

throw new SkipPageException("Exception formatting " + number

+ " format " + format);

}

}


}


tld(tag library description) 에 사용할 태그명 및 입력값을 설정

numberFormat.tld

<?xml version="1.0" encoding="UTF-8" ?>


<taglib xmlns="http://java.sun.com/xml/ns/j2ee"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"

    version="2.0">

<description>Number Formatter Custom Tag</description>

<tlib-version>2.1</tlib-version>

<short-name>mytags</short-name>

<tag>

<!-- 태크이름 -->

<name>monneyFormat</name>

<!-- 태크 클래스 -->

<tag-class>customTag.NumberFormat</tag-class>

<body-content>tagdependent</body-content>

<!-- 입력 파라메터 -->

<attribute>

<name>format</name>

<required>true</required>

</attribute>

<attribute>

<name>number</name>

<required>true</required>

</attribute>

</tag>

</taglib>


tld파일의 경로를 설정
만약에 WEB-INF 바로 아래에 두는경우에는 web.xml에 선언하지 않아도 됩니다.

web.xml

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">

<display-name>Archetype Created Web Application</display-name>

<welcome-file-list>

<welcome-file>index.jsp</welcome-file>

</welcome-file-list>

<jsp-config>

<taglib>

<taglib-uri>numberformatter</taglib-uri>

<taglib-location>/WEB-INF/tlds/numberformatter.tld</taglib-location>

</taglib>

</jsp-config>

</web-app>


JSP 에 커스텀 태크를 사용합니다

index.jsp

<%@ page language="java" contentType="text/html; charset=US-ASCII"

    pageEncoding="US-ASCII"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">

<title>Custom Tag Example</title>


<!-- 테그 경로 설정 -->

<%@ taglib uri="WEB-INF/tlds/numberFormat.tld" prefix="mytags" %>


</head>


<body>


<h2>customTag Test</h2>


<mytags:monneyFormat number="123123.574" format="#,###.00"/><br><br>


</body>

</html>





[JAVA] 문자 관련 stringUtil 모음


/**

 * @Class Name  : StringUtil.java

 * @Description : 문자열 데이터 처리 관련 유틸리티

 * @Modification Information

 *

 * @author 

 * @since 2017. 12. 21

 * @version 1.0

 * @see

 *

 */


package test.com.cmm.util;


import java.io.UnsupportedEncodingException;

import java.math.BigDecimal;

import java.security.SecureRandom;

import java.sql.Timestamp;

import java.text.SimpleDateFormat;

import java.util.Locale;


public class StringUtil {

/**

 * 빈 문자열 <code>""</code>.

 */

public static final String EMPTY = "";


/**

 * <p>Padding을 할 수 있는 최대 수치</p>

 */

// private static final int PAD_LIMIT = 8192;

/**

 * <p>An array of <code>String</code>s used for padding.</p>

 * <p>Used for efficient space padding. The length of each String expands as needed.</p>

 */

/*

private static final String[] PADDING = new String[Character.MAX_VALUE];


static {

// space padding is most common, start with 64 chars

PADDING[32] = "                                                                ";

}

 */


/**

 * 문자열이 지정한 길이를 초과했을때 지정한길이에다가 해당 문자열을 붙여주는 메서드.

 * @param source 원본 문자열 배열

 * @param output 더할문자열

 * @param slength 지정길이

 * @return 지정길이로 잘라서 더할분자열 합친 문자열

 */

public static String cutString(String source, String output, int slength) {

String returnVal = null;

if (source != null) {

if (source.length() > slength) {

returnVal = source.substring(0, slength) + output;

} else

returnVal = source;

}

return returnVal;

}


/**

 * 문자열이 지정한 길이를 초과했을때 해당 문자열을 삭제하는 메서드

 * @param source 원본 문자열 배열

 * @param slength 지정길이

 * @return 지정길이로 잘라서 더할분자열 합친 문자열

 */

public static String cutString(String source, int slength) {

String result = null;

if (source != null) {

if (source.length() > slength) {

result = source.substring(0, slength);

} else

result = source;

}

return result;

}


/**

 * <p>

 * String이 비었거나("") 혹은 null 인지 검증한다.

 * </p>

 *

 * <pre>

 *  StringUtil.isEmpty(null)      = true

 *  StringUtil.isEmpty("")        = true

 *  StringUtil.isEmpty(" ")       = false

 *  StringUtil.isEmpty("bob")     = false

 *  StringUtil.isEmpty("  bob  ") = false

 * </pre>

 *

 * @param str - 체크 대상 스트링오브젝트이며 null을 허용함

 * @return <code>true</code> - 입력받은 String 이 빈 문자열 또는 null인 경우

 */

public static boolean isEmpty(String str) {

return str == null || str.length() == 0;

}


/**

 * <p>기준 문자열에 포함된 모든 대상 문자(char)를 제거한다.</p>

 *

 * <pre>

 * StringUtil.remove(null, *)       = null

 * StringUtil.remove("", *)         = ""

 * StringUtil.remove("queued", 'u') = "qeed"

 * StringUtil.remove("queued", 'z') = "queued"

 * </pre>

 *

 * @param str  입력받는 기준 문자열

 * @param remove  입력받는 문자열에서 제거할 대상 문자열

 * @return 제거대상 문자열이 제거된 입력문자열. 입력문자열이 null인 경우 출력문자열은 null

 */

public static String remove(String str, char remove) {

if (isEmpty(str) || str.indexOf(remove) == -1) {

return str;

}

char[] chars = str.toCharArray();

int pos = 0;

for (int i = 0; i < chars.length; i++) {

if (chars[i] != remove) {

chars[pos++] = chars[i];

}

}

return new String(chars, 0, pos);

}


/**

 * <p>문자열 내부의 콤마 character(,)를 모두 제거한다.</p>

 *

 * <pre>

 * StringUtil.removeCommaChar(null)       = null

 * StringUtil.removeCommaChar("")         = ""

 * StringUtil.removeCommaChar("asdfg,qweqe") = "asdfgqweqe"

 * </pre>

 *

 * @param str 입력받는 기준 문자열

 * @return " , "가 제거된 입력문자열

 *  입력문자열이 null인 경우 출력문자열은 null

 */

public static String removeCommaChar(String str) {

return remove(str, ',');

}


/**

 * <p>문자열 내부의 마이너스 character(-)를 모두 제거한다.</p>

 *

 * <pre>

 * StringUtil.removeMinusChar(null)       = null

 * StringUtil.removeMinusChar("")         = ""

 * StringUtil.removeMinusChar("a-sdfg-qweqe") = "asdfgqweqe"

 * </pre>

 *

 * @param str  입력받는 기준 문자열

 * @return " - "가 제거된 입력문자열

 *  입력문자열이 null인 경우 출력문자열은 null

 */

public static String removeMinusChar(String str) {

return remove(str, '-');

}


/**

 * 원본 문자열의 포함된 특정 문자열을 새로운 문자열로 변환하는 메서드

 * @param source 원본 문자열

 * @param subject 원본 문자열에 포함된 특정 문자열

 * @param object 변환할 문자열

 * @return sb.toString() 새로운 문자열로 변환된 문자열

 */

public static String replace(String source, String subject, String object) {

StringBuffer rtnStr = new StringBuffer();

String preStr = "";

String nextStr = source;

String srcStr = source;


while (srcStr.indexOf(subject) >= 0) {

preStr = srcStr.substring(0, srcStr.indexOf(subject));

nextStr = srcStr.substring(srcStr.indexOf(subject) + subject.length(), srcStr.length());

srcStr = nextStr;

rtnStr.append(preStr).append(object);

}

rtnStr.append(nextStr);

return rtnStr.toString();

}


/**

 * 원본 문자열의 포함된 특정 문자열 첫번째 한개만 새로운 문자열로 변환하는 메서드

 * @param source 원본 문자열

 * @param subject 원본 문자열에 포함된 특정 문자열

 * @param object 변환할 문자열

 * @return sb.toString() 새로운 문자열로 변환된 문자열 / source 특정문자열이 없는 경우 원본 문자열

 */

public static String replaceOnce(String source, String subject, String object) {

StringBuffer rtnStr = new StringBuffer();

String preStr = "";

String nextStr = source;

if (source.indexOf(subject) >= 0) {

preStr = source.substring(0, source.indexOf(subject));

nextStr = source.substring(source.indexOf(subject) + subject.length(), source.length());

rtnStr.append(preStr).append(object).append(nextStr);

return rtnStr.toString();

} else {

return source;

}

}


/**

 * <code>subject</code>에 포함된 각각의 문자를 object로 변환한다.

 *

 * @param source 원본 문자열

 * @param subject 원본 문자열에 포함된 특정 문자열

 * @param object 변환할 문자열

 * @return sb.toString() 새로운 문자열로 변환된 문자열

 */

public static String replaceChar(String source, String subject, String object) {

StringBuffer rtnStr = new StringBuffer();

String preStr = "";

String nextStr = source;

String srcStr = source;


char chA;


for (int i = 0; i < subject.length(); i++) {

chA = subject.charAt(i);


if (srcStr.indexOf(chA) >= 0) {

preStr = srcStr.substring(0, srcStr.indexOf(chA));

nextStr = srcStr.substring(srcStr.indexOf(chA) + 1, srcStr.length());

srcStr = rtnStr.append(preStr).append(object).append(nextStr).toString();

}

}


return srcStr;

}


/**

 * <p><code>str</code> 중 <code>searchStr</code>의 시작(index) 위치를 반환.</p>

 *

 * <p>입력값 중 <code>null</code>이 있을 경우 <code>-1</code>을 반환.</p>

 *

 * <pre>

 * StringUtil.indexOf(null, *)          = -1

 * StringUtil.indexOf(*, null)          = -1

 * StringUtil.indexOf("", "")           = 0

 * StringUtil.indexOf("aabaabaa", "a")  = 0

 * StringUtil.indexOf("aabaabaa", "b")  = 2

 * StringUtil.indexOf("aabaabaa", "ab") = 1

 * StringUtil.indexOf("aabaabaa", "")   = 0

 * </pre>

 *

 * @param str  검색 문자열

 * @param searchStr  검색 대상문자열

 * @return 검색 문자열 중 검색 대상문자열이 있는 시작 위치 검색대상 문자열이 없거나 null인 경우 -1

 */

public static int indexOf(String str, String searchStr) {

if (str == null || searchStr == null) {

return -1;

}

return str.indexOf(searchStr);

}


/**

 * <p>오라클의 decode 함수와 동일한 기능을 가진 메서드이다.

 * <code>sourStr</code>과 <code>compareStr</code>의 값이 같으면

 * <code>returStr</code>을 반환하며, 다르면  <code>defaultStr</code>을 반환한다.

 * </p>

 *

 * <pre>

 * StringUtil.decode(null, null, "foo", "bar")= "foo"

 * StringUtil.decode("", null, "foo", "bar") = "bar"

 * StringUtil.decode(null, "", "foo", "bar") = "bar"

 * StringUtil.decode("하이", "하이", null, "bar") = null

 * StringUtil.decode("하이", "하이  ", "foo", null) = null

 * StringUtil.decode("하이", "하이", "foo", "bar") = "foo"

 * StringUtil.decode("하이", "하이  ", "foo", "bar") = "bar"

 * </pre>

 *

 * @param sourceStr 비교할 문자열

 * @param compareStr 비교 대상 문자열

 * @param returnStr sourceStr와 compareStr의 값이 같을 때 반환할 문자열

 * @param defaultStr sourceStr와 compareStr의 값이 다를 때 반환할 문자열

 * @return sourceStr과 compareStr의 값이 동일(equal)할 때 returnStr을 반환하며,

 *         <br/>다르면 defaultStr을 반환한다.

 */

public static String decode(String sourceStr, String compareStr, String returnStr, String defaultStr) {

if (sourceStr == null && compareStr == null) {

return returnStr;

}


if (sourceStr == null && compareStr != null) {

return defaultStr;

}


if (sourceStr.trim().equals(compareStr)) {

return returnStr;

}


return defaultStr;

}


/**

 * <p>오라클의 decode 함수와 동일한 기능을 가진 메서드이다.

 * <code>sourStr</code>과 <code>compareStr</code>의 값이 같으면

 * <code>returStr</code>을 반환하며, 다르면  <code>sourceStr</code>을 반환한다.

 * </p>

 *

 * <pre>

 * StringUtil.decode(null, null, "foo") = "foo"

 * StringUtil.decode("", null, "foo") = ""

 * StringUtil.decode(null, "", "foo") = null

 * StringUtil.decode("하이", "하이", "foo") = "foo"

 * StringUtil.decode("하이", "하이 ", "foo") = "하이"

 * StringUtil.decode("하이", "바이", "foo") = "하이"

 * </pre>

 *

 * @param sourceStr 비교할 문자열

 * @param compareStr 비교 대상 문자열

 * @param returnStr sourceStr와 compareStr의 값이 같을 때 반환할 문자열

 * @return sourceStr과 compareStr의 값이 동일(equal)할 때 returnStr을 반환하며,

 *         <br/>다르면 sourceStr을 반환한다.

 */

public static String decode(String sourceStr, String compareStr, String returnStr) {

return decode(sourceStr, compareStr, returnStr, sourceStr);

}


/**

 * 객체가 null인지 확인하고 null인 경우 "" 로 바꾸는 메서드

 * @param object 원본 객체

 * @return resultVal 문자열

 */

public static String isNullToString(Object object) {

String string = "";


if (object != null) {

string = object.toString().trim();

}


return string;

}


/**

 *<pre>

 * 인자로 받은 String이 null일 경우 &quot;&quot;로 리턴한다.

 * &#064;param src null값일 가능성이 있는 String 값.

 * &#064;return 만약 String이 null 값일 경우 &quot;&quot;로 바꾼 String 값.

 *</pre>

 */

public static String nullConvert(Object src) {

//if (src != null && src.getClass().getName().equals("java.math.BigDecimal")) {

if (src != null && src instanceof java.math.BigDecimal) {

return ((BigDecimal) src).toString();

}


if (src == null || src.equals("null")) {

return "";

} else {

return ((String) src).trim();

}

}


/**

 *<pre>

 * 인자로 받은 String이 null일 경우 &quot;&quot;로 리턴한다.

 * &#064;param src null값일 가능성이 있는 String 값.

 * &#064;return 만약 String이 null 값일 경우 &quot;&quot;로 바꾼 String 값.

 *</pre>

 */

public static String nullConvert(String src) {


if (src == null || src.equals("null") || "".equals(src) || " ".equals(src)) {

return "";

} else {

return src.trim();

}

}


/**

 *<pre>

 * 인자로 받은 String이 null일 경우 &quot;0&quot;로 리턴한다.

 * &#064;param src null값일 가능성이 있는 String 값.

 * &#064;return 만약 String이 null 값일 경우 &quot;0&quot;로 바꾼 String 값.

 *</pre>

 */

public static int zeroConvert(Object src) {


if (src == null || src.equals("null")) {

return 0;

} else {

return Integer.parseInt(((String) src).trim());

}

}


/**

 *<pre>

 * 인자로 받은 String이 null일 경우 &quot;&quot;로 리턴한다.

 * &#064;param src null값일 가능성이 있는 String 값.

 * &#064;return 만약 String이 null 값일 경우 &quot;&quot;로 바꾼 String 값.

 *</pre>

 */

public static int zeroConvert(String src) {


if (src == null || src.equals("null") || "".equals(src) || " ".equals(src)) {

return 0;

} else {

return Integer.parseInt(src.trim());

}

}


/**

 * <p>문자열에서 {@link Character#isWhitespace(char)}에 정의된

 * 모든 공백문자를 제거한다.</p>

 *

 * <pre>

 * StringUtil.removeWhitespace(null)         = null

 * StringUtil.removeWhitespace("")           = ""

 * StringUtil.removeWhitespace("abc")        = "abc"

 * StringUtil.removeWhitespace("   ab  c  ") = "abc"

 * </pre>

 *

 * @param str  공백문자가 제거도어야 할 문자열

 * @return the 공백문자가 제거된 문자열, null이 입력되면 <code>null</code>이 리턴

 */

public static String removeWhitespace(String str) {

if (isEmpty(str)) {

return str;

}

int sz = str.length();

char[] chs = new char[sz];

int count = 0;

for (int i = 0; i < sz; i++) {

if (!Character.isWhitespace(str.charAt(i))) {

chs[count++] = str.charAt(i);

}

}

if (count == sz) {

return str;

}


return new String(chs, 0, count);

}


/**

 * Html 코드가 들어간 문서를 표시할때 태그에 손상없이 보이기 위한 메서드

 *

 * @param strString

 * @return HTML 태그를 치환한 문자열

 */

public static String checkHtmlView(String strString) {

String strNew = "";


StringBuffer strTxt = new StringBuffer("");


char chrBuff;

int len = strString.length();


for (int i = 0; i < len; i++) {

chrBuff = (char) strString.charAt(i);


switch (chrBuff) {

case '<':

strTxt.append("&lt;");

break;

case '>':

strTxt.append("&gt;");

break;

case '"':

strTxt.append("&quot;");

break;

case 10:

strTxt.append("<br>");

break;

case ' ':

strTxt.append("&nbsp;");

break;

//case '&' :

//strTxt.append("&amp;");

//break;

default:

strTxt.append(chrBuff);

}

}


strNew = strTxt.toString();


return strNew;

}


/**

 * 문자열을 지정한 분리자에 의해 배열로 리턴하는 메서드.

 * @param source 원본 문자열

 * @param separator 분리자

 * @return result 분리자로 나뉘어진 문자열 배열

 */

public static String[] split(String source, String separator) throws NullPointerException {

String[] returnVal = null;

int cnt = 1;


int index = source.indexOf(separator);

int index0 = 0;

while (index >= 0) {

cnt++;

index = source.indexOf(separator, index + 1);

}

returnVal = new String[cnt];

cnt = 0;

index = source.indexOf(separator);

while (index >= 0) {

returnVal[cnt] = source.substring(index0, index);

index0 = index + 1;

index = source.indexOf(separator, index + 1);

cnt++;

}

returnVal[cnt] = source.substring(index0);


return returnVal;

}


/**

 * <p>{@link String#toLowerCase()}를 이용하여 소문자로 변환한다.</p>

 *

 * <pre>

 * StringUtil.lowerCase(null)  = null

 * StringUtil.lowerCase("")    = ""

 * StringUtil.lowerCase("aBc") = "abc"

 * </pre>

 *

 * @param str 소문자로 변환되어야 할 문자열

 * @return 소문자로 변환된 문자열, null이 입력되면 <code>null</code> 리턴

 */

public static String lowerCase(String str) {

if (str == null) {

return null;

}


return str.toLowerCase();

}


/**

 * <p>{@link String#toUpperCase()}를 이용하여 대문자로 변환한다.</p>

 *

 * <pre>

 * StringUtil.upperCase(null)  = null

 * StringUtil.upperCase("")    = ""

 * StringUtil.upperCase("aBc") = "ABC"

 * </pre>

 *

 * @param str 대문자로 변환되어야 할 문자열

 * @return 대문자로 변환된 문자열, null이 입력되면 <code>null</code> 리턴

 */

public static String upperCase(String str) {

if (str == null) {

return null;

}


return str.toUpperCase();

}


/**

 * <p>입력된 String의 앞쪽에서 두번째 인자로 전달된 문자(stripChars)를 모두 제거한다.</p>

 *

 * <pre>

 * StringUtil.stripStart(null, *)          = null

 * StringUtil.stripStart("", *)            = ""

 * StringUtil.stripStart("abc", "")        = "abc"

 * StringUtil.stripStart("abc", null)      = "abc"

 * StringUtil.stripStart("  abc", null)    = "abc"

 * StringUtil.stripStart("abc  ", null)    = "abc  "

 * StringUtil.stripStart(" abc ", null)    = "abc "

 * StringUtil.stripStart("yxabc  ", "xyz") = "abc  "

 * </pre>

 *

 * @param str 지정된 문자가 제거되어야 할 문자열

 * @param stripChars 제거대상 문자열

 * @return 지정된 문자가 제거된 문자열, null이 입력되면 <code>null</code> 리턴

 */

public static String stripStart(String str, String stripChars) {

int strLen;

if (str == null || (strLen = str.length()) == 0) {

return str;

}

int start = 0;

if (stripChars == null) {

while ((start != strLen) && Character.isWhitespace(str.charAt(start))) {

start++;

}

} else if (stripChars.length() == 0) {

return str;

} else {

while ((start != strLen) && (stripChars.indexOf(str.charAt(start)) != -1)) {

start++;

}

}


return str.substring(start);

}


/**

 * <p>입력된 String의 뒤쪽에서 두번째 인자로 전달된 문자(stripChars)를 모두 제거한다.</p>

 *

 * <pre>

 * StringUtil.stripEnd(null, *)          = null

 * StringUtil.stripEnd("", *)            = ""

 * StringUtil.stripEnd("abc", "")        = "abc"

 * StringUtil.stripEnd("abc", null)      = "abc"

 * StringUtil.stripEnd("  abc", null)    = "  abc"

 * StringUtil.stripEnd("abc  ", null)    = "abc"

 * StringUtil.stripEnd(" abc ", null)    = " abc"

 * StringUtil.stripEnd("  abcyx", "xyz") = "  abc"

 * </pre>

 *

 * @param str 지정된 문자가 제거되어야 할 문자열

 * @param stripChars 제거대상 문자열

 * @return 지정된 문자가 제거된 문자열, null이 입력되면 <code>null</code> 리턴

 */

public static String stripEnd(String str, String stripChars) {

int end;

if (str == null || (end = str.length()) == 0) {

return str;

}


if (stripChars == null) {

while ((end != 0) && Character.isWhitespace(str.charAt(end - 1))) {

end--;

}

} else if (stripChars.length() == 0) {

return str;

} else {

while ((end != 0) && (stripChars.indexOf(str.charAt(end - 1)) != -1)) {

end--;

}

}


return str.substring(0, end);

}


/**

 * <p>입력된 String의 앞, 뒤에서 두번째 인자로 전달된 문자(stripChars)를 모두 제거한다.</p>

 *

 * <pre>

 * StringUtil.strip(null, *)          = null

 * StringUtil.strip("", *)            = ""

 * StringUtil.strip("abc", null)      = "abc"

 * StringUtil.strip("  abc", null)    = "abc"

 * StringUtil.strip("abc  ", null)    = "abc"

 * StringUtil.strip(" abc ", null)    = "abc"

 * StringUtil.strip("  abcyx", "xyz") = "  abc"

 * </pre>

 *

 * @param str 지정된 문자가 제거되어야 할 문자열

 * @param stripChars 제거대상 문자열

 * @return 지정된 문자가 제거된 문자열, null이 입력되면 <code>null</code> 리턴

 */

public static String strip(String str, String stripChars) {

if (isEmpty(str)) {

return str;

}


String srcStr = str;

srcStr = stripStart(srcStr, stripChars);


return stripEnd(srcStr, stripChars);

}


/**

 * 문자열을 지정한 분리자에 의해 지정된 길이의 배열로 리턴하는 메서드.

 * @param source 원본 문자열

 * @param separator 분리자

 * @param arraylength 배열 길이

 * @return 분리자로 나뉘어진 문자열 배열

 */

public static String[] split(String source, String separator, int arraylength) throws NullPointerException {

String[] returnVal = new String[arraylength];

int cnt = 0;

int index0 = 0;

int index = source.indexOf(separator);

while (index >= 0 && cnt < (arraylength - 1)) {

returnVal[cnt] = source.substring(index0, index);

index0 = index + 1;

index = source.indexOf(separator, index + 1);

cnt++;

}

returnVal[cnt] = source.substring(index0);

if (cnt < (arraylength - 1)) {

for (int i = cnt + 1; i < arraylength; i++) {

returnVal[i] = "";

}

}


return returnVal;

}


/**

 * 문자열 A에서 Z사이의 랜덤 문자열을 구하는 기능을 제공 시작문자열과 종료문자열 사이의 랜덤 문자열을 구하는 기능

 *

 * @param startChr

 *            - 첫 문자

 * @param endChr

 *            - 마지막문자

 * @return 랜덤문자

 * @exception MyException

 * @see

 */

public static String getRandomStr(char startChr, char endChr) {


int randomInt;

String randomStr = null;


// 시작문자 및 종료문자를 아스키숫자로 변환한다.

int startInt = Integer.valueOf(startChr);

int endInt = Integer.valueOf(endChr);


// 시작문자열이 종료문자열보가 클경우

if (startInt > endInt) {

throw new IllegalArgumentException("Start String: " + startChr + " End String: " + endChr);

}


// 랜덤 객체 생성

SecureRandom rnd = new SecureRandom();


do {

// 시작문자 및 종료문자 중에서 랜덤 숫자를 발생시킨다.

randomInt = rnd.nextInt(endInt + 1);

} while (randomInt < startInt); // 입력받은 문자 'A'(65)보다 작으면 다시 랜덤 숫자 발생.


// 랜덤 숫자를 문자로 변환 후 스트링으로 다시 변환

randomStr = (char) randomInt + "";


// 랜덤문자열를 리턴

return randomStr;

}


/**

 * 문자열을 다양한 문자셋(EUC-KR[KSC5601],UTF-8..)을 사용하여 인코딩하는 기능 역으로 디코딩하여 원래의 문자열을

 * 복원하는 기능을 제공함 String temp = new String(문자열.getBytes("바꾸기전 인코딩"),"바꿀 인코딩");

 * String temp = new String(문자열.getBytes("8859_1"),"KSC5601"); => UTF-8 에서

 * EUC-KR

 *

 * @param srcString

 *            - 문자열

 * @param srcCharsetNm

 *            - 원래 CharsetNm

 * @param charsetNm

 *            - CharsetNm

 * @return 인(디)코딩 문자열

 * @exception MyException

 * @see

 */

public static String getEncdDcd(String srcString, String srcCharsetNm, String cnvrCharsetNm) {


String rtnStr = null;


if (srcString == null)

return null;


try {

rtnStr = new String(srcString.getBytes(srcCharsetNm), cnvrCharsetNm);

} catch (UnsupportedEncodingException e) {

rtnStr = null;

}


return rtnStr;

}


/**

     * 특수문자를 웹 브라우저에서 정상적으로 보이기 위해 특수문자를 처리('<' -> & lT)하는 기능이다

     * @param  srcString  - '<'

     * @return  변환문자열('<' -> "&lt"

     * @exception MyException

     * @see

     */

public static String getSpclStrCnvr(String srcString) {


String rtnStr = null;


try {

StringBuffer strTxt = new StringBuffer("");


char chrBuff;

int len = srcString.length();


for (int i = 0; i < len; i++) {

chrBuff = (char) srcString.charAt(i);


switch (chrBuff) {

case '<':

strTxt.append("&lt;");

break;

case '>':

strTxt.append("&gt;");

break;

case '&':

strTxt.append("&amp;");

break;

default:

strTxt.append(chrBuff);

}

}


rtnStr = strTxt.toString();


} catch (Exception e) {

LOGGER.debug("{}", e);

}


return rtnStr;

}


/**

 * 응용어플리케이션에서 고유값을 사용하기 위해 시스템에서17자리의TIMESTAMP값을 구하는 기능

 *

 * @param

 * @return Timestamp 값

 * @exception MyException

 * @see

 */

public static String getTimeStamp() {


String rtnStr = null;


// 문자열로 변환하기 위한 패턴 설정(년도-월-일 시:분:초:초(자정이후 초))

String pattern = "yyyyMMddhhmmssSSS";


SimpleDateFormat sdfCurrent = new SimpleDateFormat(pattern, Locale.KOREA);

Timestamp ts = new Timestamp(System.currentTimeMillis());


rtnStr = sdfCurrent.format(ts.getTime());


return rtnStr;

}


/**

 * html의 특수문자를 표현하기 위해

 *

 * @param srcString

 * @return String

 * @exception Exception

 * @see

 */

public static String getHtmlStrCnvr(String srcString) {


String tmpString = srcString;


tmpString = tmpString.replaceAll("&lt;", "<");

tmpString = tmpString.replaceAll("&gt;", ">");

tmpString = tmpString.replaceAll("&amp;", "&");

tmpString = tmpString.replaceAll("&nbsp;", " ");

tmpString = tmpString.replaceAll("&apos;", "\'");

tmpString = tmpString.replaceAll("&quot;", "\"");


return tmpString;


}


/**

 * <p>날짜 형식의 문자열 내부에 마이너스 character(-)를 추가한다.</p>

 *

 * <pre>

 *   StringUtil.addMinusChar("20100901") = "2010-09-01"

 * </pre>

 *

 * @param date  입력받는 문자열

 * @return " - "가 추가된 입력문자열

 */

public static String addMinusChar(String date) {

if (date.length() == 8)

return date.substring(0, 4).concat("-").concat(date.substring(4, 6)).concat("-").concat(date.substring(6, 8));

else

return "";

}

}



[JAVA] 날짜 관련 dateutil 모음


package test.cmm.util;


import java.security.SecureRandom;

import java.text.ParseException;

import java.text.SimpleDateFormat;

import java.util.Calendar;

import java.util.Date;

import java.util.GregorianCalendar;

import java.util.HashMap;

import java.util.Locale;

import java.util.Map;

import java.util.TimeZone;


import com.ibm.icu.util.ChineseCalendar;


/**

 *

 * Date 에 대한 Util 클래스

 * @author 

 * @since 2017.12.21

 * @version 1.0

 * @see

 *

 * <pre>

 *

 * </pre>

 */

public class DateUtil {

/**

 * <p>yyyyMMdd 혹은 yyyy-MM-dd 형식의 날짜 문자열을 입력 받아 년, 월, 일을

 * 증감한다. 년, 월, 일은 가감할 수를 의미하며, 음수를 입력할 경우 감한다.</p>

 *

 * <pre>

 * DateUtil.addYearMonthDay("19810828", 0, 0, 19)  = "19810916"

 * DateUtil.addYearMonthDay("20060228", 0, 0, -10) = "20060218"

 * DateUtil.addYearMonthDay("20060228", 0, 0, 10)  = "20060310"

 * DateUtil.addYearMonthDay("20060228", 0, 0, 32)  = "20060401"

 * DateUtil.addYearMonthDay("20050331", 0, -1, 0)  = "20050228"

 * DateUtil.addYearMonthDay("20050301", 0, 2, 30)  = "20050531"

 * DateUtil.addYearMonthDay("20050301", 1, 2, 30)  = "20060531"

 * DateUtil.addYearMonthDay("20040301", 2, 0, 0)   = "20060301"

 * DateUtil.addYearMonthDay("20040229", 2, 0, 0)   = "20060228"

 * DateUtil.addYearMonthDay("20040229", 2, 0, 1)   = "20060301"

 * </pre>

 *

 * @param  dateStr 날짜 문자열(yyyyMMdd, yyyy-MM-dd의 형식)

 * @param  year 가감할 년. 0이 입력될 경우 가감이 없다

 * @param  month 가감할 월. 0이 입력될 경우 가감이 없다

 * @param  day 가감할 일. 0이 입력될 경우 가감이 없다

 * @return  yyyyMMdd 형식의 날짜 문자열

 * @throws IllegalArgumentException 날짜 포맷이 정해진 바와 다를 경우.

 *         입력 값이 <code>null</code>인 경우.

 */

public static String addYearMonthDay(String sDate, int year, int month, int day) {


String dateStr = validChkDate(sDate);


Calendar cal = Calendar.getInstance();

SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd", Locale.getDefault());

try {

cal.setTime(sdf.parse(dateStr));

} catch (ParseException e) {

throw new IllegalArgumentException("Invalid date format: " + dateStr);

}


if (year != 0)

cal.add(Calendar.YEAR, year);

if (month != 0)

cal.add(Calendar.MONTH, month);

if (day != 0)

cal.add(Calendar.DATE, day);

return sdf.format(cal.getTime());

}


/**

 * <p>yyyyMMdd 혹은 yyyy-MM-dd 형식의 날짜 문자열을 입력 받아 년을

 * 증감한다. <code>year</code>는 가감할 수를 의미하며, 음수를 입력할 경우 감한다.</p>

 *

 * <pre>

 * DateUtil.addYear("20000201", 62)  = "20620201"

 * DateUtil.addYear("20620201", -62) = "20000201"

 * DateUtil.addYear("20040229", 2)   = "20060228"

 * DateUtil.addYear("20060228", -2)  = "20040228"

 * DateUtil.addYear("19000101", 200) = "21000101"

 * </pre>

 *

 * @param  dateStr 날짜 문자열(yyyyMMdd, yyyy-MM-dd의 형식)

 * @param  year 가감할 년. 0이 입력될 경우 가감이 없다

 * @return  yyyyMMdd 형식의 날짜 문자열

 * @throws IllegalArgumentException 날짜 포맷이 정해진 바와 다를 경우.

 *         입력 값이 <code>null</code>인 경우.

 */

public static String addYear(String dateStr, int year) {

return addYearMonthDay(dateStr, year, 0, 0);

}


/**

 * <p>yyyyMMdd 혹은 yyyy-MM-dd 형식의 날짜 문자열을 입력 받아 월을

 * 증감한다. <code>month</code>는 가감할 수를 의미하며, 음수를 입력할 경우 감한다.</p>

 *

 * <pre>

 * DateUtil.addMonth("20010201", 12)  = "20020201"

 * DateUtil.addMonth("19800229", 12)  = "19810228"

 * DateUtil.addMonth("20040229", 12)  = "20050228"

 * DateUtil.addMonth("20050228", -12) = "20040228"

 * DateUtil.addMonth("20060131", 1)   = "20060228"

 * DateUtil.addMonth("20060228", -1)  = "20060128"

 * </pre>

 *

 * @param  dateStr 날짜 문자열(yyyyMMdd, yyyy-MM-dd의 형식)

 * @param  month 가감할 월. 0이 입력될 경우 가감이 없다

 * @return  yyyyMMdd 형식의 날짜 문자열

 * @throws IllegalArgumentException 날짜 포맷이 정해진 바와 다를 경우.

 *         입력 값이 <code>null</code>인 경우.

 */

public static String addMonth(String dateStr, int month) {

return addYearMonthDay(dateStr, 0, month, 0);

}


/**

 * <p>yyyyMMdd 혹은 yyyy-MM-dd 형식의 날짜 문자열을 입력 받아 일(day)를

 * 증감한다. <code>day</code>는 가감할 수를 의미하며, 음수를 입력할 경우 감한다.

 * <br/><br/>

 * 위에 정의된 addDays 메서드는 사용자가 ParseException을 반드시 처리해야 하는 불편함이

 * 있기 때문에 추가된 메서드이다.</p>

 *

 * <pre>

 * DateUtil.addDay("19991201", 62) = "20000201"

 * DateUtil.addDay("20000201", -62) = "19991201"

 * DateUtil.addDay("20050831", 3) = "20050903"

 * DateUtil.addDay("20050831", 3) = "20050903"

 * // 2006년 6월 31일은 실제로 존재하지 않는 날짜이다 -> 20060701로 간주된다

 * DateUtil.addDay("20060631", 1) = "20060702"

 * </pre>

 *

 * @param  dateStr 날짜 문자열(yyyyMMdd, yyyy-MM-dd의 형식)

 * @param  day 가감할 일. 0이 입력될 경우 가감이 없다

 * @return  yyyyMMdd 형식의 날짜 문자열

 * @throws IllegalArgumentException 날짜 포맷이 정해진 바와 다를 경우.

 *         입력 값이 <code>null</code>인 경우.

 */

public static String addDay(String dateStr, int day) {

return addYearMonthDay(dateStr, 0, 0, day);

}


/**

 * <p>yyyyMMdd 혹은 yyyy-MM-dd 형식의 날짜 문자열 <code>dateStr1</code>과 <code>

 * dateStr2</code> 사이의 일 수를 구한다.<br>

 * <code>dateStr2</code>가 <code>dateStr1</code> 보다 과거 날짜일 경우에는

 * 음수를 반환한다. 동일한 경우에는 0을 반환한다.</p>

 *

 * <pre>

 * DateUtil.getDaysDiff("20060228","20060310") = 10

 * DateUtil.getDaysDiff("20060101","20070101") = 365

 * DateUtil.getDaysDiff("19990228","19990131") = -28

 * DateUtil.getDaysDiff("20060801","20060802") = 1

 * DateUtil.getDaysDiff("20060801","20060801") = 0

 * </pre>

 *

 * @param  dateStr1 날짜 문자열(yyyyMMdd, yyyy-MM-dd의 형식)

 * @param  dateStr2 날짜 문자열(yyyyMMdd, yyyy-MM-dd의 형식)

 * @return  일 수 차이.

 * @throws IllegalArgumentException 날짜 포맷이 정해진 바와 다를 경우.

 *         입력 값이 <code>null</code>인 경우.

 */

public static int getDaysDiff(String sDate1, String sDate2) {

String dateStr1 = validChkDate(sDate1);

String dateStr2 = validChkDate(sDate2);


if (!checkDate(sDate1) || !checkDate(sDate2)) {

throw new IllegalArgumentException("Invalid date format: args[0]=" + sDate1 + " args[1]=" + sDate2);

}

SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd", Locale.getDefault());


Date date1 = null;

Date date2 = null;

try {

date1 = sdf.parse(dateStr1);

date2 = sdf.parse(dateStr2);

} catch (ParseException e) {

throw new IllegalArgumentException("Invalid date format: args[0]=" + dateStr1 + " args[1]=" + dateStr2);

}

int days1 = (int) ((date1.getTime() / 3600000) / 24);

int days2 = (int) ((date2.getTime() / 3600000) / 24);


return days2 - days1;

}


/**

 * <p>yyyyMMdd 혹은 yyyy-MM-dd 형식의 날짜 문자열을 입력 받아 유효한 날짜인지 검사.</p>

 *

 * <pre>

 * DateUtil.checkDate("1999-02-35") = false

 * DateUtil.checkDate("2000-13-31") = false

 * DateUtil.checkDate("2006-11-31") = false

 * DateUtil.checkDate("2006-2-28")  = false

 * DateUtil.checkDate("2006-2-8")   = false

 * DateUtil.checkDate("20060228")   = true

 * DateUtil.checkDate("2006-02-28") = true

 * </pre>

 *

 * @param  dateStr 날짜 문자열(yyyyMMdd, yyyy-MM-dd의 형식)

 * @return  유효한 날짜인지 여부

 */

public static boolean checkDate(String sDate) {

String dateStr = validChkDate(sDate);


String year = dateStr.substring(0, 4);

String month = dateStr.substring(4, 6);

String day = dateStr.substring(6);


return checkDate(year, month, day);

}


/**

 * <p>입력한 년, 월, 일이 유효한지 검사.</p>

 *

 * @param  year 연도

 * @param  month 월

 * @param  day 일

 * @return  유효한 날짜인지 여부

 */

public static boolean checkDate(String year, String month, String day) {

try {

SimpleDateFormat formatter = new SimpleDateFormat("yyyy.MM.dd", Locale.getDefault());


Date result = formatter.parse(year + "." + month + "." + day);

String resultStr = formatter.format(result);

if (resultStr.equalsIgnoreCase(year + "." + month + "." + day))

return true;

else

return false;

} catch (ParseException e) {

return false;

}

}


/**

 * 날짜형태의 String의 날짜 포맷 및 TimeZone을 변경해 주는 메서드

 *

 * @param  strSource       바꿀 날짜 String

 * @param  fromDateFormat  기존의 날짜 형태

 * @param  toDateFormat    원하는 날짜 형태

 * @param  strTimeZone     변경할 TimeZone(""이면 변경 안함)

 * @return  소스 String의 날짜 포맷을 변경한 String

 */

public static String convertDate(String strSource, String fromDateFormat, String toDateFormat, String strTimeZone) {

SimpleDateFormat simpledateformat = null;

Date date = null;

String _fromDateFormat = "";

String _toDateFormat = "";


if (EgovStringUtil.isNullToString(strSource).trim().equals("")) {

return "";

}

if (EgovStringUtil.isNullToString(fromDateFormat).trim().equals(""))

_fromDateFormat = "yyyyMMddHHmmss"; // default값

if (EgovStringUtil.isNullToString(toDateFormat).trim().equals(""))

_toDateFormat = "yyyy-MM-dd HH:mm:ss"; // default값


try {

simpledateformat = new SimpleDateFormat(_fromDateFormat, Locale.getDefault());

date = simpledateformat.parse(strSource);

if (!EgovStringUtil.isNullToString(strTimeZone).trim().equals("")) {

simpledateformat.setTimeZone(TimeZone.getTimeZone(strTimeZone));

}

simpledateformat = new SimpleDateFormat(_toDateFormat, Locale.getDefault());

} catch (ParseException exception) {

LOGGER.debug("{}", exception);

}

if (simpledateformat != null && simpledateformat.format(date) != null) {

return simpledateformat.format(date);

} else {

return "";

}


}


/**

 * yyyyMMdd 형식의 날짜문자열을 원하는 캐릭터(ch)로 쪼개 돌려준다<br/>

* <pre>

* ex) 20030405, ch(.) -> 2003.04.05

* ex) 200304, ch(.) -> 2003.04

* ex) 20040101,ch(/) --> 2004/01/01 로 리턴

* </pre>

*

* @param date yyyyMMdd 형식의 날짜문자열

* @param ch 구분자

* @return 변환된 문자열

 */

public static String formatDate(String sDate, String ch) {

String dateStr = validChkDate(sDate);


String str = dateStr.trim();

String yyyy = "";

String mm = "";

String dd = "";


if (str.length() == 8) {

yyyy = str.substring(0, 4);

if (yyyy.equals("0000"))

return "";


mm = str.substring(4, 6);

if (mm.equals("00"))

return yyyy;


dd = str.substring(6, 8);

if (dd.equals("00"))

return yyyy + ch + mm;


return yyyy + ch + mm + ch + dd;

} else if (str.length() == 6) {

yyyy = str.substring(0, 4);

if (yyyy.equals("0000"))

return "";


mm = str.substring(4, 6);

if (mm.equals("00"))

return yyyy;


return yyyy + ch + mm;

} else if (str.length() == 4) {

yyyy = str.substring(0, 4);

if (yyyy.equals("0000"))

return "";

else

return yyyy;

} else

return "";

}


/**

 * HH24MISS 형식의 시간문자열을 원하는 캐릭터(ch)로 쪼개 돌려준다 <br>

 * <pre>

 *     ex) 151241, ch(/) -> 15/12/31

 * </pre>

 *

 * @param str HH24MISS 형식의 시간문자열

 * @param ch 구분자

 * @return 변환된 문자열

 */

public static String formatTime(String sTime, String ch) {

String timeStr = validChkTime(sTime);

return timeStr.substring(0, 2) + ch + timeStr.substring(2, 4) + ch + timeStr.substring(4, 6);

}


/**

 * 연도를 입력 받아 해당 연도 2월의 말일(일수)를 문자열로 반환한다.

 *

 * @param year

 * @return 해당 연도 2월의 말일(일수)

 */

public String leapYear(int year) {

if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {

return "29";

}


return "28";

}


/**

 * <p>입력받은 연도가 윤년인지 아닌지 검사한다.</p>

 *

 * <pre>

 * DateUtil.isLeapYear(2004) = false

 * DateUtil.isLeapYear(2005) = true

 * DateUtil.isLeapYear(2006) = true

 * </pre>

 *

 * @param  year 연도

 * @return  윤년 여부

 */

public static boolean isLeapYear(int year) {

if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {

return false;

}

return true;

}


/**

 * 현재(한국기준) 날짜정보를 얻는다.                     <BR>

 * 표기법은 yyyy-mm-dd                                  <BR>

 * @return  String      yyyymmdd형태의 현재 한국시간.   <BR>

 */

public static String getToday() {

return getCurrentDate("");

}


/**

 * 현재(한국기준) 날짜정보를 얻는다.                     <BR>

 * 표기법은 yyyy-mm-dd                                  <BR>

 * @return  String      yyyymmdd형태의 현재 한국시간.   <BR>

 */

public static String getCurrentDate(String dateType) {

Calendar aCalendar = Calendar.getInstance();


int year = aCalendar.get(Calendar.YEAR);

int month = aCalendar.get(Calendar.MONTH) + 1;

int date = aCalendar.get(Calendar.DATE);

String strDate = Integer.toString(year) + ((month < 10) ? "0" + Integer.toString(month) : Integer.toString(month))

+ ((date < 10) ? "0" + Integer.toString(date) : Integer.toString(date));


if (!"".equals(dateType))

strDate = convertDate(strDate, "yyyyMMdd", dateType);


return strDate;

}


/**

 * 날짜형태의 String의 날짜 포맷만을 변경해 주는 메서드

 * @param sDate 날짜

 * @param sTime 시간

 * @param sFormatStr 포멧 스트링 문자열

 * @return 지정한 날짜/시간을 지정한 포맷으로 출력

 * @See Letter  Date or Time Component  Presentation  Examples

           G  Era designator  Text  AD

           y  Year  Year  1996; 96

           M  Month in year  Month  July; Jul; 07

           w  Week in year  Number  27

           W  Week in month  Number  2

           D  Day in year  Number  189

           d  Day in month  Number  10

           F  Day of week in month  Number  2

           E  Day in week  Text  Tuesday; Tue

           a  Am/pm marker  Text  PM

           H  Hour in day (0-23)  Number  0

           k  Hour in day (1-24)  Number  24

           K  Hour in am/pm (0-11)  Number  0

           h  Hour in am/pm (1-12)  Number  12

           m  Minute in hour  Number  30

           s  Second in minute  Number  55

           S  Millisecond  Number  978

           z  Time zone  General time zone  Pacific Standard Time; PST; GMT-08:00

           Z  Time zone  RFC 822 time zone  -0800




           Date and Time Pattern  Result

           "yyyy.MM.dd G 'at' HH:mm:ss z"  2001.07.04 AD at 12:08:56 PDT

           "EEE, MMM d, ''yy"  Wed, Jul 4, '01

           "h:mm a"  12:08 PM

           "hh 'o''clock' a, zzzz"  12 o'clock PM, Pacific Daylight Time

           "K:mm a, z"  0:08 PM, PDT

           "yyyyy.MMMMM.dd GGG hh:mm aaa"  02001.July.04 AD 12:08 PM

           "EEE, d MMM yyyy HH:mm:ss Z"  Wed, 4 Jul 2001 12:08:56 -0700

           "yyMMddHHmmssZ"  010704120856-0700


 */

public static String convertDate(String sDate, String sTime, String sFormatStr) {

String dateStr = validChkDate(sDate);

String timeStr = validChkTime(sTime);


Calendar cal = null;

cal = Calendar.getInstance();


cal.set(Calendar.YEAR, Integer.parseInt(dateStr.substring(0, 4)));

cal.set(Calendar.MONTH, Integer.parseInt(dateStr.substring(4, 6)) - 1);

cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(dateStr.substring(6, 8)));

cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(timeStr.substring(0, 2)));

cal.set(Calendar.MINUTE, Integer.parseInt(timeStr.substring(2, 4)));


SimpleDateFormat sdf = new SimpleDateFormat(sFormatStr, Locale.ENGLISH);


return sdf.format(cal.getTime());

}


/**

 * 입력받은 일자 사이의 임의의 일자를 반환

 * @param sDate1 시작일자

 * @param sDate2 종료일자

 * @return 임의일자

 */

public static String getRandomDate(String sDate1, String sDate2) {

String dateStr1 = validChkDate(sDate1);

String dateStr2 = validChkDate(sDate2);


String randomDate = null;


int sYear, sMonth, sDay;

int eYear, eMonth, eDay;


sYear = Integer.parseInt(dateStr1.substring(0, 4));

sMonth = Integer.parseInt(dateStr1.substring(4, 6));

sDay = Integer.parseInt(dateStr1.substring(6, 8));


eYear = Integer.parseInt(dateStr2.substring(0, 4));

eMonth = Integer.parseInt(dateStr2.substring(4, 6));

eDay = Integer.parseInt(dateStr2.substring(6, 8));


GregorianCalendar beginDate = new GregorianCalendar(sYear, sMonth - 1, sDay, 0, 0);

GregorianCalendar endDate = new GregorianCalendar(eYear, eMonth - 1, eDay, 23, 59);


if (endDate.getTimeInMillis() < beginDate.getTimeInMillis()) {

throw new IllegalArgumentException("Invalid input date : " + sDate1 + "~" + sDate2);

}


SecureRandom r = new SecureRandom();


long rand = ((r.nextLong() >>> 1) % (endDate.getTimeInMillis() - beginDate.getTimeInMillis() + 1)) + beginDate.getTimeInMillis();


GregorianCalendar cal = new GregorianCalendar();

//SimpleDateFormat calformat = new SimpleDateFormat("yyyy-MM-dd");

SimpleDateFormat calformat = new SimpleDateFormat("yyyyMMdd", Locale.ENGLISH);

cal.setTimeInMillis(rand);

randomDate = calformat.format(cal.getTime());


// 랜덤문자열를 리턴

return randomDate;

}


/**

 * 입력받은 양력일자를 변환하여 음력일자로 반환

 * @param sDate 양력일자

 * @return 음력일자

 */

public static Map<String, String> toLunar(String sDate) {

String dateStr = validChkDate(sDate);


Map<String, String> hm = new HashMap<String, String>();

hm.put("day", "");

hm.put("leap", "0");


if (dateStr.length() != 8) {

return hm;

}


Calendar cal;

ChineseCalendar lcal;


cal = Calendar.getInstance();

lcal = new ChineseCalendar();


cal.set(Calendar.YEAR, Integer.parseInt(dateStr.substring(0, 4)));

cal.set(Calendar.MONTH, Integer.parseInt(dateStr.substring(4, 6)) - 1);

cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(dateStr.substring(6, 8)));


lcal.setTimeInMillis(cal.getTimeInMillis());


String year = String.valueOf(lcal.get(ChineseCalendar.EXTENDED_YEAR) - 2637);

String month = String.valueOf(lcal.get(ChineseCalendar.MONTH) + 1);

String day = String.valueOf(lcal.get(ChineseCalendar.DAY_OF_MONTH));

String leap = String.valueOf(lcal.get(ChineseCalendar.IS_LEAP_MONTH));


String pad4Str = "0000";

String pad2Str = "00";


String retYear = (pad4Str + year).substring(year.length());

String retMonth = (pad2Str + month).substring(month.length());

String retDay = (pad2Str + day).substring(day.length());


String SDay = retYear + retMonth + retDay;


hm.put("day", SDay);

hm.put("leap", leap);


return hm;

}


/**

 * 입력받은 음력일자를 변환하여 양력일자로 반환

 * @param sDate 음력일자

 * @param iLeapMonth 음력윤달여부(IS_LEAP_MONTH)

 * @return 양력일자

 */

public static String toSolar(String sDate, int iLeapMonth) {

String dateStr = validChkDate(sDate);


Calendar cal;

ChineseCalendar lcal;


cal = Calendar.getInstance();

lcal = new ChineseCalendar();


lcal.set(ChineseCalendar.EXTENDED_YEAR, Integer.parseInt(dateStr.substring(0, 4)) + 2637);

lcal.set(ChineseCalendar.MONTH, Integer.parseInt(dateStr.substring(4, 6)) - 1);

lcal.set(ChineseCalendar.DAY_OF_MONTH, Integer.parseInt(dateStr.substring(6, 8)));

lcal.set(ChineseCalendar.IS_LEAP_MONTH, iLeapMonth);


cal.setTimeInMillis(lcal.getTimeInMillis());


String year = String.valueOf(cal.get(Calendar.YEAR));

String month = String.valueOf(cal.get(Calendar.MONTH) + 1);

String day = String.valueOf(cal.get(Calendar.DAY_OF_MONTH));


String pad4Str = "0000";

String pad2Str = "00";


String retYear = (pad4Str + year).substring(year.length());

String retMonth = (pad2Str + month).substring(month.length());

String retDay = (pad2Str + day).substring(day.length());


return retYear + retMonth + retDay;

}


/**

 * 입력받은 요일의 영문명을 국문명의 요일로 반환

 * @param sWeek 영문 요일명

 * @return 국문 요일명

 */

public static String convertWeek(String sWeek) {

String retStr = null;


if (sWeek.equals("SUN")) {

retStr = "일요일";

} else if (sWeek.equals("MON")) {

retStr = "월요일";

} else if (sWeek.equals("TUE")) {

retStr = "화요일";

} else if (sWeek.equals("WED")) {

retStr = "수요일";

} else if (sWeek.equals("THR")) {

retStr = "목요일";

} else if (sWeek.equals("FRI")) {

retStr = "금요일";

} else if (sWeek.equals("SAT")) {

retStr = "토요일";

}


return retStr;

}


/**

 * 입력일자의 유효 여부를 확인

 * @param sDate 일자

 * @return 유효 여부

 */

public static boolean validDate(String sDate) {

String dateStr = validChkDate(sDate);


Calendar cal;

boolean ret = false;


cal = Calendar.getInstance();


cal.set(Calendar.YEAR, Integer.parseInt(dateStr.substring(0, 4)));

cal.set(Calendar.MONTH, Integer.parseInt(dateStr.substring(4, 6)) - 1);

cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(dateStr.substring(6, 8)));


String year = String.valueOf(cal.get(Calendar.YEAR));

String month = String.valueOf(cal.get(Calendar.MONTH) + 1);

String day = String.valueOf(cal.get(Calendar.DAY_OF_MONTH));


String pad4Str = "0000";

String pad2Str = "00";


String retYear = (pad4Str + year).substring(year.length());

String retMonth = (pad2Str + month).substring(month.length());

String retDay = (pad2Str + day).substring(day.length());


String retYMD = retYear + retMonth + retDay;


if (sDate.equals(retYMD)) {

ret = true;

}


return ret;

}


/**

 * 입력일자, 요일의 유효 여부를 확인

 * @param     sDate 일자

 * @param     sWeek 요일 (DAY_OF_WEEK)

 * @return    유효 여부

 */

public static boolean validDate(String sDate, int sWeek) {

String dateStr = validChkDate(sDate);


Calendar cal;

boolean ret = false;


cal = Calendar.getInstance();


cal.set(Calendar.YEAR, Integer.parseInt(dateStr.substring(0, 4)));

cal.set(Calendar.MONTH, Integer.parseInt(dateStr.substring(4, 6)) - 1);

cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(dateStr.substring(6, 8)));


int Week = cal.get(Calendar.DAY_OF_WEEK);


if (validDate(sDate)) {

if (sWeek == Week) {

ret = true;

}

}


return ret;

}


/**

 * 입력시간의 유효 여부를 확인

 * @param     sTime 입력시간

 * @return    유효 여부

 */

public static boolean validTime(String sTime) {

String timeStr = validChkTime(sTime);


Calendar cal;

boolean ret = false;


cal = Calendar.getInstance();


cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(timeStr.substring(0, 2)));

cal.set(Calendar.MINUTE, Integer.parseInt(timeStr.substring(2, 4)));


String HH = String.valueOf(cal.get(Calendar.HOUR_OF_DAY));

String MM = String.valueOf(cal.get(Calendar.MINUTE));


String pad2Str = "00";


String retHH = (pad2Str + HH).substring(HH.length());

String retMM = (pad2Str + MM).substring(MM.length());


String retTime = retHH + retMM;


if (sTime.equals(retTime)) {

ret = true;

}


return ret;

}


/**

 * 입력된 일자에 연, 월, 일을 가감한 날짜의 요일을 반환

 * @param sDate 날짜

 * @param year 연

 * @param month 월

 * @param day 일

 * @return 계산된 일자의 요일(DAY_OF_WEEK)

 */

public static String addYMDtoWeek(String sDate, int year, int month, int day) {

String dateStr = validChkDate(sDate);


dateStr = addYearMonthDay(dateStr, year, month, day);


Calendar cal = Calendar.getInstance();

SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd", Locale.ENGLISH);

try {

cal.setTime(sdf.parse(dateStr));

} catch (ParseException e) {

throw new IllegalArgumentException("Invalid date format: " + dateStr);

}


SimpleDateFormat rsdf = new SimpleDateFormat("E", Locale.ENGLISH);


return rsdf.format(cal.getTime());

}


/**

 * 입력된 일자에 연, 월, 일, 시간, 분을 가감한 날짜, 시간을 포멧스트링 형식으로 반환

 * @param sDate 날짜

 * @param sTime 시간

 * @param year 연

 * @param month 월

 * @param day 일

 * @param hour 시간

 * @param minute 분

 * @param formatStr 포멧스트링

 * @return

 */

public static String addYMDtoDayTime(String sDate, String sTime, int year, int month, int day, int hour, int minute, String formatStr) {

String dateStr = validChkDate(sDate);

String timeStr = validChkTime(sTime);


dateStr = addYearMonthDay(dateStr, year, month, day);


dateStr = convertDate(dateStr, timeStr, "yyyyMMddHHmm");


Calendar cal = Calendar.getInstance();

SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmm", Locale.ENGLISH);


try {

cal.setTime(sdf.parse(dateStr));

} catch (ParseException e) {

throw new IllegalArgumentException("Invalid date format: " + dateStr);

}


if (hour != 0) {

cal.add(Calendar.HOUR, hour);

}


if (minute != 0) {

cal.add(Calendar.MINUTE, minute);

}


SimpleDateFormat rsdf = new SimpleDateFormat(formatStr, Locale.ENGLISH);


return rsdf.format(cal.getTime());

}


/**

 * 입력된 일자를 int 형으로 반환

 * @param sDate 일자

 * @return int(일자)

 */

public static int datetoInt(String sDate) {

return Integer.parseInt(convertDate(sDate, "0000", "yyyyMMdd"));

}


/**

 * 입력된 시간을 int 형으로 반환

 * @param sTime 시간

 * @return int(시간)

 */

public static int timetoInt(String sTime) {

return Integer.parseInt(convertDate("00000101", sTime, "HHmm"));

}


/**

 * 입력된 일자 문자열을 확인하고 8자리로 리턴

 * @param sDate

 * @return

 */

public static String validChkDate(String dateStr) {

String _dateStr = dateStr;


if (dateStr == null || !(dateStr.trim().length() == 8 || dateStr.trim().length() == 10)) {

throw new IllegalArgumentException("Invalid date format: " + dateStr);

}

if (dateStr.length() == 10) {

_dateStr = EgovStringUtil.removeMinusChar(dateStr);

}

return _dateStr;

}


/**

 * 입력된 일자 문자열을 확인하고 8자리로 리턴

 * @param sDate

 * @return

 */

public static String validChkTime(String timeStr) {

String _timeStr = timeStr;


if (_timeStr.length() == 5) {

_timeStr = EgovStringUtil.remove(_timeStr, ':');

}

if (_timeStr == null || !(_timeStr.trim().length() == 4)) {

throw new IllegalArgumentException("Invalid time format: " + _timeStr);

}


return _timeStr;

}



java.security.KeyException

로컬에서는 정상적으로 동작을 하는데 서버에서는 아래와 같이 익셉션이 발생한다..
로컬은 오라클jdk를 사용하고 서버는 openjdk를 사용하고 있었다.
오라클에서 받은 jdk가 아닌 openjdk를 사용하고 계신분이 아래에 애러가 발생하신다면 jdk변경없이 아래와 같이 해결할 수 있다.

환경
CentOS release 6.5 (Final)
jave 1.7 (java-1.7.0_99-openjdk)

기본 코드

 import java.net.HttpURLConnection;

import java.net.URL;

public class Bla{
  public static void main(String[] args) throws Exception {
    System.out.print("Hello\n");
    String url=https://xxx.com/www?xxx=sss&aaa=ccc;
    try{
      HttpClient client = new HttpClient(httpClientConst.connMgr);
   PostMethod method = new PostMethod(url);
      
    int returnCode = client.executeMethod(method);
       if (returnCode == HttpStatus.SC_NOT_IMPLEMENTED) {       
          return result;
       }
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream() ;
    byte[] byteArray = new byte[1024];
    
    int count = 0 ;
    while((count = method.getResponseBodyAsStream().read(byteArray, 0, byteArray.length)) > 0){
     outputStream.write(byteArray, 0, count) ;
    }
    String responseStr = new String(outputStream.toByteArray(), "utf-8");
    }
    catch (Exception e){
      e.printStackTrace();
      System.out.print("Error\n");
    }
    System.out.print("About to loop\n");
    while(true){Thread.currentThread().sleep(1000);} //Crudest debug ever
  }
}


애러메세지

javax.net.ssl.SSLException: java.security.ProviderException: java.security.KeyException

        at sun.security.ssl.Alerts.getSSLException(Alerts.java:208)
        at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1916)
        at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1874)
        at sun.security.ssl.SSLSocketImpl.handleException(SSLSocketImpl.java:1857)
        at sun.security.ssl.SSLSocketImpl.handleException(SSLSocketImpl.java:1783)
        at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:127)
        at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)
        at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140)
        at org.apache.commons.httpclient.HttpConnection.flushRequestOutputStream(HttpConnection.java:825)
        at org.apache.commons.httpclient.MultiThreadedHttpConnectionManager
$HttpConnectionAdapter.flushRequestOutputStream(MultiThreadedHttpConnectionManager.java:1543)
        at org.apache.commons.httpclient.HttpMethodBase.writeRequest(HttpMethodBase.java:1975)
        at org.apache.commons.httpclient.HttpMethodBase.execute(HttpMethodBase.java:993)
        at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:393)
        at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:168)
        at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:396)
        at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:324)
        at com.GifTing.gw.media.business.BuyHistStatusBusiness.getGalaxiaPinInfo(BuyHistStatusBusiness.java:904)
        at com.GifTing.gw.media.business.BuyHistStatusBusiness.OutPinStatus(BuyHistStatusBusiness.java:384)
        at com.GifTing.gw.media.action.OutPinAction.pinStatus(OutPinAction.java:192)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:606)
        at org.apache.struts.actions.DispatchAction.dispatchMethod(DispatchAction.java:269)
        at com.dki.asf.action.DispatchAction.execute(DispatchAction.java:42)
        at org.apache.struts.chain.commands.servlet.ExecuteAction.execute(ExecuteAction.java:58)
        at org.apache.struts.chain.commands.AbstractExecuteAction.execute(AbstractExecuteAction.java:67)
        at org.apache.struts.chain.commands.ActionCommandBase.execute(ActionCommandBase.java:51)
        at org.apache.commons.chain.impl.ChainBase.execute(ChainBase.java:191)
        at org.apache.commons.chain.generic.LookupCommand.execute(LookupCommand.java:305)
        at org.apache.commons.chain.impl.ChainBase.execute(ChainBase.java:191)
        at org.apache.struts.chain.ComposableRequestProcessor.process(ComposableRequestProcessor.java:283)
        at org.apache.struts.action.ActionServlet.process(ActionServlet.java:1913)
        at org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:462)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:646)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
        at com.GifTing.common.filter.ForbidFilter.doFilter(ForbidFilter.java:64)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
        at com.dki.asf.web.filter.EncodingFilter.doFilter(EncodingFilter.java:44)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
        at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040)
        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)
        at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:313)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
        at java.lang.Thread.run(Thread.java:745)
Caused by: java.security.ProviderException: java.security.KeyException
        at sun.security.ec.ECKeyPairGenerator.generateKeyPair(ECKeyPairGenerator.java:146)
        at java.security.KeyPairGenerator$Delegate.generateKeyPair(KeyPairGenerator.java:704)
        at sun.security.ssl.ECDHCrypt.<init>(ECDHCrypt.java:78)
        at sun.security.ssl.ClientHandshaker.serverKeyExchange(ClientHandshaker.java:717)
        at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:278)
        at sun.security.ssl.Handshaker.processLoop(Handshaker.java:913)
        at sun.security.ssl.Handshaker.process_record(Handshaker.java:849)
        at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1035)
        at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1344)
        at sun.security.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:721)
        at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:122)
        ... 54 more
Caused by: java.security.KeyException
        at sun.security.ec.ECKeyPairGenerator.generateECKeyPair(Native Method)
        at sun.security.ec.ECKeyPairGenerator.generateKeyPair(ECKeyPairGenerator.java:126)


해결
openjdk 1.7에서만 문제가 발행하였다.
1.8로 업데이트를 하면 문제가 발생하지 않는다.
하지만 해당서버에서 다른 서비스도 돌아가기떄문에 버젼업을 하기 애매하여
라이브러리를 추가하는 작업을 하였다.

[root@WAS-SERVER-01 bin]# yum provides /usr/lib64/libssl3.so

[root@WAS-SERVER-01 lib64]# sudo yum upgrade nss





 

[전자정부 프레임워크 3.2] sql 로그 찍기

log4j란?
Log For Java란 뜻으로, 자바 어플리케이션에서 빠르고 효과적으로 로깅 할 수 있도록 도와주는 오픈소스이다.

pom.xml
        <dependency>
            <groupId>com.googlecode.log4jdbc</groupId>
            <artifactId>log4jdbc</artifactId>
            <version>1.2</version>
            <exclusions>
                <exclusion>
                    <artifactId>slf4j-api</artifactId>
                    <groupId>org.slf4j</groupId>
                </exclusion>
            </exclusions>
        </dependency>


context-datasource.xml
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
     <qualifier value="dataSource1" />
        <property name="driverClassName" value="net.sf.log4jdbc.DriverSpy"/>
        <property name="url" value="jdbc:log4jdbc::mysql://00.000.00.000:3306/test" />
        <property name="username" value="root"/>
        <property name="password" value="password"/>
    </bean>

log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
    <Appenders>
        <Console name="console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d %5p [%c] %m%n" />
        </Console>
    </Appenders>
    <Loggers>
        <Logger name="java.sql" level="DEBUG" additivity="false">
            <AppenderRef ref="console" />
        </Logger>
        <Logger name="egovframework" level="DEBUG" additivity="false">
            <AppenderRef ref="console" />
        </Logger>
          <!-- log SQL with timing information, post execution -->
        <Logger name="jdbc.sqltiming" level="DEBUG" additivity="false">
            <AppenderRef ref="console" />
        </Logger>
        <Logger name="org.springframework" level="INFO" additivity="false">
            <AppenderRef ref="console" />
        </Logger>
        <Root level="INFO">
            <AppenderRef ref="console" />
        </Root>
       
    </Loggers>
</Configuration>