Method for keeping time

Blitz3D Forums/Blitz3D Programming/Method for keeping time

Yahfree(Posted 2007) [#1]
Hey!, the project i'm working on has faced me with a great task, i knew it was coming, it was in the plans, anyways:

Lets say my program keeps track of plant growth.

my user chooses a grape plant to grow like so:
you just planet a grape, will be fully grown in 5 days!

i store a time in a .txt file, when he opens the program 4 days later i want the program to know:

your grape still needs 1 day to grow

how would i go about doing something like this? obviosly i have to store a time in a .txt file, so it can read off that when it starts up next. i got that much.


b32(Posted 2007) [#2]
You could use CurrentDate$() to read the current date, and compare it to the date stored in the file.
A user could put back his computer clock/date or he could return after 15 days, while the plant needs 5 days to grow. It could be sensible to take these situations in account.

So:
0) get currentdate$
1) if filetype = 0 then new file, write date (new plant)
2) if filetype = 1 then open file, read date (old plant)
3) substract old date from new date, result is age of plant
4) if age < 0 then ..
if age > 5 then ..
5) print 'your plant is ' + age + 'days old'


Yahfree(Posted 2007) [#3]
how would i substract the old date from the new date like for example this program doesnt work for obviose reasons..

Graphics 800,600,16,2

date$=CurrentDate()

olddate$ = "20 Jul 2007"

Print date$-olddate$

Delay 6000


the rest is pretty straight forword..


Yahfree(Posted 2007) [#4]
Duh.

Graphics 800,600,16,2

date$=CurrentDate()

olddate$ = "20 Jul 2007"

int_date%=Left(date,2)
int_olddate%=Left(olddate,2)

Print int_date-int_olddate

Delay 6000


Awsome. thanks for the help b32!


Yahfree(Posted 2007) [#5]
Hmm, is CurrentDate's month return always the first 3 letters of a month? i need to be sure before i write this function


Yahfree(Posted 2007) [#6]
ok, now i'm trying to get a hour value, lets pretend "Gas" is a fuel that runs per hour burning 450 units per hour..

heres the function i'm using to return how many days of 'gas' you have left.

Function daysleft(fueltype$,amount)
days=0

Select fueltype$
Case "Gas"
days=amount/10800
End Select


Return days

End Function

this functions pretty straight forword, you punch in Gas as your fuel, and it devides the amount of fuel you punched in by a days worth a fuel(for gas) and returns the result.

i need the same thing but returns how many hours (ontop of the days) you have left..

so that i can display this as like
You have DD days left and HH hours left of Gas


i cant just write the same function but instead of
days=amount/10800

put
days=amount/450


because that would return the total amount of hours of gas i have, not what i'm looking for..

so i tried somthing like this with a float:
Function hoursleft$(fueltype$,amount#)
tmp# = 0.0
hours=0
tmp2$=""

Select fueltype$
Case "Gas"
tmp#=amount/10800

End Select

tmp2$=tmp#
hours% = Mid(tmp2,2,1)

Return hours
End Function


to extract the first decimal of a float of days (that would be the hours)

but for some reason it doesnt seem accurate.. what am i doing wrong? or am i going about it wrong, if so any ideas on how to do this?


Yahfree(Posted 2007) [#7]
i don't get this even the simplest function is incorrect:

Function hoursleft$(amount#)

amount=amount / 10800

Return amount
End Function


if you enter 450, that would be 1 hour of the 24 hours, so it should return 0.1

can anyone explain why this isnt working?


_33(Posted 2007) [#8]
Your function works.

But dividing 450 by 10800 will not return 0.1, that's simple math.


Yahfree(Posted 2007) [#9]
no it doesnt, take this small program for example:

houramount# = 450.0

proof# = houramount*24

daysamount# = proof#

Print houramount / daysamount

Delay 6000


should display "0.1" but it displays 0.04and a bunch of numbers..


Yahfree(Posted 2007) [#10]
oi, duh, i put it backwords.

Edit and you changed your reply :D


b32(Posted 2007) [#11]
As for the remaining hours, I think you could use the Mod operator. It returns the remainder of a division.
Ie:
days = amount / 100 --> division with integers will return only whole numbers
hours = amount mod 100

In this example, if 'amount' is 201, 'days' will be 2 and 'hours' 1


Yahfree(Posted 2007) [#12]
Yeah, i'm tried, i think this mod thing might be what i'm looking for. i'll try it


Yahfree(Posted 2007) [#13]
hmm, it seems mod returns the total number in this case? i use:

days=11250/ 10800

to return the days(1)

and now using

hours = 11250 Mod 10800

and it returns 11250?

am i doing somthing wrong? as soon as i get this one i'm going to bed


b32(Posted 2007) [#14]
Yes, I think so ? Because when I try it, I get 450:
Print 11250 Mod 10800

WaitKey()



Yahfree(Posted 2007) [#15]
Duh, i see what went wrong!

thanks b32!


octothorpe(Posted 2007) [#16]
You might find dates a lot simpler to work with if you convert them to the number of seconds since X. X is conventionally January 1, 1970, but you could very easily use January 1, 2000 or (perhaps better) the date/time that the current game was started.

If you do this, all the math becomes trivial addition and subtraction.


_33(Posted 2007) [#17]
By the way Yahfree, I just posted a small collection of my date functions in the code archive, if you are interested. Currently, there are no example code, but if people are really struggling, I'll try to find time to make a couple examples.

http://www.blitzbasic.com/codearcs/codearcs.php?code=2081

Cheers.


Yahfree(Posted 2007) [#18]
Very useful, thanks _33, octhothorp, that might be easy, but i don't see any functions that return the current date/month/year/hour/minute/second in seconds..


Subirenihil(Posted 2007) [#19]
That is because Blitz uses 32 bit long integers (a standard integer is 16 bits, also known as a "short" integer), time in seconds (such as how Windows stores creation/modification dates for files) is stored in 64 bit integers(not sure what they are normally called, "double long integers" maybe?). 32 bits is just not enough to store that much data. A 32 bit signed int will work for 68 years, a 64 bit signed int will work for 292,471,208,677 years either side of your starting date (which is enough to store the age of the earth, even if you believe in an old earth)

If you want the current date and time, try this:
date$=CurrentDate$()
time$=CurrentTime$()

year%=Right$(date$,4)
month$=Mid$(date$,4,3)
day%=Left$(date$,2)
hour%=Left$(time$,2)
minutes%=Mid$(time$,3,2)
seconds%=Right$(time$,2)

If you want months in an integer then change the "month$=" to "m$=" and add the following:
Select m$
	Case "JAN":month%=1
	Case "FEB":month%=2
	Case "MAR":month%=3
	Case "APR":month%=4
	Case "MAY":month%=5
	Case "JUN":month%=6
	Case "JUL":month%=7
	Case "AUG":month%=8
	Case "SEP":month%=9
	Case "OCT":month%=10
	Case "NOV":month%=11
	Case "DEC":month%=12
	Default:   month%=1
End Select



Yahfree(Posted 2007) [#20]
yep, i wrote some functions to return ints of date ect ect here they are if they seem useful...




Yahfree(Posted 2007) [#21]
Yuck, i got a problem, i'll try to explain the best i can.

To put it into propective i'll use a pretend plant program as an example.


In this plant program, people can share information on how old their plants are.

the program reads a plant information file, in txt, thats how they share the plant information..

so person A is from a different timezone then person B

person A wants to show person B on how his plant is doing. Person B takes the information file from person A

the program then takes the old time marked on the file, and compares it with his 'new time'

because Person B is 2 hours ahead of Person A, his program takes Person A's file and reads Person A's time..

when the program grabs Person Bs current time, it calculates the plant to be 2 hours older then it should be!

thats my problem, But, i wrote my program so when it opens the first time, it subtracts everything and writes it back to the file.

in this messed up case, it would write the plant data back to the file with the information that the plant is 2 hours older then it should be.

after it writes back what it thinks is the correct data, the program then updates the time. my update time function updates the time based on the computer clock, so it would fix this issue.

but i cant use it before i update the data, otherwise it wouldnt subtract how old the plant is correctly, it would think no time has passed, and it would take any time off the plant.

i hope i was clear enough, how would i solve this problem?


b32(Posted 2007) [#22]
Maybe you could read out what timezone the user is, and convert the system time to GMT before using it ?
Here is how to use the api, not sure if the daylight saving time stuff works correctly though.

It was based on this info in the msdn:
http://msdn2.microsoft.com/en-us/library/ms885646.aspx


Yahfree(Posted 2007) [#23]
Ohh, interesting, if i could convert everyones time into gmt time so the program only has to worry about 1 time, that would solve the problem, heres a dumb question, being the kid i am, different timezones can only be plus from gmt?

like for example is there any timezone that GMT-2 or any negitives?


b32(Posted 2007) [#24]
Errrrmmm .. offcourse I know that .. (googlegoogle) ..
http://wwp.greenwichmeantime.com/info/timezone.htm
They seem to go from -12 to +12


Yahfree(Posted 2007) [#25]
Then how do you know if:

Print "Your time is GMT+"

is + or minus? if it was minus would that line be:

"Your time is GMT+-"+time?

Edit, i wasnt very clear, would time be normal int for plus I.E. 6 and a negitive int for minus I.E. -6?


Yahfree(Posted 2007) [#26]
Also, what would i have to do to add that feature? my guess would be:

somthing like this:

to get the new time i use this:


(i would modify my functions above):

function ReturnHourInt()

bank = CreateBank(255)
tmp = api_GetTimeZoneInformation(bank)
tmp2 =  PeekInt(bank, 0)

tmp2=tmp2/60


temp$ = CurrentTime()

temp2% = Left(temp$,2)

temp2%=temp2%-tmp2

Return temp2

End Function


or somthing like that? right idea? wrong idea? or failing horribly?


b32(Posted 2007) [#27]
Erm .. yes, you're right .. 'bias' would be negative in such an area.
The 'daylightbias' is a bit more difficult.
The structure also contains two dates, that indicate the start/end date for the daylight saving time.
Before the first date, the clock is set normally to GMT+bias
However, between the two dates, the clock is set to GMT+bias+daylightbias.
I think the 'timezone' structure is now read properly, however it could be good to test it a bit.



b32(Posted 2007) [#28]
Ow .. missed your last post ..
Ehm, something like that should work, first extract hours/mins, then add 'bias' en if the date is in the 'timesave' period, add 'daylightbias' too
time$ = CurrentTime$()
Print time$

hour = Mid$(time$, 1, 2)
min = Mid$(time$, 4, 2)
sec = Mid$(time$, 7, 2)

Print hour
Print min
Print sec

WaitKey()



Yahfree(Posted 2007) [#29]
mm k... but i thought the built in computer clock grabs 'daylightbias' anyways? so if its in daylightbias, it should subtract more anyways correct?


Yahfree(Posted 2007) [#30]
how do i figure the date in GMT time? i mean the month doesnt matter, because the most difference between your time and gmt time cant differ up to months..

Edit, also, why doesnt this work?

Function ReturnHourInt%()

bank = CreateBank(255)
tmp = api_GetTimeZoneInformation(bank)
tmp2 =  PeekInt(bank, 0)

tmp2=tmp2/60


temp$ = CurrentTime()

temp2% = Left(temp$,2)

If tmp2 > 0 temp2%=temp2%-tmp2
If tmp2 < 0 temp2%=temp2%+tmp2

Return temp2


End Function


should return the hour in GMT time no? well to double check i switched my computer clock to a different timezone ... and the time was different! it should be the same, as the function should compensate for that.


b32(Posted 2007) [#31]
Have you also reset the clock when changing the timezone ?
I tried the following with a few timezones and the result in 'minutes' was allways the same.
However, it is still not fullproof. The returned daylight-dates don't seem to be right and I'm not sure if the test I made for daylightsaving is full-proof.

So I would keep it in a Function, so it is easier to upgrade later on.


Yahfree(Posted 2007) [#32]
hmm, i'm confused, i'm trying to get the hour in gmt time to start with, but my atempts differ one the time zone set on my computer clock means its not working correctly..

the plan is no matter what time the users clock is based on, it does the math to convert the user current time into the current gmt time.

how would i do this?

ok lets simplefy this, how would i create 2 functions that return the current hour and current date in gmt time, i ausume it would be close to the functions i wrote above (ReturnHourInt, ReturnDateInt) as shown above i tried to modify ReturnHourInt to return the GMT time but i failed horribly..

any ideas?


Edit, and i tried your example above, and a +8 time zone would return 1000 somthing and a -8 time zone would return 300 somthing 'minutes'

Edit2, Just to clear things up a little bit, the prototype i'm building doesnt accuratly respond to hours..

it doesnt grab the current minute, for example: if you planted your plant seed at 14:59 in 1 minute it will consider it 1 hour old. i plan to change this after i get this working.


b32(Posted 2007) [#33]
Okay, I gave it another try. This time I tested it more properly, so now it should be better. I tried writing a function that converts a given time into GMT-time:



Yahfree(Posted 2007) [#34]
THATS PERFECT!

You didnt have to make it for me :\

a simple point in the right direction woulda done!

thanks anyways, the only thing remaining is a function that returns the same thing as CurrentDate() but in GMT time, then it should go in the archive, how would you go about doing this?

and wheres the documents on what each byte is on this bank?

like how do you know:
bias = -PeekInt(bank, 0)
is at 0?

oh and to clean up that function a bit you can put Date/Time in the function... IE

Function MakeGMTTime$()
date$ = CurrentDate$()
time$ = CurrentTime$()


then it only becomes:

Graphics 800, 600, 0, 2
SetBuffer BackBuffer()


Repeat

	yourtime$ = CurrentTime$()

	Cls
	Text 0,  0, "now:" + yourtime$
	Text 0, 20, "GMT:" + MakeGMTTime$()
	Flip
	
Until KeyHit(1)

End


Function MakeGMTTime$()
date$ = CurrentDate$()
time$ = CurrentTime$()


	;split date
	day = Mid$(date$, 1, 2)
	mon = (Instr("JanFebMarAprMayJunJulAugSepOctNovDec", Mid$(date$, 4, 3)) + 2) / 3	

	bank = CreateBank(255)
	;retreive timezone info	
	api_GetTimeZoneInformation(bank)
	;offset in minutes for the timezone
	bias = -PeekInt(bank, 0)
	;date when 'normal' time starts	
	month1 = PeekShort(bank, 70);month
	day1 = PeekShort(bank, 74);day
	hour1 = PeekShort(bank, 76);hour
	;date when 'normal' time starts	
	month2 = PeekShort(bank, 154);month
	day2 = PeekShort(bank, 158);day
	hour2 = PeekShort(bank, 160);hour
	;amount of minutes offset in summer	
	daylightbias = PeekInt(bank, 168)
	FreeBank bank
	
	;test for daylightsaving
	test = 0
	If (mon >= month2) And (mon <= month1) Then
		If (mon <> month1) And (mon <> month2) Then test = 1
		If (day >= day2) And (mon = month2) Then test = 1
		If (day <= day1) And (mon = month1) Then test = 1
	End If
	If test Then bias = bias - daylightbias
	
	hour = Int(Mid$(time$, 1, 2)) - (bias / 60.0)
	If hour < 0 Then hour = hour + 24
	If hour > 23 Then hour = hour - 24
	min = Int(Mid$(time$, 4, 2)) 
	
	sec$ = Right$(time$, 2)
	time$ = hour + ":" + min + ":" + sec
	
	Return time$
		
End Function



b32(Posted 2007) [#35]
About the date, first add the bias to the time. And if the new time is before 0:00 or after 23:59, then add or substract a day. With that new 'day', you can do the same thing towards months. If it is below zero or greater than the numbers of days in a month, substract or add a month. Then the same from month->years

About the bias, on the msdn page about this command:
http://msdn2.microsoft.com/en-us/library/ms885646.aspx

is a link to the TIME_ZONE_INFORMATION 'typedef':
typedef struct _TIME_ZONE_INFORMATION { 
  LONG Bias; 
  WCHAR StandardName[32]; 
  SYSTEMTIME StandardDate; 
  LONG StandardBias; 
  WCHAR DaylightName[32]; 
  SYSTEMTIME DaylightDate; 
  LONG DaylightBias; 
} TIME_ZONE_INFORMATION; 

That is the information that will be stored in the bank.
'Long' in this case is the same as an Integer/Int (4 bytes), and -after testing- I thought that WChar[32] is a 64 bytes string.

'Bias' is the first element of this type, that is why it is at zero.

SYSTEMTIME is also a type:
typedef struct _SYSTEMTIME { 
  WORD wYear; 
  WORD wMonth; 
  WORD wDayOfWeek; 
  WORD wDay; 
  WORD wHour; 
  WORD wMinute; 
  WORD wSecond; 
  WORD wMilliseconds; 
} SYSTEMTIME;

And a Word is 2 byte, I used PokeShort/PeekShort for this.


Yahfree(Posted 2007) [#36]
If it is below zero or greater than the numbers of days in a month


How do i get the number of days in each month? then store it some way to be able to compare it with the current month?


b32(Posted 2007) [#37]
I would make a string containing all the max. days in the months, each 2 characters, and then use Mid$() to get the appropriate value for the current month.

Then, I believe a leapyear can be found by this
(year mod 4 = 0)
I would multiply that with (month = 2) and add it to the number of days found with Mid$


Yahfree(Posted 2007) [#38]
hmm, can you give me a small example? i havent seen this type of technque before i think its the same as this line:

mon = (Instr("JanFebMarAprMayJunJulAugSepOctNovDec", Mid$(date$, 4, 3)) + 2) / 3



b32(Posted 2007) [#39]
:D Yes .. it is a bit weird I suppose
maxdays = Int(Mid$("312831303130313130313031", month * 2 - 1, 2)) + ((month = 2) * (year Mod 4 = 0))



Yahfree(Posted 2007) [#40]
interesting, looks very strange.. i'll mess with it thanks!


Yahfree(Posted 2007) [#41]
Question, in your code above, month would be int 1-12 correct? and year would be the full year IE 2007?


b32(Posted 2007) [#42]
Yes, although I'm not really certain about these leapyears. 2000 was a leapyear, right ? 2008..2004..2000 .. yes .. I think it is correct. And month is 1-12, integer, indeed


Yahfree(Posted 2007) [#43]
Well heres the final function, let me know what you think:

;--------------------------------------------
;      MakeGMTDate$()
;--------------------------------------------
Function MakeGMTDate$()
date$ = CurrentDate$()
time$ = CurrentTime$()

	day = Mid$(date$, 1, 2)
	mon = (Instr("JanFebMarAprMayJunJulAugSepOctNovDec", Mid$(date$, 4, 3)) + 2) / 3
	year = Mid(date$,8,4)
    maxdays = Int(Mid$("312831303130313130313031", mon * 2 - 1, 2)) + ((mon = 2) * (year Mod 4 = 0))
	hour = Int(Mid$(time$, 1, 2))
	min = Int(Mid$(time$, 4, 2)) 
	
	bank = CreateBank(255)
	;retreive timezone info	
	api_GetTimeZoneInformation(bank)
	;offset in minutes for the timezone
	bias = -PeekInt(bank, 0)
	;date when 'normal' time starts	
	month1 = PeekShort(bank, 70);month
	day1 = PeekShort(bank, 74);day
	hour1 = PeekShort(bank, 76);hour
	;date when 'normal' time starts	
	month2 = PeekShort(bank, 154);month
	day2 = PeekShort(bank, 158);day
	hour2 = PeekShort(bank, 160);hour
	;amount of minutes offset in summer	
	daylightbias = PeekInt(bank, 168)
	FreeBank bank
	
	test = 0
	If (mon >= month2) And (mon <= month1) Then
		If (mon <> month1) And (mon <> month2) Then test = 1
		If (day >= day2) And (mon = month2) Then test = 1
		If (day <= day1) And (mon = month1) Then test = 1
	End If
	If test Then bias = bias - daylightbias
	
	;If the hour is greater then 23 (24+) then day would be 1 more in GMT
	If hour+bias/60 > 23 day=day+1
	
	;If the hour is lesser then 0 (-1 and below) then the day would be 1 less in GMT
	If hour+bias/60 < 0 day=day-1
	
	;If the day exceeds the max amount of days in the month, then the month would be 1 more in GMT
	If day > maxdays mon=mon+1
	
	;if the day is less then 1 the month would be 1 less in GMT, reset maxdays so it reads the new month..
	; And set the day to the max days in the month (1 less from the next month)
	If day < 1
	mon=mon-1 
	maxdays = Int(Mid$("312831303130313130313031", mon * 2 - 1, 2)) + ((mon = 2) * (year Mod 4 = 0))
	day=maxdays
	End If
	
	;If the month exceeds 12 (months in a year) set months to 1
	If mon > 12 year=year+1 mon = 1
	
	;If the month is less then 1, set months to 12, drop year by 1
	If mon < 1 year=year-1 mon = 12
	
	
	Select mon
	Case 12: f_mon="Dec"
	Case 11: f_mon="Nov"
	Case 10: f_mon="Oct"
	Case 9: f_mon="Sep"
	Case 8: f_mon="Aug"
	Case 7: f_mon="Jul"
	Case 6: f_mon="Jun"
	Case 5: f_mon="May"
	Case 4: f_mon="Apr"
	Case 3: f_mon="Mar"
	Case 2: f_mon="Feb"
	Case 1: f_mon="Jan"
	End Select
	
Return day + " " + f_mon + " " + year

End Function


looks alright to me, but i might of missed somthing/screwed somthing up :D off to testing


b32(Posted 2007) [#44]
Two minor thingies: f_mon isn't defined as a string, so it returns a zero now instead of Aug. And 'day' should allways return 2 digits, even if it is below 10. So best is, to make that a string first, f_day, then add a zero to it if day<10.
But for the rest: it looks nice. I also hope it works okay.


Yahfree(Posted 2007) [#45]
ok it seems to work alright, thanks for pointing that out, and thanks for all the help, heres the final functions with a example:

CurrentGMTTime(Fixed a display issue)

CurrentGMTDate


Example:


I'll go code Archive these gems


Yahfree(Posted 2007) [#46]
Hmm, maybe all is not well? run the example under different timezone settings on your computer clock, the GMT time seems to change? any ideas?


b32(Posted 2007) [#47]
Hmm, I tried that, but it seems to work correctly ? I tested your last posted code under 'example'. In every timezone, I got 15:43


Yahfree(Posted 2007) [#48]
Hmm, set your timezone to somthing that has a halfhour thrown in it... like GMT + 9:30 or GMT - 4:30


b32(Posted 2007) [#49]
I see the problem.. when the bias is applied, it is divided by 60 in an integer division. That is the reason half hours 'dissapear' from this calculation.
After the line
If test Then bias = bias - daylightbias
it should go like this:
1. get hours and minutes
2. convert both to single time in minutes from midnight
minutes = hours * 60 + minutes
3. apply bias to that number
-if below zero, add 1440 minutes (=24hour)
-use Mod 1440
4. finally, divide it by 60 again to get the hours, and use Mod 60 to get the minutes.
That should work better.


Yahfree(Posted 2007) [#50]
cant i just use:

Minutes = bias mod 60

?

then use bias as normal and add minutes to minutes?

edit:

somthing like this would work?
	minutebias = - (bias Mod 60)
	
	min = Int(Mid$(time$, 4, 2)) - (minutebias)
	If min < 1 Then min = min + 60 hour=hour-1
	If min > 59 Then min = min - 60 hour=hour+1



b32(Posted 2007) [#51]
It looks like it should work. Have you tried it allready ?


Yahfree(Posted 2007) [#52]
Tried it... hmm not working it seems:


what am i doing wrong?


b32(Posted 2007) [#53]
I think the order of calculating hour/min went wrong.
First, you substract or add one to 'hour' (when checking the minutes) Then, you define 'hour' again, discarding the previous value of 'hour'.


Yahfree(Posted 2007) [#54]
I don't think i get it.. the order you discribed sounds exactly how it looks in the above example?


b32(Posted 2007) [#55]
Yes, I tried to describe what went wrong :)
First you say:
If min < 1 Then min = min + 60 hour=hour-1
If min > 59 Then min = min - 60 hour=hour+1

<- So 'hour' is 1 or -1 at this point ->
Then:
hour = Int(Mid$(time$, 1, 2)) - (bias / 60.0)
^ Here, any previous changes made to 'hour' dissapear


Yahfree(Posted 2007) [#56]
ahh i see, i made the changes, and it still doesnt seem to work :( any ideas?

function as of now:



b32(Posted 2007) [#57]
Maybe the minutebias ? It is negative, and later on it is substracted, resulting in a positive value.


Yo! Wazzup?(Posted 2007) [#58]
I don't know if you've already done this, I'm too lazy to look through the fourm... You should encrypt the date in case someone tries to change the text file.


Yahfree(Posted 2007) [#59]
Seems to be working now, thanks b32.

Wazzup, may be a possiblilty for the future, but at the moment if the users want to screw up their own data files they can be my guest :D