Skip to content

Commit

Permalink
Kotlinx datetime CalendarModel (support date picker on darwin and web) (
Browse files Browse the repository at this point in the history
#717)

implement Calender on all platforms
Co-authored-by: Oleksandr Karpovich <a.n.karpovich@gmail.com>
Co-authored-by: dima.avdeev <dima.avdeev@jetbrains.com>
  • Loading branch information
alexzhirkevich authored and igordmn committed Jan 30, 2024
1 parent 0e18414 commit 6194ef4
Show file tree
Hide file tree
Showing 19 changed files with 1,023 additions and 173 deletions.
33 changes: 28 additions & 5 deletions compose/material3/material3/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ if(AndroidXComposePlugin.isMultiplatformEnabled(project)) {

skikoMain {
dependsOn(commonMain)
dependencies {
implementation(libs.datetime)
}
}

desktopMain.dependsOn(skikoMain)
Expand Down Expand Up @@ -160,11 +163,31 @@ if(AndroidXComposePlugin.isMultiplatformEnabled(project)) {
implementation(libs.testUiautomator)
}

desktopTest.dependencies {
implementation(project(":compose:ui:ui-test-junit4"))
implementation(libs.truth)
implementation(libs.junit)
implementation(libs.skikoCurrentOs)
skikoTest.dependencies {
implementation(libs.kotlinTest)
}

desktopTest {
dependsOn(skikoTest)

dependencies {
implementation(project(":compose:ui:ui-test-junit4"))
implementation(libs.truth)
implementation(libs.junit)
implementation(libs.skikoCurrentOs)
}
}

jsWasmTest {
dependsOn(skikoTest)
}

jsTest{
dependsOn(jsWasmTest)
}

uikitTest {
dependsOn(skikoTest)
}

configureEach {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,7 @@ package androidx.compose.material3
import platform.Foundation.NSLocale
import platform.Foundation.currentLocale

/**
* Represents a Locale for the calendar. This locale will be used when formatting dates, determining
* the input format, and more.
*/

actual typealias CalendarLocale = NSLocale

/**
* Returns the default [CalendarLocale].
*/
internal actual fun defaultLocale(): CalendarLocale = NSLocale.currentLocale()
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Copyright 2023 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.compose.material3

import kotlinx.datetime.Instant
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toNSTimeZone
import platform.Foundation.NSCalendar
import platform.Foundation.NSDate
import platform.Foundation.NSDateFormatter
import platform.Foundation.NSDateFormatterShortStyle
import platform.Foundation.NSLocale
import platform.Foundation.NSTimeZone
import platform.Foundation.currentLocale
import platform.Foundation.dateWithTimeIntervalSince1970
import platform.Foundation.timeIntervalSince1970

internal actual object PlatformDateFormat {

actual val firstDayOfWeek: Int
get() = firstDayOfWeek()

actual fun formatWithPattern(
utcTimeMillis: Long,
pattern: String,
locale: CalendarLocale
): String {

val nsDate = NSDate.dateWithTimeIntervalSince1970(utcTimeMillis / 1000.0)

return NSDateFormatter().apply {
setDateFormat(pattern)
setLocale(locale)
}.stringFromDate(nsDate)
}

actual fun formatWithSkeleton(
utcTimeMillis: Long,
skeleton: String,
locale: CalendarLocale
): String {

val nsDate = NSDate.dateWithTimeIntervalSince1970(utcTimeMillis / 1000.0)

return NSDateFormatter().apply {
setLocalizedDateFormatFromTemplate(skeleton)
setLocale(locale)
}.stringFromDate(nsDate)
}

actual fun parse(
date: String,
pattern: String
): CalendarDate? {

val nsDate = NSDateFormatter().apply {
setDateFormat(pattern)
setTimeZone(TimeZone.UTC.toNSTimeZone())
}.dateFromString(date) ?: return null

return Instant
.fromEpochMilliseconds((nsDate.timeIntervalSince1970 * 1000).toLong())
.toCalendarDate(TimeZone.UTC)
}

actual fun getDateInputFormat(locale: CalendarLocale): DateInputFormat {

val pattern = NSDateFormatter().apply {
setLocale(locale)
setDateStyle(NSDateFormatterShortStyle)
}.dateFormat

val delimiter = pattern.first { !it.isLetter() }

return DateInputFormat(pattern, delimiter)
}

@Suppress("UNCHECKED_CAST")
actual fun weekdayNames(locale: CalendarLocale): List<Pair<String, String>>? {
val formatter = NSDateFormatter().apply {
setLocale(locale)
}

val fromSundayToSaturday = formatter.standaloneWeekdaySymbols
.zip(formatter.shortStandaloneWeekdaySymbols) as List<Pair<String, String>>

return fromSundayToSaturday.drop(1) + fromSundayToSaturday.first()
}

private fun firstDayOfWeek(): Int {
return (NSCalendar.currentCalendar.firstWeekday.toInt() - 1).takeIf { it > 0 } ?: 7
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright 2023 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.compose.material3

internal actual object PlatformDateFormat {

private val delegate = LegacyCalendarModelImpl()

actual val firstDayOfWeek: Int
get() = delegate.firstDayOfWeek

actual fun formatWithPattern(
utcTimeMillis: Long,
pattern: String,
locale: CalendarLocale
): String {
return delegate.formatWithPattern(utcTimeMillis, pattern, locale)
}

actual fun formatWithSkeleton(
utcTimeMillis: Long,
skeleton: String,
locale: CalendarLocale
): String {
// Note: there is no equivalent in Java for Android's DateFormat.getBestDateTimePattern.
// The JDK SimpleDateFormat expects a pattern, so the results will be "2023Jan7",
// "2023January", etc. in case a skeleton holds an actual ICU skeleton and not a pattern.

// TODO: support ICU skeleton on JVM
// Maybe it will be supported in kotlinx.datetime in the future.
// See https://github.com/Kotlin/kotlinx-datetime/pull/251
return formatWithPattern(utcTimeMillis, skeleton, locale)
}

actual fun parse(
date: String,
pattern: String
): CalendarDate? {


return delegate.parse(date, pattern)
}

actual fun getDateInputFormat(locale: CalendarLocale): DateInputFormat {
return delegate.getDateInputFormat(locale)
}

actual fun weekdayNames(locale: CalendarLocale): List<Pair<String, String>>? {
return delegate.weekdayNames(locale)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2023 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.compose.material3

import java.util.Locale

actual fun calendarLocale(language : String, country : String) : CalendarLocale{
return Locale(language, country)
}

actual val supportsDateSkeleton: Boolean
get() = false

0 comments on commit 6194ef4

Please sign in to comment.