服务端渲染中如何让时间正确的显示到用户界面

转载自: qhan.wang/posts/ssr-t...

在习惯了csr渲染的今天,我们在处理时间时通常采用时间格式化插件如:dayjsmoment(已停止维护)、date-fns等时间库来解决,由于是在客户端,因此很少出现时间不正确的问题,因为这些时间插件已经在基于客户端时区环境帮我们处理好了。但在服务端渲染时,由于服务器一般会统一个时区标准,通常为零时区(也称中时区),以此为标准存储、处理时间,因此在服服器渲染时间数据后,呈现给用户的都是以服务器时区为标准的时间。由于不同用户访问所在时区不同,但时间返回又是服务器时间,不符合生活上直观的时间,因此我们该如何解决这类问题呢?

跳过前置概念:解决方案


背景

最近在处理nextjs服务端渲染时,遇到时间格式化后在,客户端展示晚上8小时的问题,刚开始想着是如何处理服务器时区问题,因此查找了很多关于修服务器配置,以及更改nodejs环境配置的资料,但收效甚微。后来在与做后端的朋友聊及此问题时,发现从一开始我的方向都错了。因此在这里总结一下,以供需要的同学处理类似问题。

时间基本概念

时间标准

  • GMT(Greenwich Mean Time)全名是格林威治平时(也称格林威治标准时间),于1884年确立。它规定太阳每天经过位于英国伦敦郊区的皇家格林威治天文台的时间为中午12点。1972年之前GMT一直是世界时间的标准。

  • UT:Universal Time 世界时。根据原子钟计算出来的时间。

  • UTC:Coordinated Universal Time 协调世界时。因为地球自转越来越慢,每年都会比前一年多出零点几秒,每隔几年协调世界时组织都会给世界时+1秒,让基于原子钟的世界时和基于天文学(人类感知)的格林尼治标准时间相差不至于太大。并将得到的时间称为UTC,这是现在使用的世界标准时间。

协调世界时不与任何地区位置相关,也不代表此刻某地的时间,所以在说明某地时间时要加上时区,也就是说GMT并不等于UTC,而是等于UTC+0,只是格林尼治刚好在0时区上。即:GMT = UTC+0

详细介绍请查看:参考

时区

随着火车铁路与其他交通和通讯工具的发展,以及全球化贸易的推动,各地使用各自的当地太阳时间带来了时间不统一的问题,在19世纪催生了统一时间标准的需求,时区由此诞生。

从格林威治本初子午线起,经度每向东或者向西间隔15°,就划分一个时区,在这个区域内,大家使用同样的标准时间。但实际上,为了照顾到行政上的方便,常将1个国家或1个省份划在一起。所以时区并不严格按南北直线来划分,而是按自然条件来划分。另外:由于目前,国际上并没有一个批准各国更改时区的机构。一些国家会由于特定原因改变自己的时区。全球共分为24个标准时区,相邻时区的时间相差一个小时。

解决方案

搞明白时间时区的关系,那么在处理时间是也就变得相对简单的多了。为了计算方便,首先,我们需要将服务器时间统一为零时区(当然也可以其它时区)。后续在做服务端渲染时会传入客户端所在时区参数。且数据库存储数据时存的为世界标准时间,而非本地时间,除非在存储时,已格式化为本地时间字符串。

客户端处理

此处以nextjs为例,其它服务端渲染方案可参考此解决方案。封装一个客户端渲染的时间格式化组件,用来替换时间格式化部分。原理是交由客户端渲染后,时间插件处理在格式化时间时,默认采用的是客户端时区来格式化处理。此方案最为简单

js 复制代码
"use client";
import dayjs from "dayjs";

type Format = "YYYY-MM-DD HH:mm:ss" | "YYYY-MM-DD";

type DateFormatProps = {
  value?: Date | string | number;
  format?: Format;
  short?: boolean;
};

export default function DateFormat({ short, value, format }: DateFormatProps) {
  const f: Format = short ? "YYYY-MM-DD" : "YYYY-MM-DD HH:mm:ss";

  return value ? dayjs(value).format(format || f) : "-";
}

服务端处理

该方案未经验证,此处提供一个解决方案。方案相对于交由客户端渲染来说较为复杂。

  1. 服务端可以通过客户端发送的 HTTP 请求头中的 "Accept-Timezone" 字段获取客户端的时区信息。这个字段是由客户端的浏览器自动生成并发送的,通常是形如 "GMT+08:00" 的字符串。

注意:不是所有的浏览器都会发送这个字段,因此服务端需要特判是否存在该字段,并在必要时采取其他方法获取客户端时区信息。

  1. 更简单(也可能更准确),在客户端中运行new Date().getTimezoneOffset()得到的值即为时差偏移量(零时区减于当前时区),单位为分钟。将该值取反除以60即可得到当前客户端时区,然后将其作为Web请求的一部分发送给服务端即可。例如添加到请求头:Accept-Timezone: GMT+08:00

  2. 在服务端,取出时区信息。然后在格式化时间时加上客户端传来的时区信息即可。

参考

相关推荐
Csvn3 小时前
OpenSpec 详细使用教程
前端
之歆4 小时前
Day19_LESS 完全指南——从入门到工程实践
前端·css·less
云水一下5 小时前
HTML5 从入门到精通:实战收官——从零搭建完整静态网站,综合运用所有知识
前端·html5
不总是5 小时前
Windows 系统 Node.js 免安装版(zip)安装与配置教程(2026 最新)
前端·windows·node.js
冬奇Lab5 小时前
每日一个开源项目(第105篇):Twenty - 跳出 Salesforce 的圈套,定义现代开源 CRM
前端·后端·开源
zhangyao9403306 小时前
开发pc端时,表格的高度怎么设置才能铺满页面
前端·javascript·elementui
kjs--6 小时前
浏览器书签执行脚本
前端
之歆6 小时前
Day16_JavaScript 轮播图与事件工程实战(下篇)
服务器·开发语言·前端·javascript·网络·性能优化
沄媪7 小时前
CSRF 跨站请求伪造
前端·ctf·csrf
kyriewen7 小时前
我关掉了Copilot:因为我写的代码出现在了别人的建议里
前端·javascript·ai编程