• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lunarmodules / Penlight / 591

29 Dec 2025 04:36PM UTC coverage: 89.278% (+0.4%) from 88.871%
591

Pull #503

appveyor

web-flow
chore(ci): update ApVeyor config for Lua 5.5
Pull Request #503: chore(ci): Update Lua and LuaRocks versions for Lua 5.5

5479 of 6137 relevant lines covered (89.28%)

165.5 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.65
/lua/pl/Date.lua
1
--- Date and Date Format classes.
2
-- See  @{05-dates.md|the Guide}.
3
--
4
-- NOTE: the date module is deprecated! see
5
-- https://github.com/lunarmodules/Penlight/issues/285
6
--
7
-- Dependencies: `pl.class`, `pl.stringx`, `pl.utils`
8
-- @classmod pl.Date
9
-- @pragma nostrip
10

11
local class = require 'pl.class'
12✔
12
local os_time, os_date = os.time, os.date
12✔
13
local stringx = require 'pl.stringx'
12✔
14
local utils = require 'pl.utils'
12✔
15
local assert_arg,assert_string = utils.assert_arg,utils.assert_string
12✔
16

17

18
utils.raise_deprecation {
24✔
19
  source = "Penlight " .. utils._VERSION,
12✔
20
  message = "the 'Date' module is deprecated, see https://github.com/lunarmodules/Penlight/issues/285",
12✔
21
  version_removed = "2.0.0",
12✔
22
  version_deprecated = "1.9.2",
12✔
23
}
24

25

26
local Date = class()
12✔
27
Date.Format = class()
12✔
28

29
--- Date constructor.
30
-- @param t this can be either
31
--
32
--   * `nil` or empty - use current date and time
33
--   * number - seconds since epoch (as returned by `os.time`). Resulting time is UTC
34
--   * `Date` - make a copy of this date
35
--   * table - table containing year, month, etc as for `os.time`. You may leave out year, month or day,
36
-- in which case current values will be used.
37
--   * year (will be followed by month, day etc)
38
--
39
-- @param ...  true if  Universal Coordinated Time, or two to five numbers: month,day,hour,min,sec
40
-- @function Date
41
function Date:_init(t,...)
12✔
42
    local time
43
    local nargs = select('#',...)
264✔
44
    if nargs > 2 then
264✔
45
        local extra = {...}
12✔
46
        local year = t
12✔
47
        t = {
12✔
48
            year = year,
12✔
49
            month = extra[1],
12✔
50
            day = extra[2],
12✔
51
            hour = extra[3],
12✔
52
            min = extra[4],
12✔
53
            sec = extra[5]
12✔
54
        }
12✔
55
    end
56
    if nargs == 1 then
264✔
57
        self.utc = select(1,...) == true
×
58
    end
59
    if t == nil or t == 'utc' then
264✔
60
        time = os_time()
40✔
61
        self.utc = t == 'utc'
40✔
62
    elseif type(t) == 'number' then
224✔
63
        time = t
40✔
64
        if self.utc == nil then self.utc = true end
40✔
65
    elseif type(t) == 'table' then
184✔
66
        if getmetatable(t) == Date then -- copy ctor
184✔
67
            time = t.time
28✔
68
            self.utc = t.utc
28✔
69
        else
70
            if not (t.year and t.month) then
156✔
71
                local lt = os_date('*t')
16✔
72
                if not t.year and not t.month and not t.day then
16✔
73
                    t.year = lt.year
8✔
74
                    t.month = lt.month
8✔
75
                    t.day = lt.day
8✔
76
                else
77
                    t.year = t.year or lt.year
8✔
78
                    t.month = t.month or (t.day and lt.month or 1)
8✔
79
                    t.day = t.day or 1
8✔
80
                end
81
            end
82
            t.day = t.day or 1
156✔
83
            time = os_time(t)
156✔
84
        end
85
    else
86
        error("bad type for Date constructor: "..type(t),2)
×
87
    end
88
    self:set(time)
264✔
89
end
90

91
--- set the current time of this Date object.
92
-- @int t seconds since epoch
93
function Date:set(t)
12✔
94
    self.time = t
1,736✔
95
    if self.utc then
1,736✔
96
        self.tab = os_date('!*t',t)
68✔
97
    else
98
        self.tab = os_date('*t',t)
1,668✔
99
    end
100
end
101

102
--- get the time zone offset from UTC.
103
-- @int ts seconds ahead of UTC
104
function Date.tzone (ts)
12✔
105
    if ts == nil then
×
106
        ts = os_time()
×
107
    elseif type(ts) == "table" then
×
108
        if getmetatable(ts) == Date then
×
109
            ts = ts.time
×
110
        else
111
            ts = Date(ts).time
×
112
        end
113
    end
114
    local utc = os_date('!*t',ts)
×
115
    local lcl = os_date('*t',ts)
×
116
    lcl.isdst = false
×
117
    return os.difftime(os_time(lcl), os_time(utc))
×
118
end
119

120
--- convert this date to UTC.
121
function Date:toUTC ()
12✔
122
    local ndate = Date(self)
16✔
123
    if not self.utc then
16✔
124
        ndate.utc = true
12✔
125
        ndate:set(ndate.time)
12✔
126
    end
127
    return ndate
16✔
128
end
129

130
--- convert this UTC date to local.
131
function Date:toLocal ()
12✔
132
    local ndate = Date(self)
8✔
133
    if self.utc then
8✔
134
        ndate.utc = false
8✔
135
        ndate:set(ndate.time)
8✔
136
--~         ndate:add { sec = Date.tzone(self) }
137
    end
138
    return ndate
8✔
139
end
140

141
--- set the year.
142
-- @int y Four-digit year
143
-- @class function
144
-- @name Date:year
145

146
--- set the month.
147
-- @int m month
148
-- @class function
149
-- @name Date:month
150

151
--- set the day.
152
-- @int d day
153
-- @class function
154
-- @name Date:day
155

156
--- set the hour.
157
-- @int h hour
158
-- @class function
159
-- @name Date:hour
160

161
--- set the minutes.
162
-- @int min minutes
163
-- @class function
164
-- @name Date:min
165

166
--- set the seconds.
167
-- @int sec seconds
168
-- @class function
169
-- @name Date:sec
170

171
--- set the day of year.
172
-- @class function
173
-- @int yday day of year
174
-- @name Date:yday
175

176
--- get the year.
177
-- @int y Four-digit year
178
-- @class function
179
-- @name Date:year
180

181
--- get the month.
182
-- @class function
183
-- @name Date:month
184

185
--- get the day.
186
-- @class function
187
-- @name Date:day
188

189
--- get the hour.
190
-- @class function
191
-- @name Date:hour
192

193
--- get the minutes.
194
-- @class function
195
-- @name Date:min
196

197
--- get the seconds.
198
-- @class function
199
-- @name Date:sec
200

201
--- get the day of year.
202
-- @class function
203
-- @name Date:yday
204

205

206
for _,c in ipairs{'year','month','day','hour','min','sec','yday'} do
96✔
207
    Date[c] = function(self,val)
208
        if val then
96✔
209
            assert_arg(1,val,"number")
×
210
            self.tab[c] = val
×
211
            self:set(os_time(self.tab))
×
212
            return self
×
213
        else
214
            return self.tab[c]
96✔
215
        end
216
    end
217
end
218

219
--- name of day of week.
220
-- @bool full abbreviated if true, full otherwise.
221
-- @ret string name
222
function Date:weekday_name(full)
12✔
223
    return os_date(full and '%A' or '%a',self.time)
×
224
end
225

226
--- name of month.
227
-- @int full abbreviated if true, full otherwise.
228
-- @ret string name
229
function Date:month_name(full)
12✔
230
    return os_date(full and '%B' or '%b',self.time)
48✔
231
end
232

233
--- is this day on a weekend?.
234
function Date:is_weekend()
12✔
235
    return self.tab.wday == 1 or self.tab.wday == 7
×
236
end
237

238
--- add to a date object.
239
-- @param t a table containing one of the following keys and a value:
240
-- one of `year`,`month`,`day`,`hour`,`min`,`sec`
241
-- @return this date
242
function Date:add(t)
12✔
243
    local old_dst = self.tab.isdst
1,452✔
244
    local key,val = next(t)
1,452✔
245
    self.tab[key] = self.tab[key] + val
1,452✔
246
    self:set(os_time(self.tab))
1,452✔
247
    if old_dst ~= self.tab.isdst then
1,452✔
248
        self.tab.hour = self.tab.hour - (old_dst and 1 or -1)
×
249
        self:set(os_time(self.tab))
×
250
    end
251
    return self
1,452✔
252
end
253

254
--- last day of the month.
255
-- @return int day
256
function Date:last_day()
12✔
257
    local d = 28
48✔
258
    local m = self.tab.month
48✔
259
    while self.tab.month == m do
1,388✔
260
        d = d + 1
1,340✔
261
        self:add{day=1}
1,340✔
262
    end
263
    self:add{day=-1}
48✔
264
    return self
48✔
265
end
266

267
--- difference between two Date objects.
268
-- @tparam Date other Date object
269
-- @treturn Date.Interval object
270
function Date:diff(other)
12✔
271
    local dt = self.time - other.time
12✔
272
    if dt < 0 then error("date difference is negative!",2) end
12✔
273
    return Date.Interval(dt)
12✔
274
end
275

276
--- long numerical ISO data format version of this date.
277
function Date:__tostring()
12✔
278
    local fmt = '%Y-%m-%dT%H:%M:%S'
×
279
    if self.utc then
×
280
        fmt = "!"..fmt
×
281
    end
282
    local t = os_date(fmt,self.time)
×
283
    if self.utc then
×
284
        return  t .. 'Z'
×
285
    else
286
        local offs = self:tzone()
×
287
        if offs == 0 then
×
288
            return t .. 'Z'
×
289
        end
290
        local sign = offs > 0 and '+' or '-'
×
291
        local h = math.ceil(offs/3600)
×
292
        local m = (offs % 3600)/60
×
293
        if m == 0 then
×
294
            return t .. ('%s%02d'):format(sign,h)
×
295
        else
296
            return t .. ('%s%02d:%02d'):format(sign,h,m)
×
297
        end
298
    end
299
end
300

301
--- equality between Date objects.
302
function Date:__eq(other)
12✔
303
    return self.time == other.time
60✔
304
end
305

306
--- ordering between Date objects.
307
function Date:__lt(other)
12✔
308
    return self.time < other.time
4✔
309
end
310

311
--- difference between Date objects.
312
-- @function Date:__sub
313
Date.__sub = Date.diff
12✔
314

315
--- add a date and an interval.
316
-- @param other either a `Date.Interval` object or a table such as
317
-- passed to `Date:add`
318
function Date:__add(other)
12✔
319
    local nd = Date(self)
4✔
320
    if Date.Interval:class_of(other) then
4✔
321
        other = {sec=other.time}
×
322
    end
323
    nd:add(other)
4✔
324
    return nd
4✔
325
end
326

327
Date.Interval = class(Date)
12✔
328

329
---- Date.Interval constructor
330
-- @int t an interval in seconds
331
-- @function Date.Interval
332
function Date.Interval:_init(t)
24✔
333
    self:set(t)
20✔
334
end
335

336
function Date.Interval:set(t)
24✔
337
    self.time = t
20✔
338
    self.tab = os_date('!*t',self.time)
20✔
339
end
340

341
local function ess(n)
342
    if n > 1 then return 's '
4✔
343
    else return ' '
4✔
344
    end
345
end
346

347
--- If it's an interval then the format is '2 hours 29 sec' etc.
348
function Date.Interval:__tostring()
24✔
349
    local t, res = self.tab, ''
12✔
350
    local y,m,d = t.year - 1970, t.month - 1, t.day - 1
12✔
351
    if y > 0 then res = res .. y .. ' year'..ess(y) end
12✔
352
    if m > 0 then res = res .. m .. ' month'..ess(m) end
12✔
353
    if d > 0 then res = res .. d .. ' day'..ess(d) end
12✔
354
    if y == 0 and m == 0 then
12✔
355
        local h = t.hour
8✔
356
        if h > 0 then res = res .. h .. ' hour'..ess(h) end
8✔
357
        if t.min > 0 then res = res .. t.min .. ' min ' end
8✔
358
        if t.sec > 0 then res = res .. t.sec .. ' sec ' end
8✔
359
    end
360
    if res == '' then res = 'zero' end
12✔
361
    return res
12✔
362
end
363

364
------------ Date.Format class: parsing and renderinig dates ------------
365

366
-- short field names, explicit os.date names, and a mask for allowed field repeats
367
local formats = {
12✔
368
    d = {'day',{true,true}},
12✔
369
    y = {'year',{false,true,false,true}},
12✔
370
    m = {'month',{true,true}},
12✔
371
    H = {'hour',{true,true}},
12✔
372
    M = {'min',{true,true}},
12✔
373
    S = {'sec',{true,true}},
12✔
374
}
375

376
--- Date.Format constructor.
377
-- @string fmt. A string where the following fields are significant:
378
--
379
--   * d day (either d or dd)
380
--   * y year (either yy or yyy)
381
--   * m month (either m or mm)
382
--   * H hour (either H or HH)
383
--   * M minute (either M or MM)
384
--   * S second (either S or SS)
385
--
386
-- Alternatively, if fmt is nil then this returns a flexible date parser
387
-- that tries various date/time schemes in turn:
388
--
389
--  * [ISO 8601](http://en.wikipedia.org/wiki/ISO_8601), like `2010-05-10 12:35:23Z` or `2008-10-03T14:30+02`
390
--  * times like 15:30 or 8.05pm  (assumed to be today's date)
391
--  * dates like 28/10/02 (European order!) or 5 Feb 2012
392
--  * month name like march or Mar (case-insensitive, first 3 letters); here the
393
-- day will be 1 and the year this current year
394
--
395
-- A date in format 3 can be optionally followed by a time in format 2.
396
-- Please see test-date.lua in the tests folder for more examples.
397
-- @usage df = Date.Format("yyyy-mm-dd HH:MM:SS")
398
-- @class function
399
-- @name Date.Format
400
function Date.Format:_init(fmt)
24✔
401
    if not fmt then
40✔
402
        self.fmt = '%Y-%m-%d %H:%M:%S'
8✔
403
        self.outf = self.fmt
8✔
404
        self.plain = true
8✔
405
        return
8✔
406
    end
407
    local append = table.insert
32✔
408
    local D,PLUS,OPENP,CLOSEP = '\001','\002','\003','\004'
32✔
409
    local vars,used = {},{}
32✔
410
    local patt,outf = {},{}
32✔
411
    local i = 1
32✔
412
    while i < #fmt do
176✔
413
        local ch = fmt:sub(i,i)
144✔
414
        local df = formats[ch]
144✔
415
        if df then
144✔
416
            if used[ch] then error("field appeared twice: "..ch,4) end
92✔
417
            used[ch] = true
92✔
418
            -- this field may be repeated
419
            local _,inext = fmt:find(ch..'+',i+1)
92✔
420
            local cnt = not _ and 1 or inext-i+1
92✔
421
            if not df[2][cnt] then error("wrong number of fields: "..ch,4) end
92✔
422
            -- single chars mean 'accept more than one digit'
423
            local p = cnt==1 and (D..PLUS) or (D):rep(cnt)
92✔
424
            append(patt,OPENP..p..CLOSEP)
92✔
425
            append(vars,ch)
92✔
426
            if ch == 'y' then
92✔
427
                append(outf,cnt==2 and '%y' or '%Y')
28✔
428
            else
429
                append(outf,'%'..ch)
64✔
430
            end
431
            i = i + cnt
92✔
432
        else
433
            append(patt,ch)
52✔
434
            append(outf,ch)
52✔
435
            i = i + 1
52✔
436
        end
437
    end
438
    -- escape any magic characters
439
    fmt = utils.escape(table.concat(patt))
32✔
440
   -- fmt = table.concat(patt):gsub('[%-%.%+%[%]%(%)%$%^%%%?%*]','%%%1')
441
    -- replace markers with their magic equivalents
442
    fmt = fmt:gsub(D,'%%d'):gsub(PLUS,'+'):gsub(OPENP,'('):gsub(CLOSEP,')')
32✔
443
    self.fmt = fmt
32✔
444
    self.outf = table.concat(outf)
32✔
445
    self.vars = vars
32✔
446
end
447

448
local parse_date
449

450
--- parse a string into a Date object.
451
-- @string str a date string
452
-- @return date object
453
function Date.Format:parse(str)
24✔
454
    assert_string(1,str)
108✔
455
    if self.plain then
108✔
456
        return parse_date(str,self.us)
72✔
457
    end
458
    local res = {str:match(self.fmt)}
36✔
459
    if #res==0 then return nil, 'cannot parse '..str end
36✔
460
    local tab = {}
36✔
461
    for i,v in ipairs(self.vars) do
140✔
462
        local name = formats[v][1] -- e.g. 'y' becomes 'year'
104✔
463
        tab[name] = tonumber(res[i])
104✔
464
    end
465
    -- os.date() requires these fields; if not present, we assume
466
    -- that the time set is for the current day.
467
    if not (tab.year and tab.month and tab.day) then
36✔
468
        local today = Date()
4✔
469
        tab.year = tab.year or today:year()
4✔
470
        tab.month = tab.month or today:month()
4✔
471
        tab.day = tab.day or today:day()
4✔
472
    end
473
    local Y = tab.year
36✔
474
    if Y < 100 then -- classic Y2K pivot
36✔
475
        tab.year = Y + (Y < 35 and 2000 or 1999)
12✔
476
    elseif not Y then
24✔
477
        tab.year = 1970
×
478
    end
479
    return Date(tab)
36✔
480
end
481

482
--- convert a Date object into a string.
483
-- @param d a date object, or a time value as returned by @{os.time}
484
-- @return string
485
function Date.Format:tostring(d)
24✔
486
    local tm
487
    local fmt = self.outf
32✔
488
    if type(d) == 'number' then
32✔
489
        tm = d
×
490
    else
491
        tm = d.time
32✔
492
        if d.utc then
32✔
493
            fmt = '!'..fmt
×
494
        end
495
    end
496
    return os_date(fmt,tm)
32✔
497
end
498

499
--- force US order in dates like 9/11/2001
500
function Date.Format:US_order(yesno)
24✔
501
    self.us = yesno
×
502
end
503

504
--local months = {jan=1,feb=2,mar=3,apr=4,may=5,jun=6,jul=7,aug=8,sep=9,oct=10,nov=11,dec=12}
505
local months
506
local parse_date_unsafe
507
local function create_months()
508
    local ld, day1 = parse_date_unsafe '2000-12-31', {day=1}
4✔
509
    months = {}
4✔
510
    for i = 1,12 do
52✔
511
        ld = ld:last_day()
48✔
512
        ld:add(day1)
48✔
513
        local mon = ld:month_name():lower()
48✔
514
        months [mon] = i
48✔
515
    end
516
end
517

518
--[[
519
Allowed patterns:
520
- [day] [monthname] [year] [time]
521
- [day]/[month][/year] [time]
522

523
]]
524

525
local function looks_like_a_month(w)
526
    return w:match '^%a+,*$' ~= nil
60✔
527
end
528
local is_number = stringx.isdigit
12✔
529
local function tonum(s,l1,l2,kind)
530
    kind = kind or ''
224✔
531
    local n = tonumber(s)
224✔
532
    if not n then error(("%snot a number: '%s'"):format(kind,s)) end
224✔
533
    if n < l1 or n > l2 then
224✔
534
        error(("%s out of range: %s is not between %d and %d"):format(kind,s,l1,l2))
8✔
535
    end
536
    return n
216✔
537
end
538

539
local function  parse_iso_end(p,ns,sec)
540
    -- may be fractional part of seconds
541
    local _,nfrac,secfrac = p:find('^%.%d+',ns+1)
40✔
542
    if secfrac then
40✔
543
        sec = sec .. secfrac
×
544
        p = p:sub(nfrac+1)
×
545
    else
546
        p = p:sub(ns+1)
40✔
547
    end
548
    -- ISO 8601 dates may end in Z (for UTC) or [+-][isotime]
549
    -- (we're working with the date as lower case, hence 'z')
550
    if p:match 'z$' then -- we're UTC!
40✔
551
        return  sec, {h=0,m=0}
8✔
552
    end
553
    p = p:gsub(':','') -- turn 00:30 to 0030
32✔
554
    local _,_,sign,offs = p:find('^([%+%-])(%d+)')
32✔
555
    if not sign then return sec, nil end -- not UTC
32✔
556

557
    if #offs == 2 then offs = offs .. '00' end -- 01 to 0100
8✔
558
    local tz = { h = tonumber(offs:sub(1,2)), m = tonumber(offs:sub(3,4)) }
8✔
559
    if sign == '-' then tz.h = -tz.h; tz.m = -tz.m end
8✔
560
    return sec, tz
8✔
561
end
562

563
function parse_date_unsafe (s,US)
9✔
564
    s = s:gsub('T',' ') -- ISO 8601
76✔
565
    local parts = stringx.split(s:lower())
76✔
566
    local i,p = 1,parts[1]
76✔
567
    local function nextp() i = i + 1; p = parts[i] end
160✔
568
    local year,min,hour,sec,apm
569
    local tz
570
    local _,nxt,day, month = p:find '^(%d+)/(%d+)'
76✔
571
    if day then
76✔
572
        -- swop for US case
573
        if US then
8✔
574
            day, month = month, day
×
575
        end
576
        _,_,year = p:find('^/(%d+)',nxt+1)
8✔
577
        nextp()
8✔
578
    else -- ISO
579
        year,month,day = p:match('^(%d+)%-(%d+)%-(%d+)')
68✔
580
        if year then
68✔
581
            nextp()
40✔
582
        end
583
    end
584
    if p and not year and is_number(p) then -- has to be date
76✔
585
        if #p < 4 then
12✔
586
            day = p
12✔
587
            nextp()
12✔
588
        else -- unless it looks like a 24-hour time
589
            year = true
×
590
        end
591
    end
592
    if p and looks_like_a_month(p) then -- date followed by month
76✔
593
        p = p:sub(1,3)
16✔
594
        if not months then
16✔
595
            create_months()
4✔
596
        end
597
        local mon = months[p]
16✔
598
        if mon then
16✔
599
            month = mon
16✔
600
        else error("not a month: " .. p) end
×
601
        nextp()
16✔
602
    end
603
    if p and not year and is_number(p) then
76✔
604
        year = p
8✔
605
        nextp()
8✔
606
    end
607

608
    if p then -- time is hh:mm[:ss], hhmm[ss] or H.M[am|pm]
76✔
609
        _,nxt,hour,min = p:find '^(%d+):(%d+)'
48✔
610
        local ns
611
        if nxt then -- are there seconds?
48✔
612
            _,ns,sec = p:find ('^:(%d+)',nxt+1)
36✔
613
            --if ns then
614
                sec,tz = parse_iso_end(p,ns or nxt,sec)
36✔
615
            --end
616
        else -- might be h.m
617
            _,ns,hour,min = p:find '^(%d+)%.(%d+)'
12✔
618
            if ns then
12✔
619
                apm = p:match '[ap]m$'
8✔
620
            else  -- or hhmm[ss]
621
                local hourmin
622
                _,nxt,hourmin = p:find ('^(%d+)')
4✔
623
                if nxt then
4✔
624
                   hour = hourmin:sub(1,2)
4✔
625
                   min = hourmin:sub(3,4)
4✔
626
                   sec = hourmin:sub(5,6)
4✔
627
                   if #sec == 0 then sec = nil end
4✔
628
                   sec,tz = parse_iso_end(p,nxt,sec)
4✔
629
                end
630
            end
631
        end
632
    end
633
    local today
634
    if year == true then year = nil end
76✔
635
    if not (year and month and day) then
76✔
636
        today = Date()
20✔
637
    end
638
    day = day and tonum(day,1,31,'day') or (month and 1 or today:day())
76✔
639
    month = month and tonum(month,1,12,'month') or today:month()
72✔
640
    year = year and tonumber(year) or today:year()
72✔
641
    if year < 100 then -- two-digit year pivot around year < 2035
72✔
642
        year = year + (year < 35 and 2000 or 1900)
8✔
643
    end
644
    hour = hour and tonum(hour,0,apm and 12 or 24,'hour') or 12
72✔
645
    if apm == 'pm' then
68✔
646
        hour = hour + 12
4✔
647
    end
648
    min = min and tonum(min,0,59) or 0
68✔
649
    sec = sec and tonum(sec,0,60) or 0  --60 used to indicate leap second
68✔
650
    local res = Date {year = year, month = month, day = day, hour = hour, min = min, sec = sec}
68✔
651
    if tz then -- ISO 8601 UTC time
68✔
652
        local corrected = false
16✔
653
        if tz.h ~= 0 then res:add {hour = -tz.h}; corrected = true end
16✔
654
        if tz.m ~= 0 then res:add {min = -tz.m}; corrected = true end
16✔
655
        res.utc = true
16✔
656
        -- we're in UTC, so let's go local...
657
        if corrected then
16✔
658
            res = res:toLocal()
8✔
659
        end-- we're UTC!
660
    end
661
    return res
68✔
662
end
663

664
function parse_date (s)
9✔
665
    local ok, d = pcall(parse_date_unsafe,s)
72✔
666
    if not ok then -- error
72✔
667
        d = d:gsub('.-:%d+: ','')
8✔
668
        return nil, d
8✔
669
    else
670
        return d
64✔
671
    end
672
end
673

674
return Date
12✔
675

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc