Date Date - jdk 1.0 제공한 클래스, java.util 패키지 안에 있다.
Date 출력 함수
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 Date now = new Date ();System.out.println(now); System.out.println(now.getTime()); System.out.println(now.getDate()); System.out.println(now.getDay()); System.out.println(now.getMonth()+1 ); System.out.println(now.getYear()+1900 ); System.out.println(now.getHours()); System.out.println(now.getMinutes()); System.out.println(now.toString()); System.out.println(now.toGMTString()); System.out.println(now.toLocaleString()); String d = String.format("%d년 %d월 %d일 %d:%d:%d (%c)" , 1900 + now.getYear() , now.getMonth() + 1 , now.getDate() , now.getHours() , now.getMinutes() , now.getSeconds() , "일월화수목금토" .charAt( now.getDay() ) ); System.out.println(d);
Date 수정 및 비교 함수
1 2 3 4 5 6 7 8 Date when = new Date (2019 -1900 , 1 -1 , 29 ); Date past = new Date (2010 -1900 , 5 -1 , 1 ); System.out.println(now.before(past)); System.out.println(now.after(past)); now.setYear(2010 -1900 ); now.setMonth(5 -1 ); now.setDate(1 );
설정되지 않은 시, 분, 초는 현재시, 분, 초로 들어감… 이렇게 자투리로 남는 시간값 때문에 오차가 생길 수 도 있다. 날짜끼리 차이 계산할때에는 시간은 모두 0으로 세팅하거나 clone메서드를 사용해서 서로 같은 시간을 가리키는 식으로 오차를 없애야함.
날짜를 다양하게 다루기 위해서 사용되는 클래스 Format클래스중 가장 많이 사용된다.
다양한 형식을 날짜를 Calendar클래스로 저장하고Calendar나 Date 클래스의 날짜를 다양한 형식으로 변환 출력한다.
1 2 3 4 5 6 7 8 9 10 String pattern = "G" ; String pattern = "y" ; String pattern = "M" ; String pattern = "w" ; String pattern = "W" ; String pattern = "d" ; STring pattern = "D" ; STring pattern = "H" ; STring pattern = "h" ; String pattern = "E" ;
1 2 3 4 Date now = new Date ();String pattern = "yyyy년 MM월 dd일 HH'h' mm'm' ss's' (E)" ; SimpleDateFormat sdf = new SimpleDateFormat (pattern);System.out.println( sdf.format(now) );
날짜 객체를 문자열로 변환해서 출력도 가능하지만 문자열로 날짜 객체를 만들 수 있다.
1 2 3 4 5 String strDate = "2019년 02월 02일" ;SimpleDateFormat sdf2 = new SimpleDateFormat ("yyyy년 M월 d일" ); Date date = sdf2.parse(strDate);System.out.println(date.toLocaleString()); System.out.println(sdf2.format(date));
만약 문자열이 잘못된 포멧이면 ParseException 예외를 발생한다.SimpleDateFormat의 parse 메서드로 생성되는 날짜 객체 역시 설정되지 않은 시, 분, 초는 0으로 세팅된다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 String pattern = "y/M/d" ;Scanner sc = new Scanner (System.in);SimpleDateFormat sdf = new SimpleDateFormat (pattern);Date inDate = null ;System.out.print("날짜입력 y/M/d: " ); while (true ) { try { inDate = sdf.parse(sc.nextLine()); break ; } catch (ParseException e) { System.out.print("잘못된 날짜 포멧... 다시입력: (y/M/d): " ); } }
Calendar Calendar는 추상클래스로 new를 통해 객체생성 불가능하다.Calendar.getInstance() 정적 메서드로 인스턴스를 가져올 수 있음.
1 Calendar cal = Calendar.getInstance();
추상클래스인 Calender 를 구현하는 클래스는 아래 3개, 기본 GregorianCalendar 를 사용한다.
BuddhistCalendar
JapaneseImperialCalendar
GregorianCalendar
다음과 같이 GregorianCalendar 생성자에 날짜, 시간을 설정해 생성 가능. 설정하지 않은 시간, 초, 밀리초는 모두 0으로 초기화된다.
1 Calendar cal = new GregorianCalendar (2018 ,5 -1 ,20 );
1 2 3 4 5 6 7 8 9 System.out.println(cal.get(Calendar.YEAR)); System.out.println(cal.get(Calendar.MONTH)+1 ); System.out.println(cal.get(Calendar.DATE)); System.out.println( cal.get(Calendar.DAY_OF_WEEK) ); System.out.println("일월화수목금토" .charAt(cal.get(Calendar.DAY_OF_WEEK)-1 )); System.out.println(cal.get(Calendar.HOUR)); System.out.println(cal.get(Calendar.HOUR_OF_DAY)); System.out.println(cal.get(Calendar.MINUTE)); System.out.println(cal.get(Calendar.SECOND));
1 2 3 4 5 6 7 cal.set(Calendar.YEAR, 2019 ); cal.set(Calendar.MONTH, 1 -1 ); cal.set(Calendar.DATE, 1 ); cal.set(2018 , 12 -1 , 21 ); cal.add(Calendar.DATE, 100 ); cal.add(Calendar.DATE, -100 );
2월의 경우 28일까지 밖에 없음으로 set 메서드로 달 설정시 28일을 넘지 않도록 주의. 마지막 날짜를 설정하고 싶다면 Calendar.DAY_OF_MONTH 값을 사용
설령 2월에 30일을 설정한다 하더라도 내부 validation 을 통해 자동 이월된다.
1 2 3 4 5 6 7 8 9 10 11 12 Calendar cal = Calendar.getInstance();cal.set(2018 , 1 -1 , 1 ); for (int i = 0 ; i < 12 ; i++) { cal.set(Calendar.MONTH, i); System.out.println(cal.getActualMaximum(Calendar.DAY_OF_MONTH)); } cal = new GregorianCalendar (2019 , Calendar.FEBRUARY, 1 ); System.out.println("Original Date: " + cal.getTime()); cal.set(Calendar.DATE, 30 ); System.out.println("Updated Date: " + cal.getTime());
Date -> Calender, Calender -> Date 변환 함수
1 2 3 4 5 6 7 8 9 10 11 public static Calendar date2cal (Date date) { Calendar cal = Calendar.getInstance(); cal.setTime(date); return cal; } public static Date cal2date (Calendar cal) { return cal.getTime(); }
날짜사이의 차이 구하기
getTimeInMillis() 메서드를 통해 두 날짜 사이의 ‘밀리초’를 가질고 날짜 차이를 얻기 때문에 ‘시’ 이하의 시간단위로 인해 미세한 오차가 생길수 있음으로 clone을 통해 ‘시’이하 단위를 일치화.
GregorianCalendar으로 생성하면 시, 분, 초 모두 0으로 설정되기 때문에 clone()으로 복제할 필요가 없으니Calendar로 차이를 구해야 한다면 GregorianCalendar로 생성하자.
1 2 3 4 5 6 7 8 9 10 Calendar openingDay = new GregorianCalendar (2018 , Calendar.DECEMBER, 21 );Calendar now = (Calendar) openingDay.clone();now.add(Calendar.DATE, 25 ); long gapInMillis = now.getTimeInMillis() - openingDay.getTimeInMillis();System.out.println(gapInMillis + " ms" ); System.out.println((gapInMillis / 1000 ) + " s" ); System.out.println((gapInMillis / 1000 / 60 ) + " m" ); System.out.println((gapInMillis / 1000 / 60 / 60 ) + " h" ); System.out.println((gapInMillis / 1000 / 60 / 60 / 24 ) + " d" );
Calendar객체를 사용한 달력그리기 1 2 3 4 5 ... int year = 2019 ;int month = 3 ;printCalendar(year, month);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 private static void printCalendar (int year, int month) { int week = 0 ; int endDay = 0 ; System.out.printf("%d년 %d월\n" , year, month); String weeks = "일월화수목금토" ; for (int i = 0 ; i < weeks.length(); i++) { System.out.printf("%c\t" , weeks.charAt(i)); } System.out.println(); Calendar cal = Calendar.getInstance(); cal.set(year, month-1 , 1 ); week = cal.get(Calendar.DAY_OF_WEEK); cal.add(Calendar.DATE, -week+1 ); for (int i = 0 ; i < 42 ; i++) { System.out.printf("%d\t" , cal.get(Calendar.DATE)); cal.add(Calendar.DATE, 1 ); if (i%7 ==6 ) System.out.println(); } }
%(데이터형) 따로 형식을 붙여줄 필요없이 MessageFormat을 사용하면 몇번째 인자인지만 알려주면 된다. 출력할 변수 타입이 뭔지 모를때 사용하면 편할듯 함.
LocalDate, LocalTime, LocalDateTime 클래스 jdk 1.8부터 추가된 java.time 패키지.Date, Calendar 부족한 메서드들을 추가하여 시간 관련 연산을 더욱 편하게 하기 위해 만들어짐.
java.time - 날짜, 시간 다루는 핵심 클래스 대거 포함되어 있다.
java.time.chrono - 표준이 아닌 달력 시스템을 위한 클래스 제공.
java.time.format - 날짜 시간을 파싱해서 원하는 포멧으로 변환 출력.
java.time.temporal - 날짜 시간 필드와 단위를 위한 클래스 제공
java.time.zone - 시간대(timezone)과 관련된 클래스 제공
java.time 클래스들은 String클래스 처럼 불변하다.
1 2 3 4 5 6 Calendar cal = Calendar.getInstance();cal.set(field, value); LocalDate now = LocalDate.now();now = now.plusDays(1 ); System.out.println(now);
Calendar는 기존 인스턴스의 정보를 바꾸지만 java.time패키지의 객체들은 기존의 인스턴스를 버리고 새 인스턴스를 참조한다.now.plusDays(1) 하루 증가후 꼭 반환하는 값을 재참조 해야한다.
java.time 에서는 날짜 를 다루는 클래스와 시간 을 다루는 클래스들 분리해두었다.
날짜 - LocalDate 시간 - LocalTime 날짜시간 - LocalDateTime
LocalDate 날짜 생성
1 2 3 4 5 6 7 LocalDate now = LocalDate.now();LocalDate dday = LocalDate.of(2019 , 2 , 4 );LocalDate date = LocalDate.ofYearDay(2010 , 37 ); System.out.println(date);
날짜 읽기
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 LocalDate now = LocalDate.of(2019 , 2 , 4 );System.out.println(now); System.out.println(now.get(ChronoField.YEAR)); System.out.println(now.get(ChronoField.MONTH_OF_YEAR)); System.out.println(now.get(ChronoField.DAY_OF_MONTH)); System.out.println(now.get(ChronoField.DAY_OF_WEEK)); System.out.println(now.getYear()); System.out.println(now.getMonthValue()); System.out.println(now.getDayOfMonth()); System.out.println(now.lengthOfMonth()); System.out.println(now.lengthOfYear());
날짜 변경
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 now = now.plusDays(1 ); System.out.println(now); now = now.plusDays(-1 ); System.out.println(now); now = now.minusDays(1 ); System.out.println(now); now.plusMonths(long time); now.plusWeeks(long time); now.plusYears(long time); now = now.withMonth(9 ); now = now.withDayOfMonth(11 ); System.out.println(now); now = now.with(ChronoField.MONTH_OF_YEAR, 9 ); now = now.with(ChronoField.DAY_OF_MONTH, 11 ); System.out.println(now);
날짜 비교
1 2 3 4 5 6 LocalDate now = LocalDate.of(2019 , 2 , 5 );LocalDate dday = LocalDate.of(2019 , 2 , 4 );System.out.println(now.compareTo(dday)); System.out.println(now.isEqual(dday)); System.out.println(now.isAfter(dday)); System.out.println(now.isBefore(dday));
LocalTime 시간 생성
1 2 3 4 5 6 LocalTime now = LocalTime.now();System.out.println(LocalTime.now()); System.out.println(LocalTime.of(14 , 52 )); System.out.println(LocalTime.of(14 , 52 , 10 , 999999999 ));
시간 읽기
1 2 3 4 5 6 7 8 9 10 11 12 13 LocalTime now = LocalTime.now();int time = now.getHour();int min = now.getMinute();int second = now.getSecond();int mils = now.getNano();System.out.printf("%d시 %d분 %d.%d초" , time, min, second, mils); time = now.get(ChronoField.HOUR_OF_DAY); min = now.get(ChronoField.MINUTE_OF_HOUR); second = now.get(ChronoField.SECOND_OF_MINUTE); mils = now.get(ChronoField.MILLI_OF_SECOND); System.out.printf("%d시 %d분 %d.%d초" , time, min, second, mils);
시간 변경
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 LocalTime now = LocalTime.now();System.out.println(now); System.out.println(now.truncatedTo(ChronoUnit.HOURS)); now = now.withHour(10 ); now = now.withMinute(30 ); now = now.withSecond(45 ); now = now.withNano(500 ); System.out.println(now); now = now.with(ChronoField.HOUR_OF_DAY, 10 ); now = now.with(ChronoField.MINUTE_OF_HOUR, 30 ); now = now.with(ChronoField.SECOND_OF_MINUTE, 45 ); now = now.with(ChronoField.NANO_OF_SECOND, 500 ); System.out.println(now);
LocalDateTime 날짜와 시간정보 모두 포함LocalDate, LocalTime, LocalDateTime 모두 비슷한 상속관계를 가진다.
날짜 생성
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 LocalDateTime ldt1 = LocalDateTime.of(2019 , 8 , 2 , 14 , 30 );LocalDateTime ldt2 = LocalDateTime.of(2019 , 8 , 2 , 14 , 30 , 45 );LocalDateTime ldt3 = LocalDateTime.of(2019 , 8 , 2 , 14 , 30 , 45 , 123456789 );LocalDateTime ldt4 = LocalDateTime.of(2019 , Month.AUGUST, 2 , 14 , 30 );LocalDate d = LocalDate.of(2015 , 12 , 31 );LocalTime t = LocalTime.of(12 , 34 , 56 );LocalDateTime ldt5 = LocalDateTime.of(d, t);System.out.println(ldt1); System.out.println(ldt2); System.out.println(ldt3); System.out.println(ldt4); System.out.println(ldt5);
parse : String -> [LocalDate, LocalTime, LocalDateTime] 변환
format : [LocalDate, LocalTime, LocalDateTime] -> String 변환
LocalDate 파싱
LocalDate.parse 메서드의 기본 문자열 패턴은 yyyy-MM-dd 이다.DateTimeFormatter.ofPattern 메서드로 문자열 패턴 변경 가능하다.
1 2 3 4 5 6 7 8 9 10 LocalDate d1 = LocalDate.parse("2018-09-18" );LocalDate d2 = LocalDate.parse("2018년 09월 19일" , DateTimeFormatter.ofPattern("yyyy년 MM월 dd일" )); System.out.println(d1); System.out.println(d2); String date_str = d1.format(s, DateTimeFormatter.ofPattern("yyyy년 MM월 dd일" ));System.out.println(date_str);
LocalTime.parse 메서드의 기본 문자열 패턴은 hh:mm:ss 이다(설정하지 않은 초 이하는 다 0으로 설정됨).LocalTime 역시 DateTimeFormatter 를 사용해서 문자열 패턴 변경이 가능하다.
1 2 3 4 5 6 7 8 9 10 LocalTime t1 = LocalTime.parse("10:10:10" );LocalTime t2 = LocalTime.parse("10.10.10" , DateTimeFormatter.ofPattern("HH.mm.ss" )); System.out.println(t1); System.out.println(t2); String time_str = time.format(DateTimeFormatter.ofPattern("HH.mm.ss" ));System.out.println(time_str);
TemporalAdjusters - 시간조정자 해당일자로 다음주 토요일, 다음달 2번째 월요일 등, 까다로운 날짜 계산이 필요할때 TemporalAdjusters 를 사용하면 편하다.
위같은 연산을 plus, minus, with 메서드 로만 진행하면 분명 복잡한 연산이 필요하겠지만 TemporalAdjusters 에는 이미 해당 메서드들이 다 정의 되어있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 LocalDate today = LocalDate.now();System.out.println(today); System.out.println(today.with(TemporalAdjusters.firstDayOfMonth())); System.out.println(today.with(TemporalAdjusters.lastDayOfMonth())); System.out.println(today.with(TemporalAdjusters.firstInMonth(DayOfWeek.TUESDAY))); System.out.println(today.with(TemporalAdjusters.lastInMonth(DayOfWeek.TUESDAY))); System.out.println(today.with(TemporalAdjusters.previous(DayOfWeek.TUESDAY))); System.out.println(today.with(TemporalAdjusters.next(DayOfWeek.TUESDAY)));
Period, Duration - 두 시간, 날짜 차이를 구하는 클래스 날짜 차이가 얼마나 나는지 구하고 싶을땐 Period 메서드를 사용하면 편하다.
1 2 3 4 5 6 7 LocalDate d1 = LocalDate.of(2014 , 1 , 1 );LocalDate d2 = LocalDate.of(2015 , 12 , 31 );Period pe = Period.between(d1, d2);System.out.println(pe.getYears()); System.out.println(pe.getMonths()); System.out.println(pe.getDays());
총 1년 11개월 30일 차이난다는것을 알 수 있다.
시간 차이가 얼마나 나는지 구하고 싶을땐 Duration
1 2 3 4 5 6 7 8 9 LocalTime t1 = LocalTime.of(00 , 00 , 10 );LocalTime t2 = LocalTime.of(12 , 34 , 56 );Duration du = Duration.between(t1, t2);System.out.printf("%d시 %d분 %d초\n" , du.toHours(), du.toMinutes()%60 , du.getSeconds()%60 );
특정 값 하나만 얻어와도 된다면 until 메서드를 사용하면 편하다.
1 2 3 4 5 LocalDate endDay = LocalDate.of(2019 , 7 , 19 );LocalDate today = LocalDate.now();long dday = today.until(endDay, ChronoUnit.DAYS);System.out.println(dday);
Zone 여러 국가에서 지원하는 서비스의 경우 서버가 위치한 Local Time 보다는Universal Time Coordinated(UTC: 세계 협정시) 을 지원해야 한다.
그리니치 표준시라고도 하는데 런던 웰링턴의 그리니치 시계탑을 기준으로 표준시를 결정했기 때문 Zulu time 이라고도 하는데 군에서 UTC 를 뜻하는 단어이다.
ISO 8601 의 마지막 특수문자 Z 가 Zulu time 을 뜻한다.2011-08-12T20:17:46.384Z - 뒤에 Z(Zulu Time) 특수문자가 붙어서 표준시를 뜻함.
대표적인 나라 도시의 UTC Time Zone 은 아래와 같다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 0:00 GMT/LON(런던) GMT+0 1:00 PAR(파리) GMT+1 2:00 CAI/JRS(카이로) GMT+2 3:00 JED(제다) GMT+3 3:30 THR(테헤란) GMT+3.5 4:00 DXB(두바이) GMT+4 4:30 KBL(카불) GMT+4.5 5:00 KHI(카라치) GMT+5 5:30 DEL(델리) GMT+5.5 6:00 DAC(다카) GMT+6 6:30 RGN(양곤) GMT+6.5 7:00 BKK(방콕) GMT+7 8:00 HKG(홍콩) GMT+8 9:00 SEL(서울) GMT+9 9:30 ADL(다윈) GMT+9.5 10:00 SYD(시드니) GMT+10 11:00 NOU(누메아) GMT+11 12:00 WLG(웰링턴) GMT+12
더많은 도시의 타임존 을 확인하고 싶다면 아래 url 참고https://jp.cybozu.help/general/en/admin/list_systemadmin/list_localization/timezone.html
UTC 를 위한 Formatter 는 아래 참고
1 2 3 4 SimpleDateFormat sdf = new SimpleDateFormat ("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" );format.setTimeZone(TimeZone.getTimeZone("UTC" )); DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX" ).withZone(ZoneId.of("UTC" ));
'Z' 는 일반 문자열, 그리고 TimeZone 을 UTC 로 설정해서 Formatter 를 구현하면 된다.
2020-09-10T10:58:19+09:00 - UTC+9 를 뜻하며 서울이나 도쿄 등의 도시에서 사용한다.yyyy-MM-dd'T'HH:mm:ssXXX 를 Formatter 의 format 문자열로 정의하면 된다.
ZoneDateTime java8 에서 시간 표기를 위한 클래스는 아래와 같다.
LocalDateTime
OffsetDateTime
ZoneDateTime
Instant
OffsetDateTime 보다 ZoneDateTime 이 더 많은 정보를 가지고 있는데ZoneDateTime 에는 국가와 같은 ZoneId Asia/seoul 같은 정보도 가지고 있을 수 있다.
ZoneDateTime 이라 하더라도 반드시 ZoneId 를 넣을 필요는 없기에 가장 범위가 작은 LocalDateTime, 가장 범위가 큰 ZoneDateTime 둘중 하나를 자주 사용한다.
DateTimeFormatter 에 이미 여러가지 형식을 지정해두었는데 어떻게 출력되는지 알아보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ZonedDateTime zdt = ZonedDateTime.parse("2019-03-10T02:30:00Z" ).withZoneSameInstant(ZoneId.of("Asia/Seoul" ));System.out.println(zdt.format(DateTimeFormatter.ISO_LOCAL_DATE)); System.out.println(zdt.format(DateTimeFormatter.ISO_OFFSET_DATE)); System.out.println(zdt.format(DateTimeFormatter.ISO_DATE)); System.out.println(zdt.format(DateTimeFormatter.ISO_LOCAL_TIME)); System.out.println(zdt.format(DateTimeFormatter.ISO_OFFSET_TIME)); System.out.println(zdt.format(DateTimeFormatter.ISO_TIME)); System.out.println(zdt.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)); System.out.println(zdt.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)); System.out.println(zdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME)); System.out.println(zdt.format(DateTimeFormatter.ISO_DATE_TIME)); System.out.println(zdt.format(DateTimeFormatter.ISO_ORDINAL_DATE)); System.out.println(zdt.format(DateTimeFormatter.ISO_WEEK_DATE)); System.out.println(zdt.format(DateTimeFormatter.ISO_INSTANT)); System.out.println(zdt.format(DateTimeFormatter.BASIC_ISO_DATE)); System.out.println(zdt.format(DateTimeFormatter.RFC_1123_DATE_TIME));
ISO_ZONED_DATE_TIME ISO_DATE_TIME 두개의 포멧 차이가 없는데 구현부를 보면 offsetId 가 optional 한지 아닌지 정도 차이이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ISO_ZONED_DATE_TIME = (new DateTimeFormatterBuilder ()) .append(ISO_OFFSET_DATE_TIME) .optionalStart() .appendLiteral('[' ) .parseCaseSensitive() .appendZoneRegionId() .appendLiteral(']' ) .toFormatter(ResolverStyle.STRICT, IsoChronology.INSTANCE); ISO_DATE_TIME = (new DateTimeFormatterBuilder ()) .append(ISO_LOCAL_DATE_TIME) .optionalStart() .appendOffsetId() .optionalStart() .appendLiteral('[' ) .parseCaseSensitive() .appendZoneRegionId() .appendLiteral(']' ) .toFormatter(ResolverStyle.STRICT, IsoChronology.INSTANCE);
ZoneId 문자열을 deseiralize 하는 경우는 많이 없기 때문에 가장 많이 사용하는 것은 오프셋을 출력하지 않는 ISO_LOCAL_DATE_TIME, 오프셋을 출력하는 ISO_DATE_TIME 정도.
ISO_DATE_TIME 가 optional 설정에 묶인 정보가 가장 많기 때문에 웬만한 포멧은 다 처리 가능하다.
ZondId 가 지정되어 있지 않은 ZonedDateTime 의 경우 ZondId 가 출력되지 않는다.
1 2 3 4 ZonedDateTime zdt1 = ZonedDateTime.parse("2019-03-10T02:30:00Z" ).withZoneSameInstant(ZoneId.of("Asia/Seoul" ));ZonedDateTime zdt2 = ZonedDateTime.parse("2019-03-10T02:30:00Z" );System.out.println(zdt1.format(DateTimeFormatter.ISO_DATE_TIME)); System.out.println(zdt2.format(DateTimeFormatter.ISO_DATE_TIME));
withZoneSameInstant() vs withZoneSameLocal
withZoneSameInstant() 은 시간과 함께 영역을 변경
withZoneSameLocal() 은 영역만 변경
1 2 3 4 ZonedDateTime zdt = ZonedDateTime.parse("2019-03-10T02:30:00Z" );ZoneId zoneId = ZoneId.of("Asia/Seoul" );System.out.println(zdt.withZoneSameInstant(zoneId)); System.out.println(zdt.withZoneSameLocal(zoneId));
Instant UTC 기준으로 초와 나노초를 표현하는 객체.
객체만으로 명확한 UTC 기준을 알수 있고 문자열 변환도 기본 ISO-8601 변환방식을 바로 사용할 수 있어 많이 사용한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 Instant now = Instant.now();System.out.println(now.toString()) long seconds = instant.getEpochSecond(); int nanos = instant.getNano(); long millis = instant.toEpochMilli(); System.out.println(Instant.ofEpochSecond(1609459200L )); System.out.println(Instant.ofEpochSecond(1609459200L , 123456789L )); Instant i = Instant.parse("2024-11-29T01:58:17.575975Z" )
1 2 3 4 5 6 7 8 9 10 Instant now = Instant.now();Instant later = now.plusSeconds(3600 ); Instant earlier = now.minusMillis(5000 ); Instant instant1 = Instant.now();Instant instant2 = instant1.plusSeconds(10 );boolean isBefore = instant1.isBefore(instant2); boolean isAfter = instant1.isAfter(instant2);
1 2 3 4 LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());ZonedDateTime zonedDateTime = instant.atZone(ZoneId.of("Asia/Seoul" ));ZonedDateTime utcZonedDateTime = instant.atZone(ZoneOffset.UTC);
달려그리기 {: .shadow}
윤년확인하기 먼저 윤년인지 여부를 구하는 함수를 작성, 내장함수와 직접구현 가능.
1 2 LocalDate now = LocalDate.now();now.isLeapYear();
윤년을 구하는 공식을 간단하게 java코드로 만들어보자. 다른 언어에서도 마찬가지로 적용 가능할 것이다.
서력 기원 연수가 4로 나누어떨어지는 해는 윤년으로 한다.
서력 기원 연수가 4, 100으로 나누어떨어지는 해는 평년으로 한다.
서력 기원 연수가 4, 100, 400으로 나누어떨어지는 해는 윤년으로 둔다.
즉 년도가 4로 나누어 떨어지면서 100으로 나뉘지 않으면서 그중에 400으로 나뉘어지면 윤년으로 본다!
1 2 3 4 5 6 7 8 public static int getLastDay (int year, int month) { int [] days = {31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31 }; return isleapyear(year) && month==2 ? 29 : days[month-1 ]; } public static boolean isleapyear (int year) { return year%100 !=0 && year%4 ==0 || year%400 ==0 ? true : false ; }
시작요일 구하기 달의 시작요일이 무슨요일인지, 마지막일이 몇일인지 알아야한다.
마지막일은 윤년일 제외하곤 고정값이다(30, 31 반복).
1 2 3 4 public static int getLastDay (int year, int month) { int [] days = {31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31 }; return isleapyear(year) && month==2 ? 29 : days[month-1 ]; }
시작요일을 구하는 공식은 1년 1월 1일이 월요일임으로, 원하는 날짜까지의 일 수 를 구한후 7로 나눈 나머지값이 요일이 된다.
그럼 2019-4-1 일의 요일을 구하고 싶다면 1-1-1부터 2019-4-1까지의 일 수 를 구하면 된다.
먼저 1년 ~ 2018년 까지의 일수를 구한다. 365 곱한것 뒤의 + (year-1)/4 - (year-1)/100 + (year-1)/400는 윤년에 해당하는 일수를 추가로 더해주기 위한것, 윤년의 경우 366일이니까! 그리고 4월 1일의 까지의 일수를 for문을 통해 추가로 더해준다.
1 2 3 4 5 6 7 8 9 public static int getDayOfWeek (int year, int month) { int totDays = (year-1 )*365 + (year-1 )/4 - (year-1 )/100 + (year-1 )/400 ; for (int i = 1 ; i < month; i++) { totDays+=getLastDay(year, i); } totDays+=1 ; return totDays%7 ; }
미리 마지막일을 저장해 놓은 배열을 정의해두었다. (2월은 윤년의 경우 29일로 반환)
이제 달력 그리기를 위한 모든 값이 구해졌으니 달력을 그려보자!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 public class Calender { public static void main (String[] args) throws IOException { int year, month; System.out.print("년 월 입력하세요: " ); Scanner sc = new Scanner (System.in); year = sc.nextInt(); month = sc.nextInt(); createCalendar(year, month); } public static void createCalendar (int year, int month) { int dayOfWeek = getDayOfWeek(year, month); int lastDay = getLastDay(year, month); printCalender(year, month, dayOfWeek, lastDay); } public static void drawLine (int n) { for (int i = 0 ; i < n; i++) System.out.print('-' ); System.out.println(); } public static void printCalender (int year, int month, int dayOfWeek, int lastDay) { System.out.printf("\t\t%d년 %d월\n" ,year,month); String week = "일월화수목금토" ; drawLine(50 ); for (int i = 0 ; i < 7 ; i++) System.out.printf("%c \t" , week.charAt(i)); System.out.println(); drawLine(50 ); drawLine(50 ); int cnt=0 ; for (int i = 0 ; i < dayOfWeek; i++) { System.out.print('\t' ); cnt++; } for (int d = 1 ; d <= lastDay; d++) { System.out.printf("%d \t" , d); if (cnt%7 ==6 ) System.out.println(); cnt++; } } public static int getDayOfWeek (int year, int month) { int totDays = (year-1 )*365 + (year-1 )/4 - (year-1 )/100 + (year-1 )/400 ; for (int i = 1 ; i < month; i++) { totDays+=getLastDay(year, i); } totDays+=1 ; return totDays%7 ; } public static int getLastDay (int year, int month) { int [] days = {31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31 }; return isleapyear(year) && month==2 ? 29 : days[month-1 ]; } public static boolean isleapyear (int year) { return year%100 !=0 && year%4 ==0 || year%400 ==0 ? true : false ; } }
출력
1 2 3 4 5 6 7 8 9 10 11 년 월 입력하세요: 2019 4 2019년 4월 -------------------------------------------------- 일 월 화 수 목 금 토 -------------------------------------------------- -------------------------------------------------- 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
모든 2019년에 해당하는 달력을 출력해보자(1~12 모두 출력) 3차원 배열을 사용해 출력해보자.
위 그림의 달력 형식을 보면 총 6행 7열까지 나올 수 있다. 위 사진의 경우 5행 7열이지만 1일이 일요일부터 시작한다던가 하는 경우 6줄이 출력될 수 있다.
그럼으로 int형 배열 [6][7]짜리 2차원 배열을 사용해야 1달을 출력할 수 있다.
12달을 출력하려면 [12][6][7] 3차월 배열이 필요
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 import java.util.Scanner;public class Calender { public static void main (String[] args) { Scanner sc = new Scanner (System.in); int year = sc.nextInt(); int [][][] dal = new int [12 ][6 ][7 ]; createDaliek(dal, year); printDaliek(dal, year, 4 ); } private static void createDaliek (int [][][] dal, int year) { int [] lastDays = new int [12 ]; int [] dayOfWeek = new int [12 ]; int day = 1 ; for (int i = 0 ; i < lastDays.length; i++) { lastDays[i] = getLastDay(year, i); dayOfWeek[i] = getDayOfWeek(year, i); } for (int i = 0 ; i < dal.length; i++) { day = 1 ; for (int j = 0 ; j < dal[0 ].length; j++) { for (int k = 0 ; k < dal[0 ][0 ].length; k++) { if (j * 7 + k < dayOfWeek[i] || day > lastDays[i]) dal[i][j][k] = -1 ; else dal[i][j][k] = day++; } } } } private static void printDaliek (int [][][] dal, int year, int num) { String week = "일월화수목금토" ; for (int z = 0 ; z < 12 / num; z++) { for (int i = 0 ; i < num; i++) { System.out.printf("\t%d년 %d월\t\t" , year, z * num + i + 1 ); } System.out.println(); for (int i = 0 ; i < num; i++) { drawLine(27 ); } System.out.println(); for (int i = 0 ; i < num; i++) { for (int p = 0 ; p < week.length(); p++) { System.out.printf("%c " , week.charAt(p)); } System.out.print("\t" ); } System.out.println(); for (int j = 0 ; j < 2 ; j++) { for (int i = 0 ; i < num; i++) { drawLine(27 ); } System.out.println(); } for (int i = 0 ; i < dal[0 ].length; i++) { for (int j = num * z; j < num + num * z; j++) { for (int k = 0 ; k < dal[0 ][0 ].length; k++) { if (dal[j][i][k] == -1 ) System.out.print(" " ); else System.out.printf("%2d " , dal[j][i][k]); } System.out.print("\t" ); } System.out.println(); } System.out.println(); } } public static void drawLine (int n) { for (int i = 0 ; i < n; i++) System.out.print('-' ); System.out.print("\t" ); } public static int getDayOfWeek (int year, int month) { int totDays = (year - 1 ) * 365 + (year - 1 ) / 4 - (year - 1 ) / 100 + (year - 1 ) / 400 ; for (int i = 0 ; i < month; i++) totDays += getLastDay(year, i); totDays += 1 ; return totDays % 7 ; } public static int getLastDay (int year, int month) { int [] days = {31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31 }; return isleapyear(year) && month == 1 ? 29 : days[month]; } public static boolean isleapyear (int year) { return year % 100 != 0 && year % 4 == 0 || year % 400 == 0 ? true : false ; } }
일단 3차원 배열로 저장해 놓았다면 출력에서 for문막 약간 변경시켜주면 된다.
출력
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 2019 2019년 1월 2019년 2월 2019년 3월 2019년 4월 --------------------------- --------------------------- --------------------------- --------------------------- 일 월 화 수 목 금 토 일 월 화 수 목 금 토 일 월 화 수 목 금 토 일 월 화 수 목 금 토 --------------------------- --------------------------- --------------------------- --------------------------- --------------------------- --------------------------- --------------------------- --------------------------- 1 2 3 4 5 1 2 1 2 1 2 3 4 5 6 6 7 8 9 10 11 12 3 4 5 6 7 8 9 3 4 5 6 7 8 9 7 8 9 10 11 12 13 13 14 15 16 17 18 19 10 11 12 13 14 15 16 10 11 12 13 14 15 16 14 15 16 17 18 19 20 20 21 22 23 24 25 26 17 18 19 20 21 22 23 17 18 19 20 21 22 23 21 22 23 24 25 26 27 27 28 29 30 31 24 25 26 27 28 24 25 26 27 28 29 30 28 29 30 31 2019년 5월 2019년 6월 2019년 7월 2019년 8월 --------------------------- --------------------------- --------------------------- --------------------------- 일 월 화 수 목 금 토 일 월 화 수 목 금 토 일 월 화 수 목 금 토 일 월 화 수 목 금 토 --------------------------- --------------------------- --------------------------- --------------------------- --------------------------- --------------------------- --------------------------- --------------------------- 1 2 3 4 1 1 2 3 4 5 6 1 2 3 5 6 7 8 9 10 11 2 3 4 5 6 7 8 7 8 9 10 11 12 13 4 5 6 7 8 9 10 12 13 14 15 16 17 18 9 10 11 12 13 14 15 14 15 16 17 18 19 20 11 12 13 14 15 16 17 19 20 21 22 23 24 25 16 17 18 19 20 21 22 21 22 23 24 25 26 27 18 19 20 21 22 23 24 26 27 28 29 30 31 23 24 25 26 27 28 29 28 29 30 31 25 26 27 28 29 30 31 30 2019년 9월 2019년 10월 2019년 11월 2019년 12월 --------------------------- --------------------------- --------------------------- --------------------------- 일 월 화 수 목 금 토 일 월 화 수 목 금 토 일 월 화 수 목 금 토 일 월 화 수 목 금 토 --------------------------- --------------------------- --------------------------- --------------------------- --------------------------- --------------------------- --------------------------- --------------------------- 1 2 3 4 5 6 7 1 2 3 4 5 1 2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 6 7 8 9 10 11 12 3 4 5 6 7 8 9 8 9 10 11 12 13 14 15 16 17 18 19 20 21 13 14 15 16 17 18 19 10 11 12 13 14 15 16 15 16 17 18 19 20 21 22 23 24 25 26 27 28 20 21 22 23 24 25 26 17 18 19 20 21 22 23 22 23 24 25 26 27 28 29 30 27 28 29 30 31 24 25 26 27 28 29 30 29 30 31