Part of Github Copilot with Kotlin

Copilot with Kotlin: Create a list of months in 3 letter format

Sandy LaneSandy Lane

Video: Copilot with Kotlin: Create a list of months in 3 letter format by Taught by Celeste AI - AI Coding Coach

Take the quiz on the full lesson page
Test what you've read · interactive walkthrough

Kotlin: List of Months in 3-Letter Format

Month.values().map { it.getDisplayName(TextStyle.SHORT, Locale.ENGLISH).uppercase() }. Get all 12 months, format each as a 3-letter abbreviation, uppercase. The locale-aware way.

A small puzzle: produce ["JAN", "FEB", "MAR", ..., "DEC"]. The naive approach hardcodes the strings; the correct approach uses java.time.Month so it's locale-aware.

The Copilot prompt

import java.time.Month
import java.time.format.TextStyle
import java.util.Locale

// Generate a list of months in 3-letter uppercase format
fun main() {

Copilot generates:

fun main() {
  val months = Month.values()
    .map { it.getDisplayName(TextStyle.SHORT, Locale.ENGLISH).uppercase() }

  println(months)
  // [JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC]
}

Walkthrough

1. Month.values(). Returns Array<Month> — the 12 enum values in order (JANUARY=1, ..., DECEMBER=12). It's an array, but the standard collection operations work.

2. .map { ... }. Transform each Month to a String. Since Array's map returns List<T>, we get List<String>.

3. getDisplayName(TextStyle.SHORT, Locale.ENGLISH). Returns "Jan", "Feb", etc. for English. Locale.JAPANESE returns "1月", "2月", etc.

4. .uppercase(). Capitalize. "Jan".uppercase()"JAN".

For locales where the short name is already uppercase (or is non-letter), uppercase() is a no-op.

Locales

val french = Month.JANUARY.getDisplayName(TextStyle.SHORT, Locale.FRENCH)
// "janv." (with period)

val spanish = Month.JANUARY.getDisplayName(TextStyle.SHORT, Locale.forLanguageTag("es"))
// "ene."

val japanese = Month.JANUARY.getDisplayName(TextStyle.SHORT, Locale.JAPANESE)
// "1月"

Different locales, different conventions. For "JAN, FEB, ..." specifically, you want English. For "show months in the user's language," use Locale.getDefault() or a Compose locale.

TextStyle options

  • TextStyle.FULL — "January"
  • TextStyle.SHORT — "Jan"
  • TextStyle.NARROW — "J" (single letter; not always unique — Jan/Jun/Jul all are "J" in some locales)

Plus FULL_STANDALONE, SHORT_STANDALONE, NARROW_STANDALONE for languages where the standalone form differs (Russian, Polish, etc.).

Hardcoded alternative

val months = listOf("JAN", "FEB", "MAR", "APR", "MAY", "JUN",
                    "JUL", "AUG", "SEP", "OCT", "NOV", "DEC")

Works. Same output. Brittle: - Wrong for non-English UIs. - Have to update if you change the locale. - One typo and you have a silent bug.

For "this is always uppercase English," the hardcoded version is fine. For anything user-facing, use the API.

Variations

// Lowercase
val lower = Month.values().map { it.getDisplayName(TextStyle.SHORT, Locale.ENGLISH).lowercase() }
// [jan, feb, mar, ...]

// Full names
val full = Month.values().map { it.getDisplayName(TextStyle.FULL, Locale.ENGLISH) }
// [January, February, March, ...]

// Single-letter
val narrow = Month.values().map { it.getDisplayName(TextStyle.NARROW, Locale.ENGLISH) }
// [J, F, M, A, M, J, J, A, S, O, N, D] — note J/M repeated

// As "01", "02", ...
val numbers = Month.values().map { it.value.toString().padStart(2, '0') }
// [01, 02, 03, ..., 12]

Month.value returns the 1-12 integer. padStart(2, '0') pads with leading zero.

Building a month picker

val months = Month.values().map { month ->
  val short = month.getDisplayName(TextStyle.SHORT, Locale.ENGLISH).uppercase()
  val full = month.getDisplayName(TextStyle.FULL, Locale.ENGLISH)
  Pair(month.value, "$short ($full)")
}

months.forEach { (num, name) -> println("$num: $name") }
// 1: JAN (January)
// 2: FEB (February)
// ...

A pair of (number, label) for dropdown UIs. Adapt to whatever format your widget needs.

Edge case: case-insensitive locales

In Turkish (Locale.forLanguageTag("tr")), the lowercase of "I" is "ı" (without dot), and the uppercase of "i" is "İ" (with dot). For non-locale-sensitive uppercase, use Locale.ROOT:

month.getDisplayName(TextStyle.SHORT, Locale.ROOT).uppercase(Locale.ROOT)

Locale.ROOT is the locale-neutral default. Safer for "computer-readable" values like keys.

Common mistakes

Hardcoding for one locale. Breaks for international users.

Forgetting to call uppercase(). "Jan", "Feb" → getDisplayName returns title case. Add .uppercase() for "JAN", "FEB".

Forgetting Locale. getDisplayName(TextStyle.SHORT) doesn't compile — Locale is required. Pick Locale.ENGLISH, Locale.ROOT, or the user's locale.

Confusing Month with LocalDate. Month is just the enum (no year). LocalDate.of(2024, Month.JANUARY, 15).month would extract the Month from a date.

Using Locale.getDefault() for "computer" output. The user's locale might be Turkish; "I" might lowercase to "ı". For user-facing display, default is fine; for keys/identifiers, use Locale.ROOT or Locale.ENGLISH.

What's next

Episode 37: Calculate age from a birthday. Period.between(birthday, today).years — the modern way.

Recap

Month.values().map { it.getDisplayName(TextStyle.SHORT, Locale.ENGLISH).uppercase() } for ["JAN", "FEB", ..., "DEC"]. Locale changes language; TextStyle changes length (FULL, SHORT, NARROW). For locale-neutral output, Locale.ROOT. Hardcoded listOf("JAN", ...) works for English-only contexts but is brittle. Month.value for the 1-12 number; padStart(2, '0') for "01", "02", etc.

Next episode: age calculation.

Ready? Take the quiz on the full lesson page →
Test what you've learned. Watch the lesson and try the interactive quiz on the same page.