import kotlinx.datetime.*
import kotlin.time.Duration
import kotlin.time.DurationUnit
import kotlin.time.toDuration
object DateTimeUtils {
val currentTimestamp: Long
get() = Clock.System.now().toEpochMilliseconds()
fun getCurrentInstant(): Instant = Clock.System.now()
fun getCurrentLocalDateTime(): LocalDateTime =
getCurrentInstant().toLocalDateTime(TimeZone.currentSystemDefault())
fun getCurrentDateString(): String =
getCurrentLocalDateTime().date.toString()
fun getCurrentTimeString(): String =
getCurrentLocalDateTime().time.toString()
fun timestampToLocalDateTime(
timestamp: Long,
timeZone: TimeZone = TimeZone.currentSystemDefault()
): LocalDateTime =
Instant.fromEpochMilliseconds(timestamp).toLocalDateTime(timeZone)
fun localDateTimeToTimestamp(
localDateTime: LocalDateTime,
timeZone: TimeZone = TimeZone.currentSystemDefault()
): Long =
localDateTime.toInstant(timeZone).toEpochMilliseconds()
fun formatLocalDateTime(localDateTime: LocalDateTime, pattern: String): String {
return when (pattern) {
PATTERN_DEFAULT -> "${localDateTime.year}-${
localDateTime.monthNumber.toString().padStart(2, '0')
}-${localDateTime.dayOfMonth.toString().padStart(2, '0')} " +
"${
localDateTime.hour.toString().padStart(2, '0')
}:${
localDateTime.minute.toString().padStart(2, '0')
}:${localDateTime.second.toString().padStart(2, '0')}"
PATTERN_YYYY_MM_DD_HH_MM -> "${localDateTime.year}-${
localDateTime.monthNumber.toString().padStart(2, '0')
}-${localDateTime.dayOfMonth.toString().padStart(2, '0')} " +
"${
localDateTime.hour.toString().padStart(2, '0')
}:${localDateTime.minute.toString().padStart(2, '0')}"
PATTERN_DATE_ONLY -> "${localDateTime.year}-${
localDateTime.monthNumber.toString().padStart(2, '0')
}-${localDateTime.dayOfMonth.toString().padStart(2, '0')}"
PATTERN_TIME_ONLY -> "${
localDateTime.hour.toString().padStart(2, '0')
}:${localDateTime.minute.toString().padStart(2, '0')}:${
localDateTime.second.toString().padStart(2, '0')
}"
PATTERN_DATE_ONLY_ -> "${localDateTime.year}/${
localDateTime.monthNumber.toString().padStart(2, '0')
}/${localDateTime.dayOfMonth.toString().padStart(2, '0')}"
PATTERN_CHINESE_DATE -> "${localDateTime.year}年${
localDateTime.monthNumber.toString().padStart(2, '0')
}月${localDateTime.dayOfMonth.toString().padStart(2, '0')}日"
PATTERN_HH_MM_ONLY -> "${
localDateTime.hour.toString().padStart(2, '0')
}:${localDateTime.minute.toString().padStart(2, '0')}"
PATTERN_YEAR_MONTH -> "${localDateTime.year}-${
localDateTime.monthNumber.toString().padStart(2, '0')
}"
PATTERN_MONTH_DAY -> "${
localDateTime.monthNumber.toString().padStart(2, '0')
}-${localDateTime.dayOfMonth.toString().padStart(2, '0')}"
PATTERN_MONTH_DAY_ -> "${
localDateTime.monthNumber.toString().padStart(2, '0')
}/${localDateTime.dayOfMonth.toString().padStart(2, '0')}"
else -> localDateTime.toString()
}
}
fun getDateTimePeriod(
start: Instant,
end: Instant,
timeZone: TimeZone = TimeZone.currentSystemDefault()
): DateTimePeriod =
start.periodUntil(end, timeZone)
fun getDurationBetween(start: Instant, end: Instant): Duration =
end - start
}
fun Instant.toLocalDateTimeIn(timeZone: TimeZone = TimeZone.currentSystemDefault()): LocalDateTime =
this.toLocalDateTime(timeZone)
fun Long.toInstant(): Instant = Instant.fromEpochMilliseconds(this)
fun LocalDateTime.toInstantIn(timeZone: TimeZone = TimeZone.currentSystemDefault()): Instant =
this.toInstant(timeZone)
val Long.instant: Instant
get() = Instant.fromEpochMilliseconds(this)
val Long.localDateTime: LocalDateTime
get() = this.instant.toLocalDateTime(TimeZone.currentSystemDefault())
fun Long.toLocalDateTimeIn(timeZone: TimeZone): LocalDateTime =
this.instant.toLocalDateTime(timeZone)
fun LocalDateTime.toTimestamp(timeZone: TimeZone = TimeZone.currentSystemDefault()): Long =
this.toInstant(timeZone).toEpochMilliseconds()
val Long.year: Int
get() = this.localDateTime.year
val Long.month: Int
get() = this.localDateTime.monthNumber
val Long.monthName: String
get() = this.localDateTime.month.name.lowercase()
.replaceFirstChar { it.uppercase() }
val Long.day: Int
get() = this.localDateTime.dayOfMonth
val Long.dayOfWeek: String
get() = this.localDateTime.dayOfWeek.name.lowercase()
.replaceFirstChar { it.uppercase() }
val Long.hour: Int
get() = this.localDateTime.hour
val Long.minute: Int
get() = this.localDateTime.minute
val Long.second: Int
get() = this.localDateTime.second
val Long.millisecond: Int
get() = this.localDateTime.nanosecond / 1_000_000
fun Long.toFormattedString(): String = this.toFormattedString(PATTERN_DEFAULT)
fun Long.toFormattedString(pattern: String): String {
val dateTime = this.localDateTime
return DateTimeUtils.formatLocalDateTime(dateTime, pattern)
}
fun Long.toRelativeTimeString(): String {
val duration = (DateTimeUtils.currentTimestamp - this).toDuration(DurationUnit.MILLISECONDS)
return when {
duration.isNegative() -> "未来"
duration.inWholeDays > 365 -> "${duration.inWholeDays / 365}年前"
duration.inWholeDays > 30 -> "${duration.inWholeDays / 30}个月前"
duration.inWholeDays > 7 -> "${duration.inWholeDays / 7}周前"
duration.inWholeDays > 0 -> "${duration.inWholeDays}天前"
duration.inWholeHours > 0 -> "${duration.inWholeHours}小时前"
duration.inWholeMinutes > 0 -> "${duration.inWholeMinutes}分钟前"
else -> "刚刚"
}
}
fun Long.toSmartTimeString(): String {
return when {
this.isToday() -> this.toFormattedString(PATTERN_HH_MM_ONLY)
this.isYesterday() -> "昨天 ${this.toFormattedString(PATTERN_HH_MM_ONLY)}"
this.isThisYear() -> this.toFormattedString("MM-dd HH:mm")
else -> this.toFormattedString(PATTERN_DATE_ONLY)
}
}
fun Long.isToday(): Boolean {
val today = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()).date
val targetDate = this.instant.toLocalDateTime(TimeZone.currentSystemDefault()).date
return today == targetDate
}
fun Long.isTomorrow(): Boolean {
val today = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()).date
val tomorrow = today.plus(DatePeriod(days = 1))
val targetDate = this.instant.toLocalDateTime(TimeZone.currentSystemDefault()).date
return tomorrow == targetDate
}
fun Long.isYesterday(): Boolean {
val today = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()).date
val yesterday = today.minus(DatePeriod(days = 1))
val targetDate = this.instant.toLocalDateTime(TimeZone.currentSystemDefault()).date
return yesterday == targetDate
}
fun Long.isThisYear(): Boolean {
val currentYear = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()).year
return this.year == currentYear
}
fun Long.isInYear(targetYear: Int): Boolean = this.year == targetYear
fun Long.isInMonth(targetMonth: Int): Boolean = this.month == targetMonth
fun Long.plusDuration(duration: Duration): Long =
(this.duration + duration).inWholeMilliseconds
fun Long.plusDays(days: Int): Long = this.plusDuration(days.toDuration(DurationUnit.DAYS))
fun Long.plusHours(hours: Int): Long = this.plusDuration(hours.toDuration(DurationUnit.HOURS))
fun Long.plusMinutes(minutes: Int): Long =
this.plusDuration(minutes.toDuration(DurationUnit.MINUTES))
fun Long.plusSeconds(seconds: Int): Long =
this.plusDuration(seconds.toDuration(DurationUnit.SECONDS))
fun Long.minusDuration(duration: Duration): Long =
(this.duration - duration).inWholeMilliseconds
fun Long.minusDays(days: Int): Long = this.minusDuration(days.toDuration(DurationUnit.DAYS))
fun Long.minusHours(hours: Int): Long = this.minusDuration(hours.toDuration(DurationUnit.HOURS))
fun Long.minusMinutes(minutes: Int): Long =
this.minusDuration(minutes.toDuration(DurationUnit.MINUTES))
fun Long.timeUntil(otherTimestamp: Long): Duration =
(otherTimestamp - this).toDuration(DurationUnit.MILLISECONDS)
fun Long.timeFromNow(): Duration =
(DateTimeUtils.currentTimestamp - this).toDuration(DurationUnit.MILLISECONDS)
fun Long.daysBetween(otherTimestamp: Long): Int {
val startDate = this.instant.toLocalDateTime(TimeZone.currentSystemDefault()).date
val endDate = otherTimestamp.instant.toLocalDateTime(TimeZone.currentSystemDefault()).date
return startDate.daysUntil(endDate)
}
fun Long.hoursBetween(otherTimestamp: Long): Long =
this.timeUntil(otherTimestamp).inWholeHours
fun Long.minutesBetween(otherTimestamp: Long): Long =
this.timeUntil(otherTimestamp).inWholeMinutes
fun Long.atStartOfDay(timeZone: TimeZone = TimeZone.currentSystemDefault()): Long {
val localDate = this.instant.toLocalDateTime(timeZone).date
return localDate.atStartOfDayIn(timeZone).toEpochMilliseconds()
}
fun Long.atEndOfDay(timeZone: TimeZone = TimeZone.currentSystemDefault()): Long {
return this.atStartOfDay(timeZone).plusDays(1) - 1
}
fun Long.atStartOfWeek(timeZone: TimeZone = TimeZone.currentSystemDefault()): Long {
val localDateTime = this.instant.toLocalDateTime(timeZone)
val daysFromMonday = (localDateTime.dayOfWeek.isoDayNumber - 1) % 7
return this.minusDays(daysFromMonday).atStartOfDay(timeZone)
}
fun Long.atStartOfMonth(timeZone: TimeZone = TimeZone.currentSystemDefault()): Long {
val localDateTime = this.instant.toLocalDateTime(timeZone)
val firstDayOfMonth = LocalDate(localDateTime.year, localDateTime.month, 1)
return firstDayOfMonth.atStartOfDayIn(timeZone).toEpochMilliseconds()
}
fun Long.isBetween(startTimestamp: Long, endTimestamp: Long): Boolean =
this in startTimestamp..endTimestamp
val Long.duration: Duration
get() = this.toDuration(DurationUnit.MILLISECONDS)
val Long.quarter: Int
get() = (this.month - 1) / 3 + 1
val Long.weekOfYear: Int
get() {
val localDate = this.instant.toLocalDateTime(TimeZone.currentSystemDefault()).date
val firstDayOfYear = LocalDate(localDate.year, 1, 1)
val daysBetween = firstDayOfYear.daysUntil(localDate)
return daysBetween / 7 + 1
}
const val PATTERN_DEFAULT = "yyyy-MM-dd HH:mm:ss"
const val PATTERN_YYYY_MM_DD_HH_MM = "yyyy-MM-dd HH:mm"
const val PATTERN_CHINESE_DATE = "yyyy年MM月dd日"
const val PATTERN_DATE_ONLY = "yyyy-MM-dd"
const val PATTERN_DATE_ONLY_ = "yyyy/MM/dd"
const val PATTERN_TIME_ONLY = "HH:mm:ss"
const val PATTERN_HH_MM_ONLY = "HH:mm"
const val PATTERN_YEAR_MONTH = "yyyy-MM"
const val PATTERN_MONTH_DAY = "MM-dd"
const val PATTERN_MONTH_DAY_ = "MM/dd"
fun Long.toInstantFromSeconds(): Instant = Instant.fromEpochSeconds(this)
fun Long.toLocalDateTimeFromSeconds(): LocalDateTime =
this.toInstantFromSeconds().toLocalDateTime(TimeZone.currentSystemDefault())
fun Long.toFormattedStringFromSeconds(pattern: String): String {
val dateTime = this.toLocalDateTimeFromSeconds()
return DateTimeUtils.formatLocalDateTime(dateTime, pattern)
}