mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-30 22:07:57 +01:00 
			
		
		
		
	Refactor use of Java.util.date to Java.time.*, to fix localized date issues. (#402)
* Add support for localdate based relative times * Update History Screen to use new localdate based relative times * Update Updates Screen to use new localdate based relative times * Cleaned up date util classes * Updated build time display * Code cleanup * Fixed crash in settings * Updated Preferences item * Worker Info works * Fixed Tracker date display * Code changes to pass detekt
This commit is contained in:
		| @@ -7,8 +7,8 @@ import eu.kanade.tachiyomi.util.system.DeviceUtil | ||||
| import eu.kanade.tachiyomi.util.system.isDynamicColorAvailable | ||||
| import tachiyomi.core.common.preference.PreferenceStore | ||||
| import tachiyomi.core.common.preference.getEnum | ||||
| import java.text.DateFormat | ||||
| import java.text.SimpleDateFormat | ||||
| import java.time.format.DateTimeFormatter | ||||
| import java.time.format.FormatStyle | ||||
| import java.util.Locale | ||||
|  | ||||
| class UiPreferences( | ||||
| @@ -31,9 +31,9 @@ class UiPreferences( | ||||
|     fun tabletUiMode() = preferenceStore.getEnum("tablet_ui_mode", TabletUiMode.AUTOMATIC) | ||||
|  | ||||
|     companion object { | ||||
|         fun dateFormat(format: String): DateFormat = when (format) { | ||||
|             "" -> DateFormat.getDateInstance(DateFormat.SHORT) | ||||
|             else -> SimpleDateFormat(format, Locale.getDefault()) | ||||
|         fun dateFormat(format: String): DateTimeFormatter = when (format) { | ||||
|             "" -> DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT) | ||||
|             else -> DateTimeFormatter.ofPattern(format, Locale.getDefault()) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -4,25 +4,31 @@ import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.remember | ||||
| import androidx.compose.ui.platform.LocalContext | ||||
| import eu.kanade.domain.ui.UiPreferences | ||||
| import eu.kanade.tachiyomi.util.lang.toRelativeString | ||||
| import eu.kanade.tachiyomi.util.lang.toRelativeSting | ||||
| import tachiyomi.i18n.MR | ||||
| import tachiyomi.presentation.core.i18n.stringResource | ||||
| import uy.kohesive.injekt.Injekt | ||||
| import uy.kohesive.injekt.api.get | ||||
| import java.util.Date | ||||
| import java.time.Instant | ||||
| import java.time.LocalDate | ||||
| import java.time.ZoneId | ||||
|  | ||||
| @Composable | ||||
| fun relativeDateText( | ||||
|     dateEpochMillis: Long, | ||||
| ): String { | ||||
|     return relativeDateText( | ||||
|         date = Date(dateEpochMillis).takeIf { dateEpochMillis > 0L }, | ||||
|         localDate = LocalDate.ofInstant( | ||||
|             Instant.ofEpochMilli(dateEpochMillis), | ||||
|             ZoneId.systemDefault(), | ||||
|         ) | ||||
|             .takeIf { dateEpochMillis > 0L }, | ||||
|     ) | ||||
| } | ||||
|  | ||||
| @Composable | ||||
| fun relativeDateText( | ||||
|     date: Date?, | ||||
|     localDate: LocalDate?, | ||||
| ): String { | ||||
|     val context = LocalContext.current | ||||
|  | ||||
| @@ -30,11 +36,10 @@ fun relativeDateText( | ||||
|     val relativeTime = remember { preferences.relativeTime().get() } | ||||
|     val dateFormat = remember { UiPreferences.dateFormat(preferences.dateFormat().get()) } | ||||
|  | ||||
|     return date | ||||
|         ?.toRelativeString( | ||||
|             context = context, | ||||
|             relative = relativeTime, | ||||
|             dateFormat = dateFormat, | ||||
|         ) | ||||
|     return localDate?.toRelativeSting( | ||||
|         context = context, | ||||
|         relative = relativeTime, | ||||
|         dateFormat = dateFormat, | ||||
|     ) | ||||
|         ?: stringResource(MR.strings.not_applicable) | ||||
| } | ||||
|   | ||||
| @@ -28,7 +28,7 @@ import tachiyomi.presentation.core.components.material.Scaffold | ||||
| import tachiyomi.presentation.core.i18n.stringResource | ||||
| import tachiyomi.presentation.core.screens.EmptyScreen | ||||
| import tachiyomi.presentation.core.screens.LoadingScreen | ||||
| import java.util.Date | ||||
| import java.time.LocalDate | ||||
|  | ||||
| @Composable | ||||
| fun HistoryScreen( | ||||
| @@ -133,7 +133,7 @@ private fun HistoryScreenContent( | ||||
| } | ||||
|  | ||||
| sealed interface HistoryUiModel { | ||||
|     data class Header(val date: Date) : HistoryUiModel | ||||
|     data class Header(val date: LocalDate) : HistoryUiModel | ||||
|     data class Item(val item: HistoryWithRelations) : HistoryUiModel | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -5,6 +5,7 @@ import eu.kanade.tachiyomi.ui.history.HistoryScreenModel | ||||
| import tachiyomi.domain.history.model.HistoryWithRelations | ||||
| import tachiyomi.domain.manga.model.MangaCover | ||||
| import java.time.Instant | ||||
| import java.time.LocalDate | ||||
| import java.time.temporal.ChronoUnit | ||||
| import java.util.Date | ||||
| import kotlin.random.Random | ||||
| @@ -71,10 +72,10 @@ class HistoryScreenModelStateProvider : PreviewParameterProvider<HistoryScreenMo | ||||
|     private object HistoryUiModelExamples { | ||||
|         val headerToday = header() | ||||
|         val headerTomorrow = | ||||
|             HistoryUiModel.Header(Date.from(Instant.now().plus(1, ChronoUnit.DAYS))) | ||||
|             HistoryUiModel.Header(LocalDate.now().plusDays(1)) | ||||
|  | ||||
|         fun header(instantBuilder: (Instant) -> Instant = { it }) = | ||||
|             HistoryUiModel.Header(Date.from(instantBuilder(Instant.now()))) | ||||
|             HistoryUiModel.Header(LocalDate.from(instantBuilder(Instant.now()))) | ||||
|  | ||||
|         fun items() = sequence { | ||||
|             var count = 1 | ||||
|   | ||||
| @@ -26,7 +26,7 @@ import tachiyomi.presentation.core.i18n.stringResource | ||||
| import tachiyomi.presentation.core.util.collectAsState | ||||
| import uy.kohesive.injekt.Injekt | ||||
| import uy.kohesive.injekt.api.get | ||||
| import java.time.Instant | ||||
| import java.time.LocalDate | ||||
|  | ||||
| object SettingsAppearanceScreen : SearchableSettings { | ||||
|  | ||||
| @@ -101,7 +101,7 @@ object SettingsAppearanceScreen : SearchableSettings { | ||||
|         val context = LocalContext.current | ||||
|         val navigator = LocalNavigator.currentOrThrow | ||||
|  | ||||
|         val now = remember { Instant.now().toEpochMilli() } | ||||
|         val now = remember { LocalDate.now() } | ||||
|  | ||||
|         val dateFormat by uiPreferences.dateFormat().collectAsState() | ||||
|         val formattedNow = remember(dateFormat) { | ||||
|   | ||||
| @@ -55,10 +55,10 @@ import tachiyomi.presentation.core.icons.Reddit | ||||
| import tachiyomi.presentation.core.icons.X | ||||
| import uy.kohesive.injekt.Injekt | ||||
| import uy.kohesive.injekt.api.get | ||||
| import java.text.DateFormat | ||||
| import java.text.SimpleDateFormat | ||||
| import java.time.LocalDateTime | ||||
| import java.time.ZoneId | ||||
| import java.time.format.DateTimeFormatter | ||||
| import java.util.Locale | ||||
| import java.util.TimeZone | ||||
|  | ||||
| object AboutScreen : Screen() { | ||||
|  | ||||
| @@ -269,16 +269,9 @@ object AboutScreen : Screen() { | ||||
|  | ||||
|     internal fun getFormattedBuildTime(): String { | ||||
|         return try { | ||||
|             val inputDf = SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'", Locale.US) | ||||
|             inputDf.timeZone = TimeZone.getTimeZone("UTC") | ||||
|             val buildTime = inputDf.parse(BuildConfig.BUILD_TIME) | ||||
|  | ||||
|             val outputDf = DateFormat.getDateTimeInstance( | ||||
|                 DateFormat.MEDIUM, | ||||
|                 DateFormat.SHORT, | ||||
|                 Locale.getDefault(), | ||||
|             ) | ||||
|             outputDf.timeZone = TimeZone.getDefault() | ||||
|             val df = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm'Z'", Locale.US) | ||||
|                 .withZone(ZoneId.of("UTC")) | ||||
|             val buildTime = LocalDateTime.from(df.parse(BuildConfig.BUILD_TIME)) | ||||
|  | ||||
|             buildTime!!.toDateTimestampString(UiPreferences.dateFormat(Injekt.get<UiPreferences>().dateFormat().get())) | ||||
|         } catch (e: Exception) { | ||||
|   | ||||
| @@ -42,7 +42,9 @@ import tachiyomi.presentation.core.i18n.stringResource | ||||
| import tachiyomi.presentation.core.util.plus | ||||
| import uy.kohesive.injekt.Injekt | ||||
| import uy.kohesive.injekt.api.get | ||||
| import java.util.Date | ||||
| import java.time.Instant | ||||
| import java.time.LocalDateTime | ||||
| import java.time.ZoneId | ||||
|  | ||||
| class WorkerInfoScreen : Screen() { | ||||
|  | ||||
| @@ -149,11 +151,17 @@ class WorkerInfoScreen : Screen() { | ||||
|                     appendLine("State: ${workInfo.state}") | ||||
|                     if (workInfo.state == WorkInfo.State.ENQUEUED) { | ||||
|                         appendLine( | ||||
|                             "Next scheduled run: ${Date(workInfo.nextScheduleTimeMillis).toDateTimestampString( | ||||
|                                 UiPreferences.dateFormat( | ||||
|                                     Injekt.get<UiPreferences>().dateFormat().get(), | ||||
|                                 ), | ||||
|                             )}", | ||||
|                             "Next scheduled run: ${ | ||||
|                                 LocalDateTime.ofInstant( | ||||
|                                     Instant.ofEpochMilli(workInfo.nextScheduleTimeMillis), | ||||
|                                     ZoneId.systemDefault(), | ||||
|                                 ) | ||||
|                                     .toDateTimestampString( | ||||
|                                         UiPreferences.dateFormat( | ||||
|                                             Injekt.get<UiPreferences>().dateFormat().get(), | ||||
|                                         ), | ||||
|                                     ) | ||||
|                             }", | ||||
|                         ) | ||||
|                         appendLine("Attempt #${workInfo.runAttemptCount + 1}") | ||||
|                     } | ||||
|   | ||||
| @@ -52,17 +52,18 @@ import eu.kanade.presentation.theme.TachiyomiPreviewTheme | ||||
| import eu.kanade.presentation.track.components.TrackLogoIcon | ||||
| import eu.kanade.tachiyomi.data.track.Tracker | ||||
| import eu.kanade.tachiyomi.ui.manga.track.TrackItem | ||||
| import eu.kanade.tachiyomi.util.lang.toLocalDate | ||||
| import eu.kanade.tachiyomi.util.system.copyToClipboard | ||||
| import tachiyomi.i18n.MR | ||||
| import tachiyomi.presentation.core.i18n.stringResource | ||||
| import java.text.DateFormat | ||||
| import java.time.format.DateTimeFormatter | ||||
|  | ||||
| private const val UnsetStatusTextAlpha = 0.5F | ||||
|  | ||||
| @Composable | ||||
| fun TrackInfoDialogHome( | ||||
|     trackItems: List<TrackItem>, | ||||
|     dateFormat: DateFormat, | ||||
|     dateFormat: DateTimeFormatter, | ||||
|     onStatusClick: (TrackItem) -> Unit, | ||||
|     onChapterClick: (TrackItem) -> Unit, | ||||
|     onScoreClick: (TrackItem) -> Unit, | ||||
| @@ -104,11 +105,11 @@ fun TrackInfoDialogHome( | ||||
|                         .takeIf { supportsScoring && item.track.score != 0.0 }, | ||||
|                     onScoreClick = { onScoreClick(item) } | ||||
|                         .takeIf { supportsScoring }, | ||||
|                     startDate = remember(item.track.startDate) { dateFormat.format(item.track.startDate) } | ||||
|                     startDate = remember(item.track.startDate) { dateFormat.format(item.track.startDate.toLocalDate()) } | ||||
|                         .takeIf { supportsReadingDates && item.track.startDate != 0L }, | ||||
|                     onStartDateClick = { onStartDateEdit(item) } // TODO | ||||
|                         .takeIf { supportsReadingDates }, | ||||
|                     endDate = dateFormat.format(item.track.finishDate) | ||||
|                     endDate = dateFormat.format(item.track.finishDate.toLocalDate()) | ||||
|                         .takeIf { supportsReadingDates && item.track.finishDate != 0L }, | ||||
|                     onEndDateClick = { onEndDateEdit(item) } | ||||
|                         .takeIf { supportsReadingDates }, | ||||
|   | ||||
| @@ -5,7 +5,8 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider | ||||
| import eu.kanade.tachiyomi.ui.manga.track.TrackItem | ||||
| import eu.kanade.test.DummyTracker | ||||
| import tachiyomi.domain.track.model.Track | ||||
| import java.text.DateFormat | ||||
| import java.time.format.DateTimeFormatter | ||||
| import java.time.format.FormatStyle | ||||
|  | ||||
| internal class TrackInfoDialogHomePreviewProvider : | ||||
|     PreviewParameterProvider<@Composable () -> Unit> { | ||||
| @@ -46,7 +47,7 @@ internal class TrackInfoDialogHomePreviewProvider : | ||||
|                 trackItemWithoutTrack, | ||||
|                 trackItemWithTrack, | ||||
|             ), | ||||
|             dateFormat = DateFormat.getDateInstance(), | ||||
|             dateFormat = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM), | ||||
|             onStatusClick = {}, | ||||
|             onChapterClick = {}, | ||||
|             onScoreClick = {}, | ||||
| @@ -61,7 +62,7 @@ internal class TrackInfoDialogHomePreviewProvider : | ||||
|     private val noTrackers = @Composable { | ||||
|         TrackInfoDialogHome( | ||||
|             trackItems = listOf(), | ||||
|             dateFormat = DateFormat.getDateInstance(), | ||||
|             dateFormat = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM), | ||||
|             onStatusClick = {}, | ||||
|             onChapterClick = {}, | ||||
|             onScoreClick = {}, | ||||
|   | ||||
| @@ -36,7 +36,7 @@ import tachiyomi.presentation.core.components.material.Scaffold | ||||
| import tachiyomi.presentation.core.i18n.stringResource | ||||
| import tachiyomi.presentation.core.screens.EmptyScreen | ||||
| import tachiyomi.presentation.core.screens.LoadingScreen | ||||
| import java.util.Date | ||||
| import java.time.LocalDate | ||||
| import kotlin.time.Duration.Companion.seconds | ||||
|  | ||||
| @Composable | ||||
| @@ -206,6 +206,6 @@ private fun UpdatesBottomBar( | ||||
| } | ||||
|  | ||||
| sealed interface UpdatesUiModel { | ||||
|     data class Header(val date: Date) : UpdatesUiModel | ||||
|     data class Header(val date: LocalDate) : UpdatesUiModel | ||||
|     data class Item(val item: UpdatesItem) : UpdatesUiModel | ||||
| } | ||||
|   | ||||
| @@ -5,7 +5,7 @@ import cafe.adriel.voyager.core.model.StateScreenModel | ||||
| import cafe.adriel.voyager.core.model.screenModelScope | ||||
| import eu.kanade.core.util.insertSeparators | ||||
| import eu.kanade.presentation.history.HistoryUiModel | ||||
| import eu.kanade.tachiyomi.util.lang.toDateKey | ||||
| import eu.kanade.tachiyomi.util.lang.toLocalDate | ||||
| import kotlinx.coroutines.Dispatchers | ||||
| import kotlinx.coroutines.channels.Channel | ||||
| import kotlinx.coroutines.flow.Flow | ||||
| @@ -28,7 +28,7 @@ import tachiyomi.domain.history.interactor.RemoveHistory | ||||
| import tachiyomi.domain.history.model.HistoryWithRelations | ||||
| import uy.kohesive.injekt.Injekt | ||||
| import uy.kohesive.injekt.api.get | ||||
| import java.util.Date | ||||
| import java.time.LocalDate | ||||
|  | ||||
| class HistoryScreenModel( | ||||
|     private val getHistory: GetHistory = Injekt.get(), | ||||
| @@ -60,10 +60,12 @@ class HistoryScreenModel( | ||||
|     private fun List<HistoryWithRelations>.toHistoryUiModels(): List<HistoryUiModel> { | ||||
|         return map { HistoryUiModel.Item(it) } | ||||
|             .insertSeparators { before, after -> | ||||
|                 val beforeDate = before?.item?.readAt?.time?.toDateKey() ?: Date(0) | ||||
|                 val afterDate = after?.item?.readAt?.time?.toDateKey() ?: Date(0) | ||||
|                 val beforeDate = before?.item?.readAt?.time?.toLocalDate() ?: LocalDate.MIN | ||||
|                 val afterDate = after?.item?.readAt?.time?.toLocalDate() ?: LocalDate.MIN | ||||
|                 when { | ||||
|                     beforeDate.time != afterDate.time && afterDate.time != 0L -> HistoryUiModel.Header(afterDate) | ||||
|                     beforeDate.isAfter(afterDate) | ||||
|                         or afterDate.equals(LocalDate.MIN) | ||||
|                         or beforeDate.equals(LocalDate.MIN) -> HistoryUiModel.Header(afterDate) | ||||
|                     // Return null to avoid adding a separator between two items. | ||||
|                     else -> null | ||||
|                 } | ||||
|   | ||||
| @@ -16,7 +16,7 @@ import eu.kanade.tachiyomi.data.download.DownloadCache | ||||
| import eu.kanade.tachiyomi.data.download.DownloadManager | ||||
| import eu.kanade.tachiyomi.data.download.model.Download | ||||
| import eu.kanade.tachiyomi.data.library.LibraryUpdateJob | ||||
| import eu.kanade.tachiyomi.util.lang.toDateKey | ||||
| import eu.kanade.tachiyomi.util.lang.toLocalDate | ||||
| import kotlinx.collections.immutable.PersistentList | ||||
| import kotlinx.collections.immutable.mutate | ||||
| import kotlinx.collections.immutable.persistentListOf | ||||
| @@ -45,8 +45,8 @@ import tachiyomi.domain.updates.interactor.GetUpdates | ||||
| import tachiyomi.domain.updates.model.UpdatesWithRelations | ||||
| import uy.kohesive.injekt.Injekt | ||||
| import uy.kohesive.injekt.api.get | ||||
| import java.time.LocalDate | ||||
| import java.time.ZonedDateTime | ||||
| import java.util.Date | ||||
|  | ||||
| class UpdatesScreenModel( | ||||
|     private val sourceManager: SourceManager = Injekt.get(), | ||||
| @@ -374,12 +374,12 @@ class UpdatesScreenModel( | ||||
|             return items | ||||
|                 .map { UpdatesUiModel.Item(it) } | ||||
|                 .insertSeparators { before, after -> | ||||
|                     val beforeDate = before?.item?.update?.dateFetch?.toDateKey() ?: Date(0) | ||||
|                     val afterDate = after?.item?.update?.dateFetch?.toDateKey() ?: Date(0) | ||||
|                     val beforeDate = before?.item?.update?.dateFetch?.toLocalDate() ?: LocalDate.MIN | ||||
|                     val afterDate = after?.item?.update?.dateFetch?.toLocalDate() ?: LocalDate.MIN | ||||
|                     when { | ||||
|                         beforeDate.time != afterDate.time && afterDate.time != 0L -> { | ||||
|                             UpdatesUiModel.Header(afterDate) | ||||
|                         } | ||||
|                         beforeDate.isAfter(afterDate) | ||||
|                             or afterDate.equals(LocalDate.MIN) | ||||
|                             or beforeDate.equals(LocalDate.MIN) -> UpdatesUiModel.Header(afterDate) | ||||
|                         // Return null to avoid adding a separator between two items. | ||||
|                         else -> null | ||||
|                     } | ||||
|   | ||||
| @@ -6,15 +6,17 @@ import tachiyomi.core.common.i18n.stringResource | ||||
| import tachiyomi.i18n.MR | ||||
| import java.text.DateFormat | ||||
| import java.time.Instant | ||||
| import java.time.LocalDate | ||||
| import java.time.LocalDateTime | ||||
| import java.time.ZoneId | ||||
| import java.time.format.DateTimeFormatter | ||||
| import java.time.format.FormatStyle | ||||
| import java.time.temporal.ChronoUnit | ||||
| import java.util.Calendar | ||||
| import java.util.Date | ||||
|  | ||||
| fun Date.toDateTimestampString(dateFormatter: DateFormat): String { | ||||
|     val date = dateFormatter.format(this) | ||||
|     val time = DateFormat.getTimeInstance(DateFormat.SHORT).format(this) | ||||
| fun LocalDateTime.toDateTimestampString(dateTimeFormatter: DateTimeFormatter): String { | ||||
|     val date = dateTimeFormatter.format(this) | ||||
|     val time = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT).format(this) | ||||
|     return "$date $time" | ||||
| } | ||||
|  | ||||
| @@ -32,52 +34,28 @@ fun Long.convertEpochMillisZone( | ||||
|         .toEpochMilli() | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Get date as time key | ||||
|  * | ||||
|  * @param date desired date | ||||
|  * @return date as time key | ||||
|  */ | ||||
| fun Long.toDateKey(): Date { | ||||
|     val instant = Instant.ofEpochMilli(this) | ||||
|     return Date.from(instant.truncatedTo(ChronoUnit.DAYS)) | ||||
| fun Long.toLocalDate(): LocalDate { | ||||
|     return LocalDate.ofInstant(Instant.ofEpochMilli(this), ZoneId.systemDefault()) | ||||
| } | ||||
|  | ||||
| fun Date.toRelativeString( | ||||
| fun LocalDate.toRelativeSting( | ||||
|     context: Context, | ||||
|     relative: Boolean = true, | ||||
|     dateFormat: DateFormat = DateFormat.getDateInstance(DateFormat.SHORT), | ||||
|     dateFormat: DateTimeFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT), | ||||
| ): String { | ||||
|     if (!relative) { | ||||
|         return dateFormat.format(this) | ||||
|     } | ||||
|     val now = Date() | ||||
|     val difference = now.timeWithOffset.floorNearest(MILLISECONDS_IN_DAY) - | ||||
|         this.timeWithOffset.floorNearest(MILLISECONDS_IN_DAY) | ||||
|     val days = difference.floorDiv(MILLISECONDS_IN_DAY).toInt() | ||||
|     val now = LocalDate.now() | ||||
|     val difference = ChronoUnit.DAYS.between(this, now) | ||||
|     return when { | ||||
|         difference < 0 -> dateFormat.format(this) | ||||
|         difference < MILLISECONDS_IN_DAY -> context.stringResource(MR.strings.relative_time_today) | ||||
|         difference < MILLISECONDS_IN_DAY.times(7) -> context.pluralStringResource( | ||||
|         difference < 0 -> difference.toString() | ||||
|         difference < 1 -> context.stringResource(MR.strings.relative_time_today) | ||||
|         difference < 7 -> context.pluralStringResource( | ||||
|             MR.plurals.relative_time, | ||||
|             days, | ||||
|             days, | ||||
|             difference.toInt(), | ||||
|             difference.toInt(), | ||||
|         ) | ||||
|         else -> dateFormat.format(this) | ||||
|     } | ||||
| } | ||||
|  | ||||
| private const val MILLISECONDS_IN_DAY = 86_400_000L | ||||
|  | ||||
| private val Date.timeWithOffset: Long | ||||
|     get() { | ||||
|         return Calendar.getInstance().run { | ||||
|             time = this@timeWithOffset | ||||
|             val dstOffset = get(Calendar.DST_OFFSET) | ||||
|             this@timeWithOffset.time + timeZone.rawOffset + dstOffset | ||||
|         } | ||||
|     } | ||||
|  | ||||
| private fun Long.floorNearest(to: Long): Long { | ||||
|     return this.floorDiv(to) * to | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user