全球化处理之时间

处理全球化问题的重要部分,时间处理。


基础

时间戳

距离某一时刻的到现在所经过的秒数(毫秒数),通常用字符串表示。
一般取 格林威治时间 1970年01月01日00时00分00秒,即 1970-01-01 00:00:00 UTC
同一时刻,全球各时区的时间戳是一致的,并不会根据时区而变化。



时区

由于世界各地区的经度不同,地方时也有所不同,为了克服各时间上的混乱,划分时区。

1884年在华盛顿召开的国际经度会议上,规定将全球划分为24个时区,中时区(零时区)、东1-12区,西1-12区,每个时区横跨经度15度,时间正好1小时,最后东西12区,各跨经度7.5度,以东西经180度为界。

实际上,很多国家或地区都不严格按照时区来计算时间,时区的划分是为了方便沟通克服混乱,以中国为例,横跨5个时区,但一般都只用北京时间作为标准(东八区)。



GMT & UTC

GMT

  • Greenwich Mean Time
  • 格林威治标准时间、世界时
  • GMT以地球自转运动为标准的时间计量系统

UTC

  • Universal Time Cooridinated
  • 协调世界时、世界统一时间、世界标准时间、国际协调时间
  • UTC是以原子时秒长为基础,在时刻上尽量接近于世界时的一种时间计量系统,更加精准,广泛用在科技领域
    • 原子时:物质的原子内部发射的电磁振荡频率为基准的时间计量系统

划分24时区后,东时区可表示为 GMT/UTC +1, GMT/UTC +2 …,西时区可表示为 GMT/UTC -1, GMT/UTC -2 …



夏令时 & 冬令时

出于种种原因,推行一种时间机制,在一年的某个时间开始,把时间调快一个小时,某个时间之后,再把时间调慢一个小时。
将时间调快后到下次调慢前的这段时间,称为夏令时;以此衍生出冬令时概念,就是与夏令时对应,其实可以理解为正常的时间。
这种方式的好处,据说节能减排等,没感觉到,但是弊端,计时混乱却显而易见,中国曾在1986年至1991年实行过,后面就不再实行了。

不管是夏令时还是所谓的冬令时,最大的问题是在于 时间调快 和 时间调慢 的那两个时间点;正常两天的时间间隔是 86400 秒,但是时间被调整的那天时间间隔将变成 82800 秒 和 90000 秒。

以时区 UTC -5 东部时间(美国和加拿大)为例,时间戳与对应日期如下

2022-03-11 06:00:00 1646996400
2022-03-12 06:00:00 1647082800
2022-03-13 06:00:00 1647165600
2022-03-14 06:00:00 1647252000

2022-11-05 06:00:00 1667642400
2022-11-06 06:00:00 1667732400
2022-11-07 06:00:00 1667818800

2022年3月12日到2022年3月13日 6点,时间戳间隔是 82800 秒
2022年11月5日到2022年11月6日 6点,时间戳间隔是 90000 秒




实践

lua时间方法

lua中主要有这几种方法来做时间相关转换

  • os.time
  • os.date

os.time([table])

  • return: string
    • 时间戳字符串
  • param: table
    • [可选参数] table
      • 若不传参数,则返回当前时间时间戳;若传参,返回参数指定时间(年月日时分秒及是否夏令时)时间戳
      • table结构,必须包含字段 year、month、day,可选包含 hour(默认12)、min(默认0)、sec(默认0)、isdst(默认nil)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- 环境:系统时区为 UTC -5  东部时间(美国加拿大)


local date = {
year = 2021, -- 年
month = 1, -- 月
day = 1, -- 日
hour = 0, -- 时
min = 0, -- 分
sec = 0, -- 秒
isdst = false, -- 是否夏令时
}

print(os.time()) -- 1647329606
print(os.time(date)) -- 1609477200

os.date([format [, time]])

  • return: string/table
    • 返回包含日期及时间的字符串或表结构
  • param: string, string
    • [可选参数] format, 格式
      • 前缀为 ‘!’ 则代表以UTC格式化
      • *t 代表返回table结构,其他则返回string结构
      • 对于字符串的情况,可以预制一些参数,方便填充
    • [可选参数] time, 时间戳
      • 若不传,则使用当前的时间戳

format格式符

格式符 含义 样例
%Y 2022
%y 年(后两位数) 22
%m 2
%d 20
%H 时(24小时制) 15
%I 时(12小时制) 3
%M 30
%S 20
%x 日期 03/15/22
%X 时间 05:05:35
%c 日期和时间 03/15/22 05:05:35
%P 上午/下午 am/pm
%a 星期(简写) Fri
%A 星期(全写) Friday
%b 月份(简写) Sep
%B 月份(全写) September
%w 该星期中第几天 2 (0-6, 周日-周六)
%W 该年中第几星期 4
%j 该年中第几天 230
%% 字符串’%’ %
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
-- 环境:系统时区为 UTC -5  东部时间(美国加拿大)


print(os.date())
-- 03/15/22 05:05:35

print(os.date("*t"))
--[[
"<var>" = {
"day" = 15 日
"hour" = 5 时
"isdst" = true 是否夏令时
"min" = 5 分
"month" = 3 月
"sec" = 35 秒
"wday" = 3 星期几(星期天为1,星期一为2,以此类推)
"yday" = 74 当年第几天
"year" = 2022 年
}
--]]

print(os.date("!*t"))
--[[
"<var>" = {
"day" = 15
"hour" = 9
"isdst" = false
"min" = 5
"month" = 3
"sec" = 35
"wday" = 3
"yday" = 74
"year" = 2022
}
]]

print(os.date(nil, 1647329606))
-- 03/15/22 03:33:26

print(os.date("*t", 1647329606))
--[[
"<var>" = {
"day" = 15
"hour" = 3
"isdst" = true
"min" = 33
"month" = 3
"sec" = 26
"wday" = 3
"yday" = 74
"year" = 2022
}
]]

print(os.date("!*t", 1647329606))
--[[
"<var>" = {
"day" = 15
"hour" = 7
"isdst" = false
"min" = 33
"month" = 3
"sec" = 26
"wday" = 3
"yday" = 74
"year" = 2022
}
]]



工具类

作为处理时间的工具类,要考虑几个问题

  • 时间戳
  • 客户端时区(本地时区)
  • 服务器时区

一般来说,禁止使用本地时间进行计算,都是用服务器时间进行计算,但是在时间戳与实践转换时,不可避免受到本地时区影响,要注意将这些影响消除掉,尤其是在做全球化的游戏。
就是一旦用到Lua自带的 os.time / os.date 方法,必然会带上本地的时区去计算,所以要转换成时间戳,就需要将本地时区的影响消除。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
--[[
Filename: TimeUtils.lua
Author: ltree98
Des:
定义:
时间戳 - 0时区,现在距离某一时刻(一般为 1970年01月01日 00时:00分:00秒)经历的秒数
时区 - 克服时间上的混乱,划分出时区
夏令时/冬令时 - 在一年的某一时刻开始,把时间调快一小时,某一时刻之后,再把时间调慢一小时

时间 - 可理解为 时间戳 + 时区
客户端时间(本地时间)- 时间戳 + 客户端所在时区
服务器时间 - 时间戳 + 服务器所在时区


Notion1:
format格式符对应含义
| %Y | 年 | 2022 |
| %y | 年(后两位数)| 22 |
| %m | 月 | 02 |
| %d | 日 | 20 |
| %H | 时(24小时制) | 15 |
| %I | 时(12小时制) | 03 |
| %M | 分 | 30 |
| %S | 秒 | 20 |
| %x | 日期 | 03/15/22 |
| %X | 时间 | 05:05:35 |
| %c | 日期和时间 | 03/15/22 05:05:35 |
| %p | 上午/下午 | AM/PM |
| %a | 星期(简写) | Fri |
| %A | 星期(全写) | Friday |
| %b | 月份(简写) | Sep |
| %B | 月份(全写) | September |
| %w | 该星期中第几天 | 2 (0-6, 周日-周六) |
| %W | 该年中第几星期 | 11 |
| %j | 该年中第几天 | 074 |
| %% | 字符串'%' | % |
--]]

----------------------------------------
-- global variable
--
local os = os
local math = math
local string = string
local tonumber = tonumber

----------------------------------------
-- const value
--
local TIMEZONE_PREFIX = "UTC " -- GMT/UTC
local DEFAULT_RESET_TIME = "05:00:00"


local TimeUtils = {
-- 本地时间戳 & 本地时区
clientUTC = nil,
clientTimezone = 0,
clientTimezoneDes = "",

-- 服务器时间戳 & 服务器时区
serverUTC = nil,
serverTimezone = 3600,
serverTimezoneDes = "",
}

local timezone2des = function(timezone)
local prefix = TIMEZONE_PREFIX
if timezone > 0 then
prefix = prefix .. '+'
end

return prefix .. timezone / 3600
end

local refreshLocalTime = function()
local now = os.time()
local timezone = os.difftime(now, os.time(os.date("!*t", now)))

TimeUtils.clientUTC = now
TimeUtils.clientTimezone = timezone
TimeUtils.clientTimezoneDes = timezone2des(timezone)
end
refreshLocalTime()


--------------------------------------------------------------------------------
-- private
--

--[[DESC: 解析日期格式

支持格式:
年-月-日 时:分:秒 例:2022-03-23 15:30:00
年/月/日 时/分/秒 例: 2022/03/23 15/30/00

dateDes: string
日期格式

return:
table: 年月日,{1 = 年, 2 = 月, 3 = 日}
table: 时分秒,{1 = 时, 2 = 分, 3 = 秒}
]]
local analyseDateFormat = function(dateDes)
local t = string.split(dateDes, " ")
local tDate, tTime, splitFlag

local len = #t
if len > 1 then
splitFlag = string.find(t[1], '-') and '-' or '/'
tDate = string.split(t[1], splitFlag)

splitFlag = string.find(t[2], ':') and ':' or '/'
tTime = string.split(t[2], splitFlag)
elseif len > 0 then
tDate = {0, 0, 0}

splitFlag = string.find(t[1], ':') and ':' or '/'
tTime = string.split(t[1], splitFlag)
else
tDate = {0, 0, 0}
tTime = {0, 0, 0}
end

return tDate, tTime
end

-- 将服务器时间转换为时间戳
local getTimestampByServerDate = function(dateDes)
local tDate, tTime = analyseDateFormat(dateDes)

local timestamp = TimeUtils.time({
year = tDate[1],
month = tDate[2],
day = tDate[3],
hour = tTime[1],
min = tTime[2],
sec = tTime[3],
}) or 0

-- check summer time
if TimeUtils.date("*t", timestamp).isdst then
timestamp = timestamp + 3600
end

-- check time zone
timestamp = timestamp + TimeUtils.clientTimezone
if TimeUtils.serverTimezone then
timestamp = timestamp - TimeUtils.serverTimezone
end

return timestamp
end

-- 将客户端时间转换为时间戳
local getTimestampByLocalDate = function(dateDes)
local tDate, tTime = analyseDateFormat(dateDes)

local timestamp = TimeUtils.time({
year = tDate[1],
month = tDate[2],
day = tDate[3],
hour = tTime[1],
min = tTime[2],
sec = tTime[3],
}) or 0

-- check summer time
if TimeUtils.date("*t", timestamp).isdst then
timestamp = timestamp + 3600
end

timestamp = timestamp + TimeUtils.clientTimezone

return timestamp
end


--------------------------------------------------------------------------------
-- override
--

--[[DESC: 封装Lua os.time, 外部禁止直接调用 os.time ]]
TimeUtils.time = function(param)
return os.time(param)
end

--[[DESC: 封装Lua os.date, 外部禁止直接调用 os.date ]]
TimeUtils.date = function(format, time)
return os.date(format, time)
end

--[[DESC: 封装Lua os.difftime, 外部禁止直接调用 os.difftime ]]
TimeUtils.difftime = function(t2, t1)
return os.difftime(t2, t1)
end



--------------------------------------------------------------------------------
-- interface
--

--[[ DESC: 更新服务器时间戳及时区 ]]
TimeUtils.updateServerTime = function(timestamp, timezone)
TimeUtils.serverUTC = timestamp

if timezone then
TimeUtils.serverTimezone = timezone or 0
TimeUtils.serverTimezoneDes = timezone2des(timezone)
end
end


--[[DESC: 根据时间戳与格式填充日期信息

替换lua的os.date方法,
如果有服务器时区,计算服务器时区,转化为格林尼治时间
如果无服务器时区,转化为本地时间

timestamp: string
时间戳
[format]: string
日期格式,默认 "%Y-%m-%d %H:%M:%S"
[isLocalTime]: bool[false]
本地时间

return:
string: 根据格式填充后的日期信息
]]
TimeUtils.serverDate = function(timestamp, format, isLocalTime)
timestamp = timestamp or 0
isLocalTime = isLocalTime or false
format = format or "%Y-%m-%d %H:%M:%S"

if isLocalTime or TimeUtils.serverTimezone == nil then
return TimeUtils.date(format, timestamp)
else
timestamp = timestamp + TimeUtils.serverTimezone
end

return TimeUtils.date('!' .. format, timestamp)
end


--[[DESC: 格式化时间戳
格式化相关含义,可见顶部文件注释,Notion1-format格式符含义

timestamp: string
时间戳
[format]: string
格式
格式符可见文件顶部注释
若要获取详细表结构,可传 '*t'
[timezoneType]: 0/1/2
计算的时区类型
1 - 算上客户端时区
2 - 算上服务器时区
其他 - 不算时区(0时区)

return:
string/table: 格式化后的时间字符串或表结构
]]
TimeUtils.formatTime = function(timestamp, format, timezoneType)
format = format or "%Y-%m-%d %H:%M:%S"
timezoneType = timezoneType or 0

if timezoneType == 1 then
timestamp = timestamp - TimeUtils.clientTimezone
elseif timezoneType == 2 then
timestamp = timestamp + TimeUtils.serverTimezone
else
format = '!' .. format
end

return TimeUtils.date(format, timestamp)
end


--[[DESC: 秒数转换为 时:分:秒 形式

seconds: int
秒数
[gapFlag]: string[':']
小时,分钟,秒 之间的分割符,默认 ':'
[fillWithZero]: bool[false]
填充0
[unitType]: int[0]
单位类型,默认0
其他 - 2 11:20:35
1 - 2D 11H:20M:35S
2 - 2d 11h:20m:35s
3 - 2天 11时:20分:35秒

是否带单位,比如 xx天 xx时:xx分钟:xx秒数
[assignType]: int[0]
强制位数,默认0
0 - 不强制
1 - 强制显示到天
2 - 强制显示到时
3 - 强制显示到分

return:
string: 按要求转换后的时间字符串
array: 时间结构,索引从1开始对应 {天, 时, 分, 秒}
]]
local UNIT_TYPE_MAP = {
[1] = {'D', 'H', 'M', 'S'},
[2] = {'d', 'h', 'm', 's'},
[3] = {'天', '时', '分', '秒'},
}
local UNIT_TO_SEC = {86400, 3600, 60, 1}

TimeUtils.convertSecToDate = function(seconds, gapFlag, fillWithZero, unitType, assignType)
gapFlag = gapFlag or ':'
fillWithZero = fillWithZero or false
assignType = assignType or 0
local unitMap = UNIT_TYPE_MAP[unitType or 0]

local assemblyPart
assemblyPart = function(des, type)
if fillWithZero then
des = des .. "%02d"
else
des = des .. '%d'
end

if unitMap then des = des .. unitMap[type] end

-- 天
if type == 1 then
des = des .. ' '
des = assemblyPart(des , type + 1)
-- 时
elseif type == 2 then
des = des .. gapFlag
des = assemblyPart(des , type + 1)
-- 分
elseif type == 3 then
des = des .. gapFlag
des = assemblyPart(des , type + 1)
-- 秒
elseif type == 4 then
-- pass
end

return des
end

local day = math.floor(seconds / 86400)
local hour = math.floor(seconds / 3600) % 24
local min = math.floor(seconds / 60) % 60
local sec = seconds % 60

local format = ""
local timeStr = ""
if day > 0 or assignType == 1 then
format = assemblyPart(format, 1)
timeStr = string.format(format, day, hour, min, sec)
elseif hour > 0 or assignType == 2 then
format = assemblyPart(format, 2)
timeStr = string.format(format, hour, min, sec)
elseif min > 0 or assignType == 3 then
format = assemblyPart(format, 3)
timeStr = string.format(format, min, sec)
else
format = assemblyPart(format, 4)
timeStr = string.format(format, sec)
end

return timeStr, {day, hour, min, sec}
end


--[[DESC: 将 时:分:秒 转换为 秒数


format: string
时间格式
[gapFlag]: string[':']
小时,分钟,秒 之间的分割符,默认 ':'
[unitType]: int[0]
单位类型,默认0

return:
int: 秒数
]]
TimeUtils.convertDateToSec = function(dateDes, gapFlag, unitType)
gapFlag = gapFlag or ':'
local unitMap = UNIT_TYPE_MAP[unitType or 0] or {}

local sec = 0

local t = string.split(dateDes, ' ')
local len = #t
if len > 1 then
local days = string.split(t[1], unitMap[1])
sec = sec + days[1] * UNIT_TO_SEC[1]

local times = string.split(t[2], gapFlag)
for i = 1, 3 do
local splitMap = string.split(times[i], unitMap[i + 1])
sec = sec + splitMap[1] * UNIT_TO_SEC[i + 1]
end

elseif len > 0 then
local times = string.split(t[1], gapFlag)
for i = 1, 3 do
local splitMap = string.split(times[i], unitMap[i + 1])
sec = sec + splitMap[1] * UNIT_TO_SEC[i + 1]
end
end

return sec
end


--[[DESC: 根据指定日期字符串,返回0时区时间(时间戳)

dateDes: string
日期详情字符串,包含 年月日时分秒 信息

支持格式:
年-月-日 时:分:秒 例:2022-03-23 15:30:00
年/月/日 时/分/秒 例: 2022/03/23 15/30/00
[isLocalTime]: bool[false]
是否本地时间,默认false

return:
string: 时间戳
]]
TimeUtils.getSecByDate = function(dateDes, isLocalTime)
isLocalTime = isLocalTime or false

local timestamp
if isLocalTime then
timestamp = getTimestampByLocalDate(dateDes)
else
timestamp = getTimestampByServerDate(dateDes)
end

return timestamp
end


--[[DESC: 计算给定时间戳所在天的重置时间

timestamp: string
时间戳
[resetTimeFormat]: string[DEFAULT_RESET_TIME]
每日重置时间,默认 DEFAULT_RESET_TIME
[isLocalTime]: bool[false]
是否本地时间

return:
string: 时间戳
]]
TimeUtils.getResetTimestamp = function(timestamp, resetTimeFormat, isLocalTime)
resetTimeFormat = resetTimeFormat or DEFAULT_RESET_TIME
isLocalTime = isLocalTime or false

local t = TimeUtils.getAssignTimestamp(timestamp, resetTimeFormat, isLocalTime)
if timestamp < t then
t = t - 86400
end

return t
end


--[[DESC: 计算给定时间戳所在天的指定时间的时间戳
注意,假设重置时间为 05:00:00
给定时间为 2022年3月10日 07:00:00,求当天 04:00:00,所得时间戳为 2022年3月11日 04:00:00
因为理论上 3月10日范围为 3月10日 05:00:00 - 3月11日 05:00:00

timestamp: string
时间戳
[resetTimeFormat]: string[DEFAULT_RESET_TIME]
重置时间
[assignedTime]: string[00:00:00]
指定时间
[isLocalTime]: bool[false]
是否本地时间

return:
string: 时间戳
]]
TimeUtils.getAssignTimestamp = function(timestamp, resetTimeFormat, assignedTimeFormat, isLocalTime)
resetTimeFormat = resetTimeFormat or DEFAULT_RESET_TIME
assignedTimeFormat = assignedTimeFormat or "00:00:00"
isLocalTime = isLocalTime or false

local assignedTimeSec = TimeUtils.getSecByDate(assignedTimeFormat)
local tstamp = TimeUtils.getResetTimestamp(timestamp, resetTimeFormat, isLocalTime)
tstamp = tstamp + assignedTimeSec

return tstamp
end


--[[DESC: 判断是否跨天

timestamp1: string
时间戳
timestamp2: string
时间戳
[resetTimeFormat]: string[DEFAULT_RESET_TIME]
每日重置时间,默认 DEFAULT_RESET_TIME
[isLocalTime]: bool[false]
是否本地时间

return:
bool: 是否跨天
]]
TimeUtils.checkCrossDay = function(timestamp1, timestamp2, resetTimeFormat, isLocalTime)
isLocalTime = isLocalTime or false
resetTimeFormat = resetTimeFormat or DEFAULT_RESET_TIME

local t1 = TimeUtils.getResetTimestamp(timestamp1, resetTimeFormat, isLocalTime)
local t2 = TimeUtils.getResetTimestamp(timestamp2, resetTimeFormat, isLocalTime)

return t1 ~= t2
end


--[[DESC: 判断是否跨周

timestamp1: string
时间戳
timestamp2: string
时间戳
[resetTimeFormat]: string[DEFAULT_RESET_TIME]
每日重置时间,默认 DEFAULT_RESET_TIME
[isLocalTime]: bool[false]
是否本地时间

return:
bool: 是否跨周
]]
TimeUtils.checkCrossWeek = function(timestamp1, timestamp2, resetTimeFormat, isLocalTime)
isLocalTime = isLocalTime or false
resetTimeFormat = resetTimeFormat or DEFAULT_RESET_TIME

local weekStartTimestamp, weekEndTimestamp = TimeUtils.getWeekRangeByTimestamp(timestamp1, resetTimeFormat, isLocalTime)
return not (timestamp2 >= weekStartTimestamp and timestamp2 <= weekEndTimestamp)
end


--[[DESC: 计算给定时间戳所在周起止点

timestamp: string
时间戳
[resetTimeFormat]: string[DEFAULT_RESET_TIME]
每日重置时间,默认 DEFAULT_RESET_TIME
[isLocalTime]: bool[false]
是否本地时间

return:
string: 周起始时间戳
string: 周结束时间戳
int: 当天是周几(0为周日,1-6为周一-周六)
]]
TimeUtils.getWeekRangeByTimestamp = function(timestamp, resetTimeFormat, isLocalTime)
isLocalTime = isLocalTime or false
resetTimeFormat = resetTimeFormat or DEFAULT_RESET_TIME

local weekStart, weekEnd, curWeekday
local totalFormat = "%Y-%m-%d " .. resetTimeFormat

-- 0 -> Sunday, 1 -> 6 Monday -> Saturday
curWeekday = tonumber(TimeUtils.serverDate(timestamp, '%w', isLocalTime))
if curWeekday == 0 then
weekEnd = TimeUtils.getSecByDate(TimeUtils.serverDate(timestamp + 86400, totalFormat, isLocalTime), isLocalTime)
else
local time = timestamp + (8 - curWeekday) * 86400
weekEnd = TimeUtils.getSecByDate(TimeUtils.serverDate(time, totalFormat, isLocalTime), isLocalTime)
end

weekStart = weekEnd - 604800 -- 7 * 86400
return weekStart, weekEnd, curWeekday
end


--[[DESC: 时间戳所在月份有几天

timestamp: string
时间戳

return:
int: 天
]]
TimeUtils.getDaysOfMonth = function(timestamp)
local year = TimeUtils.serverDate(timestamp, "%Y")
local month = TimeUtils.serverDate(timestamp, "%m")

return TimeUtils.date("%d", TimeUtils.time({year = year, month = month + 1, day = 0}))
end

return TimeUtils