V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
yifeng1212
V2EX  ›  Node.js

Nodejs 获取北京时间

  •  
  •   yifeng1212 · 2018-02-08 18:58:37 +08:00 · 11559 次点击
    这是一个创建于 2466 天前的主题,其中的信息可能已经有所发展或是发生改变。

    当服务器不在东八区时,处理时区问题有时可能比较烦, 特别是当客户端是北京时间,跟服务器有时差时。

    因为自己项目遇到了类似的问题,自己写了个包来解决这问题。见~

    install

    yarn add china-time
    

    if you prefer npm:

    npm i china-time
    

    usage

    const chinaTime = require('china-time');
    
    console.log(chinaTime()); // 2018-02-07T04:38:00.000Z
    console.log(chinaTime().getTime()); // 1517978280000
    console.log(chinaTime('YYYY-MM-DD HH:mm:ss')); // 2018-02-07 13:08:17
    console.log(chinaTime('YY/MM/DD HH:mm')); // 18/02/07 13:08
    console.log(chinaTime('YYYY MM DD')); // 2018 02 07
    
    21 条回复    2018-06-04 13:59:13 +08:00
    zhengxiaowai
        1
    zhengxiaowai  
       2018-02-08 19:11:57 +08:00   ❤️ 1
    moment().utcOffset(8);
    i730
        2
    i730  
       2018-02-08 19:39:41 +08:00 via Android
    直接把服务器改成+8 的时间即可
    slion
        3
    slion  
       2018-02-08 20:28:26 +08:00   ❤️ 2
    let date = new Date();
    date.setHours(date.getHours() + 8);
    mdluo
        4
    mdluo  
       2018-02-08 20:55:40 +08:00   ❤️ 2
    多时区之间通信,一般用 ISO 8601 的 UTC 时间,像这样的格式:2018-02-08T12:51:00.936Z

    统一用这个时间对应的 GMT +0 (格林威治时间)的时间来存储和传输,AWS 和 Azure 都是这么做的。

    JavaScript 里把一个 date 对象转换成 ISO 8601 的时间,一个 toISOString() 就行了,然后把一个 ISO 8601 的时间转换成 date 对象的时候也会自动作用当前机器的时区。
    lzvezr
        5
    lzvezr  
       2018-02-08 20:57:31 +08:00 via iPhone
    我一般这么干
    ```JavaScript
    const csttime = Date.now() + 8 * 60 * 60 * 1000
    const cst = new Date(csttime)
    const cstHour = cst.getUTCHours()
    const cstMin = cst.getUTCMinutes()
    ```
    julyclyde
        6
    julyclyde  
       2018-02-09 08:14:00 +08:00
    @mdluo 不是用 gmt,而是用 utc
    这俩的法律效力不同
    toono
        7
    toono  
       2018-02-09 08:39:37 +08:00
    moment.js
    mdluo
        8
    mdluo  
       2018-02-09 09:31:47 +08:00
    @julyclyde 我说的就是用 UTC 啊,我提 GMT 只是用来解释 toISOString() 的时间是 GMT+0 时区的时间为基准的时间,ISO 8601 格式最后结尾的 Z 也是代表 Zero 也就是 0 时区。

    另外 JavaScript 本身的 toGMTString() 和 toUTCString() 输出结果是一毛一样的,而且还带 GMT,可见 toUTCString 这个名字里的 UTC 根本就不是真正的 UTC,应该用 toISOString() 才像是有 UTC 的样子。当然服务器通过网络同步的时间也是不够精确的,更精确的时间应该通过 GPS 设备从卫星去获得。
    julyclyde
        9
    julyclyde  
       2018-02-09 14:18:26 +08:00
    @mdluo GMT 自己会变的,有夏令时
    mdluo
        10
    mdluo  
       2018-02-09 14:34:30 +08:00   ❤️ 1
    mingl0280
        11
    mingl0280  
       2018-02-09 14:57:04 +08:00
    首先,直接搞 GMT/UTC+8 就完了……
    其次,GMT/UTC 法律效力不同是什么鬼,这两个理论上只有精准度差别,实际用起来谁管你
    e8c47a0d
        12
    e8c47a0d  
       2018-03-07 14:47:06 +08:00
    始终使用 ISO8601 的 UTC 格式是个好习惯,永远不要用本地时间。
    Js 的日期型在微观上是 UTC,与时区无关。
    另外现在没有 GMT,只有 UTC。
    e8c47a0d
        13
    e8c47a0d  
       2018-03-07 14:50:18 +08:00
    无论服务器地理位置在哪里,时区设置成什么,JS 获得的日期都是 UTC。
    e8c47a0d
        14
    e8c47a0d  
       2018-03-07 15:01:51 +08:00
    即,假设美国和中国各有一台服务器,且时钟绝对准确,且网络没有延迟。那么即使两台服务器的时区不同,同一时间用 new Date() 获得的日期时间是完全相同的。
    yifeng1212
        15
    yifeng1212  
    OP
       2018-03-07 15:17:02 +08:00
    @e8c47a0d #14
    嗯,谢谢解答。

    其实我当时遇到的问题是这样的,我在前端页面上设置了 var str = "2018-03-07 13:00"这个时间(前端是北京时间),然后用 new Date(str) 的时间存到了数据库。所以遇到了问题。

    因为我希望在 3.7 号的 13:00 这个时间做点什么,所以相当于在服务器时间为 2018-03-07 05:00 (假设差 8 个小时)的时候触发。但是上面用 new Date(str) 存的又是服务器的 13:00,明显是不对的。

    所以,我才需要做个转换。

    不知是否有更好的办法?
    e8c47a0d
        16
    e8c47a0d  
       2018-03-07 15:52:06 +08:00   ❤️ 1
    let date = new Date('2018-03-07T13:00+08:00')
    这样可以生成一个 UTC 为 2018-03-07T05:00:00Z 的日期。不论是储存,还是提取,都不要去转换时区,因为现在几乎所有语言、数据库,都是用 UTC 来保存时间的。你可以直接把这个日期保存到数据库(我这里用 MongoDB )。
    e8c47a0d
        17
    e8c47a0d  
       2018-03-07 15:55:46 +08:00
    服务器的时区只是一个用来决定如何显示时间的属性,任何服务器的时钟都是 UTC,所以:
    永!远!不!要!转!换!时!区!
    yifeng1212
        18
    yifeng1212  
    OP
       2018-03-07 16:00:14 +08:00
    @e8c47a0d #17 感谢大佬耐心解答,受教了~
    dany813
        19
    dany813  
       2018-03-29 12:01:26 +08:00
    @e8c47a0d 老哥 let now = Date.now();
    let date = new Date(now)
    把这个 date 存到数据库,那我取出来的展示的时候怎么处理下呢 怎么自动的加下当前时区 比如东八区 自动加八小时
    e8c47a0d
        20
    e8c47a0d  
       2018-06-04 12:32:55 +08:00
    @dany813
    我通常是在前端页面里面有日期的地方放一个 time 标签,然后给它一个 datetime 属性,设置为 ISO8601 的格式。
    <time datetime="2018-06-04T04:18:45Z">2018-06-04T04:18:45Z</time>
    Safari 的 JS 引擎不能很好的理解其他日期格式,所以强烈建议 datetime 使用国际标准 ISO8601 格式
    node.js 中,用 日期.toISOString() 可以把日期做成 ISO8601 格式的字符串( UTC+0 )
    php 中,用 date('c', 1528085925) 可以把{以秒为单位的时间戳}整数做成 ISO8601 格式的字符串( UTC+0 )

    以下代码放在前端,网页 onload 的时候调用一次 localTime() 即可,localTime 这个函数可以把页面中所有 time 标签找出,然后设置定时器,每 n 秒根据当前浏览器的时区和时间,去对比 time 标签的 datetime 属性,自动算出时差并以本地时间输出、更新标签的内容。(我知道回复不能用 markdown 不过还是用了,因为用 switch 很乱所以都换成了 if else )

    比如:3 分前、4 小时前、昨天 16:20、2017 年 12 月 31 日 10:09
    注意闰秒在这里被无视,但问题不大。

    ```js
    function localTime () {
    let _ = Math.floor
    let pad = n => ('00' + n).slice(-2)
    let localize = () => {
    let $times = document.getElementsByTagName('time')
    for (let i = 0; i < $times.length; i++) { // also for in / for of loop
    let $time = $times[i]
    let d = new Date($time.getAttribute('datetime')), // parse ISO 8601
    dYea = d.getFullYear(),
    dMon = d.getMonth() + 1, // getMonth returns (0-11)
    dDay = d.getDate(), // NOT getDay
    dHou = d.getHours(),
    dMin = d.getMinutes(),
    dSec = d.getSeconds()
    let n = new Date(),
    nYea = n.getFullYear(),
    nMon = n.getMonth() + 1,
    nDay = n.getDate(),
    nHou = n.getHours(),
    nMin = n.getMinutes(),
    nSec = n.getSeconds()
    let l = null
    if (nYea - dYea === 0 && nMon - dMon === 0) {
    let dayDiff = nDay - dDay
    if (dayDiff === 0) {
    let s = (
    (nHou * 3600 + nMin * 60 + nSec) -
    (dHou * 3600 + dMin * 60 + dSec)
    )
    if (s === 0)
    l = '现在'
    else if (s < 60 && s > 0)
    l = s + ' 秒前'
    else if (s < 3600 && s >= 60)
    l = _(s / 60) + ' 分钟前'
    else if (s >= 3600)
    l = _(s / 3600) + ' 小时前'
    }
    else if (dayDiff === 1)
    l = '昨天 ' + pad(dHou) + ':' + pad(dMin)
    else if (dayDiff === 2)
    l = '前天 ' + pad(dHou) + ':' + pad(dMin)
    }
    if (l === null)
    l = (
    dYea + ' 年 ' + dMon + ' 月 ' + dDay + ' 日 ' +
    pad(dHou) + ':' + pad(dMin)
    )
    $time.innerText = l
    }
    }
    setInterval(localize, 1000)
    localize()
    }
    ```
    e8c47a0d
        21
    e8c47a0d  
       2018-06-04 13:59:13 +08:00
    @dany813 看到这个不解:let now = Date.now(); let date = new Date(now) ?直接 let now = new Date() 就可以了啊。Date 类的实例默认就是当前时间。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2704 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 09:28 · PVG 17:28 · LAX 01:28 · JFK 04:28
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.