版本基线
| 类别 | 版本 |
|---|---|
| c# | 12 (.net 8) |
| java | 17 (lts) |
| spring boot | 3.2.x |
| mybatis-plus | 3.5.9 |
1、日期时间对照
1.1 类型对比
| c# | java | 特点 |
|---|---|---|
datetime | localdatetime | 不含时区,日期 + 时间 |
datetimeoffset | offsetdatetime | 含 utc 偏移量 |
datetime.utcnow | instant | utc 时间戳(瞬时点) |
datetime + timezoneinfo | zoneddatetime | 带时区规则的完整时间 |
timespan | duration | 时间跨度 |
dateonly (.net 6+) | localdate | 仅日期 |
timeonly (.net 6+) | localtime | 仅时间 |
java 17 推荐:instant(时间戳)/ localdatetime(本地时间)/ zoneddatetime(带时区)是业内经验中覆盖绝大多数业务场景的三类时间类型,避免使用已过时的 java.util.date 与 calendar。对于仅日期或仅时间的场景,分别使用 localdate 和 localtime。
1.2 数据结构图

// java 17 时间 api(推荐 java.time)
import java.time.instant;
import java.time.localdatetime;
import java.time.offsetdatetime;
import java.time.zoneddatetime;
import java.time.zoneid;
import java.time.localdate;
import java.time.localtime;
import java.time.format.datetimeformatter;
public class datetimeexample {
public static void main(string[] args) {
// 创建各种时间类型实例
instant now = instant.now();
localdatetime local = localdatetime.now();
offsetdatetime offset = offsetdatetime.now();
zoneddatetime shanghai = zoneddatetime.now(zoneid.of("asia/shanghai"));
localdate today = localdate.now();
localtime currenttime = localtime.now();
// 格式化(iso 8601:instant.tostring() 默认就是 iso 8601 格式)
string iso = now.tostring();
system.out.println("iso 8601 格式: " + iso);
// 解析
instant parsed = instant.parse("2026-08-02t12:00:00z");
system.out.println("解析后的时间: " + parsed);
// 其他示例
system.out.println("本地日期时间: " + local);
system.out.println("上海时区时间: " + shanghai);
system.out.println("仅日期: " + today);
system.out.println("仅时间: " + currenttime);
}
}1.3 常用操作示例
java 常用操作
import java.time.duration;
import java.time.period;
import java.time.zoneid;
import java.time.instant;
import java.time.localdatetime;
import java.time.zoneddatetime;
import java.time.localdate;
import java.time.format.datetimeformatter;
public class commonoperationsexample {
public static void main(string[] args) {
// 创建特定时间
localdatetime specifictime = localdatetime.of(2026, 8, 5, 14, 30, 0);
instant specificinstant = instant.parse("2026-08-05t06:30:00z");
system.out.println("特定时间: " + specifictime);
system.out.println("特定时间戳: " + specificinstant);
// 时间计算
localdatetime tomorrow = localdatetime.now().plusdays(1);
instant onehourlater = instant.now().plus(duration.ofhours(1));
system.out.println("明天: " + tomorrow);
system.out.println("一小时后: " + onehourlater);
// 时区转换
zoneddatetime utctime = zoneddatetime.now(zoneid.of("utc"));
zoneddatetime newyorktime = utctime.withzonesameinstant(zoneid.of("america/new_york"));
system.out.println("utc时间: " + utctime);
system.out.println("纽约时间: " + newyorktime);
// 时间间隔
localdatetime starttime = localdatetime.of(2026, 8, 5, 9, 0, 0);
localdatetime endtime = localdatetime.of(2026, 8, 5, 17, 30, 0);
duration duration = duration.between(starttime, endtime);
system.out.println("工作时间间隔: " + duration.tohours() + "小时");
// 日期间隔
localdate startdate = localdate.of(2026, 1, 1);
localdate enddate = localdate.of(2026, 12, 31);
period period = period.between(startdate, enddate);
system.out.println("日期间隔: " + period.getyears() + "年" +
period.getmonths() + "月" + period.getdays() + "天");
}
}c# 常用操作
using system;
using system.globalization;
public class commonoperationsexample
{
public static void main(string[] args)
{
// 创建特定时间
datetime specifictime = new datetime(2026, 8, 5, 14, 30, 0);
datetimeoffset specificoffset = new datetimeoffset(2026, 8, 5, 14, 30, 0, timespan.fromhours(8));
console.writeline($"特定时间: {specifictime}");
console.writeline($"特定时间偏移: {specificoffset}");
// 时间计算
datetime tomorrow = datetime.now.adddays(1);
datetimeoffset onehourlater = datetimeoffset.utcnow.addhours(1);
console.writeline($"明天: {tomorrow}");
console.writeline($"一小时后: {onehourlater}");
// 时区转换
timezoneinfo chinazone = timezoneinfo.findsystemtimezonebyid("china standard time");
datetime utctime = datetime.utcnow;
datetime chinatime = timezoneinfo.converttimefromutc(utctime, chinazone);
console.writeline($"utc时间: {utctime}");
console.writeline($"中国时间: {chinatime}");
// 时间间隔
datetime starttime = new datetime(2026, 8, 5, 9, 0, 0);
datetime endtime = new datetime(2026, 8, 5, 17, 30, 0);
timespan timespan = endtime - starttime;
console.writeline($"工作时间间隔: {timespan.totalhours}小时");
// 日期间隔
dateonly startdate = new dateonly(2026, 1, 1);
dateonly enddate = new dateonly(2026, 12, 31);
int daysbetween = enddate.daynumber - startdate.daynumber;
console.writeline($"日期间隔: {daysbetween}天");
}
}
对比总结
| 操作 | java (java.time) | c# (.net) |
|---|---|---|
| 创建时间 | localdatetime.of() | new datetime() |
| 解析字符串 | localdatetime.parse() | datetime.parse() |
| 格式化 | datetimeformatter | tostring() |
| 时区转换 | withzonesameinstant() | timezoneinfo.converttime() |
| 时间计算 | plusdays() / minushours() | adddays() / addhours() |
| 时间间隔 | duration / period | timespan |
1.4 跨时区序列化注意事项
跨语言(前后端 / 跨服务)序列化时,统一用 utc + iso 8601 字符串。避免直接传 datetime.now(带本地时区)导致接收方误判。
| 注意事项 | 说明 |
|---|---|
| 统一 utc | 数据库 / 消息队列 / api 一律用 utc;展示层再转本地时区 |
| iso 8601 字符串 | 例:2026-08-02t12:00:00z,自描述、可读、跨语言无歧义 |
| java jackson | spring.jackson.time-zone=utc、spring.jackson.date-format=yyyy-mm-dd't'hh:mm:ss'z' |
| c# json | system.text.json 配置:jsonserializeroptions { propertynamingpolicy = jsonnamingpolicy.camelcase, converters = { new jsonstringenumconverter() } },默认序列化 datetime 和 datetimeoffset 为 iso 8601 格式。如需强制 utc,可使用 jsonconverter 或确保数据源为 utc。 |
| 数据库 | postgresql 用 timestamp with time zone、mysql 用 datetime + 应用层统一 utc |
| 时区存储 | 数据库额外存储时区信息(如 zone_id 列),用于展示层转换 |
1.5 日期与字符串转化
日期时间与字符串之间的相互转换是日常开发中的高频操作。java 的 java.time.format.datetimeformatter 与 c# 的 datetime.tostring() / datetime.parse() 提供了丰富的格式化与解析能力。
java 17 格式化与解析
import java.time.localdatetime;
import java.time.zoneddatetime;
import java.time.instant;
import java.time.zoneid;
import java.time.format.datetimeformatter;
import java.time.format.datetimeparseexception;
import java.time.format.formatstyle;
import java.util.locale;
public class formatparseexample {
public static void main(string[] args) {
// 1. 预定义格式器(iso 标准)
instant instant = instant.now();
string isostring = instant.tostring(); // "2026-08-04t15:57:18.123z"
instant parsedinstant = instant.parse(isostring);
system.out.println("iso 8601格式: " + isostring);
system.out.println("解析后的时间戳: " + parsedinstant);
// 2. 自定义格式器
datetimeformatter formatter = datetimeformatter.ofpattern("yyyy-mm-dd hh:mm:ss");
localdatetime now = localdatetime.now();
// 格式化:日期时间 → 字符串
string formatted = now.format(formatter); // "2026-08-04 23:57:18"
system.out.println("自定义格式: " + formatted);
// 解析:字符串 → 日期时间
localdatetime parsed = localdatetime.parse("2026-08-04 23:57:18", formatter);
system.out.println("解析后的时间: " + parsed);
// 3. 带时区的格式化
datetimeformatter zonedformatter = datetimeformatter.ofpattern("yyyy-mm-dd hh:mm:ss z");
zoneddatetime zoned = zoneddatetime.now();
string zonedstring = zoned.format(zonedformatter); // "2026-08-04 23:57:18 cst"
system.out.println("带时区格式: " + zonedstring);
// 4. 本地化格式
datetimeformatter localizedformatter = datetimeformatter.oflocalizeddatetime(formatstyle.medium)
.withlocale(locale.china);
string localized = now.format(localizedformatter); // "2026年8月4日 下午11:57:18"
system.out.println("本地化格式: " + localized);
// 5. 错误处理
try {
localdatetime invalid = localdatetime.parse("2026-13-45 25:61:99", formatter);
system.out.println("无效时间: " + invalid);
} catch (datetimeparseexception e) {
system.err.println("解析失败: " + e.getmessage());
}
}
}c# 12 (.net 8) 格式化与解析
using system;
using system.globalization;
public class formatparseexample
{
public static void main(string[] args)
{
// 1. 标准格式字符串
datetime utcnow = datetime.utcnow;
string isostring = utcnow.tostring("o"); // iso 8601: "2026-08-04t15:57:18.1234567z"
string rfc1123 = utcnow.tostring("r"); // rfc 1123: "thu, 04 aug 2026 15:57:18 gmt"
console.writeline($"iso 8601格式: {isostring}");
console.writeline($"rfc 1123格式: {rfc1123}");
// 2. 自定义格式字符串
datetime localnow = datetime.now;
string customformat = localnow.tostring("yyyy-mm-dd hh:mm:ss"); // "2026-08-04 23:57:18"
string chineseformat = localnow.tostring("yyyy年m月d日 hh时mm分ss秒"); // "2026年8月4日 23时57分18秒"
console.writeline($"自定义格式: {customformat}");
console.writeline($"中文格式: {chineseformat}");
// 3. 解析字符串
datetime parsed1 = datetime.parse("2026-08-04 23:57:18");
console.writeline($"解析结果1: {parsed1}");
datetime parsed2 = datetime.parseexact("04/08/2026 23:57", "dd/mm/yyyy hh:mm", cultureinfo.invariantculture);
console.writeline($"解析结果2: {parsed2}");
// 4. 带时区信息的 datetimeoffset
datetimeoffset offsetnow = datetimeoffset.utcnow;
string offsetstring = offsetnow.tostring("yyyy-mm-ddthh:mm:sszzz"); // "2026-08-04t15:57:18+00:00"
console.writeline($"带时区偏移格式: {offsetstring}");
// 5. 使用 cultureinfo 本地化
cultureinfo chinaculture = new cultureinfo("zh-cn");
string localized = localnow.tostring("f", chinaculture); // "2026年8月4日 23:57:18"
console.writeline($"本地化完整格式: {localized}");
// 6. 错误处理 - 使用 tryparse 避免异常
string invaliddatestr = "2026-13-45 25:61:99";
if (datetime.tryparse(invaliddatestr, out datetime validdate))
{
console.writeline($"解析成功: {validdate}");
}
else
{
console.writeline($"解析失败: 字符串 '{invaliddatestr}' 不是有效的 datetime");
}
// 7. 使用 tryparseexact 进行精确解析
string exactdatestr = "2026-08-04 23:57:18";
if (datetime.tryparseexact(exactdatestr, "yyyy-mm-dd hh:mm:ss",
cultureinfo.invariantculture, datetimestyles.none, out datetime exactdate))
{
console.writeline($"精确解析成功: {exactdate}");
}
else
{
console.writeline($"精确解析失败: 字符串 '{exactdatestr}' 不符合指定格式");
}
}
}
最佳实践对比
| 场景 | java 17 (java.time) | c# 12 (.net 8) | 说明 |
|---|---|---|---|
| iso 8601 格式 | instant.tostring() / instant.parse() | datetime.tostring("o") / datetime.parse() | 跨语言通信首选 |
| 数据库时间格式 | localdatetime.format(datetimeformatter.iso_local_date_time) | datetime.tostring("yyyy-mm-dd hh:mm:ss") | mysql datetime 格式 |
| 前端展示格式 | zoneddatetime.format(datetimeformatter.ofpattern("yyyy-mm-dd hh:mm:ss z")) | datetime.tostring("yyyy-mm-dd hh:mm:ss zzz") | 带时区信息 |
| 仅日期 | localdate.format(datetimeformatter.iso_local_date) | dateonly.tostring("yyyy-mm-dd") | 生日、纪念日等 |
| 仅时间 | localtime.format(datetimeformatter.iso_local_time) | timeonly.tostring("hh:mm:ss") | 营业时间、会议时间 |
| 错误处理 | datetimeparseexception | formatexception | 解析失败时捕获异常 |
性能优化建议
重用 formatter 实例:datetimeformatter 和格式字符串解析都是线程安全的,应创建静态实例重用:
// java - 类级别定义
public class datetimeutils {
private static final datetimeformatter custom_formatter =
datetimeformatter.ofpattern("yyyy-mm-dd hh:mm:ss");
public static string formatdatetime(localdatetime datetime) {
return datetime.format(custom_formatter);
}
public static localdatetime parsedatetime(string text) {
return localdatetime.parse(text, custom_formatter);
}
}// c# - 类级别定义
public static class datetimeutils
{
private static readonly string customformat = "yyyy-mm-dd hh:mm:ss";
private static readonly cultureinfo invariantculture = cultureinfo.invariantculture;
public static string formatdatetime(datetime datetime)
{
return datetime.tostring(customformat, invariantculture);
}
public static datetime parsedatetime(string text)
{
return datetime.parseexact(text, customformat, invariantculture);
}
}
避免频繁解析:对于高频转换,考虑缓存解析结果或使用对象池。
使用 tryparse 避免异常:
// java - 使用 try-catch
public class safeparser {
private static final datetimeformatter formatter =
datetimeformatter.ofpattern("yyyy-mm-dd hh:mm:ss");
public static optional<localdatetime> tryparse(string text) {
try {
localdatetime result = localdatetime.parse(text, formatter);
return optional.of(result);
} catch (datetimeparseexception e) {
return optional.empty();
}
}
}// c# - 使用 tryparse/tryparseexact
using system.globalization;
public static class safeparser
{
private static readonly string customformat = "yyyy-mm-dd hh:mm:ss";
private static readonly cultureinfo invariantculture = cultureinfo.invariantculture;
public static bool tryparsedatetime(string text, out datetime result)
{
return datetime.tryparse(text, out result);
}
public static bool tryparseexactdatetime(string text, out datetime result)
{
return datetime.tryparseexact(text, customformat,
invariantculture, datetimestyles.none, out result);
}
}
- 时区转换优化:批量处理时区转换时,预先计算时区偏移量,避免每次转换都查询时区数据库。#### 常见陷阱
- 时区丢失:
localdatetime和datetime(未指定 kind)不含时区信息,序列化到不同时区环境会出错。 - 夏令时:
zoneddatetime和datetime带datetimekind.local会自动处理夏令时,但跨时区计算时仍需谨慎。 - 格式混淆:
yyyy(年) vsyyyy(周年年),mm(月) vsmm(分),大小写敏感。 - 性能瓶颈:频繁创建
datetimeformatter实例或使用复杂正则表达式解析会导致性能下降。
faq
- q1:instant vs localdatetime vs zoneddatetime 区别?
instant 绝对时间点(utc 基准);localdatetime 墙钟时间(无时区);zoneddatetime 带时区时间(含 dst)。跨时区用 instant;本地业务用 localdatetime;显示用 zoneddatetime。 - q2:时区序列化反序列化如何处理?
jackson 默认javatimemodule序列化instant为 iso-8601 字符串(如2026-08-03t12:00:00z)。前端用new date(...)解析,再tolocalestring()按用户时区展示。spring boot 3.2 默认配置spring.jackson.serialization.write_dates_as_timestamps=false,输出 iso 字符串。
以上就是c#与java在日期时间处理方面的差异对比详解的详细内容,更多关于c#与java日期时间处理对比的资料请关注代码网其它相关文章!
发表评论