在NetSuite中,由于存在客户端时间、服务器时间、个人首选项时区时间。在代码中如果没有区分好这几个时间,则在国际化项目中就会出现问题。我们就在近期的一个项目上进了这个坑。在中国时区的我们,让位于纽约的顾问看时间错误,他让巴基斯坦的外包顾问改代码。项目上这个时间Bug,我们日不落整了一大圈。
西老师给整了篇总结,加上实验,给大家谈谈这个事。
以下文字转自他的文章。
西老师《NS中日期格式与时区问题》
1. JS中的日期对象
JS日期对象中存储的两个重要信息:
- 毫秒数:相对1970/1/1 00:00:00 GMT 所经过的毫秒数
- 时区:本机时区(不可更改)
如果在两个时区设置不同的计算机上 new Date(), 那么日期对象中所存储的毫秒数相同,时区不同。
2. 使用NS时的不同时区
- 客户端时区,即NS用户操作的计算机的时区
- 服务器时区,即NS所在服务器上的时区
- 本机时区,当下文提到本机时区时,若代码运行在客户端,则本机时区为客户端时区,若代码运行在服务器端,本机时区为服务器时区
- NS时区,NS个人设置中的时区
3. N/format中当type为DATE、DATETIME的大致行为
- format.format, type为DATE : 则value需要是Date对象,使用本机时区(即Date对象中存储的时区),NS个人设置中日期格式,输出日期字符串
- format.parse, type为DATE : value需是与NS个人设置中日期格式相匹配的日期字符串,将其当作本机时区的时间,转为DATE对象
- format.format,type为DATETIME : value需是Date对象,使用NS时区(不指定时区的默认行为),NS个人设置中的日期与时间格式,输出字符串,(12小时制时 “am/pm” "上午/下午"按个人设置中的语言输出)
- format.parse,type为DATETIME : value需是与NS个人设置中日期与时间格式相匹配的日期+时间字符串,将其当作NS时区的时间(不指定时区的默认行为),转为DATE对象(12小时制时同样注意语言)
4. 字段类型“日期”、“日期/时间” 与 API “setValue”、“setText”
- 字段类型“日期”:可以看作是存储了“年月日”三个值(便于理解,实际情况可能不同),展示时按个人设置的日期格式输出
- 字段类型“日期/时间“:可以看作存储了一个Date对象且时区可变(便于理解,实际情况可能不同),展示时按NS时区,个人设置的日期与时间格式输出
- 在字段中设置默认值为当前时间时,按NS时区给默认值
5. getText, getValue, setText, setValue用于 日期、日期/时间类型字段时的大致行为
- getText用于日期类型字段:取出年月日,按个人设置的日期格式输出
- getValue用于日期类型字段:将使用getText取出的字符串用format.parse转为日期对象输出
- setText用于日期类型字段:text需要是与个人设置中的日期格式相匹配的字符串,取出字符串中的年月日,设置到字段上
- setValue用于日期类型字段:value需是Date对象,使用format.format转为字符串后使用setText设置
- getValue用于日期/时间类型字段:直接取出日期对象
- getText用于日期/时间类型字段:取出日期对象后,使用format.format转为字符串
- setValue用于日期/时间类型字段:value为Date对象,直接设置到字段上
- setText用于日期/时间类型字段:text为格式匹配的日期时间字符串,使用format.parse转为日期对象,设置到字段上
6. search 中的日期与时间
- 格式化: TO_CHAR({date_or_datetime_field_id}, ‘YYYY-MM-DD hh24:mi:ss’), Sample Functions of TO_CHAR (custhelp.com)
- 日期字段格式化: 时分秒始终为0
- 日期/时间字段格式化:使用NS时区的时间
- {now}/{today}格式化:NS时区的当前时间
- 计算时间差: 日期,日期/时间类型的字段在公式中可以相减计算时间差
- 日期/时间类型 和 日期类型相减:结果为带符号的整数加时分秒
- 日期/时间类型 和日期/时间类型相减:结果为带符号的整数加时分秒
- 日期类型 和 日期类型相减:结果为带符号整数
- 日期类型和{now}/{today}/Date Created/Last Modified相减:结果为以天数换算的带符号小数
7. 实践
- 新建一个自定义记录,增加字段:
- 类型为日期,默认值为当前时间,下文用 custrecord_date 代表此字段id
- 类型为日期,默认值为当前时间,下文用custrecord_date2代表此字段id
- 类型为日期/时间,默认值为当前时间,下文用custrecord_datetime代表此字段id
- 建记录:
- 新建记录,使用默认值。
- 用schedule脚本建记录,用 setValue, value为new Date()设置custrecord_date字段
- 用client脚本建记录,用 setValue, value为new Date()设置custrecord_date2字段
- 观察有什么区别(一天中的不同时间,结果可能不同)
- 调整个人设置中的时区,重复2.
- 建记录:
- 新建记录,使用默认值。
- 用schedule脚本建记录,用setText, text为 ‘2021-06-06 06:06:06’(根据个人设置中日期与时间格式变化),设置custrecord_datetime
- 用client脚本,用setText, text为 ‘2021-06-06 06:06:06’(根据个人设置中日期与时间格式变化),设置custrecord_datetime
- 观察有什么区别
-
新建此记录类型的search , 结果中增加:
- custrecord_date
- formula text: TO_CHAR({custrecord_date}, “YYYY-MM-DD hh24:mi:ss”)
- custrecord_datetime
- formula text: TO_CHAR({custrecord_datetime}, “YYYY-MM-DD hh24:mi:ss”)
- Date Created
- formula text: TO_CHAR({created}, “YYYY-MM-DD hh24:mi:ss”)
- Last Modified
- formula text: {custrecord_date}-{custrecord_date2}
- formula text: {custrecord_date}-{custrecord_datetime}
- formula text: {now}-{custrecord_date}
- formula text: {now}-{custrecord_datetime}
- formula text: {created}-{custrecord_date}
- formula text: {created}-{custrecord_datetime}
-
查看结果, 更改个人设置中的时区,再次查看结果。
常见问题
- setValue设置时间用的值是js的日期对象,不能用字符串
- setText设置时间用的日期格式要与个人设置里一致,不同用户的个人设置可能不一样,要注意
- 时间格式里带am pm的话,会因语言不同而不同,使用时需注意,否则会报unexpected error
- 使用setValue设置日期时会因时区问题导致与预期不同
- 日期字段类型不随NS时区变化,日期/时间字段类型随NS时区变化,两者计算时间差会出问题