23.07.07 스터디
Calendar와 Date는 자바의 탄생부터 지금가지 20년이 넘게 사용되어왔고, 지금도 계속 사용되고 있으므로 배우지 않고 넘어갈 수가 없습니다. 그렇다고 해서 Calendar와 Date를 깊게 배울 필요는 없고 여기서 소개하는 내용들을 이해하고 필요할 때 활용 및 이용하는 정도면 충분합니다.
Calendar는 추상클래스이기 때문에 직접 객체를 생성할 수 없고, 메서드를 통해서 완전히 구현된 클래스의 인스턴스를 얻어야만합니다.
class MP{
public static void main(String[]args){
Calendar cal = new Calendar(); // ERR Code. --> 추상클래스는 인스턴스 생성 불가능
Calendar cal = Calendar.getInstance();
이 코드에서 cal의 해당 메서드가 static 인지 아시나요 !?
--> getinstance()가 static이 아니라면, 위와 같이 객체를 생성한 다음에 호출해야하는데 Calendar는 추상클래스이기 때문에 객체를 생성할 수 없기 때문입니다.
<참고1>
- 클래스의 멤버 변수 중 모든 인스턴스에 공통된 값을 유지해야하는 것이 있는지 살펴보고 있다면,
static
을 붙여줍니다.- 작성한 메서드 중에서 인스턴스 변수나 인스턴스 메서드를 사용하지 않는 메서드에
static
을 붙일 것을 고려해야합니다.
<참고2>
클래스 영역에 선언된 변수를 "멤버변수" 라고 한다.
멤버변수 중에 'static'이 붙는 것은클래스변수(static변수)
, static이 붙지 않은 것은인스턴스변수
라고 합니다. (멤버변수= 인스턴스,static변수 모두 통칭하는 말)
getInstance() 메서드가 static인 이유는 추상 클래스인 Calendar 클래스를 직접 인스턴스화 할 수 없기 때문입니다
숫자를 형식화 하는데 사용되는 것이 DecimalFormat입니다.
DecimalFormat을 이용하면 숫자 데이터를 정수, 부동소수점, 금액 등의 다양한 형식으로 표현할 수 있습니다.
또한, 일정한 형식의 텍스터 데이터를 숫자로 쉽게 변환하는 것도 가능합니다.
0
(숫자): 사용자가 원하는 위치에 0을 넣으면 해당 자리에 숫자가 들어갑니다. 숫자가 없는 경우 0을 표시합니다.DecimalFormat df1 = new DecimalFormat("0000");
System.out.println(df1.format(123)); // 출력: 0123
#
(숫자): 사용자가 원하는 위치에 #을 넣으면 해당 자리에 숫자가 들어갑니다. 숫자가 없으면 공백을 표시합니다.DecimalFormat df2 = new DecimalFormat("####");
System.out.println(df2.format(123)); // 출력: 123
.
(소수점): 패턴에 .를 넣음으로써 소수점 이하를 표현할 수 있습니다.DecimalFormat df3 = new DecimalFormat("0.00");
System.out.println(df3.format(123.456)); // 출력: 123.46
,
(천 단위 구분자): 패턴에 ,를 넣으면 천 단위로 구분해 표시합니다.DecimalFormat df4 = new DecimalFormat("#,###");
System.out.println(df4.format(1234567)); // 출력: 1,234,567
%
(퍼센트): 패턴에 %를 추가하면 숫자를 퍼센트 형식으로 표현합니다. 숫자에 100을 곱한 다음 패턴을 적용합니다.DecimalFormat df5 = new DecimalFormat("0.00%");
System.out.println(df5.format(0.1234)); // 출력: 12.34%
E
(지수): 패턴에 E를 추가하면 숫자를 지수 형식으로 표현합니다.DecimalFormat df6 = new DecimalFormat("0.##E0");
System.out.println(df6.format(1234)); // 출력: 1.23E3
;
(패턴 구분자): 패턴에 ;을 사용하여 양수와 음수에 대한 서로 다른 형식을 지정할 수 있습니다.DecimalFormat df7 = new DecimalFormat("#,##0.00;(#,##0.00)");
System.out.println(df7.format(-1234.56)); // 출력: (1,234.56)
-
(마이너스 기호): 패턴의 시작부분에 -를 추가하여 음수를 표시할 수 있습니다.DecimalFormat df8 = new DecimalFormat("-#,###");
System.out.println(df8.format(-1234)); // 출력: -1,234
'
(따옴표 기호): 패턴 문자로 인식되는 문자를 일반 문자로 처리하고 싶을 때 해당 문자 앞에 '를 추가합니다.DecimalFormat df9 = new DecimalFormat("0.00 'km'");
System.out.println(df9.format(12.345)); // 출력: 12.35 km
¤
(통화 기호): 패턴에 ¤를 넣으면 해당 자리에 통화 기호가 표시됩니다. 로케일에 따른 통화 기호를 사용하려면 NumberFormat의 getCurrencyInstance() 메서드를 사용합니다.DecimalFormat df10 = new DecimalFormat("¤ #,##0");
System.out.println(df10.format(1234)); // 출력: $ 1,234 (달러 기호는 로케일 설정에 따라 다름)
()
(괄호): 패턴에 ()를 사용하여 음수를 괄호로 감싸 표시할 수 있습니다. 이렇게 하면, 회계 시스템에서 음수를 표시하는 데 영감을 얻은 형식이 생성됩니다.DecimalFormat df11 = new DecimalFormat("#,##0;(#,##0)");
System.out.println(df11.format(-1234)); // 출력: (1,234)
필자가 자주 봤던 기호와 의미만 정리해보겠습니다.
y
: 년도
M
: 월(1~12 또는 1월~12월)
w
: 년의 몇 번째 주(1~53)
d
: 월의 몇 번째 일(1~31)
E
: 요일
psvm{
DateFormat df1 = new SimpleDateFormat("yyyy년MM월dd일");
DateFormat df2 = new SimpleDateFormat("yyyy/MM/dd");
Date d = df1.pasre("2015년 11월 23일");
sout(df2.format(d));
}
특정 범위에 속하는 값을 문자열로 변환해줍니다.
연속적 또는 불연속적인 범위의 값들을 처리하는 데 있어서 if문이나 switch문은 적절하지 않은 경우가 많습니다.
이럴 때, ChoiceFormat을 잘 사용하면 복잡하게 처리될 수 밖에 없었던 코드를 직관적이고 간돤하게 구현할 수 있습니다.
또한, 주로 숫자에 따라 상태, 등급 또는 수량에 따른 문자열을 표현하는 데 유용합니다.
ChoiceFormat을 사용하려면 먼저 범위를 double 배열로 지정하고 해당 범위에 해당하는 문자열을 배열 또는 문자열로 지정해야 합니다.
import java.text.ChoiceFormat;
public class ChoiceFormatExample {
public static void main(String[] args) {
double[] limits = {0, 1, 2}; // 범위를 지정하는 double 배열
String[] grades = {"Fail", "Pass", "Excellent"}; // 범위에 해당하는 문자열 배열
// ChoiceFormat 객체 생성
ChoiceFormat cf = new ChoiceFormat(limits, grades);
System.out.println(cf.format(0)); // 출력: Fail
System.out.println(cf.format(1)); // 출력: Pass
System.out.println(cf.format(1.5)); // 출력: Pass
System.out.println(cf.format(2)); // 출력: Excellent
}
}
ChoiceFormat에서는 #
와 <
기호를 사용하여 범위의 상한과 하한을 정의할 수 있습니다. #
은 해당 범위의 상한(이상)을 의미하며, <
은 하한(미만)을 의미합니다.
아래 코드로 좀더 알아보겠습니다.
import java.text.ChoiceFormat;
public class ChoiceFormatExample {
public static void main(String[] args) {
double[] limits = {0, 1, 2};
String[] formats = {"0<Fail", "1#Pass", "2<Excellent"};
ChoiceFormat cf = new ChoiceFormat(limits, formats);
System.out.println(cf.format(-1)); // 출력: Fail (# 기호 미사용시 주어진 범위 이외의 값에 대한 포맷 정의에 주의)
System.out.println(cf.format(0)); // 출력: Pass (0 또는 1에서 Pass로 지정)
System.out.println(cf.format(0.5)); // 출력: Pass (범위 0 미만, 1 이상)
System.out.println(cf.format(1)); // 출력: Pass (1에서도 Pass로 지정)
System.out.println(cf.format(1.5)); // 출력: Excellent (범위 2 미만)
System.out.println(cf.format(2)); // 출력: Excellent (범위 2 이상)
}
}
#
와 <
를 사용할 때 주의해야 할 점즉, #
와 <
기호를 활용하여 필요에 따른 범위와 문자열 매핑을 구성할 수 있지만,
기호 사용으로 인한 올바른 포맷 정의나 예외 처리에 충분한 주의를 기울여야 합니다
데이터를 정해진 양식에 맞게 출력할 수 있ㄷ도록 도와줍니다.
데이터가 들어갈 자리를 마련해 놓은 양식을 미리 작성하고, 프로그램을 이용해서 다수의 데이터를 같은 양식으로 출력할 때 사용하면 좋습니다.
parse를 이용해서 출력된 데이터로부터 필요한 데이터만을 뽑아내는 방법의 예시
import java.util.*;
import java.io.*;
import java.text.*;
class d{
psvm{
String tableName = "CUst_INFO";
String fileName = "date.txt";
String msg = "INSERT INTO" + tableName + " VALUES({0},{1},{2},{3})";
Scanner s = new Scanner(new File(fileName));
String pattern = "{0},{1},{2},{3}";
MessageFormat mf = new MessageFormat(pattern);
while(s.hasNextline()){
String line = s.nextLine();
Object[] objs = mf.parse(line);
sout(MessageFormat.format(msg, objs));
}
위 코드의 방법을 알아두면, 어려운 작업을 쉽게 처리할 수 있어, 잘 기억해두시길 바랍니다.
그러나, 필자는 MessageFormat 클래스는 주어진 패턴에 따라 텍스트를 구성하고, 다양한 인수를 사용하여 메시지를 동적으로 형식화할 수 있는 도구라고 생각합니다.
따라서 내장 parse() 메서드는 일반적으로 사용자 친화적인 출력 결과를 얻기 어렵다고 생각됩니다.
따라서, 우선 정규 표현식과 Pattern 클래스와 Matcher 클래스를 사용하여 출력된 데이터에서 필요한 정보를 추출하는 것이 좋다고 생각됩니다.
아래 예제에서는 MessageFormat을 사용하여 출력된 데이터에서 과일과 가격 정보를 추출하는 방법입니다. 외우기보단 이해하며 넘기는 것을 추천드립니다.
import java.text.MessageFormat;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class MessageFormatExample {
public static void main(String[] args) {
Object[] data = {"Apple", 1.5};
MessageFormat mf = new MessageFormat("Fruit: {0}, Price: {1}");
String formattedMessage = mf.format(data);
System.out.println("Original message: " + formattedMessage);
// 정규 표현식을 사용하여 원하는 데이터 추출
Pattern pattern = Pattern.compile("Fruit:\\s([a-zA-Z]+),\\sPrice:\\s([0-9.]+)");
Matcher matcher = pattern.matcher(formattedMessage);
if (matcher.find()) {
String fruit = matcher.group(1);
String price = matcher.group(2);
System.out.println("Fruit: " + fruit);
System.out.println("Price: " + price);
}
}
}
java.time 패키지는 Java 8에서 도입되었습니다.
이 패키지는 기존의 java.util.Date와 java.util.Calendar와 같은 날짜 및 시간을 처리하는 클래스들의 한계와 단점을 해결하기 위해 만들어졌습니다.
이러한 기존 클래스들은 불변 객체가 아니라서 변경 가능한 상태를 가지고 있어 여러 문제가 발생할 수 있었으며, 가독성이 떨어지고 코드 작성이 번거로웠습니다. Java 8에서 도입된 java.time 패키지는 JSR-310을 기반으로 만들어졌습니다.
이러한 이유와 내용 때문에, 자바에서 java.time 패키지는 날짜 및 시간 처리에 있어서 강력하고 유용한 도구로 인식되어지며 사용됩니다.
기존의 Date와 Calendar 클래스를 대신해서 이 패키지의 클래스들을 사용하는 것이 좋습니다. But 레거시 코드들엔 호환성 때문에 Date 와 Calendar을 사용하는 경우가 종종 있습니다.
에포크 타임부터 경과된 시간을 나노초 단위로 표현합니다.
사람에겐 불편하지만, 단일 진법으로만 다루기 때문에 계산하기 쉽습니다.
그러나 사람이 사용하는 날짜와 시간은 여러 진법이 섞여있어서 계산하기 어렵습니다.
Instant 클래스는 Java 8에서 도입된 새로운 날짜 및 시간 API 중 하나입니다. 이 클래스는 시간을 나노초 정밀도로 표현할 수 있는 일련의 순간들을 나타내며, 1970년 1월 1일 0시 0분 0초를 기준으로 한 유닉스 타임스탬프로써 구현되어 있습니다.
Instant 클래스는 불변 객체로서, 값을 변경하지 않고 연산을 수행한 결과를 새로운 Instant 객체로 반환합니다.
이 클래스는 시간대 정보 없이 순수하게 시간을 나타내는 용도로 사용되며, 시간대를 고려한 날짜 및 시간 연산을 원한다면 ZonedDateTime, LocalDate, LocalTime 등의 클래스를 사용하는 것이 좋습니다.
import java.time.Instant;
import java.time.Duration;
public class InstantExample {
public static void main(String[] args) {
// 현재 시점의 Instant 객체 생성
Instant now = Instant.now();
System.out.println("현재 시각: " + now);
// 특정 밀리초(epoch time)로부터 Instant 객체 생성
Instant epochInstant = Instant.ofEpochMilli(1620000000000L);
System.out.println("에포크 밀리초로 생성한 시각: " + epochInstant);
// 현재 시각에 10초를 더한 시각 계산
Instant tenSecondsLater = now.plus(Duration.ofSeconds(10));
System.out.println("10초 후: " + tenSecondsLater);
// 현재 시각에 5초를 뺀 시각 계산
Instant fiveSecondsBefore = now.minus(Duration.ofSeconds(5));
System.out.println("5초 전: " + fiveSecondsBefore);
// 현재 시각이 에포크 밀리초로 생성한 시각보다 이후인지 확인
boolean isAfter = now.isAfter(epochInstant);
System.out.println("현재 시각이 에포크 밀리초로 생성한 시각보다 이후인가? " + isAfter);
}
}
import java.time.ZonedDateTime;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.Duration;
import java.time.temporal.TemporalAdjusters;
public class DateTimeExamples {
public static void main(String[] args) {
// 현재 ZonedDateTime 객체 생성
ZonedDateTime now = ZonedDateTime.now();
System.out.println("현재 날짜 및 시간 (시간대 포함): " + now);
// 현재 LocalDate 객체 생성
LocalDate today = LocalDate.now();
System.out.println("현재 날짜: " + today);
// 현재 LocalTime 객체 생성
LocalTime currentTime = LocalTime.now();
System.out.println("현재 시간: " + currentTime);
// 다른 시간대의 현재 시간 생성
ZonedDateTime newYorkTime = ZonedDateTime.now(ZoneId.of("America/New_York"));
System.out.println("뉴욕 현재 시간: " + newYorkTime);
// LocalDate에 날짜를 더하고 빼기
LocalDate tomorrow = today.plusDays(1);
LocalDate yesterday = today.minusDays(1);
System.out.println("내일 날짜: " + tomorrow);
System.out.println("어제 날짜: " + yesterday);
// LocalTime에 시간 더하기
LocalTime twoHoursLater = currentTime.plus(Duration.ofHours(2));
System.out.println("2시간 후: " + twoHoursLater);
// 다음 달의 첫날 구하기
LocalDate firstDayOfNextMonth = today.with(TemporalAdjusters.firstDayOfNextMonth());
System.out.println("다음 달의 첫날: " + firstDayOfNextMonth);
}
}
java.time 패키지의 날짜와 시간 객체들은 문자열을 파싱하여 객체를 생성하거나, 객체를 문자열로 포맷하는 기능을 제공합니다.
이 작업은 주로 DateTimeFormatter 클래스를 사용하여 수행됩니다.
Java의 날짜와 시간 객체들은 DateTimeFormatter를 사용하여 손쉽게 문자열로 포맷하거나 파싱할 수 있습니다.
파싱이란,
해석입니다.
즉, 날짜와 시간을 원하는 형식으로 출력하고 해석하는 바업ㅂ이라고 보시면됩니다.
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class ParsingAndFormattingExample {
public static void main(String[] args) {
// 문자열로부터 LocalDate 객체 파싱
String dateString = "2023-07-05";
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate parsedDate = LocalDate.parse(dateString, dateFormatter);
System.out.println("파싱된 날짜: " + parsedDate);
// 문자열로부터 LocalDateTime 객체 파싱
String dateTimeString = "2023-07-05 14:30";
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
LocalDateTime parsedDateTime = LocalDateTime.parse(dateTimeString, dateTimeFormatter);
System.out.println("파싱된 날짜 및 시간: " + parsedDateTime);
// LocalDate 객체를 문자열로 포맷
LocalDate today = LocalDate.now();
String formattedDate = today.format(dateFormatter);
System.out.println("포맷된 날짜: " + formattedDate);
// LocalDateTime 객체를 문자열로 포맷
LocalDateTime currentTime = LocalDateTime.now();
String formattedDateTime = currentTime.format(dateTimeFormatter);
System.out.println("포맷된 날짜 및 시간: " + formattedDateTime);
}
}
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class ParsingCurrentDateTimeExample {
public static void main(String[] args) {
// 현재 날짜 및 시간을 가져옵니다.
LocalDateTime currentDateTime = LocalDateTime.now();
System.out.println("현재 날짜 및 시간: " + currentDateTime);
// DateTimeFormatter를 사용하여 현재 날짜 및 시간의 문자열 표현을 생성합니다.
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
String currentDateTimeString = currentDateTime.format(dateTimeFormatter);
System.out.println("현재 날짜 및 시간의 문자열 표현: " + currentDateTimeString);
// 위에서 생성한 문자열을 DateTimeFormatter를 사용해 파싱합니다.
LocalDateTime parsedDateTime = LocalDateTime.parse(currentDateTimeString, dateTimeFormatter);
System.out.println("파싱된 현재 날짜 및 시간: " + parsedDateTime);
}
}