Exposed时间类型全解析:Java Time vs Joda-Time vs Kotlinx DateTime
Exposed时间类型全解析:Java Time vs Joda-Time vs Kotlinx DateTime
【免费下载链接】Exposed Kotlin SQL Framework 项目地址: https://gitcode.com/gh_mirrors/ex/Exposed
你还在为Kotlin项目选择合适的时间处理方案而烦恼吗?Exposed框架提供了三种时间模块,却不知道它们的区别和适用场景?本文将帮你彻底理清exposed-java-time、exposed-jodatime和exposed-kotlin-datetime的差异,读完你将能够:
- 掌握三种时间模块的核心特性与适用场景
- 正确选择符合项目需求的时间处理方案
- 熟练使用各模块的时间类型与数据库交互
时间模块对比分析
Exposed通过三个独立模块提供时间支持,每个模块基于不同的时间库,适用于不同场景:
| 模块 | 基础库 | 适用场景 |
|---|---|---|
exposed-kotlin-datetime |
kotlinx-datetime | 现代Kotlin优先方案,推荐新项目使用 |
exposed-java-time |
Java 8 Time API | 需与Java代码集成或需要Java时间API兼容性 |
exposed-jodatime |
Joda-Time | 遗留系统支持,提供额外时间部分提取功能 |
模块文件路径
- Kotlin DateTime模块源码:exposed-kotlin-datetime/
- Java Time模块源码:exposed-java-time/
- Joda-Time模块源码:exposed-jodatime/
- 官方文档:documentation-website/Writerside/topics/Date-and-time-types.topic
基础使用示例
三种模块的表定义语法相似,但导入和时间类型实例化有所区别:
import org.jetbrains.exposed.v1.core.Table
// For exposed-kotlin-datetime (推荐)
import org.jetbrains.exposed.v1.datetime.*
// For exposed-java-time
import org.jetbrains.exposed.v1.javatime.*
// For exposed-jodatime
import org.jetbrains.exposed.v1.jodatime.*
object Events : Table() {
val id = integer("id").autoIncrement()
val name = varchar("name", 50)
val startDate = date("start_date") // 映射到LocalDate
val startTime = time("start_time") // 映射到LocalTime
val createdAt = datetime("created_at") // 映射到LocalDateTime
.defaultExpression(CurrentDateTime)
val updatedAt = timestamp("updated_at") // 映射到Instant
val scheduledAt = timestampWithTimeZone("scheduled_at") // 映射到OffsetDateTime
override val primaryKey = PrimaryKey(id)
}
支持的时间类型详解
1. 日期类型 (date())
映射到数据库DATE类型,用于存储无时间部分的日期值:
val birthDate = date("birth_date")
// 使用exposed-kotlin-datetime (推荐)
Events.insert {
it[birthDate] = LocalDate(1990, 1, 1)
}
// 使用exposed-java-time
Events.insert {
it[birthDate] = LocalDate.of(1990, 1, 1)
}
// 使用exposed-jodatime
Events.insert {
it[birthDate] = org.joda.time.LocalDate(1990, 1, 1)
}
2. 日期时间类型 (datetime())
映射到数据库DATETIME类型,存储日期和时间:
val createdAt = datetime("created_at")
.defaultExpression(CurrentDateTime) // 插入时自动设置当前日期时间
// 使用exposed-kotlin-datetime (推荐)
Events.insert {
it[createdAt] = Clock.System.now()
.toLocalDateTime(TimeZone.UTC)
}
// 使用exposed-java-time
Events.insert {
it[createdAt] = LocalDateTime.now()
}
// 使用exposed-jodatime
Events.insert {
it[createdAt] = DateTime.now()
}
注意:使用
exposed-jodatime时,可以使用year()、month()、day()、hour()、minute()和second()函数提取时间部分
3. 时间类型 (time())
映射到数据库TIME类型,存储无日期部分的时间值:
val startTime = time("start_time")
// 使用exposed-kotlin-datetime (推荐)
Events.insert {
it[startTime] = LocalTime(9, 0) // 09:00
}
// 使用exposed-java-time
Events.insert {
it[startTime] = LocalTime.of(9, 0) // 09:00
}
// 使用exposed-jodatime
Events.insert {
it[startTime] = org.joda.time.LocalTime(9, 0) // 09:00
}
4. 时间戳类型 (timestamp())
映射到数据库TIMESTAMP类型,不支持exposed-jodatime:
val lastModified = timestamp("last_modified")
// 使用exposed-kotlin-datetime (推荐)
Events.insert {
it[lastModified] = Clock.System.now()
}
// 使用exposed-java-time
Events.insert {
it[lastModified] = Instant.now()
}
5. 带时区时间戳 (timestampWithTimeZone())
映射到数据库TIMESTAMP WITH TIME ZONE类型,不支持exposed-jodatime:
val scheduledAt = timestampWithTimeZone("scheduled_at")
// 使用exposed-kotlin-datetime (推荐)
Events.insert {
it[scheduledAt] = Clock.System.now().toOffsetDateTime(TimeZone.UTC)
}
// 使用exposed-java-time
Events.insert {
it[scheduledAt] = OffsetDateTime.now(ZoneOffset.UTC)
}
警告:PostgreSQL和MySQL会将时间戳存储为UTC,从而丢失原始时区信息。要在这些数据库中保留原始时区,需将时区信息存储在单独的列中。
类型支持对比
三种模块支持的时间类型有所不同,以下是详细对比:
| 类型 | exposed-kotlin-datetime | exposed-java-time | exposed-jodatime |
|---|---|---|---|
date() |
支持 (LocalDate) | 支持 (LocalDate) | 支持 (LocalDate) |
time() |
支持 (LocalTime) | 支持 (LocalTime) | 支持 (LocalTime) |
datetime() |
支持 (LocalDateTime) | 支持 (LocalDateTime) | 支持 (DateTime) |
timestamp() |
支持 (Instant) | 支持 (Instant) | 不支持 |
timestampWithTimeZone() |
支持 (OffsetDateTime) | 支持 (OffsetDateTime) | 不支持 |
项目选择指南
推荐使用exposed-kotlin-datetime的场景
- 新建Kotlin项目
- 无需与Java时间API交互
- 需要现代、简洁的Kotlin风格API
- 关注未来兼容性和框架推荐方案
推荐使用exposed-java-time的场景
- 需要与Java代码或库集成
- 团队更熟悉Java Time API
- 项目中已有大量Java时间类型使用
推荐使用exposed-jodatime的场景
- 维护遗留系统
- 需要时间部分提取功能
- 项目已深度使用Joda-Time库
实践示例:事件调度系统
假设我们正在构建一个事件调度系统,需要存储不同时区的事件时间。使用exposed-kotlin-datetime模块的实现如下:
import org.jetbrains.exposed.v1.core.Database
import org.jetbrains.exposed.v1.datetime.*
import org.jetbrains.exposed.v1.sql.*
import org.jetbrains.exposed.v1.sql.transactions.transaction
import kotlinx.datetime.Clock
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toOffsetDateTime
object Events : Table() {
val id = integer("id").autoIncrement()
val title = varchar("title", 100)
val startTime = datetime("start_time")
val endTime = datetime("end_time")
val scheduledAt = timestampWithTimeZone("scheduled_at")
override val primaryKey = PrimaryKey(id)
}
fun main() {
Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver")
transaction {
SchemaUtils.create(Events)
val now = Clock.System.now()
Events.insert {
it[title] = "Kotlin Meetup"
it[startTime] = now.toLocalDateTime(TimeZone.of("Asia/Shanghai"))
it[endTime] = now.toLocalDateTime(TimeZone.of("Asia/Shanghai")).plusHours(2)
it[scheduledAt] = now.toOffsetDateTime(TimeZone.of("Asia/Shanghai"))
}
// 查询今天的事件
val today = Clock.System.now().toLocalDate(TimeZone.currentSystemDefault())
val todayEvents = Events.select {
Events.startTime greaterEq today.atStartOfDay() and
Events.startTime lessEq today.atEndOfDay()
}
println("今天的事件:")
todayEvents.forEach {
println("${it[Events.title]}: ${it[Events.startTime]}")
}
}
}
总结与最佳实践
-
优先选择官方推荐的
exposed-kotlin-datetime:作为现代Kotlin项目的首选,提供最简洁的API和最佳的未来兼容性。 -
注意时区处理:始终明确指定时区,避免依赖系统默认时区导致的问题。
-
合理设计时间列:
- 仅需日期时使用
date() - 本地时间使用
datetime() - 跨时区时间使用
timestampWithTimeZone() - 记录修改时间使用
timestamp()并设置默认值
- 仅需日期时使用
-
避免混合使用不同模块:项目中坚持使用一种时间模块,避免类型转换复杂性。
通过本文的介绍,你已经了解了Exposed三种时间模块的核心差异和使用方法。选择合适的时间处理方案,将为你的项目带来更好的可维护性和性能。如有更多疑问,可以查阅官方时间类型文档或Exposed示例项目:samples/
希望本文对你有所帮助!如果你觉得内容有价值,请点赞收藏,关注获取更多Exposed使用技巧。下期我们将探讨Exposed中的事务管理最佳实践。
【免费下载链接】Exposed Kotlin SQL Framework 项目地址: https://gitcode.com/gh_mirrors/ex/Exposed
更多推荐
所有评论(0)