Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
LPeg-talk/strftime.lua
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
387 lines (314 sloc)
11.5 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| -- ********************************************************************** | |
| -- | |
| -- Copyright 2015 by Sean Conner. All Rights Reserved. | |
| -- | |
| -- This program is free software: you can redistribute it and/or modify | |
| -- it under the terms of the GNU General Public License as published by | |
| -- the Free Software Foundation, either version 3 of the License, or | |
| -- any later version. | |
| -- | |
| -- This program is distributed in the hope that it will be useful, | |
| -- but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| -- GNU General Public License for more details. | |
| -- | |
| -- You should have received a copy of the GNU General Public License | |
| -- along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| -- | |
| -- Comments, questions and criticisms can be sent to: sean@conman.org | |
| -- | |
| -- ********************************************************************** | |
| local print = print | |
| local tonumber = tonumber | |
| local error = error | |
| local string = string | |
| local os = require "os" | |
| local lpeg = require "lpeg" | |
| local Cmt = lpeg.Cmt | |
| local Cg = lpeg.Cg | |
| local Ct = lpeg.Ct | |
| local Cf = lpeg.Cf | |
| local Cc = lpeg.Cc | |
| local Cs = lpeg.Cs | |
| local C = lpeg.C | |
| local P = lpeg.P | |
| local S = lpeg.S | |
| local R = lpeg.R | |
| module("strftime") | |
| local USE_C99 = true -- formats defined by C99 | |
| local USE_UNIX = true -- formats commonly found on Unix systems | |
| local USE_TZ = true -- more formats found on Unix systems | |
| local USE_GNU = true -- GNU extensions | |
| -- *********************************************************************** | |
| -- The following could be done easier and simpler with nl_langinfo(), | |
| -- but that isn't portable (being a GNU extension). This *is* portable, | |
| -- although we cannot get the formats used for "%c", "%x" or "%X". | |
| -- *********************************************************************** | |
| local short_months = {} | |
| local long_months = {} | |
| local short_days = {} | |
| local long_days = {} | |
| local am_pm = {} | |
| do | |
| local key | |
| local now = { year = 2013 , month = 1 , day = 1 , hour = 0 } | |
| for m = 1 , 12 do | |
| now.month = m | |
| key = os.date("%b", os.time(now)) | |
| short_months[key] = m | |
| key = os.date("%B", os.time(now)) | |
| long_months[key] = m | |
| end | |
| for d = 1 , 7 do | |
| now.day = d | |
| key = os.date("%a", os.time(now)) | |
| short_days[key] = d | |
| key = os.date("%A", os.time(now)) | |
| long_days[key] = d | |
| end | |
| now.hour = 0 | |
| key = os.date("%p", os.time(now)) | |
| am_pm[key] = 0 | |
| if USE_GNU then | |
| am_pm[key:lower()] = 0 | |
| end | |
| now.hour = 12 | |
| key = os.date("%p", os.time(now)) | |
| am_pm[key] = 12 | |
| if USE_GNU then | |
| am_pm[key:lower()] = 12 | |
| end | |
| end | |
| -- ******************************************************************** | |
| local function chkrange(min,max) | |
| return function(subject,position,capture) | |
| local val = tonumber(capture) | |
| if val < min or val > max then | |
| return false | |
| else | |
| return position,val | |
| end | |
| end | |
| end | |
| local utf8c = R"\128\191" | |
| local utf8 = R"\192\207" * utf8c | |
| + R"\208\223" * utf8c * utf8c | |
| + R"\224\247" * utf8c * utf8c * utf8c | |
| + R"\248\251" * utf8c * utf8c * utf8c * utf8c | |
| + R"\252\253" * utf8c * utf8c * utf8c * utf8c * utf8c | |
| local char = R("AZ","az") + utf8 | |
| local digit = R"09" | |
| local dday = Cmt(digit * digit, chkrange(1, 31)) | |
| local d24hour = Cmt(digit * digit, chkrange(0, 23)) | |
| local d12hour = Cmt(digit * digit, chkrange(1, 12)) | |
| local ddyear = Cmt(digit * digit * digit,chkrange(1,366)) | |
| local dmonth = Cmt(digit * digit, chkrange(1, 12)) | |
| local dminute = Cmt(digit * digit, chkrange(0, 59)) | |
| local dsecond = Cmt(digit * digit, chkrange(0, 61)) | |
| local dweeknum = Cmt(digit * digit, chkrange(0, 53)) | |
| local dweek = Cmt(digit, chkrange(0, 6)) | |
| local d2year = (digit * digit) / function(c) return c + 1900 end | |
| local d4year = (digit * digit * digit * digit) / tonumber | |
| local number = (S"-+" * digit * digit * digit * digit) | |
| local dweek1 = Cmt(digit, function(subject,position,capture) | |
| local v = tonumber(capture) | |
| if v < 1 or v > 7 then | |
| return false | |
| else | |
| if v == 7 then | |
| v = 0 | |
| end | |
| return position,v | |
| end | |
| end) | |
| -- ******************************************************************** | |
| local oct = R"07" | |
| local hex = R("09","AF","af") | |
| local normal = C(P(1) - (P[[\]] + P"%")) | |
| local escaped = P[[\n]] / "\n" | |
| + P[[\t]] / "\t" | |
| + P[[\v]] / "\v" | |
| + P[[\b]] / "\b" | |
| + P[[\r]] / "\r" | |
| + P[[\f]] / "\f" | |
| + P[[\a]] / "\a" | |
| + P[[\\]] / "\\" | |
| + P[[\?]] / "?" | |
| + P[[\']] / "'" | |
| + P[[\"]] / '"' | |
| + P[[\]] * C(oct * oct * oct) / function(c) return tonumber(c,8) end | |
| + P[[\x]] * C(hex * hex) / function(c) return tonumber(c,16) end | |
| local text = Cs((escaped + normal)^1) | |
| / function(c) return P(c) end | |
| -- ------------------------------------------------------------------- | |
| local directives = P"a" / function() | |
| return Cg(char^1 / short_days,"wday") | |
| end | |
| + P"A" / function() | |
| return Cg(char^1 / long_days,"wday") | |
| end | |
| + P"b" / function() | |
| return Cg(char^1 / short_months,"month") | |
| end | |
| + P"B" / function() | |
| return Cg(char^1 / long_months,"month") | |
| end | |
| + P"c" / function() | |
| error("%c format specifier not supported") | |
| end | |
| + P"d" / function() | |
| return Cg(dday,"day") | |
| end | |
| + P"H" / function() | |
| return Cg(d24hour,"hour") | |
| end | |
| + P"I" / function() | |
| return Cg(d12hour,"hour") | |
| end | |
| + P"j" / function() | |
| return Cg(ddyear,"yday") | |
| end | |
| + P"m" / function() | |
| return Cg(dmonth,"month") | |
| end | |
| + P"M" / function() | |
| return Cg(dminute,"min") | |
| end | |
| + P"p" / function() | |
| return Cg(char^1 / am_pm,"pm") | |
| end | |
| + P"S" / function() | |
| return Cg(dsecond,"sec") | |
| end | |
| + P"U" / function() | |
| return Cg(dweeknum,"weeknums") | |
| end | |
| + P"w" / function() | |
| return Cg(dweek,"wday") | |
| end | |
| + P"W" / function() | |
| return Cg(dweeknum,"weeknumm") | |
| end | |
| + P"x" / function() | |
| error("%x format specifier not supported") | |
| end | |
| + P"X" / function() | |
| error("%X format specifier not supported") | |
| end | |
| + P"y" / function() | |
| return Cg(d2year,"year") | |
| end | |
| + P"Y" / function() | |
| return Cg(d4year,"year") | |
| end | |
| + P"Z" / function() | |
| return Cg(char^1,"zone") | |
| end | |
| + P"%" / function() | |
| return P"%" | |
| end | |
| -- ------------------------------------------------------------------- | |
| if USE_C99 then | |
| directivies = directives | |
| + P"F" / function() | |
| return Cg(d4year,"year") * P"-" | |
| * Cg(dmonth,"month") * P"-" | |
| * Cg(dday,"day") | |
| end | |
| end | |
| -- ------------------------------------------------------------------- | |
| if USE_UNIX then | |
| directives = directives | |
| + P"C" / function() | |
| return Cg(d2year,"year") | |
| end | |
| + P"D" / function() | |
| return Cg(dmonth,"month") * P"/" | |
| * Cg(dday,"day") * P"/" | |
| * Cg(d2year,"year") | |
| end | |
| + P"e" / function() | |
| return P" "^-1 | |
| * Cg(Cmt(digit^1,chkrange(1,31)),"day") | |
| end | |
| + P"E" / function() | |
| error("%E format modifier not supported") | |
| end | |
| + P"h" / function() | |
| return Cg(char^1 / short_months,"month") | |
| end | |
| + P"n" / function() | |
| return P"\n" | |
| end | |
| + P"O" / function() | |
| error("%O format modifier not supported") | |
| end | |
| + P"r" / function() | |
| return Cg(d12hour,"hour") * P":" | |
| * Cg(dminute,"min") * P":" | |
| * Cg(dsecond,"sec") * P" " | |
| * Cg(char^1 / am_pm,"pm") | |
| end | |
| + P"R" / function() | |
| return Cg(d24hour,"hour") * P":" | |
| * Cg(dminute,"min") | |
| end | |
| + P"t" / function() | |
| return P"\t" | |
| end | |
| + P"T" / function() | |
| return Cg(d24hour,"hour") * P":" | |
| * Cg(dminute,"min") * P":" | |
| * Cg(dsecond,"sec") | |
| end | |
| + P"u" / function() | |
| return Cg(dweek1,"wday") | |
| end | |
| + P"V" / function() | |
| return Cg(dweeknum,"weeknumm4") | |
| end | |
| end | |
| -- ------------------------------------------------------------------- | |
| if USE_TZ then | |
| directives = directives | |
| + P"g" / function() | |
| return Cg(d2year,"year") | |
| end | |
| + P"G" / function() | |
| return Cg(d4year,"year") | |
| end | |
| + P"k" / function() | |
| return P" "^-1 | |
| * Cg(Cmt(digit^1,chkrange(1,23)),"hour") | |
| end | |
| + P"l" / function() | |
| return P" "^-1 | |
| * Cg(Cmt(digit^1,chkrange(1,12)),"hour") | |
| end | |
| + P"s" / function() | |
| return Cg(digit^1 / tonumber,"epoch") | |
| end | |
| + P"+" / function() | |
| error("%+ format specifier not supported") | |
| end | |
| end | |
| -- ------------------------------------------------------------------- | |
| if USE_GNU then | |
| directives = directives | |
| + P"P" / function() | |
| return Cg(char^1 / am_pm,"pm") | |
| end | |
| + P"z" / function() | |
| return Cg(number,"zone") | |
| end | |
| end | |
| -- ------------------------------------------------------------------- | |
| directives = directives | |
| + P(1) / function(c) | |
| error(string.format("%%%s format specifier not supported",c)) | |
| end | |
| directive = P"%" * directives | |
| return Cf((text + directive)^0,function(a,b) return a * b end) | |
| / function(c) | |
| return Ct(c) | |
| end |