Convert string to date

[quote=“rob,post:3,topic:309743”]

basically you have to parse the string into its component numbers, but its really not that hard. Here is a function I use for it:

function M.makeTimeStamp(dateString) local pattern = "(%d+)%-(%d+)%-(%d+)T(%d+):(%d+):(%d+)([%+%-])(%d+)%:(%d+)" local xyear, xmonth, xday, xhour, xminute, xseconds, xoffset, xoffsethour, xoffsetmin = dateString:match(pattern) local convertedTimestamp = os.time({year = xyear, month = xmonth, day = xday, hour = xhour, min = xminute, sec = xseconds}) local offset = xoffsethour \* 60 + xoffsetmin if xoffset == "-" then offset = offset \* -1 end return convertedTimestamp + offsetend

This looks much scarier than it really is. The goal is to turn that string into a Unix timestamp (number of seconds since Jan 1, 1970, the standard used by most systems). So we use the string.match() method to fetch the various date and time parts into their own variables: xyear, xmonth,etc.

Next we use the API call os.time() to convert all those individual parts into the timestamp. My code tries to adjust for time zones…

Now you have the time in a nice integer that you can easily manipulate and do date math. Now to determine how many days have passed since that date:

then = makeTimeStamp("2013-01-01T00:00:00Z")now = os.time()timeDifference = now - thendaysDifference = math.floor(timeDifference / (24 \* 60 \* 60)) -- 24 hours, 60 min, 60 seconds. I think it works out to 86400 seconds in a day or something like that...

[import]uid: 199310 topic_id: 34760 reply_id: 138198[/import] [/quote]

I get an error using this, it says “Field ‘day’ missing in date table”.

xday is indeed nil (in fact, all the other values too, it seems) from the match on this string: 2013-09-09T00:00:00Z, but… no, I don’t see where the fault lies either. :slight_smile:

Any ideas?

I made a simplified version, which works for strings like 2013-09-09:

function datetotime(s)     local xyear, xmonth, xday = s:match("(%d+)%-(%d+)%-(%d+)")     return os.time({year = xyear, month = xmonth, day = xday, hour = 0, min = 0, sec = 0}) end  

The function I use looks like this:

local makeTimeStamp = function(dateString) local pattern = "(%d+)%-(%d+)%-(%d+)%a(%d+)%:(%d+)%:([%d%.]+)([Z%p])(%d\*)%:?(%d\*)"; local year, month, day, hour, minute, seconds, tzoffset, offsethour, offsetmin = dateString:match(pattern); local timestamp = os.time({year=year, month=month, day=day, hour=hour, min=minute, sec=seconds}); local offset = 0; if (tzoffset) then if ((tzoffset == "+") or (tzoffset == "-")) then -- we have a timezone! offset = offsethour \* 60 + offsetmin; if (tzoffset == "-") then offset = offset \* -1; end timestamp = timestamp + offset; end end return timestamp; end

Sure, the pattern is different so that might have worked for me also.

Looks like the patterns are not flexible enough.  Try this:

function M.makeTimeStamp(dateString, mode)     local pattern = "(%d+)%-(%d+)%-(%d+)%a(%d+)%:(%d+)%:([%d%.]+)([Z%p])(%d\*)%:?(%d\*)";     local xyear, xmonth, xday, xhour, xminute, xseconds, xoffset, xoffsethour, xoffsetmin     local monthLookup = {Jan = 1, Feb = 2, Mar = 3, Apr = 4, May = 5, Jun = 6, Jul = 7, Aug = 8, Sep = 9, Oct = 10, Nov = 11, Dec = 12}     local convertedTimestamp     local offset = 0     if mode and mode == "ctime" then         pattern = "%w+%s+(%w+)%s+(%d+)%s+(%d+)%:(%d+)%:(%d+)%s+(%w+)%s+(%d+)"         local monthName, TZName         monthName, xday, xhour, xminute, xseconds, TZName, xyear = string.match(dateString,pattern)         xmonth = monthLookup[monthName]         convertedTimestamp = os.time({year = xyear, month = xmonth,         day = xday, hour = xhour, min = xminute, sec = xseconds})     else         xyear, xmonth, xday, xhour, xminute, xseconds, xoffset, xoffsethour, xoffsetmin = string.match(dateString,pattern)         convertedTimestamp = os.time({year = xyear, month = xmonth,         day = xday, hour = xhour, min = xminute, sec = xseconds})         if xoffsetHour then             offset = xoffsethour \* 60 + xoffsetmin             if xoffset == "-" then                 offset = offset \* -1             end         end     end     return convertedTimestamp + offset end

@ conor:

math.floor(os.date("%M", os.time(date))/15)*15

always rounds downwards, not to nearest quarter hour

I will say that I was unable to get this to work as part of your tutorial on IAP purchases, Rob.

I am horrible with regular expressions, but neither of the patterns that you have there match what I am getting back from the Google Play store today.

This is the string that I just got back as the transaction date:

Tue Jan 07 01:06:53 GMT+00:00 2014

Both of the patterns fail with all fields being nil.  And not sure why, because everything I have seen would indicate that this pattern would work for that string.

Try:

function M.makeTimeStamp(dateString, mode)     local pattern = "(%d+)%-(%d+)%-(%d+)T(%d+):(%d+):(%d+)([%+%-])(%d+)%:(%d+)"     local xyear, xmonth, xday, xhour, xminute, xseconds, xoffset, xoffsethour, xoffsetmin     local monthLookup = {Jan = 1, Feb = 2, Mar = 3, Apr = 4, May = 5, Jun = 6, Jul = 7, Aug = 8, Sep = 9, Oct = 10, Nov = 11, Dec = 12}     local convertedTimestamp     local offset = 0     if mode and mode == "ctime" then         pattern = "%w+ (%w+) (%d+) (%d+):(%d+):(%d+) (%w+) (%d+)"         local monthName, TZName         monthName, xday, xhour, xminute, xseconds, TZName, xyear = dateString:match(pattern)         xmonth = monthLookup[monthName]         convertedTimestamp = os.time({year = xyear, month = xmonth,         day = xday, hour = xhour, min = xminute, sec = xseconds})     else         xyear, xmonth, xday, xhour, xminute, xseconds, xoffset, xoffsethour, xoffsetmin = dateString:match(pattern)         convertedTimestamp = os.time({year = xyear, month = xmonth,         day = xday, hour = xhour, min = xminute, sec = xseconds})         offset = xoffsethour \* 60 + xoffsetmin         if xoffset == "-" then offset = offset \* -1 end     end     return convertedTimestamp + offset end

That above date works with dates that have Z timezone info, not +offset type dates.  This version should work (pulled it from code I use in my IAP based apps)…

Rob

Actually, Rob, that did not work for me either.  It appears that the GMT+00:00 is what is causing the issue.

From what I can see, I don’t think %w+ will capture all that.

I just changed the pattern to:

"%w+%s+(%w+)%s+(%d+)%s+(%d+)%:(%d+)%:(%d+)%s+(.+)%s+(%d+)"

(substituting a . for the %w ) and then it works.  But not sure if there are other possible date string formats that might come back from Google Play (different GEO stores perhaps?) that this would then break on.

I will say that I was unable to get this to work as part of your tutorial on IAP purchases, Rob.

I am horrible with regular expressions, but neither of the patterns that you have there match what I am getting back from the Google Play store today.

This is the string that I just got back as the transaction date:

Tue Jan 07 01:06:53 GMT+00:00 2014

Both of the patterns fail with all fields being nil.  And not sure why, because everything I have seen would indicate that this pattern would work for that string.

Try:

function M.makeTimeStamp(dateString, mode)     local pattern = "(%d+)%-(%d+)%-(%d+)T(%d+):(%d+):(%d+)([%+%-])(%d+)%:(%d+)"     local xyear, xmonth, xday, xhour, xminute, xseconds, xoffset, xoffsethour, xoffsetmin     local monthLookup = {Jan = 1, Feb = 2, Mar = 3, Apr = 4, May = 5, Jun = 6, Jul = 7, Aug = 8, Sep = 9, Oct = 10, Nov = 11, Dec = 12}     local convertedTimestamp     local offset = 0     if mode and mode == "ctime" then         pattern = "%w+ (%w+) (%d+) (%d+):(%d+):(%d+) (%w+) (%d+)"         local monthName, TZName         monthName, xday, xhour, xminute, xseconds, TZName, xyear = dateString:match(pattern)         xmonth = monthLookup[monthName]         convertedTimestamp = os.time({year = xyear, month = xmonth,         day = xday, hour = xhour, min = xminute, sec = xseconds})     else         xyear, xmonth, xday, xhour, xminute, xseconds, xoffset, xoffsethour, xoffsetmin = dateString:match(pattern)         convertedTimestamp = os.time({year = xyear, month = xmonth,         day = xday, hour = xhour, min = xminute, sec = xseconds})         offset = xoffsethour \* 60 + xoffsetmin         if xoffset == "-" then offset = offset \* -1 end     end     return convertedTimestamp + offset end

That above date works with dates that have Z timezone info, not +offset type dates.  This version should work (pulled it from code I use in my IAP based apps)…

Rob

Actually, Rob, that did not work for me either.  It appears that the GMT+00:00 is what is causing the issue.

From what I can see, I don’t think %w+ will capture all that.

I just changed the pattern to:

"%w+%s+(%w+)%s+(%d+)%s+(%d+)%:(%d+)%:(%d+)%s+(.+)%s+(%d+)"

(substituting a . for the %w ) and then it works.  But not sure if there are other possible date string formats that might come back from Google Play (different GEO stores perhaps?) that this would then break on.

Thanks a lot thegdog !

I also followed the tutorial about IAP and got stucked with this date format problem.

Good that I started another search and found your post :slight_smile:

I just hope that this will not cause any other problems in other timezones or places on the earth or what ever AND, that Robs code will not cause his games to break :wink:

Cheers, Felix

Glad it helped!

I will say that after thinking it over, I decided to remove that from my code.  I just wasn’t sure what the real need for it was.  Instead, I went with setting a variable when the “Restore purchases” button is pressed, and then just reset that when my store screen goes away or if the user presses a “Purchase” button.

I just found that an easier way to test if the “purchased” return value was an actual purchase or a restored purchase.  And it wouldn’t have any issues with different GEO stores (that I may never know about).

I also was wondering if my game need that part of code at all.

I only want to unlock some feature if the player paid it once, even if he is at another mobile or removed all files of the game.

If I understood right, than I simply make store.restore and if the player ever bought this feature, than it should return that he purchased this and this is what I want to know.

I am just starting with IAP and its lots of new things to think about and care for…

Thanks again for the reply.

Thanks a lot thegdog !

I also followed the tutorial about IAP and got stucked with this date format problem.

Good that I started another search and found your post :slight_smile:

I just hope that this will not cause any other problems in other timezones or places on the earth or what ever AND, that Robs code will not cause his games to break :wink:

Cheers, Felix

Glad it helped!

I will say that after thinking it over, I decided to remove that from my code.  I just wasn’t sure what the real need for it was.  Instead, I went with setting a variable when the “Restore purchases” button is pressed, and then just reset that when my store screen goes away or if the user presses a “Purchase” button.

I just found that an easier way to test if the “purchased” return value was an actual purchase or a restored purchase.  And it wouldn’t have any issues with different GEO stores (that I may never know about).

I also was wondering if my game need that part of code at all.

I only want to unlock some feature if the player paid it once, even if he is at another mobile or removed all files of the game.

If I understood right, than I simply make store.restore and if the player ever bought this feature, than it should return that he purchased this and this is what I want to know.

I am just starting with IAP and its lots of new things to think about and care for…

Thanks again for the reply.

None of these fixes helped until I followed thegdog’s advice and just removed the time checking. Like him, I just set flags for when a restore was appropriate or not. 

None of these fixes helped until I followed thegdog’s advice and just removed the time checking. Like him, I just set flags for when a restore was appropriate or not.