You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
163 lines
4.0 KiB
163 lines
4.0 KiB
4 weeks ago
|
# PostgreSQL DateTime 时间戳问题修复总结
|
||
|
|
||
|
## 问题描述
|
||
|
|
||
|
在PostgreSQL数据库中,当使用`timestamp with time zone`类型时,只支持UTC时间的DateTime值。错误信息:
|
||
|
|
||
|
```
|
||
|
Cannot write DateTime with Kind=Unspecified to PostgreSQL type 'timestamp with time zone', only UTC is supported.
|
||
|
```
|
||
|
|
||
|
## 问题分析
|
||
|
|
||
|
### 1. 根本原因
|
||
|
- PostgreSQL的`timestamp with time zone`类型要求DateTime的Kind必须是UTC
|
||
|
- 代码中虽然使用了`DateTime.UtcNow`,但实体配置中没有明确指定数据库列类型
|
||
|
- 响应类中使用了`DateTime.UtcNow`而不是从实体中获取的时间
|
||
|
|
||
|
### 2. 问题位置
|
||
|
1. **实体配置类**:缺少`.HasColumnType("timestamp with time zone")`配置
|
||
|
2. **响应构建**:使用了`DateTime.UtcNow`而不是实体的时间字段
|
||
|
|
||
|
## 修复方案
|
||
|
|
||
|
### 1. 修复的实体配置类
|
||
|
|
||
|
#### Device相关
|
||
|
- ✅ `ProtocolVersionConfiguration`
|
||
|
- ✅ `CellularDeviceConfiguration`
|
||
|
|
||
|
#### Identity相关
|
||
|
- ✅ `AppRoleConfiguration`
|
||
|
- ✅ `AppUserConfiguration`
|
||
|
|
||
|
#### 其他
|
||
|
- ✅ `BaseEntityConfiguration`
|
||
|
- ✅ `PermissionConfiguration`
|
||
|
- ✅ `LoginLogConfiguration`
|
||
|
|
||
|
### 2. 修复的CommandHandler响应
|
||
|
|
||
|
#### ProtocolVersion相关
|
||
|
- ✅ `UpdateProtocolVersionCommandHandler`:使用`existingProtocolVersion.UpdatedAt`
|
||
|
|
||
|
#### Device相关
|
||
|
- ✅ `UpdateDeviceCommandHandler`:使用`existingDevice.UpdatedAt`
|
||
|
|
||
|
### 3. 修复内容
|
||
|
|
||
|
#### 实体配置类修复
|
||
|
```csharp
|
||
|
// 修复前
|
||
|
builder.Property(e => e.CreatedAt)
|
||
|
.IsRequired()
|
||
|
.HasComment("创建时间");
|
||
|
|
||
|
// 修复后
|
||
|
builder.Property(e => e.CreatedAt)
|
||
|
.IsRequired()
|
||
|
.HasColumnType("timestamp with time zone")
|
||
|
.HasComment("创建时间");
|
||
|
```
|
||
|
|
||
|
#### 响应构建修复
|
||
|
```csharp
|
||
|
// 修复前
|
||
|
UpdatedAt = DateTime.UtcNow
|
||
|
|
||
|
// 修复后
|
||
|
UpdatedAt = existingEntity.UpdatedAt
|
||
|
```
|
||
|
|
||
|
## 修复的字段类型
|
||
|
|
||
|
### 1. 时间戳字段
|
||
|
- `CreatedAt` / `CreatedTime`:创建时间
|
||
|
- `UpdatedAt` / `ModifiedTime`:更新时间
|
||
|
- `LastLoginTime`:最后登录时间
|
||
|
- `LoginTime`:登录时间
|
||
|
- `ReleaseDate`:发布日期
|
||
|
|
||
|
### 2. 数据库类型
|
||
|
- PostgreSQL: `timestamp with time zone`
|
||
|
- 确保所有DateTime字段都使用UTC时间
|
||
|
|
||
|
## 最佳实践
|
||
|
|
||
|
### 1. 时间处理原则
|
||
|
```csharp
|
||
|
// ✅ 正确:使用UTC时间
|
||
|
DateTime.UtcNow
|
||
|
|
||
|
// ❌ 错误:使用本地时间
|
||
|
DateTime.Now
|
||
|
|
||
|
// ❌ 错误:使用未指定Kind的时间
|
||
|
new DateTime(2024, 1, 1)
|
||
|
```
|
||
|
|
||
|
### 2. 实体配置
|
||
|
```csharp
|
||
|
// ✅ 正确:明确指定PostgreSQL时间戳类型
|
||
|
builder.Property(e => e.CreatedAt)
|
||
|
.HasColumnType("timestamp with time zone");
|
||
|
|
||
|
// ❌ 错误:依赖默认类型推断
|
||
|
builder.Property(e => e.CreatedAt);
|
||
|
```
|
||
|
|
||
|
### 3. 响应构建
|
||
|
```csharp
|
||
|
// ✅ 正确:使用实体的时间字段
|
||
|
response.UpdatedAt = entity.UpdatedAt;
|
||
|
|
||
|
// ❌ 错误:重新生成时间
|
||
|
response.UpdatedAt = DateTime.UtcNow;
|
||
|
```
|
||
|
|
||
|
## 数据库迁移
|
||
|
|
||
|
### 1. 现有数据库
|
||
|
如果数据库中已有数据,需要确保:
|
||
|
- 所有DateTime字段都存储为UTC时间
|
||
|
- 列类型为`timestamp with time zone`
|
||
|
|
||
|
### 2. 新数据库
|
||
|
- 实体配置会自动生成正确的列类型
|
||
|
- 所有时间字段都会使用UTC时间
|
||
|
|
||
|
## 验证方法
|
||
|
|
||
|
### 1. 代码验证
|
||
|
```csharp
|
||
|
// 检查DateTime.Kind
|
||
|
if (entity.CreatedAt.Kind != DateTimeKind.Utc)
|
||
|
{
|
||
|
throw new InvalidOperationException("DateTime must be UTC");
|
||
|
}
|
||
|
```
|
||
|
|
||
|
### 2. 数据库验证
|
||
|
```sql
|
||
|
-- 检查列类型
|
||
|
SELECT column_name, data_type
|
||
|
FROM information_schema.columns
|
||
|
WHERE table_name = 'your_table'
|
||
|
AND column_name LIKE '%time%';
|
||
|
|
||
|
-- 检查时间值
|
||
|
SELECT created_at, updated_at
|
||
|
FROM your_table
|
||
|
LIMIT 5;
|
||
|
```
|
||
|
|
||
|
## 总结
|
||
|
|
||
|
这次修复确保了:
|
||
|
|
||
|
1. **数据类型一致性**:所有DateTime字段都使用PostgreSQL的`timestamp with time zone`类型
|
||
|
2. **时间标准统一**:所有时间都使用UTC标准
|
||
|
3. **配置明确性**:实体配置中明确指定了数据库列类型
|
||
|
4. **响应准确性**:响应中使用实体的实际时间而不是重新生成的时间
|
||
|
|
||
|
这种修复方式为整个系统提供了统一的时间处理标准,确保了与PostgreSQL数据库的完全兼容性。
|