Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kotlinx datetime CalendarModel (support date picker on darwin and web) #717

Merged
merged 27 commits into from
Aug 5, 2023
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
32d04ea
darwin CalendarModel
alexzhirkevich Jul 29, 2023
23c8f9b
test
alexzhirkevich Jul 29, 2023
68ffc21
weekdayNames desktop
alexzhirkevich Jul 29, 2023
2c29d94
move skiko actual to actual.skiko.kt
alexzhirkevich Jul 29, 2023
2ac3dc9
reverse popups
alexzhirkevich Jul 29, 2023
36c2594
date skeleton js
alexzhirkevich Jul 29, 2023
a893355
js better format
alexzhirkevich Jul 29, 2023
32a54c7
format
alexzhirkevich Jul 29, 2023
3803d39
null fix
alexzhirkevich Jul 29, 2023
d0e7517
fix weekdays, implement iOS getDateInputFormat
alexzhirkevich Jul 29, 2023
8fab673
implement js getDateInputFormat
alexzhirkevich Jul 29, 2023
57f6b50
js firstDayOfWeek
alexzhirkevich Jul 29, 2023
91e6e94
weekdays localization js
alexzhirkevich Jul 30, 2023
e7452ef
cleanup
alexzhirkevich Jul 30, 2023
7075cce
js format without leading zeros
alexzhirkevich Jul 30, 2023
988fd88
cleanup
alexzhirkevich Aug 1, 2023
4824a56
fix string format
alexzhirkevich Aug 1, 2023
a196c46
remove invalid \ in strings
alexzhirkevich Aug 1, 2023
13bb8fe
fixed bugs, implemented all tests
alexzhirkevich Aug 2, 2023
864a0a7
fixed bugs, implemented all tests
alexzhirkevich Aug 2, 2023
1893bef
unmute date input test for desktop
alexzhirkevich Aug 2, 2023
ae52b6b
fix
alexzhirkevich Aug 2, 2023
5132740
add link to datetime format PR
alexzhirkevich Aug 3, 2023
6af9b8e
set new model as an actual in skikoMain
alexzhirkevich Aug 3, 2023
97ca44f
set proper name for test file
alexzhirkevich Aug 3, 2023
2f70816
change year in js date input format to avoid epoch inconsistencies
alexzhirkevich Aug 3, 2023
99b2813
Update copyright
alexzhirkevich Aug 5, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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,20 @@
/*
* 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

@ExperimentalMaterial3Api
internal actual fun CalendarModel(): CalendarModel = KotlinxDatetimeCalendarModel()
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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,33 +16,8 @@

package androidx.compose.material3

import java.util.Locale

/**
* Returns a [CalendarModel] to be used by the date picker.
*/
@ExperimentalMaterial3Api
internal actual fun CalendarModel(): CalendarModel = LegacyCalendarModelImpl()

/**
* Formats a UTC timestamp into a string with a given date format skeleton.
*
* @param utcTimeMillis a UTC timestamp to format (milliseconds from epoch)
* @param skeleton a date format skeleton
* @param locale the [Locale] to use when formatting the given timestamp
*/
@ExperimentalMaterial3Api
internal actual fun formatWithSkeleton(
utcTimeMillis: Long,
skeleton: String,
locale: Locale
): 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.
return LegacyCalendarModelImpl.formatWithPattern(
utcTimeMillis = utcTimeMillis,
pattern = skeleton,
locale = locale
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* 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.
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
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,6 @@
package androidx.compose.material3

// TODO(https://github.com/JetBrains/compose-multiplatform/issues/3359) Support CalendarModel

/**
* Returns a [CalendarModel] to be used by the date picker.
*/
@ExperimentalMaterial3Api
internal actual fun CalendarModel(): CalendarModel =
throw UnsupportedOperationException("DatePicker isn't supported on Web yet. Follow https://github.com/JetBrains/compose-multiplatform/issues/3359")

/**
* Formats a UTC timestamp into a string with a given date format skeleton.
*
* @param utcTimeMillis a UTC timestamp to format (milliseconds from epoch)
* @param skeleton a date format skeleton
* @param locale the [Locale] to use when formatting the given timestamp
*/
@ExperimentalMaterial3Api
internal actual fun formatWithSkeleton(
utcTimeMillis: Long,
skeleton: String,
locale: CalendarLocale
): String {
throw UnsupportedOperationException("DatePicker isn't supported on Web yet. Follow https://github.com/JetBrains/compose-multiplatform/issues/3359")
}
KotlinxDatetimeCalendarModel()