Jump to content

[TOPIC: topicViewTemplate]
[GLOBAL: userSmallPhoto]
Photo

Convert string to date
Started by fac Jan 09 2013 02:34 AM

- - - - -
24 replies to this topic
[TOPIC CONTROLS]
This topic has been archived. This means that you cannot reply to this topic.
[/TOPIC CONTROLS]
[modOptionsDropdown]
[/modOptionsDropdown]
[reputationFilter]
[TOPIC: post.html]
#1

fac

[GLOBAL: userInfoPane.html]
fac
  • Observer

  • 28 posts
  • Corona SDK

Hi,

I have a string on this format: "2013-01-01T00:00:00Z"

How can I convert the string to a date?
How can I identify how many days have passed since that date?

Thanks
uid: 189638 topic_id: 34760 reply_id: 334760


[TOPIC: post.html]
#2

Rob Miracle

[GLOBAL: userInfoPane.html]
Rob Miracle
  • Moderator

  • 26,400 posts
  • Enterprise

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 + offset
end

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 - then
daysDifference = 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...
uid: 199310 topic_id: 34760 reply_id: 138198


[TOPIC: post.html]
#3

conor1

[GLOBAL: userInfoPane.html]
conor1
  • Contributor

  • 166 posts
  • Corona SDK

I'm looking for a variation on this, in effect the reverse.
I know about os.time and converting to day, month, hour etc.
My problem is that I want to subtract from the current time and still have valid hours, days, months and even (once a year) year.
I'm building filenames of images that I grab from a server, one is written every 15 minutes.
A crude way is to get current day, hour etc. and do lots of if else statements to determine when I'm in previous hour, day,month or year.

I don't think I can manipulate the input to os.date or os.time, can I?
uid: 120570 topic_id: 34760 reply_id: 141021


[TOPIC: post.html]
#4

Rob Miracle

[GLOBAL: userInfoPane.html]
Rob Miracle
  • Moderator

  • 26,400 posts
  • Enterprise

@conor1, please check out this blog post and see if it help you:

http://www.coronalabs.com/blog/2013/01/15/working-with-time-and-dates-in-corona/
uid: 199310 topic_id: 34760 reply_id: 141126


[TOPIC: post.html]
#5

conor1

[GLOBAL: userInfoPane.html]
conor1
  • Contributor

  • 166 posts
  • Corona SDK

@rob
I'd already seen that and it explains the subject well.
What I need is some means of subtracting set amounts of time from the current time and having it automatically adjust hour, day, month and year when appropriate.

I don't think there's a way to do os.time(
the time 15 minutes ago
) is there?

uid: 120570 topic_id: 34760 reply_id: 141145


[TOPIC: post.html]
#6

conor1

[GLOBAL: userInfoPane.html]
conor1
  • Contributor

  • 166 posts
  • Corona SDK

Further issue.
print( os.date("%FT%X%z") ) -- This crashes the simulator, no error code (using windows)

strftime listed here does not have a %F
http://www.cplusplus.com/reference/ctime/strftime/

Listed here it does.
http://php.net/manual/en/function.strftime.php

Also noticed that %D %R %T do the same. Am I missing something?
This is happening with 971 and 1019.
uid: 120570 topic_id: 34760 reply_id: 141167


[TOPIC: post.html]
#7

ingemar

[GLOBAL: userInfoPane.html]
ingemar
  • Corona Geek

  • 2,733 posts
  • Enterprise

@conor1
You can still use the [text]t[/text] table to subtract values.
It will work even if the result becomes a negative value:

local t = os.date('*t'); -- get current date and time
print(os.date("%Y-%m-%d %H:%m:%S", os.time(t)));
t.min = t.min - 9000; -- subtract 9000 minutes
print(os.date("%Y-%m-%d %H:%m:%S", os.time(t)));

Output:
[text]
2013-02-01 18:02:31
2013-01-26 12:01:31
[/text]
uid: 70847 topic_id: 34760 reply_id: 141171


[TOPIC: post.html]
#8

conor1

[GLOBAL: userInfoPane.html]
conor1
  • Contributor

  • 166 posts
  • Corona SDK

Great, with just a slight correction
local t = os.date('*t'); -- get current date and time
print(os.date("%Y-%m-%d %H:%M:%S", os.time(t)));
Capital M for minutes.

Thanks for that.
uid: 120570 topic_id: 34760 reply_id: 141172


[TOPIC: post.html]
#9

ingemar

[GLOBAL: userInfoPane.html]
ingemar
  • Corona Geek

  • 2,733 posts
  • Enterprise

Ooooops! Typo alert! ;-)
Sorry about that...
uid: 70847 topic_id: 34760 reply_id: 141173


[TOPIC: post.html]
#10

conor1

[GLOBAL: userInfoPane.html]
conor1
  • Contributor

  • 166 posts
  • Corona SDK

So, just to give something back.
Here is code to calculate times going back every 15 minutes, rounded to nearest 15 minutes.
Still puzzled by @rob and the issue of %F etc.

local function makeDate(date)
	if string.len(date)==1 then date="0"..date end
	return date
end

local date = os.date( "*t" ) 
date.min = date.min - 15; 
t1=os.date("%Y%m%d%H", os.time(date))..makeDate(math.floor(os.date("%M", os.time(date))/15)*15)
date.min = date.min - 15; 
t2=os.date("%Y%m%d%H", os.time(date))..makeDate(math.floor(os.date("%M", os.time(date))/15)*15)

uid: 120570 topic_id: 34760 reply_id: 141174


[TOPIC: post.html]
#11

ingemar

[GLOBAL: userInfoPane.html]
ingemar
  • Corona Geek

  • 2,733 posts
  • Enterprise

print(os.date("%FT%X%z"))
works fine and doesn't crash on Mac...
uid: 70847 topic_id: 34760 reply_id: 141187


[TOPIC: post.html]
#12

Rob Miracle

[GLOBAL: userInfoPane.html]
Rob Miracle
  • Moderator

  • 26,400 posts
  • Enterprise

According to the strftime man page, %F is the same as: %Y-%m-%d (the ISO 8601 date format).

http://linux.die.net/man/3/strftime

I'm guessing that Windows may have a different definition for strftime and maybe %F isn't supported in Visual C++'s libraries. But you can sub the %Y-%m-%d in it's place.
uid: 199310 topic_id: 34760 reply_id: 141373


[TOPIC: post.html]
#13

henrik5

[GLOBAL: userInfoPane.html]
henrik5
  • Contributor

  • 251 posts
  • Corona SDK

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...
uid: 199310 topic_id: 34760 reply_id: 138198

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. :)

Any ideas?



[TOPIC: post.html]
#14

henrik5

[GLOBAL: userInfoPane.html]
henrik5
  • Contributor

  • 251 posts
  • Corona SDK

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
 


[TOPIC: post.html]
#15

ingemar

[GLOBAL: userInfoPane.html]
ingemar
  • Corona Geek

  • 2,733 posts
  • Enterprise

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


[TOPIC: post.html]
#16

henrik5

[GLOBAL: userInfoPane.html]
henrik5
  • Contributor

  • 251 posts
  • Corona SDK

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



[TOPIC: post.html]
#17

Rob Miracle

[GLOBAL: userInfoPane.html]
Rob Miracle
  • Moderator

  • 26,400 posts
  • Enterprise

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


[TOPIC: post.html]
#18

johannes_lalala

[GLOBAL: userInfoPane.html]
johannes_lalala
  • Enthusiast

  • 49 posts
  • Corona SDK

@ conor:

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

 

always rounds downwards, not to nearest quarter hour



[TOPIC: post.html]
#19

thegdog

[GLOBAL: userInfoPane.html]
thegdog
  • Contributor

  • 564 posts
  • Corona SDK

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

 

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

 

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.



[TOPIC: post.html]
#20

Rob Miracle

[GLOBAL: userInfoPane.html]
Rob Miracle
  • Moderator

  • 26,400 posts
  • Enterprise

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



[TOPIC: post.html]
#21

thegdog

[GLOBAL: userInfoPane.html]
thegdog
  • Contributor

  • 564 posts
  • Corona SDK

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.



[TOPIC: post.html]
#22

Dev-Ze.rocks

[GLOBAL: userInfoPane.html]
Dev-Ze.rocks
  • Contributor

  • 213 posts
  • Corona SDK

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 :)

 

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 ;)

 

Cheers, Felix



[TOPIC: post.html]
#23

thegdog

[GLOBAL: userInfoPane.html]
thegdog
  • Contributor

  • 564 posts
  • Corona SDK

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).



[TOPIC: post.html]
#24

Dev-Ze.rocks

[GLOBAL: userInfoPane.html]
Dev-Ze.rocks
  • Contributor

  • 213 posts
  • Corona SDK

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.



[TOPIC: post.html]
#25

linh8

[GLOBAL: userInfoPane.html]
linh8
  • Observer

  • 3 posts
  • Corona SDK

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. 




[topic_controls]
[/topic_controls]