# Date์™€ Time

# 1. Date์™€ Time ์†Œ๊ฐœ

๋‚ ์งœ์™€ ์‹œ๊ฐ„์„ ํ‘œํ˜„ํ•˜๊ธฐ ์œ„ํ•ด Java์—์„œ ์‚ฌ์šฉํ•ด์™”๋‹ค.

# 1-1. Java8์— ์ƒˆ๋กœ์šด ๋‚ ์งœ์™€ ์‹œ๊ฐ„ API๊ฐ€ ๋“ฑ์žฅํ•œ ์ด์œ 

Date date = new Date();
        Calendar calendar = new GregorianCalendar();
        SimpleDateFormat dateFormat = new SimpleDateFormat();

Java 8 ์ด์ „ ์‚ฌ์šฉ๋˜๋Š” ๋‚ ์งœ์™€ ์‹œ๊ฐ„ API๋Š” Date์™€ Calendar, GregorianCalendar๋“ฑ์ด ์žˆ์Œ.

์ด๋ ‡๊ฒŒ ๊ธฐ์กด์— ์กด์žฌํ•˜๋˜ API๋“ค์€ ๋ฌธ์ œ์ ์ด ์žˆ์—ˆ๋Š”๋ฐ ํ•˜๋‚˜์”ฉ ์‚ดํŽด๋ณด์ž.

  1. ํด๋ž˜์Šค ์ด๋ฆ„์ด ๋ช…ํ™•ํ•˜์ง€๊ฐ€ ์•Š๋‹ค.

    • ๋‚ ์งœ ํด๋ž˜์Šค์ค‘ Date ๋Š” ์‹œ๊ฐ„๊ณผ TimeStamp ๋ชจ๋‘ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. (์‚ฌ์‹ค์ƒ TimeStamp)
    • ์‹œ๊ฐ„ ๊ฐ’์ด ์—ํญ์ž„ ์ด๋ผ ํ•˜์—ฌ ์„ธ๊ณ„ ํ‘œ์ค€์‹œ(UTC)๋กœ 1970๋…„ 1์›” 1์ผ 00์‹œ 00๋ถ„ 00์ดˆ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ํ˜„์žฌ๊นŒ์ง€ ํ๋ฅธ ๋ชจ๋“  ์‹œ๊ฐ„์„ ์ดˆ(sec)๋‹จ์œ„๋กœ ํ‘œํ˜„ (์‚ฌ๋žŒ์ด ์•Œ์•„๋ณด๊ธฐ ์–ด๋ ค์›€)
  2. Mutable ํ•˜์—ฌ thread safeํ•˜์ง€ ์•Š๋‹ค.

    public static void main(String[] args) throws InterruptedException {
        Date date = new Date();
        long time = date.getTime();
        System.out.println("date = " + date);
        Thread.sleep(1000 * 3);
        Date after3Seconds = new Date();
        System.out.println("after3Seconds = " + after3Seconds);
    
        after3Seconds.setTime(time);
        System.out.println("after3Seconds = " + after3Seconds);
    }
    
    /*
    [์‹คํ–‰ ๊ฒฐ๊ณผ]
    date = Thu Oct 28 20:22:24 KST 2021
    after3Seconds = Thu Oct 28 20:22:27 KST 2021
    after3Seconds = Thu Oct 28 20:22:24 KST 2021
    */
    
    • ์ตœ์ดˆ ์ฝ”๋“œ ์‹คํ–‰์‹œ ์‹œ๊ฐ„์„ ์ถœ๋ ฅ ํ›„ 3์ดˆ ํ›„์— ๋‹ค์‹œ Date ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•ด ์ถœ๋ ฅํ–ˆ๋‹ค.
    • ๊ทธ ๋‹ค์Œ setTime(time)์œผ๋กœ ์ตœ์ดˆ ์ƒ์„ฑํ•œ date๋ฅผ ์ƒˆ๋กœ ์ƒ์„ฑํ•œ after3Seconds์— set ํ•ด ์ค€๋’ค ์ถœ๋ ฅํ•˜๋‹ˆ ์ตœ์ดˆ์— ์ƒ์„ฑํ–ˆ์„ ๋‹น์‹œ์˜ ์‹œ๊ฐ„์ด ์ถœ๋ ฅ๋œ๋‹ค.
    • ์ด๋Š” Date ํด๋ž˜์Šค๊ฐ€ mutable ํ•˜๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธ
    • thread unsafe : Date ์ธ์Šคํ„ด์Šค์˜ ๊ฐ’์„ ๊ฐ๊ฐ ๋‹ค๋ฅธ Thread์—์„œ ์ ‘๊ทผํ•ด์„œ ๋ณ€๊ฒฝ์ด ๊ฐ€๋Šฅํ•˜๋ฉด ๊ธฐ์กด์— ์‚ฌ์šฉํ•˜๋˜ Thread์—์„œ ๋ณ€๊ฒฝ ๋˜์–ด ์ž˜๋ชป๋œ Date ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์™€์„œ ๋ฒ„๊ทธ๊ฐ€ ๋ฐœ์ƒํ•  ์œ„ํ—˜์ด ์žˆ๋‹ค๋Š” ๋œป.
  3. ๋ฒ„๊ทธ ๋ฐœ์ƒํ•  ์—ฌ์ง€๊ฐ€ ๋งŽ๋‹ค.

    • Thread unsafe ํ•  ๋ฟ ์•„๋‹ˆ๋ผ ์‚ฌ์šฉ๋ฒ• ์ž์ฒด์—์„œ๋„ ์‚ฌ์šฉ๋ฒ•์— ๋Œ€ํ•ด ์˜คํ•ดํ•  ์ˆ˜ ์žˆ์–ด ๋ฒ„๊ทธ๊ฐ€ ๋ฐœ์ƒํ•  ์—ฌ์ง€๊ฐ€ ์žˆ๋‹ค.
    Calendar birthDay = new GregorianCalendar(1988, 6, 10);
    
    • ์ƒ์ผ์ด 1988๋…„ 6์›” 10์ผ์ž„์„ ํ‘œํ˜„ํ•˜๊ณ  ์‹ถ์ง€๋งŒ, GregorianCalendar์—์„œ month๋Š” 0๋ถ€ํ„ฐ์‹œ์ž‘ํ•˜๊ธฐ ๋•Œ๋ฌธ์— 6์„ ๋„ฃ์œผ๋ฉด 7์›”์ด๋ผ๋Š” ์˜๋ฏธ๊ฐ€ ๋œ๋‹ค.
    • ํ˜ผ๋™ํ•˜์ง€ ์•Š๊ธฐ ์œ„ํ•ด์„œ ์ƒ์ˆ˜ ๊ฐ’์„ ์“ฐ๊ณค ํ–ˆ๋‹ค. (ex: Calendar.JUNE)
  4. ๊ทธ๋ž˜์„œ Java 8 ์ด์ „์—๋Š” Joda-Time (opens new window) ์„ ์‚ฌ์šฉํ–ˆ๋‹ค.

# 1-2. Java ๋‚ ์งœ ๋ฐ ์‹œ๊ฐ„ API๊ฐ€ ๊ฐ€์ ธ์•ผํ•  ๋””์ž์ธ ์ฒ ํ•™

  1. Clear

    • API ๋ฉ”์†Œ๋“œ๋Š” ๋ช…ํ™•ํ•ด์•ผํ•œ๋‹ค.
    • ์˜ˆ๋ฅผ๋“ค์–ด ํด๋ž˜์Šค๋ช…์ด Date์ง€๋งŒ ๋‚ ์งœ ๋ฟ ์•„๋‹ˆ๋ผ ์‹œ๊ฐ„(Time)๊นŒ์ง€ ๋‹ค๋ฃจ๊ฒŒ๋˜๋ฉด ๋ช…ํ™•ํ•˜์ง€ ์•Š๋‹ค.
    • ๋”ํ•˜์—ฌ ์‹œ๊ฐ„(Time)์—ญ์‹œ ์‚ฌ๋žŒ์—๊ฒŒ ์ต์ˆ™ํ•œ ์ผ๋ฐ˜ ์‹œ๊ฐ„์ด ์•„๋‹Œ ์—ํญํƒ€์ž„์ธ ๊ฒƒ์€ Clearํ•˜์ง€ ์•Š๋‹ค.
  2. Fluent

    • ๋ฉ”์†Œ๋“œ๊ฐ€ null์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์— ๋ฉ”์†Œ๋“œ ์ฒด์ด๋‹์ด ๊ฐ€๋Šฅํ•˜๊ธฐ์— ์ฝ”๋“œ๊ฐ€ ์œ ๋ คํ•ด์ง„๋‹ค.
  3. Immutable

    • ๊ธฐ์กด ๋‚ ์งœ ์ธ์Šคํ„ด์Šค์˜ ๋‚ด์šฉ์ด ๋ณ€ํ•˜์ง€ ์•Š์œผ๋ฉฐ ๋ณ€๊ฒฝ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ์‹œ ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ ์ƒˆ๋กœ์šด ๋‚ ์งœ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•ด ๋ฐ˜ํ™˜ํ•ด์•ผํ•œ๋‹ค.
    LocalDate today = LocalDate.of(2021, Month.OCTOBER, 28);
    LocalDate nextMonth = today.plusMonths(1);
    
  4. Extensible

    • ํ™•์žฅ ๊ฐ€๋Šฅํ•ด์•ผ ํ•œ๋‹ค.
    • ๋‹ฌ๋ ฅ์—๋Š” ์œค๋‹ฌ, ์Œ๋ ฅ, ์–‘๋ ฅ, ๋ถˆ๊ต๋‹ฌ๋ ฅ ๋“ฑ ๋‹ค์–‘ํ•œ ์ข…๋ฅ˜์˜ ๋‹ฌ๋ ฅ์˜ ํ™•์žฅ์ด ๊ฐ€๋Šฅํ•ด์•ผ ํ•œ๋‹ค.

# 1-3. ์ฃผ์š” API ํŠน์ง•*

  1. ๊ธฐ๊ณ„์šฉ ์‹œ๊ฐ„(machine time)๊ณผ ์ธ๋ฅ˜์šฉ ์‹œ๊ฐ„(human time)์œผ๋กœ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ๋‹ค.
  2. ๊ธฐ๊ณ„์šฉ ์‹œ๊ฐ„์€ EPOCK(1970๋…„ 1์›” 1์ผ 0์‹œ 0๋ถ„ 0์ดˆ)๋ถ€ํ„ฐ ํ˜„์žฌ๊นŒ์ง€์˜ ํƒ€์ž„์Šคํƒฌํ”„๋ฅผ ํ‘œ์‹œํ•œ๋‹ค.
  3. ์ธ๋ฅ˜์šฉ ์‹œ๊ฐ„์€ ์šฐ๋ฆฌ๊ฐ€ ํ”ํžˆ ์‚ฌ์šฉํ•˜๋Š” ๋…„, ์›”, ์ผ, ์‹œ, ๋ถ„, ์ดˆ ๋“ฑ์„ ๋งํ•œ๋‹ค.
  4. ํƒ€์ž„์Šคํƒฌํ”„๋Š” Instant๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
  5. ํŠน์ • ๋‚ ์งœ(LocalDate), ์‹œ๊ฐ„(LocalTime), ์ผ์‹œ(LocalDateTime)๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  6. ๊ธฐ๊ฐ„์„ ํ‘œํ˜„ํ•  ๋•Œ๋Š” Duration(์‹œ๊ฐ„ ๊ธฐ๋ฐ˜)๊ณผ Period(๋‚ ์งœ ๊ธฐ๋ฐ˜)๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  7. DateTimeFormatter๋ฅผ ์‚ฌ์šฉํ•ด ์ผ์‹œ๋ฅผ ํŠน์ •ํ•œ ๋ฌธ์ž์—ด๋กœ ํฌ๋งคํŒ… ํ•  ์ˆ˜ ์žˆ๋‹ค.

# 2. Date์™€ Time API

๊ธฐ๊ณ„์ ์ธ ์‹œ๊ฐ„๊ณผ ์ธ๋ฅ˜์šฉ ์‹œ๊ฐ„์„ ์‚ฌ์šฉ&ํ‘œํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•

# 2-1. ๊ธฐ๊ณ„์ ์ธ ์‹œ๊ฐ„

  1. Instant.now(): ํ˜„์žฌ UTC(GMT)๋ฅผ ๋ฐ˜ํ™˜
  2. Universal Time Coordinated == Greenwich Mean Time
public static void main(String[] args) throws InterruptedException {
    Instant instant = Instant.now();//๊ธฐ์ค€์‹œ UTC (GMT)
    System.out.println("instant = " + instant);

    ZoneId zone = ZoneId.systemDefault();
    System.out.println("zone = " + zone);
    ZonedDateTime zonedDateTime = instant.atZone(zone);
    System.out.println("zonedDateTime = " + zonedDateTime);
}

/*
instant = 2021-10-28T11:59:22.897293900Z
zone = Asia/Seoul
zonedDateTime = 2021-10-28T20:59:22.897293900+09:00[Asia/Seoul]
*/
  1. ZoneId.systemDefault();
    • ํ˜„์žฌ ์‹œ์Šคํ…œ์ƒ์˜ zone ์ •๋ณด๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. (ex: Asia/Seoul)
  2. instant.atZone(zone);
    • UTC ๊ธฐ์ค€์ด์•„๋‹Œ zone ์˜ ์œ„์น˜ ๊ธฐ๋ฐ˜์˜ ์‹œ๊ฐ„์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

# 2-2. ์ธ๋ฅ˜์šฉ ์‹œ๊ฐ„

  1. LocalDate์™€ LocalDateTime์ด ์žˆ๋‹ค.
    • LocalDate: ๋…„/์›”/์ผ ๊นŒ์ง€ ํ‘œํ˜„ํ•  ๋•Œ ์‚ฌ์šฉ
    • LocalDateTime: ๋…„/์›”/์ผ/์‹œ/๋ถ„/์ดˆ ๊นŒ์ง€ ํ‘œํ˜„ํ•  ๋•Œ ์‚ฌ์šฉ
public static void main(String[] args) {
		LocalDateTime now = LocalDateTime.now();
    System.out.println("now = " + now);
    LocalDateTime birthDay = LocalDateTime.of(1988, Month.JUNE, 10, 0, 0, 0);
    ZonedDateTime nowInUTC = ZonedDateTime.now(ZoneId.of("UTC"));
    System.out.println("nowInUTC = " + nowInUTC);
    ZonedDateTime birthDayByUTC = ZonedDateTime.of(1988, Month.JUNE.getValue(),10,0,0,0,0, ZoneId.of("UTC"));
    System.out.println("birthDayByUTC = " + birthDayByUTC);
}
  1. LocalDateTime.now()
    • ํ˜„์žฌ ์‹œ์Šคํ…œ Zone์— ํ•ด๋‹นํ•˜๋Š”(๋กœ์ปฌ) ์ผ์‹œ๋ฅผ ๋ฐ˜ํ™˜
  2. LocalDateTime.of(1988, Month.JUNE, 10, 0, 0, 0);
    • ๋กœ์ปฌ์˜ ํŠน์ • ์ผ์‹œ๋ฅผ ๋ฐ˜ํ™˜
  3. ZonedDateTime.now(ZoneId.of("UTC"));
    • ํŠน์ • Zone์˜ ํ˜„์žฌ ์‹œ๊ฐ„์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  4. ZonedDateTime.of(1988, Month.JUNE.getValue(),10,0,0,0,0, ZoneId.of("UTC"));
    • ํŠน์ • Zone์˜ ํŠน์ • ์ผ์‹œ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

# 2-3. ๊ธฐ๊ฐ„์„ ํ‘œํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•

  1. Period
    • ๋‚ ์งœ ๊ธฐ๊ฐ„ ๋‹จ์œ„๋ฅผ ํ‘œํ˜„ํ•  ๋•Œ ์‚ฌ์šฉ
  2. Duration
    • ์‹œ๊ฐ„ ๋‹จ์œ„ ๊ธฐ๊ฐ„์„ ํ‘œํ˜„ํ•  ๋•Œ ์‚ฌ์šฉ
public static void main(String[] args) {
    LocalDate today = LocalDate.now();
    LocalDate birthDay = LocalDate.of(2020, Month.JUNE, 10);

    Period period = Period.between(birthDay, today);
    System.out.println(period.getMonths()+"๊ฐœ์›” "+period.getDays()+"์ผ");

    Period until = today.until(birthDay);
    System.out.println("until.get(ChronoUnit.DAYS) = " + until.get(ChronoUnit.DAYS));

    Instant now = Instant.now();
    Instant plus = now.plus(10, ChronoUnit.SECONDS);
    Duration duration = Duration.between(now, plus);
    System.out.println(duration.getSeconds());
}
  1. Period.between(birthDay, today)
    • ์˜ฌํ•ด ์ƒ์ผ๋‚ ์งœ์™€ ์˜ค๋Š˜๊ณผ ๋น„๊ตํ•˜์—ฌ Periodํƒ€์ž…์˜ ์ธ์Šคํ„ด์Šค๋กœ ๋ฐ˜ํ™˜
  2. Period until = today.until(birthDay)
    • LocalDate ๊ฐ์ฒด์˜ until ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด ์ „๋‹ฌํ•œ ์ธ์ž๊ฐ’๊ณผ ์ธ์Šคํ„ด์Šค์˜ ๊ธฐ๊ฐ„์„ ๊ณ„์‚ฐํ•œ Period ํƒ€์ž…์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐ˜ํ™˜
  3. Duration.between(now, plus)
    • ํ˜„์žฌ์‹œ๊ฐ„๊ณผ 10์ดˆ ๋’ค์˜ ์‹œ๊ฐ„์„ ์ธ์Šคํ„ด์Šค๋กœ ๋งŒ๋“  ๋’ค ๋น„๊ตํ•˜์—ฌ Duration ํƒ€์ž…์˜ ์ธ์Šคํ„ด์Šค๋กœ ๋ฐ˜ํ™˜

# 2-4. ํŒŒ์‹ฑ ๋˜๋Š” ํฌ๋งคํŒ…

  1. LocalDateTime์„ ๊ฐ€์ง€๊ณ  ๋ฌธ์ž์—ด์„ LocalDateTime์œผ๋กœ ํ˜น์€ ๋ฐ˜๋Œ€๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š” ๋ฐฉ๋ฒ•.
  2. LocalDateTime.parse(String, DateTimeFormatter);
  3. ๊ธฐ๋ณธ ์ •์˜ ํฌ๋งทํŒ… ์ƒ์ˆ˜์ •๋ณด์™€ ํŒจํ„ด์ •๋ณด ์ฐธ์กฐ (opens new window)
public static void main(String[] args) {
    LocalDateTime now = LocalDateTime.now();
    DateTimeFormatter MMddyyyy = DateTimeFormatter.ofPattern("MM/dd/yyyy");
    System.out.println(now.format(DateTimeFormatter.BASIC_ISO_DATE));
    System.out.println(now.format(MMddyyyy));

    LocalDate birthDay = LocalDate.parse("06/10/1988", MMddyyyy);
    System.out.println(birthDay);
}
  1. DateTimeFormatter.ofPattern("MM/dd/yyyy")
    • DateTimeFormatter๋ฅผ ์ด์šฉํ•ด ๊ฐ์ข… ํ˜•์‹์œผ๋กœ LocalDateTime์„ ํฌ๋งทํŒ… ํ•ด์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ofPattern("MM/dd/yyy")๋Š” ์‚ฌ์šฉ์ž ์ •์˜ ํŒจํ„ด์œผ๋กœ ํฌ๋งทํŒ…์„ ํ•˜๊ฒ ๋‹ค๋Š” ์˜๋ฏธ์ž…๋‹ˆ๋‹ค.
  2. now.format(DateTimeFormatter.BASIC_ISO_DATE)
    • ์‹œ๊ฐ„์„ DateTimeFormatter๊ฐ€ ์ง€์›ํ•˜๋Š” BASIC_ISO_DATEํ˜•ํƒœ๋กœ ํฌ๋งทํŒ…
  3. now.format(MMddyyyy)
    • ์œ„์—์„œ ofPattern์œผ๋กœ ์ง€์ •ํ•œ ํŒจํ„ด์œผ๋กœ ์‹œ๊ฐ„์„ ํฌ๋งคํŒ…
  4. LocalDate.parse("06/10/1988", MMddyyyy)
    • ๋ฌธ์ž์—ด์„ ofPattern์—์„œ ์„ ์–ธํ•œ ํŒจํ„ด ๋ฐฉ์‹์œผ๋กœ ํŒŒ์‹ฑํ•˜์—ฌ LocalDate ํƒ€์ž…์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•ด ๋ฐ˜ํ™˜

# 2-5. ๋ ˆ๊ฑฐ์‹œ API ์ง€์›

์˜ˆ์ „์— ๊ตฌํ˜„ ๋ฐ ์‚ฌ์šฉํ•˜๋˜ ๋‚ ์งœ์™€ ์‹œ๊ฐ„(Date) API์™€ ํ˜„์žฌ ์ถ”๊ฐ€๋œ LocalDate, LocalDateTime, Instant๋Š” ์„œ๋กœ ํ˜ธํ™˜ ๋œ๋‹ค!

public static void main(String[] args) {
    Date date = new Date();
    Instant instant = date.toInstant();
    Date newDate = Date.from(instant);

    GregorianCalendar gregorianCalendar = new GregorianCalendar();
    LocalDateTime dateTime = gregorianCalendar.toInstant()
            .atZone(ZoneId.systemDefault())
            .toLocalDateTime();
    ZonedDateTime zonedDateTime = ZonedDateTime.from(dateTime);

    GregorianCalendar from = GregorianCalendar.from(zonedDateTime);

    ZoneId zoneId = TimeZone.getTimeZone("PST").toZoneId();
    TimeZone timeZone = TimeZone.getTimeZone(zoneId);
}
  1. GregorianCalendar์™€ Date ํƒ€์ž…์˜ ์ธ์Šคํ„ด์Šค๋ฅผ Instant๋‚˜ ZonedDateTime์œผ๋กœ ๋ณ€ํ™˜
  2. java.util.TimeZone์—์„œ java.time.ZoneId๋กœ ์ƒํ˜ธ ๋ณ€ํ™˜ ๊ฐ€๋Šฅ

# Reference

Last Updated: 6/18/2023, 2:13:15 PM