[Java 1.8] 날짜 정리

Java 1.8 날짜 정리

자바 1.8 이전에는 날짜 연산이 쉽지 않았다. Joda 같은 라이브러리들을 쓰면 된다고 하는데, 필자는 Util 클래스에서 내부적으로 Calendar를 사용하여 연산하고 Date/long/String 간의 변환을 통하여 사용해왔다. 그러던 와중에 자바 1.8의 새로운 날짜들을 보니 신세계가 열렸다. 그래서 간단하게 소개하고 자주 사용될 만한 예제들도 나열하려 한다.

먼저 중요하다고 생각되는 클래스들을 소개하겠다.

클래스

날짜 (Temporal)

Instant : machine time에 유용한 1970년 1월 1일부터 시간을 세는 클래스 (millisecond 뿐만 아니라 nanosecond까지 센다)

LocalDate : [년,월,일]과 같은 날짜만 표현하는 클래스 (시간은 포함하지 않는다)

LocalDateTime : [년,월,일,시,분,초]를 표현하는 클래스 (LocalDate와 함께 가장 많이 쓰이는 클래스가 될 것 같다)

LocalTime : [시,분,초]와 같이 시간만 표현하는 클래스

기간 (TemporalAmount)

Period : 두 날짜 사이의 [년,월,일]로 표현되는 기간 (시간을 다루지 않다 보니 LocalDate를 사용한다)

Duration : 두 시간 사이의 [일,시,분,초]로 표현되는 기간 (Instant 클래스를 사용하고, seconds와 nanoseconds로 측정 되지만 [일,시,분,초]로 변환해 주는 메쏘드를 제공)

기타

ChronoUnit : 한가지의 단위를 표현하기 위한 클래스 (년,월,일,시,분,초 등)

DayOfWeek : 요일

자주 쓰는 메쏘드들

  • 날짜 가져오기

      LocalDate.now(); // 오늘
      LocalDateTime.now(); // 지금
      LocalDate.of(2015, 4, 17); // 2015년4월17일
      LocalDateTime.of(2015, 4, 17, 23, 23, 50); // 2015년4월17일23시23분50초
      Year.of(2015).atMonth(3).atDay(4).atTime(10, 30); // 2015년3월4일 10시30분00초
    
  • 기간 가져오기

      Period.ofYears(2); // 2년간(P2Y)
      Period.ofMonths(5); // 5개월간(P5M)
      Period.ofWeeks(3); // 3주간(P21D)
      Period.ofDays(20); // 20일간(P20D)
    
      Duration.ofDays(2); // 48시간(PT48H)
      Duration.ofHours(8); // 8시간(PT8H)
      Duration.ofMinutes(10); // 10분간(PT10M)
      Duration.ofSeconds(30); // 30초간(PT30S)
    
  • 날짜 + 기간 = 날짜

      LocalTime.of(9, 0, 0).plus(Duration.ofMinutes(10)); // (9시 + 10분간) = 9시10분
      LocalDate.of(2015, 5, 15).plus(Period.ofDays(1)); // (2015년5월15일 + 1일간) = 2015년5월16일
      LocalDateTime.of(2015, 4, 17, 23, 47, 5).minus(Period.ofWeeks(3)); // (2015년4월17일 23시47분05초 - 3주간) = 2015년3월27일 23시47분05초
      LocalDate.now().plusDays(1); // (오늘 + 1일) = 내일
      LocalTime.now().minusHours(3); // (지금 - 3시간) = 3시간 전
    
  • 날짜 - 날짜 = 기간

      Period.between(LocalDate.of(1950, 6, 25), LocalDate.of(1953, 7, 27)); // (1953년7월27일 - 1950년6월25일) = 3년1개월2일간(P3Y1M2D)
      Period.between(LocalDate.of(1950, 6, 25), LocalDate.of(1953, 7, 27)).getDays(); // 3년1개월2일간 => 2일간
      LocalDate.of(1950, 6, 25).until(LocalDate.of(1953, 7, 27), ChronoUnit.DAYS); // 3년1개월2일간 => 1128일간
      ChronoUnit.DAYS.between(LocalDate.of(1950, 6, 25), LocalDate.of(1953, 7, 27)); // 3년1개월2일간 => 1128일간
    
      Duration.between(LocalTime.of(10, 50), LocalTime.of(19, 0)); // (19시00분00초 - 10시50분00초) = 8시간10분간(PT8H10M)
      Duration.between(LocalDateTime.of(2015, 1, 1, 0, 0), LocalDateTime.of(2016, 1, 1, 0, 0)).toDays(); // 365일간
      ChronoUnit.YEARS.between(LocalDate.of(2015, 5, 5), LocalDate.of(2017, 2, 1)); // 1년간
    
  • 날짜 변환하기

    • LocalDate -> String

      LocalDate.of(2020, 12, 12).format(DateTimeFormatter.BASIC_ISO_DATE); // 20201212
      
    • LocalDateTime -> String

      LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); // 2015-04-18 00:42:24
      
    • LocalDateTime -> java.util.Date

      Date.from(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant()); // Sat Apr 18 01:00:30 KST 2015
      
    • LocalDate -> java.sql.Date

      Date.valueOf(LocalDate.of(2015, 5, 5)); // 2015-05-05
      
    • LocalDateTime -> java.sql.Timestamp

      Timestamp.valueOf(LocalDateTime.now()); // 2015-04-18 01:06:55.323
      
    • String -> LocalDate

      LocalDate.parse("2002-05-09"); // 2002-05-09
      LocalDate.parse("20081004", DateTimeFormatter.BASIC_ISO_DATE); // 2008-10-04
      
    • String -> LocalDateTime

      LocalDateTime.parse("2007-12-03T10:15:30"); // 2007-12-03T10:15:30
      LocalDateTime.parse("2010-11-25 12:30:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); // 2010-11-25T12:30
      
    • java.util.Date -> LocalDateTime

      LocalDateTime.ofInstant(new Date().toInstant(), ZoneId.systemDefault()); // 2015-04-18T01:16:46.755
      
    • java.sql.Date -> LocalDate

      new Date(System.currentTimeMillis()).toLocalDate(); // 2015-04-18
      
    • java.sql.Timestamp -> LocalDateTime

      new Timestamp(System.currentTimeMillis()).toLocalDateTime(); // 2015-04-18T01:20:07.364
      
    • LocalDateTime -> LocalDate

      LocalDate.from(LocalDateTime.now()); // 2015-04-18
      
    • LocalDate -> LocalDateTime

      LocalDate.now().atTime(2, 30); // 2015-04-18T02:30
      
  • 요일로 날짜 가져오기

      LocalDate.now().with(TemporalAdjusters.next(DayOfWeek.SATURDAY)); // 다음 토요일
      LocalDate.of(2016, 5, 1).with(TemporalAdjusters.dayOfWeekInMonth(3, DayOfWeek.SUNDAY)); // 2016년5월 세번째 일요일
      LocalDate.of(2015, 7, 1).with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY)); // 2015년7월 첫번째 월요일
    
  • 언어별 출력

      DayOfWeek.MONDAY.getDisplayName(TextStyle.FULL, Locale.ENGLISH); // Monday
      DayOfWeek.MONDAY.getDisplayName(TextStyle.NARROW, Locale.ENGLISH); // M
      DayOfWeek.MONDAY.getDisplayName(TextStyle.SHORT, Locale.ENGLISH); // Mon
    
      DayOfWeek.MONDAY.getDisplayName(TextStyle.FULL, Locale.KOREAN); // 월요일
      DayOfWeek.MONDAY.getDisplayName(TextStyle.NARROW, Locale.KOREAN); // 월
      DayOfWeek.MONDAY.getDisplayName(TextStyle.SHORT, Locale.KOREAN); // 월
    
      Month.FEBRUARY.getDisplayName(TextStyle.FULL, Locale.US); // February
      Month.FEBRUARY.getDisplayName(TextStyle.FULL, Locale.KOREA); // 2월
    

참고

[Java] Http 요청 보내기 (HttpClient)

개요

자바스크립트에서는 태그에 넣으면 host, pathname, protocol, port, search 등을 지원해서 url 관리하기가 편리하다.

하지만 자바에서는 Url을 핸들링하기가 쉽지않다. 그래서 아파치에서 제공하는 유틸을 소개하려 한다.

예제

  • Uri 생성하기

    먼저 http 요청을 하게 해주는 라이브러리를 추가해야 한다.

      <dependency>
          <groupId>org.apache.httpcomponents</groupId>
          <artifactId>httpclient</artifactId>
          <version>4.3.5</version>
      </dependency>
    

    그리고 아래와 같이 코드를 작성해준다.

    HttpTest.java

      package com.tistory.jekalmin;
    
      import java.io.IOException;
      import java.net.URI;
      import java.net.URISyntaxException;
    
      import org.apache.http.client.ClientProtocolException;
      import org.apache.http.client.utils.URIBuilder;
    
      public class HttpTest {
    
          public static void main(String[] args) throws URISyntaxException, ClientProtocolException, IOException {
    
              URI uri = new URI("http://jekalmin.tistory.com");
              uri = new URIBuilder(uri).addParameter("aaa", "bbb").addParameter("ccc", "ddd").build();
              System.out.println(uri);
    
          }
    
      }
    

    URIBuilder 클래스를 사용해서 처음부터 host, protocol, port 까지 정해주며 생성할 수도 있고, 예제와 같이 파라미터만 추가도 가능하다. 결과는 다음과 같다.

      http://jekalmin.tistory.com?aaa=bbb&ccc=ddd
    
  • http 요청하기

    이제 생성한 uri를 가지고 http 요청을 해보자. 아래와 같이 코드를 추가해준다.

    HttpTest.java

      package cpackage com.tistory.jekalmin;
    
      import java.io.IOException;
      import java.net.URI;
      import java.net.URISyntaxException;
    
      import org.apache.http.HttpEntity;
      import org.apache.http.HttpResponse;
      import org.apache.http.client.ClientProtocolException;
      import org.apache.http.client.HttpClient;
      import org.apache.http.client.methods.HttpGet;
      import org.apache.http.client.utils.URIBuilder;
      import org.apache.http.impl.client.HttpClientBuilder;
      import org.apache.http.util.EntityUtils;
    
      public class HttpTest {
    
          public static void main(String[] args) throws URISyntaxException, ClientProtocolException, IOException {
    
              URI uri = new URI("http://jekalmin.tistory.com");
              uri = new URIBuilder(uri).addParameter("aaa", "bbb").addParameter("ccc", "ddd").build();
    
              HttpClient httpClient = HttpClientBuilder.create().build();
              HttpResponse response = httpClient.execute(new HttpGet(uri)); // post 요청은 HttpPost()를 사용하면 된다. 
              HttpEntity entity = response.getEntity();
              String content = EntityUtils.toString(entity);
              System.out.println(content);
    
          }
    
      }
    

    4.0부터는 DefaultHttpClient 클래스를 사용했으나 4.3부터는 deprecated 된 상태이다. 대신 HttpClientBuilder를 사용하여 생성하기를 권장하고 있다.
    http 요청후 내용 받아오는 부분은 원래 entity.getContent() 인데 getContent()는 InputStream을 반환한다. 바로 String으로 받기 위해 EntityUtils를 사용했다.

  • Uri에서 파라미터 파싱하기

    host, port, path 등의 정보는 URI 객체에서 getHost() 등과 같은 메소드로 바로 가져올 수 있다. 하지만 파라미터의 경우는 제공하지 않는다.
    그래서 아파치에서 제공하는 유틸을 사용했다. 아래 예제를 보자.

    HttpTest.java

      package com.tistory.jekalmin;
    
      import java.io.IOException;
      import java.net.URI;
      import java.net.URISyntaxException;
      import java.util.List;
    
      import org.apache.http.NameValuePair;
      import org.apache.http.client.ClientProtocolException;
      import org.apache.http.client.utils.URIBuilder;
      import org.apache.http.client.utils.URLEncodedUtils;
    
      public class HttpTest {
    
          public static void main(String[] args) throws URISyntaxException, ClientProtocolException, IOException {
    
              URI uri = new URI("http://jekalmin.tistory.com");
              uri = new URIBuilder(uri).addParameter("aaa", "bbb").addParameter("ccc", "ddd").build();
              System.out.println(uri);
    
              List<NameValuePair> paramList = URLEncodedUtils.parse(uri, "utf-8");
              for(NameValuePair param : paramList){
                  System.out.println(param.getName() + " = " + param.getValue());
              }
          }
    
      }
    

    결과는 아래와 같다.

      http://jekalmin.tistory.com?aaa=bbb&ccc=ddd
      aaa = bbb
      ccc = ddd
    

    아파치에서는 URLEncodedUtils 라는 클래스를 제공한다. 위의 parse 외에도 거꾸로 변환해주는 format 기능도 있다.
    두 메소드는 오버로딩을 통해 다양한 파라미터 타입을 제공하기도 한다.

결론

예전에는 자바에서 http 요청을 보낼 때, string을 결합하여 uri를 직접 생성하거나 그 작업을 도와주는 유틸을 만들곤 했었는데, 이미 다 제공되고 있었다. EntityUtil과 URLEncodedUtils는 4.0 버전부터 지원한다.
코딩하다가 불편하다고 느낄때 무작정 유틸을 만드는 것은 많은 테스트를 안하면 안전하지 않을 뿐만 아니라 많은 시간이 소요된다. 내가 불편함을 느낀다면, 다른 사람들도 똑같이 느꼈을 것이다. 직접 만드는 것은 최후의 보루로 남겨두자.

java에서 정규표현식으로 원하는 문자 파싱하기

글 쓰기에 앞서 정규표현식의 기본적인 문법을 알고 있다고 가정하겠다. 로그파일이나 html소스등 파싱해야 할 경우가 종종 생기는데 자바스크립트에서 정규표현식을 사용하여 원하는 문자열을 뽑아오는 방법이다.

"jekalmin 25 male 2014-08-22" 라는 로그가 있다고 가정하고 하나씩 뽑아와보자.

먼저 정규표현식을 작성해야 하는데, 작성할 때 유의할 사항은 뽑아내고자 하는 문자열을 괄호로 감싸야 한다.


위 코드의 실행결과는 다음과 같다.


group안에 0번은 매치되는 문자열 전체,

1번부터는 첫번째 괄호, 2번은 두번째 괄호에 상응 하는 문자열을 뽑아온다.

테스트 코드 주소 :

https://github.com/jekalmin/Jekal/blob/master/java/test/RegexpTest.java