IDateTimeFormatterImpl.kt

/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.core.i18n

import android.os.Build
import androidx.annotation.RequiresApi
import java.text.DateFormat
import java.text.FieldPosition
import java.util.Calendar
import java.util.Locale

internal interface IDateTimeFormatterImpl {
    fun format(calendar: Calendar): String
}

internal class DateTimeFormatterImplAndroid(skeleton: String, locale: Locale) :
    IDateTimeFormatterImpl {

    private val sdf = java.text.SimpleDateFormat(
        android.text.format.DateFormat.getBestDateTimePattern(locale, skeleton), locale
    )

    override fun format(calendar: Calendar): String {
        return sdf.format(calendar.time)
    }
}

// ICU4J will give better results, so we use it if we can.
// And android.text.format.DateFormat.getBestDateTimePattern has a bug in API 26 and 27.
// For some skeletons it returns "b" instead of "a" in patterns (the am/pm).
// That is invalid, and throws when used to build the SimpleDateFormat
// Using ICU avoids that.
// Should also be faster, as the Android used JNI.
@RequiresApi(Build.VERSION_CODES.N)
internal class DateTimeFormatterImplIcu(skeleton: String, private var locale: Locale) :
    IDateTimeFormatterImpl {

    private val sdf = android.icu.text.DateFormat.getInstanceForSkeleton(skeleton, locale)

    override fun format(calendar: Calendar): String {
        val result = StringBuffer()
        val tz = android.icu.util.TimeZone.getTimeZone(calendar.timeZone.id)
        val ucal = android.icu.util.Calendar.getInstance(tz, locale)
        sdf.calendar = ucal
        val fp = FieldPosition(0)
        sdf.format(calendar.timeInMillis, result, fp)
        return result.toString()
    }
}

internal class DateTimeFormatterImplJdkStyle(dateStyle: Int, timeStyle: Int, locale: Locale) :
    IDateTimeFormatterImpl {

    private val sdf =
        if (dateStyle == -1) {
            if (timeStyle == -1) {
                DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, locale)
            } else {
                DateFormat.getTimeInstance(timeStyle, locale)
            }
        } else {
            if (timeStyle == -1) {
                DateFormat.getDateInstance(dateStyle, locale)
            } else {
                DateFormat.getDateTimeInstance(dateStyle, timeStyle, locale)
            }
        }

    override fun format(calendar: Calendar): String {
        return sdf.format(calendar.time)
    }
}