๐Ÿ“… ๋‚ ์งœ์™€ ์‹œ๊ฐ„์— ๋Œ€ํ•ด์„œ ๐Ÿ•›

Davidยท2024๋…„ 3์›” 30์ผ
0
post-thumbnail

๐Ÿ—ฃ ํ”„๋กœํ•„ ์ˆ˜์ •์‹œ์— ๊ฐ€๋” ๋‚ ์งœ๊ฐ€ ๋ฐ”๋€๋‹ˆ๋‹ค


๋™๋ฃŒ ๋ถ„๊ป˜์„œ ๋ฆฌํฌํŒ…์„ ํ•ด์ฃผ์…จ๋Š”๋ฐ
ํ”„๋กœํ•„ ์ˆ˜์ • ์‹œ์— ๋‚ ์งœ๊ฐ€ ๊ฐ„ํ˜ˆ์ ์œผ๋กœ
๋‹ค๋ฅด๊ฒŒ ์…‹ํŒ… ๋˜์–ด ์žˆ๋‹ค๋Š” ์ œ๋ณด์˜€์Šต๋‹ˆ๋‹ค.

์ƒํ™ฉ์„ ๋“ค์–ด๋ณด๋‹ˆ ์‹œ๊ฐ„์— ๋”ฐ๋ผ์„œ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ ๊ฐ™์•„
์˜์‹ฌ๊ฐ€๋Š” ์ƒํ™ฉ์œผ๋กœ ์žฌํ˜„์„ ํ•ด๋ณด๋‹ˆ ์„ฑ๊ณต ํ–ˆ์Šต๋‹ˆ๋‹ค.

2024๋…„ 1์›” 1์ผ ์ƒ์ผ > ํ”„๋กœํ•„ ์ˆ˜์ • > ๐Ÿ› 2023๋…„ 12์›” 31์ผ?!

๐Ÿ“… ๋‚ ์งœ ๊ตฌํ˜„ ์‹œ

์„œ๋ฒ„๋Š” ISO8601 ํ˜•์‹์œผ๋กœ ๋‚ ์งœ๋ฅผ ๋‚ด๋ ค์ฃผ๊ณ 
์•ฑ์€ Material3 > DatePickerDialog ๋กœ ๊ตฌํ˜„ ํ–ˆ์Šต๋‹ˆ๋‹ค.

DatePickerDialog ๊ตฌํ˜„ ์‹œ
์ปดํฌ์ฆˆ๋‹ต๊ฒŒ ๋ฐ์ดํ„ฐ๋Š” ์ƒํƒœ๋กœ ๊ด€๋ฆฌํ•˜๋Š”๋ฐ์š”.

rememberDatePickerState๋กœ
์—ฐ๋„ ๋‚ ์งœ๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ”„๋กœํ•„ ์ˆ˜์ • ์‹œ > ๊ธฐ์กด์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ์กด์žฌํ•˜๋ฏ€๋กœ
initialSelectedDateMillis: Long ์˜
๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๊ณ  Calandar๋กœ ํŒŒ์‹ฑ ํ›„
time์„ ๋„˜๊ฒผ์—ˆ์Šต๋‹ˆ๋‹ค.


๐Ÿ•• ๊ตญ์ œ ํ‘œ์ค€๊ณผ API์˜ ์ดํ•ด


์œ„์˜ ์ƒํ™ฉ์„ ์ดํ•ดํ•˜๊ธฐ ์œ„ํ•ด์„œ
rememberDatePickerState๋ฅผ ์ดํ•ดํ–ˆ์–ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.
(์ •ํ™•ํžˆ๋Š” initialSelectedDateMillis ํŒŒ๋ผ๋ฏธํ„ฐ)

์•„๋ž˜๋Š” ํ•ด๋‹น ํ•จ์ˆ˜์˜ ์ฃผ์„์ž…๋‹ˆ๋‹ค.

ํ•ด๋‹น ์ฃผ์„์„ ๋ณด๊ณ  timestamp, UTC, epoch์— ๋Œ€ํ•ด
์ƒ์†Œํ•ด์„œ ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์—†์—ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ๋‚ ์งœ์™€ ์‹œ๊ฐ„์— ๋Œ€ํ•ด์„œ
์ดํ•ดํ•œ ๋‚ด์šฉ์„ ์ •๋ฆฌํ•˜๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿค UTC

ํ˜‘์ • ์„ธ๊ณ„์‹œ(ๅ”ๅฎšไธ–็•Œๆ™‚, ์˜์–ด: Coordinated Universal Time/Universal Time Coordinated, UTC)๋Š” 1972๋…„ 1์›” 1์ผ๋ถ€ํ„ฐ ์‹œํ–‰๋œ โœŒ๐Ÿป๊ตญ์ œ ํ‘œ์ค€์‹œโœŒ๐Ÿป

๐Ÿ“Œ UTC ํ‘œ๊ธฐ ๋ฐฉ๋ฒ•

2024-03-30T09:00Z ๋˜๋Š” 20240330T0900Z

  • UTC ์‹œ๊ฐ„๋Œ€์—์„œ์˜ 2024๋…„ 3์›” 30์ผ ์˜ค์ „ 9์‹œ
  • Z๋Š”(Zulu) UTC ์‹œ๊ฐ„์„ ๋‚˜ํƒ€๋‚ด๋Š” ์ถ•์•ฝ๋ฌธ์ž์—ด
    • Z ๋ถ™์–ด ์žˆ์œผ๋ฉด ํ•ด๋‹น ์‹œ๊ฐ„์€ UTC ๊ธฐ์ค€
  • T๋Š” ISO-8601 ํ˜•์‹์—์„œ ์‹œ๊ฐ„์‚ฌ์ด์˜ ๊ตฌ๋ถ„์ž(Delimiter for the time)

๐Ÿ‘จ๐Ÿปโ€๐Ÿ’ป API(Java) ์— ๋Œ€ํ•œ ์ดํ•ด

์šฐ์„  ํฐ ํ‹€์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋‚˜๋ˆ„๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค.


๐Ÿ’๐Ÿป ์‚ฌ๋žŒ์ด ์ดํ•ดํ•˜๊ธฐ ์‰ฌ์šด API

๋งํฌํ•œ ๋ฌธ์„œ์˜ ๋งํฌ๋กœ ๊ฐ€์‹œ๋ฉด
๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ณตํ†ต์ ๊ณผ ์ฐจ์ด์ ์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

๊ณตํ†ต์ 

  • ISO8601 calendar system ์— ์˜๊ฑฐํ•œ๋‹ค.
  • immutable ํ•˜๋‹ค.
  • thread-safe ํ•˜๋‹ค.
  • ์‹œ๊ฐ„์„ ์œ ์šฉํ•˜๊ฒŒ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ๋‹ค.

์ฐจ์ด์ 

  • time-zone ํฌํ•จ ์—ฌ๋ถ€

์—ฌ๊ธฐ์„œ ์‹œ๊ฐ„์— ๋Œ€ํ•ด ์ดํ•ด๊ฐ€ ์กฐ๊ธˆ ๋” ํ•„์š” ํ•ฉ๋‹ˆ๋‹ค.
ํ˜„์žฌ ํ•œ๊ตญ์˜ ์‹œ๊ฐ„๊ณผ ํŒŒ๋ฆฌ์˜ ์‹œ๊ฐ„์ด ๋™์ผํ•œ๊ฐ€์š”?
๊ทธ๋ ‡์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

ํ•œ๊ตญ์€ UTC๋ฅผ ๊ธฐ์ค€์œผ๋กœ +9 ์‹œ๊ฐ„(์˜คํ”„์…‹)์„ ํ•˜๋ฉด ํ•œ๊ตญ ์‹œ๊ฐ„์ž…๋‹ˆ๋‹ค.

ํŒŒ๋ฆฌ๋Š” +1 ์‹œ๊ฐ„(์˜คํ”„์…‹)์„ ํ•˜๋ฉด ๋˜์ง€๋งŒ
ํ•ด๋‹น ๊ตญ๊ฐ€๋Š” Summer Time์ด ์กด์žฌ ํ•ฉ๋‹ˆ๋‹ค.
์ด ๋•Œ๋Š” +2์‹œ๊ฐ„์˜(์˜คํ”„์…‹) ์ ์šฉ ๋ฉ๋‹ˆ๋‹ค.
๊ทธ๋ž˜์„œ ํŒŒ๋ฆฌ๋Š” ์‹œ๊ฐ„(์˜คํ”„์…‹)์„ ๋”ํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค > ์ง€์—ญID์— ๋”ฐ๋ผ ์ฒ˜๋ฆฌ > TimeZoneId

์œ„์˜ ๋‚ด์šฉ์„ ์ •๋ฆฌํ•ด๋ณด๋ฉด Time API ์—์„œ

  • ๊ณ ์ •๋œ ์‹œ๊ฐ„์ด๋ผ๋ฉด ์˜คํ”„์…‹ > ZoneOffset ์œผ๋กœ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ
  • ์„œ๋จธ ํƒ€์ž„๊ณผ ๊ฐ™์€ ๋ณต์žกํ•œ ์‹œ๊ฐ„ ๊ณ„์‚ฐ์˜ ๊ฒฝ์šฐ > TimeZoneId ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ

๊ทธ๋Ÿฌ๋ฉด ZoneId ์™€ ZoneOffset ์€ ๊ฐ™์€ ๊ฐœ๋…์€ ์•„๋‹ˆ์ง€๋งŒ
๊ตญ๊ฐ€๋ณ„ ์‹œ๊ฐ„ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด์„œ๋Š”
ZoneId๋ฅผ ํฌ๊ด„ํ•˜๋Š” ๊ฐœ๋…์œผ๋กœ
์œ ์—ฐํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒ ์Šต๋‹ˆ๋‹ค.

ZoneId ๋Š” ๐Ÿ’๐Ÿป TimeZones.html ์—์„œ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์‹œ๊ฐ„์— ๋Œ€ํ•œ ๊ฐœ๋…์€ ์ด ์ •๋„๋กœ ์ •๋ฆฌํ•˜๊ณ 
์ด์ œ API๋ฅผ ์–ธ์ œ ์‚ฌ์šฉํ•ด์•ผํ• ์ง€
์‚ฌ์šฉํ•˜๋Š” ๋ฒ•์„ ์ •๋ฆฌํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์šฐ์„  ๋Š์–ด์„œ ์ดํ•ดํ•˜๋ฉด ์กฐ๊ธˆ ๋” ์ง๊ด€์ ์ž…๋‹ˆ๋‹ค.

  • Local - ํƒ€์ž„์กด ๊ฐœ๋…์ด ํ•„์š”์—†๋Š” Local ์ •๋ณด
  • Date - ๋‚ ์งœ
  • Time - ์‹œ๊ฐ„
  • DateTime - ๋‚ ์งœ+์‹œ๊ฐ„
  • Zoned - ํƒ€์ž„์กด ๊ฐœ๋…์ด ํ•„์š”ํ•œ

๋ถ™์—ฌ์„œ ๋ณด๋ฉด ์ด์ œ ์ดํ•ด๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

  • LocalDate: ํƒ€์ž„์กด ๊ฐœ๋…์ด ํ•„์š”์—†๋Š” ๋‚ ์งœ ์„ค์ • ๊ด€๋ จ API
  • LocalTime: ํƒ€์ž„์กด ๊ฐœ๋…์ด ํ•„์š”์—†๋Š” ์‹œ๊ฐ„ ์„ค์ • ๊ด€๋ จ API
  • LocalDateTIme: ํƒ€์ž„์กด ๊ฐœ๋…์ด ํ•„์š”์—†๋Š” Date + Time API
  • ZonedDateTime: ํƒ€์ž„์กด ๊ฐœ๋…์ด ํ•„์š”ํ•œ Date + Time API

์œ„์˜ API๋“ค์€ ์‹œ๊ฐ„/๋‚ ์งœ ์ฒ˜๋ฆฌ ๋ฐ ํŒŒ์‹ฑ์„ ์šฐ๋ฆฌ๊ฐ€ ์‚ฌ์šฉํ•˜๊ธฐ ์‰ฝ๊ฒŒ ์ œ๊ณต์„ ํ•ด์ค๋‹ˆ๋‹ค.
ํฌ๊ฒŒ ์ƒ์„ฑ, ์ฝ๊ธฐ, ์กฐ์ž‘, ๋ณ€๊ฒฝ์œผ๋กœ ๋ณผ ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๐Ÿ“Œ ์ƒ์„ฑ

  • now(), of()๋ฅผ ํ†ตํ•ด ์ƒ์„ฑ์ด ๊ฐ€๋Šฅ
fun main() {
    val localDateTime = LocalDateTime.now(ZoneId.systemDefault())
    println("localDateTime: $localDateTime")

    val localTime = LocalTime.now()
    println("localTime: $localTime")

    val zonedDateTime = ZonedDateTime.now()
    println("zonedDateTIme: $zonedDateTime")

    val myBirthDateTime = Year.of(1991).atMonth(12).atDay(2).atTime(20,0)
    println("myBirthDateTime: $myBirthDateTime")
}

// result
localDateTime: 2024-04-07T18:43:17.322065
localTime: 18:43:17.326843
zonedDateTIme: 2024-04-07T18:43:17.327536+09:00[Asia/Seoul]
myBirthDateTime: 1991-12-02T20:00

๐Ÿ“Œ ์ฝ๊ธฐ

  • get ์œผ๋กœ ์‹œ์ž‘ํ•˜๋Š” ํ•จ์ˆ˜ > ์—ฐ๋„, ๋‚ ์งœ, ์‹œ๊ฐ„, ์š”์ผ ๋“ฑ๋“ฑ
fun main() {
    val localDateTime = LocalDateTime.now(ZoneId.systemDefault())
    println("localDateTime > get Time: ${localDateTime.hour}")

    val localTime = LocalTime.now()
    println("localTime > get Minute: ${localTime.minute}")

    val myBirthDate = Year.of(1991).atMonth(12).atDay(2).atTime(20,0)
    val currentZonedDateTime = ZonedDateTime.now()
    println("zonedDateTIme > DayOfWeek : ${currentZonedDateTime.dayOfWeek}")
    println("zonedDateTime > is After myBirthDate : ${currentZonedDateTime.isAfter(myBirthDate.atZone(ZoneId.systemDefault()))}")
}


// result
localDateTime > get Time: 18
localTime > get Minute: 55
currentZonedDateTime > DayOfWeek : SUNDAY
currentZonedDateTime > is After myBirthDate : true

๐Ÿ“Œ ์กฐ์ž‘

  • plus, minus ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด์„œ ์‹œ๊ฐ„ ๋‚ ์งœ ๋“ฑ์„ ์‰ฝ๊ฒŒ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ

๐Ÿ“Œ ๋ณ€๊ฒฝ(ํŒŒ์‹ฑ)

  • parse() ์™€ format() ๋ฅผ ํ†ตํ•ด ํŒŒ์‹ฑ ๊ฐ€๋Šฅ
fun main() {
    val currentDateTimeUTC = "2024-03-30T20:20:20Z"
    val parsedZonedDateTime = utcToZonedDateTime(currentDateTimeUTC)
    val korZonedDateTime = parsedZonedDateTime.withZoneSameInstant(ZoneId.systemDefault())
    println("korZonedDateTime: $korZonedDateTime") 
    
    // ์›ํ•˜๋Š” ํฌ๋งท์œผ๋กœ ์„ค์ •
    val fullLocalDateTimeFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL)
    println("korZonedDateTimeFullFormat: ${korZonedDateTime.format(fullLocalDateTimeFormatter)}")

}

private fun utcToZonedDateTime(utcTime: String): ZonedDateTime {
    return ZonedDateTime.parse(utcTime)
}


// ๊ฒฐ๊ณผ
korZonedDateTime: 2024-03-31T05:20:20+09:00[Asia/Seoul]
korZonedDateTimeFullFormat: 2024๋…„ 3์›” 31์ผ ์ผ์š”์ผ ์˜ค์ „ 5์‹œ 20๋ถ„ 20์ดˆ ๋Œ€ํ•œ๋ฏผ๊ตญ ํ‘œ์ค€์‹œ
  • ๋‚ ์งœ์™€ ์‹œ๊ฐ„์˜ ํฌ๋งท์„ ์ง€์ •ํ•˜์—ฌ ํŒŒ์‹ฑ
fun main() {
    val currentDateTimeUTC = "2024-03-30T20:20:20Z"
    val systemZonedDateTime = utcToSystemZonedDateTime(currentDateTimeUTC)
    println("systemZonedDateTime: $systemZonedDateTime")

    val formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG, FormatStyle.SHORT) // Here ๐Ÿ“Œ
    println("koreaStyleZonedDateTime: ${systemZonedDateTime.format(formatter)}")
}



private fun utcToSystemZonedDateTime(utcTime: String): ZonedDateTime {
    val instant = Instant.parse(utcTime)
    return ZonedDateTime.ofInstant(instant, ZoneId.systemDefault())
}


// ๊ฒฐ๊ณผ
systemZonedDateTime: 2024-03-31T05:20:20+09:00[Asia/Seoul]
koreaStyleZonedDateTime: 2024๋…„ 3์›” 31์ผ ์˜ค์ „ 5:20

๐Ÿ’๐Ÿป Instant API

This class models a single instantaneous point on the time-line. This might be used to record event time-stamps in the application

์œ„์˜ ์„ค๋ช…์„ ๋ณด๋ฉด Instant ํด๋ž˜์Šค๋Š”
์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์‹œ๊ฐ„์„ ํƒ€์ž„์Šคํƒฌํ”„๋กœ ๋‹ค๋ฃจ๊ธฐ ์œ„ํ•ด์„œ
์‚ฌ์šฉ๋˜๋Š” API ๋ผ๊ณ  ๋˜์–ด์žˆ์Šต๋‹ˆ๋‹ค.

์œ„์— epoch ๋ผ๋Š” ๊ฐœ๋…์ด ์—ฌ๊ธฐ์„œ ๋“ฑ์žฅํ•˜๋Š”๋ฐ์š”

The epoch-seconds are measured from the standard Java epoch of 1970-01-01T00:00:00Z where instants after the epoch have positive values, and earlier instants have negative values.

์„ค๋ช…์„ ๋ณด๋ฉด 1970-01-01T00:00:00Z(Z๊ฐ€ ๋ถ™์€๊ฑธ๋กœ ๋ด์„œ UTC ๊ธฐ์ค€)๋กœ๋ถ€ํ„ฐ
์ธก์ •๋˜๋Š” ๊ฒƒ์„ epoch-seconds ๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์ฝ”๋“œ๋กœ ๋ณด๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ํ˜•ํƒœ๋กœ ์ถœ๋ ฅ ๋ฉ๋‹ˆ๋‹ค.

fun main() {
    val utcLong: Long = 1711830020
    val utcTime = Instant.ofEpochSecond(utcLong)
    println("utc Long($utcLong) to Instant > $utcTime")
    
    val epoch = utcTime.epochSecond
    println("Instant.epoch: $epoch")
}

// ๊ฒฐ๊ณผ
utc Long(1711830020) to Instant > 2024-03-30T20:20:20Z
Instant.epoch: 1711830020

์—ฌ๊ธฐ๊นŒ์ง€ ๊ฐ API์— ๋Œ€ํ•ด์„œ ์ดํ•ดํ•œ ๊ฒƒ์„ ๋ฐ”ํƒ•์œผ๋กœ
์ตœ๋Œ€ํ•œ ์‰ฝ๊ฒŒ ์„ค๋ช…ํ•ด ๋ณด์•˜์Šต๋‹ˆ๋‹ค.


๐Ÿ’ก ๋‹ค์‹œ ์ฝ”๋“œ๋กœ ๋Œ์•„๊ฐ€์„œ

์ปดํฌ์ฆˆ ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ์ฃผ์„์„ ๋‹ค์‹œ ๋ณด๋ฉด ์ด์ œ๋Š” ์ดํ•ด๊ฐ€ ๊ฐ‘๋‹ˆ๋‹ค โญ๏ธ

// @param initialSelectedDateMillis timestamp in _UTC_ milliseconds from the epoch

timestamp > Instant ์˜ milliseconds epoch ๋กœ ๊ฐ’์„ ์ „๋‹ฌํ•˜๋ผ๋Š” ์˜๋ฏธ์˜€์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ํ•ด๊ฒฐ์€
1 Instant ํด๋ž˜์Šค๋กœ ๋ณ€ํ™˜ ํ›„
2 .toEpochMilli()
์ „๋‹ฌํ•˜์—ฌ ํ•ด๊ฒฐ ๋์Šต๋‹ˆ๋‹ค.


๐Ÿ‘๐Ÿป ํฌ์ŠคํŠธ๋ฅผ ๋งˆ์น˜๋ฉฐ

DatePicker๋กœ ์‹œ์ž‘ํ•˜์—ฌ
Date ์™€ Time ์— ๋Œ€ํ•ด์„œ
๊ฝค ํฐ ๋ฒ”์œ„๋ฅผ ๊ณต๋ถ€ํ•ด๋ณด๊ณ  ์ •๋ฆฌ๊นŒ์ง€ ํ•ด๋ดค๋Š”๋ฐ
์‹ค์ˆ˜๋กœ ๋ถ€ํ„ฐ ๋งŽ์€ ๊ฒƒ์„ ๋ฐฐ์šด ๊ณ„๊ธฐ๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

ํ”ผ๋“œ๋ฐฑ์€ ์–ธ์ œ๋‚˜ ํ™˜์˜์ด๋ฉฐ ๐Ÿ™‡๐Ÿป
์ด๋งŒ ๋งˆ์น˜๊ฒ ์Šต๋‹ˆ๋‹ค.

profile
๊ณต๋ถ€ํ•˜๋Š” ๊ฐœ๋ฐœ์ž

0๊ฐœ์˜ ๋Œ“๊ธ€

๊ด€๋ จ ์ฑ„์šฉ ์ •๋ณด