You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
427 lines
19 KiB
427 lines
19 KiB
--===========================================================================--
|
|
-- --
|
|
-- System.Date --
|
|
-- --
|
|
--===========================================================================--
|
|
|
|
--===========================================================================--
|
|
-- Author : kurapica125@outlook.com --
|
|
-- URL : http://github.com/kurapica/PLoop --
|
|
-- Create Date : 2016/03/08 --
|
|
-- Update Date : 2021/06/01 --
|
|
-- Version : 1.0.3 --
|
|
--===========================================================================--
|
|
|
|
PLoop(function(_ENV)
|
|
namespace "System"
|
|
|
|
import "System.Serialization"
|
|
|
|
export {
|
|
strfind = string.find,
|
|
strgmatch = string.gmatch,
|
|
|
|
-- %a Abbreviated weekday name Thu
|
|
-- %A Full weekday namespace Thursday
|
|
-- %b Abbreviated month name Aug
|
|
-- %B Full month name August
|
|
-- %c Date and time representation Thu Aug 23 14:55:02 2001
|
|
-- %d Day of the month, zero-padded (01-31) 23
|
|
-- %H Hour in 24h format (00-23) 14
|
|
-- %I Hour in 12h format (01-12) 02
|
|
-- %j Day of the year (001-366) 235
|
|
-- %m Month as a decimal number (01-12) 08
|
|
-- %M Minute (00-59) 55
|
|
-- %p AM or PM designation PM
|
|
-- %S Second (00-61) 02
|
|
-- %U Week number with the first Sunday as the first day of week one (00-53) 33
|
|
-- %w Weekday as a decimal number with Sunday as 0 (0-6) 4
|
|
-- %W Week number with the first Monday as the first day of week one (00-53) 34
|
|
-- %x Date representation 08/23/01
|
|
-- %X Time representation 14:55:02
|
|
-- %y Year, last two digits (00-99) 01
|
|
-- %Y Year 2001
|
|
-- %Z Timezone name or abbreviation CDT
|
|
ALLOW_TIMEFORMAT = (function() local r = {} ("aAbBcdHIjmMpSUwWxXyYZ"):gsub("%w", function(w) r[w] = true end) return r end)()
|
|
}
|
|
|
|
--- Represents the time format can be used in date api
|
|
__Sealed__() __Base__(String)
|
|
struct "TimeFormat" {
|
|
function(val, onlyvalid)
|
|
if strfind(val, "*t") then
|
|
return onlyvalid or "the %s can't contains '*t' as time format"
|
|
else
|
|
local hasfmt = false
|
|
for s in strgmatch(val, "%%(.)") do
|
|
if ALLOW_TIMEFORMAT[s] then
|
|
hasfmt = true
|
|
else
|
|
return onlyvalid or "the '" .. s .. "' can't be used as time format"
|
|
end
|
|
end
|
|
if not hasfmt then return "the %s doesn't contains any time formats" end
|
|
end
|
|
end
|
|
}
|
|
|
|
--- Represents the date object
|
|
__Final__() __Sealed__() __Serializable__() __NoRawSet__(false) __NoNilValue__(false) __SerializeFormat__(TargetFormat.NUMBER)
|
|
class "Date" (function (_ENV)
|
|
extend "ICloneable" "ISerializable"
|
|
|
|
export {
|
|
date = _G.os and os.date or _G.date,
|
|
time = _G.os and os.time or _G.time,
|
|
diff = _G.os and os.difftime or _G.difftime,
|
|
floor = math.floor,
|
|
pairs = pairs,
|
|
type = type,
|
|
getmetatable = getmetatable,
|
|
strfind = strfind,
|
|
rawset = rawset,
|
|
tonumber = tonumber,
|
|
|
|
Serialization.TargetFormat
|
|
}
|
|
|
|
local offset = diff(time(date("*t", 10^8)), time(date("!*t", 10^8)))
|
|
local r2Time = function (self) self.time = time(self) end
|
|
local r4Time = function (self) for k, v in pairs(date("*t", self.time)) do rawset(self, k, v) end end
|
|
local getnow = time
|
|
|
|
local parseString = function(s)
|
|
local year, month, day, hour, min, sec
|
|
local index = 0
|
|
|
|
for n in s:gmatch("%d+") do
|
|
index = index + 1
|
|
if index == 1 then
|
|
year = tonumber(n)
|
|
elseif index == 2 then
|
|
month = tonumber(n)
|
|
elseif index == 3 then
|
|
day = tonumber(n)
|
|
elseif index == 4 then
|
|
hour = tonumber(n)
|
|
elseif index == 5 then
|
|
min = tonumber(n)
|
|
elseif index == 6 then
|
|
sec = tonumber(n)
|
|
break
|
|
end
|
|
end
|
|
|
|
if (year and month and day and year >= 1970 and month >= 1 and month <= 12 and day >= 1 and day <= 31)
|
|
and (not hour or (hour >= 0 and hour < 24))
|
|
and (not min or (min >= 0 and min < 60))
|
|
and (not sec or (sec >= 0 and sec < 60)) then
|
|
|
|
return year, month, day, hour, min, sec
|
|
end
|
|
end
|
|
|
|
-----------------------------------------------------------
|
|
-- static property --
|
|
-----------------------------------------------------------
|
|
--- Gets a DateTime object that is set to the current date and time on this computer, expressed as the local time.
|
|
__Static__()
|
|
property "Now" { get = function() return Date() end }
|
|
|
|
--- Gets and Sets the function that return the current time value(the total second from 1970/1/1 00:00:00)
|
|
__Static__()
|
|
property "GetTimeOfDay" { type = Function, set = function(self, val) getnow = val end, get = function() return getnow end }
|
|
|
|
-----------------------------------------------------------
|
|
-- property --
|
|
-----------------------------------------------------------
|
|
--- The year of the date
|
|
property "Year" { type = Integer, field = "year", handler = r2Time }
|
|
|
|
--- The month of the year, 1-12
|
|
property "Month" { type = Integer, field = "month",handler = function(self, value) r2Time(self) if value < 1 or value > 12 then r4Time(self) end end }
|
|
|
|
--- The day of the month, 1-31
|
|
property "Day" { type = Integer, field = "day", handler = function(self, value) r2Time(self) if value < 1 or value > 28 then r4Time(self) end end }
|
|
|
|
--- The hour of the day, 0-23
|
|
property "Hour" { type = Integer, field = "hour", handler = function(self, value) r2Time(self) if value < 0 or value > 23 then r4Time(self) end end }
|
|
|
|
--- The minute of the hour, 0-59
|
|
property "Minute" { type = Integer, field = "min", handler = function(self, value) r2Time(self) if value < 0 or value > 59 then r4Time(self) end end }
|
|
|
|
--- The Second of the minute, 0-61
|
|
property "Second" { type = Integer, field = "sec", handler = function(self, value) r2Time(self) if value < 0 or value > 59 then r4Time(self) end end }
|
|
|
|
--- The week number, with the mondy is the first day
|
|
property "Week" { get = function(self) return tonumber(date("%W", self.time)) end}
|
|
|
|
--- The weekday 1 (for Monday) to 7 (for Sunday)
|
|
property "DayOfWeek" { get = function(self) return date("*t", self.time).wday -1 end }
|
|
|
|
--- The day of the year
|
|
property "DayOfYear" { get = function(self) return date("*t", self.time).yday end }
|
|
|
|
--- Indicates whether this instance of DateTime is within the daylight saving time range for the current time zone.
|
|
property "IsDaylightSavingTime" { get = function(self) return date("*t", self.time).isdst end }
|
|
|
|
--- Gets the time that represent the date and time of this instance.
|
|
property "Time" { type = Integer, field = "time", handler = r4Time }
|
|
|
|
-----------------------------------------------------------
|
|
-- static method --
|
|
-----------------------------------------------------------
|
|
--- Parse a string data to Date object
|
|
__Static__() __Arguments__{ NEString, TimeFormat, Boolean/false }
|
|
function Parse(s, format, isutc)
|
|
local year, month, day, hour, min, sec
|
|
local index = 1
|
|
|
|
if format:find("^!") then
|
|
isutc = true
|
|
format = format:sub(2)
|
|
end
|
|
|
|
-- %d Day of the month, zero-padded (01-31) 23
|
|
-- %H Hour in 24h format (00-23) 14
|
|
-- %m Month as a decimal number (01-12) 08
|
|
-- %M Minute (00-59) 55
|
|
-- %S Second (00-61) 02
|
|
-- %X Time representation 14:55:02
|
|
-- %Y Year 2001
|
|
local pattern = format:gsub("%%(.)", function(w)
|
|
if w == "d" then
|
|
day = index
|
|
index = index + 1
|
|
return "(%d?%d)"
|
|
elseif w == "H" then
|
|
hour = index
|
|
index = index + 1
|
|
return "(%d?%d)"
|
|
elseif w == "m" then
|
|
month = index
|
|
index = index + 1
|
|
return "(%d?%d)"
|
|
elseif w == "M" then
|
|
min = index
|
|
index = index + 1
|
|
return "(%d?%d)"
|
|
elseif w == "S" then
|
|
sec = index
|
|
index = index + 1
|
|
return "(%d?%d)"
|
|
elseif w == "X" then
|
|
hour = index
|
|
index = index + 1
|
|
|
|
min = index
|
|
index = index + 1
|
|
|
|
sec = index
|
|
index = index + 1
|
|
|
|
return "(%d?%d):(%d?%d):(%d?%d)"
|
|
elseif w == "Y" then
|
|
year = index
|
|
index = index + 1
|
|
return "(%d%d%d%d)"
|
|
end
|
|
end)
|
|
|
|
if not (year and month and day) or ((hour or min or sec) and not (hour and min and sec)) then
|
|
error("Usage: Date.Parse(s[, format][,isutc]) - the format isn't valid", 2)
|
|
end
|
|
|
|
local rs = { s:match(pattern) }
|
|
year = tonumber(rs[year])
|
|
month = tonumber(rs[month])
|
|
day = tonumber(rs[day])
|
|
|
|
if hour then
|
|
hour = tonumber(rs[hour])
|
|
min = tonumber(rs[min])
|
|
sec = tonumber(rs[sec])
|
|
end
|
|
|
|
if not (year and month and day) or ((hour or min or sec) and not (hour and min and sec)) then
|
|
return
|
|
end
|
|
|
|
return Date(year, month, day, hour, min, sec, isutc)
|
|
end
|
|
|
|
__Static__() __Arguments__{ NEString, Boolean/false, Boolean/false }
|
|
function Parse(s, isEndOfDay, isutc)
|
|
local year, month, day, hour, min, sec = parseString(s)
|
|
if year then
|
|
return Date(year, month, day, hour or isEndOfDay and 23 or 0, min or isEndOfDay and 59 or 0, sec or isEndOfDay and 59 or 0, isutc)
|
|
end
|
|
end
|
|
|
|
-----------------------------------------------------------
|
|
-- method --
|
|
-----------------------------------------------------------
|
|
--- Serialize the date
|
|
function Serialize(self, info)
|
|
if info == TargetFormat.STRING then
|
|
return self:ToString()
|
|
elseif info == TargetFormat.NUMBER then
|
|
return self.Time
|
|
else
|
|
info:SetValue("time", self.Time)
|
|
end
|
|
end
|
|
|
|
--- Return the diff second for the two Date object
|
|
__Arguments__{ Date }
|
|
function Diff(self, obj) return diff(self.time, obj.time) end
|
|
|
|
--- Converts the value of the current DateTime object to its equivalent string representation using the specified format.
|
|
__Arguments__{ TimeFormat/"%Y-%m-%d %X" }
|
|
function ToString(self, fmt)
|
|
return date(fmt, self.time)
|
|
end
|
|
|
|
--- Converts the value of the current DateTime object to its equivalent UTC string representation using the specified format.
|
|
__Arguments__{ TimeFormat/"!%Y-%m-%d %X" }
|
|
function ToUTCString(self, fmt)
|
|
if not strfind(fmt, "^!") then fmt = "!" .. fmt end
|
|
return date(fmt, self.time)
|
|
end
|
|
|
|
--- Adds the specified number of years to the value of this instance, and return a new date object
|
|
__Arguments__{ Integer }
|
|
function AddYears(self, years)
|
|
return Date(self.year + years, self.month, self.day, self.hour, self.min, self.sec)
|
|
end
|
|
|
|
--- Adds the specified number of months to the value of this instance, and return a new date object
|
|
__Arguments__{ Integer }
|
|
function AddMonths(self, months)
|
|
return Date(self.year, self.month + months, self.day, self.hour, self.min, self.sec)
|
|
end
|
|
|
|
--- Adds the specified number of months to the value of this instance, and return a new date object
|
|
__Arguments__{ Integer }
|
|
function AddDays(self, days)
|
|
return Date(self.year, self.month, self.day + days, self.hour, self.min, self.sec)
|
|
end
|
|
|
|
--- Adds the specified number of hours to the value of this instance, and return a new date object
|
|
__Arguments__{ Integer }
|
|
function AddHours(self, hours)
|
|
return Date(self.year, self.month, self.day, self.hour + hours, self.min, self.sec)
|
|
end
|
|
|
|
--- Adds the specified number of minutes to the value of this instance, and return a new date object
|
|
__Arguments__{ Integer }
|
|
function AddMinutes(self, minutes)
|
|
return Date(self.year, self.month, self.day, self.hour, self.min + minutes, self.sec)
|
|
end
|
|
|
|
--- Adds the specified number of seconds to the value of this instance, and return a new date object
|
|
__Arguments__{ Integer }
|
|
function AddSeconds(self, seconds)
|
|
return Date(self.year, self.month, self.day, self.hour, self.min, self.sec + seconds)
|
|
end
|
|
|
|
--- Return a Clone of the date oject
|
|
function Clone(self)
|
|
return Date(self.Time)
|
|
end
|
|
|
|
-----------------------------------------------------------
|
|
-- constructor --
|
|
-----------------------------------------------------------
|
|
__Arguments__{ TargetFormat, Any }
|
|
function __new(_, fmt, value)
|
|
local vtype = type(value)
|
|
if vtype == "number" then
|
|
local self = { time = floor(value) }
|
|
r4Time(self)
|
|
return self, true
|
|
elseif vtype == "string" then
|
|
local year, month, day, hour, min, sec = parseString(value)
|
|
if year then
|
|
local self = {
|
|
year = year,
|
|
month = month,
|
|
day = day,
|
|
hour = hour,
|
|
min = min,
|
|
sec = sec,
|
|
}
|
|
|
|
r2Time(self)
|
|
r4Time(self)
|
|
|
|
return self, true
|
|
end
|
|
end
|
|
throw("The value can't be deserialized to Date object")
|
|
end
|
|
|
|
__Arguments__{ SerializationInfo }
|
|
function __new(_, info)
|
|
local self = { time = info:GetValue("time") or 0 }
|
|
r4Time(self)
|
|
return self, true
|
|
end
|
|
|
|
__Arguments__{ RawTable }
|
|
function __new(_, time)
|
|
-- No more check
|
|
return time, true
|
|
end
|
|
|
|
__Arguments__{ Variable("time", Integer, true) }
|
|
function __new(_, tm)
|
|
local self = { time = tm or getnow() }
|
|
r4Time(self)
|
|
return self, true
|
|
end
|
|
|
|
__Arguments__{
|
|
Variable("year", Integer),
|
|
Variable("month", Integer),
|
|
Variable("day", Integer),
|
|
Variable("hour", Integer, true, 12),
|
|
Variable("min", Integer, true, 0),
|
|
Variable("sec", Integer, true, 0),
|
|
Variable("utc", Boolean, true, false)
|
|
}
|
|
function __new(_, year, month, day, hour, min, sec, utc)
|
|
local self = {
|
|
year = year,
|
|
month = month,
|
|
day = day,
|
|
hour = hour,
|
|
min = min,
|
|
sec = utc and (sec + offset) or sec,
|
|
}
|
|
|
|
r2Time(self)
|
|
r4Time(self)
|
|
|
|
return self, true
|
|
end
|
|
|
|
------------------------------------
|
|
-- Meta-method
|
|
------------------------------------
|
|
__Arguments__{ Date }
|
|
function __eq(self, obj) return self.time == obj.time end
|
|
|
|
__Arguments__{ Date }
|
|
function __lt(self, obj) return self.time < obj.time end
|
|
|
|
__Arguments__{ Date }
|
|
function __le(self, obj) return self.time <= obj.time end
|
|
|
|
__add = AddSeconds
|
|
__sub = Diff
|
|
__tostring = ToString
|
|
|
|
export { Date }
|
|
end)
|
|
end)
|
|
|