I am trying to make a countdown timer, the idea is to set the time in text edit property and after i click set timer(button), that time to be sent to Label, which will then start the countdown to 0. I have gotten to this part, but i cant figure out a way to make seconds countdown, If any of you guys can help I would appreciate it.
I tried this from an example I found online but it didnt work because this is Firemonkey application.
dec(TotalTime); {decrement the total time counter}
// Timer code..
procedure TForm1.ButtonSetTimerClick(Sender: TObject);
var
GetTime : TDateTime;
begin
Timer3.Enabled := True;
Label11.Text := Edit1.Text;
ButtonSetTimer.Enabled := False;
Edit1.Enabled := False;
GetTime := StrToTime(Edit1.Text);
end;
procedure TForm1.ButtonStopTimerClick(Sender: TObject);
begin
Timer3.Enabled := False;
ButtonSetTimer.Enabled := True;
Edit1.Enabled := True;
end;
procedure TForm1.Timer3Timer(Sender: TObject);
var
GetTime : TDateTime;
Hour, Min, Sec, MSec: Word;
begin
DecodeTime(GetTime, Hour, Min, Sec, Msec);
Label11.Text := TimeToStr(GetTime);
Label11.Text := IntToStr(Hour) + ':'+ IntToStr(Min) + ':'+ IntToStr(Sec);
Label11.Text := Format('%2.2u:%2.2u:%2.2u',[Hour,Min,Sec]);
end;
Cheers.
You did not say how (in which format) the time is to be entered in the TEdit, so here are three alternative time entry possibilities. The output is anyway formatted as H:M:S.
I modified the code from yesterday to use TryStrToInt / TryStrToTime to catch errors. Also, a Seconds counter together with OnTimer event as in my previous example has a poor accuracy and can drift several seconds within 5 minutes. Edijs solution to compare Now with a calculated end time is insensitive to the inaccuracy of OnTimer events, so I adopted that too.
var
TimeOut: TDateTime;
function SecsToHmsStr(ASecs: integer):string;
begin
Result := Format('%2d:%2.2d:%2.2d',
[ASecs div 3600, ASecs mod 3600 div 60, ASecs mod 3600 mod 60]);
;end;
procedure TForm6.Timer1Timer(Sender: TObject);
begin
Label1.Caption := SecsToHmsStr(SecondsBetween(Now, TimeOut));
if Now > Timeout then Timer1.Enabled := False;
end;
Time entry alternative one, Timeout after a given number of seconds
// Timeout after a given number of seconds
procedure TForm6.Button1Click(Sender: TObject);
var
Seconds: integer;
begin
if TryStrToInt(Edit1.Text, Seconds) then
begin
TimeOut := IncSecond(Now, Seconds);
Timer1.Enabled := True;
Label1.Caption := SecsToHmsStr(SecondsBetween(Now, TimeOut));
end
else
ShowMessage('Error in number of seconds');
end;
Time entry alternative two, Timeout after a given number of hours, minutes and seconds
// Timeout after a given number of hours, minutes and seconds
procedure TForm6.Button2Click(Sender: TObject);
begin
if TryStrToTime(Edit1.Text, TimeOut) then
begin
TimeOut := Now + TimeOut;
Timer1.Enabled := True;
Label1.Caption := SecsToHmsStr(SecondsBetween(Now, TimeOut));
end
else
ShowMessage('Error in time format');
end;
Time entry alternative three, Timeout at a given time within 24 hours
// Timeout at a given time within 24 hours
procedure TForm6.Button3Click(Sender: TObject);
begin
if TryStrToTime(Edit1.Text, TimeOut) then
begin
if TimeOut <= Time then
TimeOut := Tomorrow + TimeOut
else
TimeOut := Today + TimeOut;
Timer1.Enabled := True;
Label1.Caption := SecsToHmsStr(SecondsBetween(Now, TimeOut));
end
else
ShowMessage('Error in time format');
end;
This should do it:
Uses
System.DateUtils;
type
..
private
FDateTimeTo: TDateTime;
end;
function IntToTimeStr(const ASeconds: Int64): string;
begin
Result := Format('%2d:%2.2d:%2.2d', [ASeconds div 3600, ASeconds mod 3600 div 60,
ASeconds mod 3600 mod 60]);
end;
procedure TForm1.BitBtn1Click(Sender: TObject);
begin
FDateTimeTo := StrToDateTime(FormatDateTime('yyyy' + FormatSettings.DateSeparator + 'mm' +
FormatSettings.DateSeparator + 'dd 00:00:00', Now)) + StrToTime(Edit1.Text);
if CompareDateTime(Now, FDateTimeTo) = 1 then
FDateTimeTo := IncDay(FDateTimeTo);
end;
procedure TfrmMain.Timer1Timer(Sender: TObject);
begin
Label1.Caption := IntToTimeStr(SecondsBetween(Now, FDateTimeTo));
end;
Related
I have two TDateTime variables, like this:
s := StrToDateTime('03/03/2017 10:10:12');
e := StrToDateTime('04/04/2017 10:10:12');
I need to find out the difference between them, in hh:mm:ss format.
The ...Between() functions are not helping me here.
Use the DateUtils.SecondsBetween function:
Uses
DateUtils,SysUtils;
function TimeDiffStr(const s1,s2: String): String;
var
t1,t2: TDateTime;
secs: Int64;
begin
t1 := StrToDateTime(s1);
t2 := StrToDateTime(s2);
secs := SecondsBetween(t1,t2);
Result := Format('%2.2d:%2.2d:%2.2d',[secs div SecsPerHour,(secs div SecsPerMin) mod SecPerMin,secs mod SecsPerMin]);
end;
begin
WriteLn(TimeDiffStr('03/03/2017 10:10:12','04/04/2017 10:10:12'));
ReadLn;
end.
From the number of seconds, calculate the hours,minutes and remaining seconds.
If you want the difference in minutes, use the DateUtils.MinutesBetween function:
function TimeDiffStr(const s1,s2: String): String;
var
t1,t2: TDateTime;
minutes: Int64;
begin
t1 := StrToDateTime(s1);
t2 := StrToDateTime(s2);
minutes := MinutesBetween(t1,t2);
Result := Format('%2.2d:%2.2d:%2.2d',[minutes div MinsPerHour,minutes mod MinsPerHour,0]);
end;
You can use TTimeSpan (from the System.TimeSpan unit).
program Project1;
{$APPTYPE CONSOLE}
uses
System.SysUtils, System.TimeSpan;
var
StartDate, EndDate: TDateTime;
TS: TTimeSpan;
Temp: string;
begin
StartDate := StrToDateTime('03/03/2017 10:10:12');
EndDate := StrToDateTime('04/04/2017 10:10:12');
TS := TTimeSpan.Subtract(EndDate, StartDate);
Temp := TS;
WriteLn(Temp); // Outputs 32.00:00:00
// The next line outputs the same as the one above
WriteLn(Format('%.2d:%.2d:%.2d:%.2d', [TS.Days, TS.Hours, TS.Minutes, TS.Seconds]));
WriteLn(TS.TotalMinutes); // Outputs 4.60800000000000E+0004
WriteLn(Trunc(TS.TotalMinutes)); // Outputs 46080
// This one will give the output you want (768:00:00)
WriteLn(Format('%.2d:%.2d:%.2d', [TS.Days * 24 + TS.Hours, TS.Minutes, TS.Seconds]));
ReadLn;
end.
First off, don't use hard-coded strings for date/time values. That is subject to localization issues, and it is just wasted overhead anyway. Use the SysUtils.EncodeDate() and SysUtils.EncodeTime() functions, or the DateUtils.EncodeDateTime() function.
Second, the ...Between() functions can indeed be usedneed, in particular SecondsBetween(). You can calculate the individual components from that return value.
Try something like this:
uses
..., SysUtils, DateUtils;
var
s, e: TDateTime;
diff: Int64;
days, hours, mins, secs: Integer;
s: string;
begin
s := EncodeDateTime(2017, 3, 3, 10, 10, 12, 0);
e := EncodeDateTime(2017, 4, 4, 10, 10, 12, 0);
diff := SecondsBetween(e, s);
days := diff div SecsPerDay;
diff := diff mod SecsPerDay;
hours := diff div SecsPerHour;
diff := diff mod SecsPerHour;
mins := diff div SecsPerMin;
diff := diff mod SecsPerMin;
secs := diff;
s := Format('%d:%d:%d:%d', [days, hours, mins, secs]);
end;
Is possible to convert
'Thu Jul 17 17:20:38 2014'
with this function? Tried my best, but no result. This format uses justin.tv API, for twitch.tv i use code below and it works. Thanks for help.
var
t1, t2: Tdate;
dzien: integer;
begin
t1 := StrToDateTime('"2014-07-21T12:49:08Z"');
t2 := TTimeZone.Local.ToUniversalTime(Now);
dzien := trunc(t2 - t1);
if dzien > 0 then
Result := (Format('%d days, %s', [dzien, FormatDateTime('hh:nn:ss',
Frac(t2 - t1))]))
else
Result := (Format('%s', [FormatDateTime('hh:nn:ss', Frac(t2 - t1))]));
end;
It is easy enough to parse the string yourself. Like this:
uses
Types, SysUtils, DateUtils, StrUtils;
function DecodeJustinTvDateTime(const Value: string): TDateTime;
function MonthNumber(const MonthStr: string): Integer;
var
FormatSettings: TFormatSettings;
begin
FormatSettings := TFormatSettings.Create('en-us');
for Result := low(FormatSettings.ShortMonthNames) to high(FormatSettings.ShortMonthNames) do begin
if SameText(MonthStr, FormatSettings.ShortMonthNames[Result]) then begin
exit;
end;
end;
raise EConvertError.Create('Unrecognised month name');
end;
var
items: TStringDynArray;
Day, Month, Year, Time, Hour, Minute, Second: string;
begin
items := SplitString(Value, ' ');
if Length(items)<>5 then begin
raise EConvertError.Create('Unrecognised date time format');
end;
// items[0] is day of the week which we can ignore
Month := items[1];
Day := items[2];
Time := items[3];
Year := items[4];
items := SplitString(Time, ':');
Assert(Length(items)=3);
if Length(items)<>3 then begin
raise EConvertError.Create('Unrecognised time format');
end;
Hour := items[0];
Minute := items[1];
Second := items[2];
Result := EncodeDateTime(
StrToInt(Year),
MonthNumber(Month),
StrToInt(Day),
StrToInt(Hour),
StrToInt(Minute),
StrToInt(Second),
0
);
end;
The error checking here is a little lame and you might care to improve on it.
procedure TForm6.Button1Click(Sender: TObject);
var
t1: TDateTime;
ts:TFormatSettings;
begin
ts:=TFormatSettings.Create;
ts.ShortDateFormat:='yyyy-MM-dd';
ts.DateSeparator:='-';
ts.TimeSeparator:=':';
t1 := StrToDateTime('2014-07-21T12:49:08Z',ts);
end;
t1 contains date and time from your string.
I need to calculate the elapsed time (nicely formatted) between now and a file's last modification date/time, ie. something like this, only in my case, the difference can be in days, months or even years.
I tried this:
var
TimeDiff : Double;
begin
TimeDiff := Now - FileAgeEx('C:\my-file.txt');
if (TimeDiff >= 1) then
Caption := FormatDateTime('dd hh:nn:ss', TimeDiff)
else
Caption := FormatDateTime('hh:nn:ss', TimeDiff);
end;
But (1) it doesn't work and (2) I'd like a better formatting.
Ultimately my goal is to have something like this:
Time Diff < 1 day ==> display this: 12:00:01
Time Diff >= 1 day ==> display this: 25 days, 12:00:01
Time Diff >= 1 year ==> display this: 2 years, 3 months, 10 days, 12:00:01
Anyone knows how can I do that?
Thanks!
The main problem would appear to be getting hold of the last modified time of the file. I use the following code:
function LastWriteTime(const FileName: string): TFileTime;
var
AttributeData: TWin32FileAttributeData;
begin
if not GetFileAttributesEx(PChar(FileName), GetFileExInfoStandard, #AttributeData) then
RaiseLastOSError;
Result := AttributeData.ftLastWriteTime;
end;
function UTCFileTimeToSystemTime(const FileTime: TFileTime): TSystemTime;
//returns equivalent time in current locality, taking account of daylight saving
var
LocalFileTime: Windows.TFileTime;
begin
Windows.FileTimeToLocalFileTime(FileTime, LocalFileTime);
Windows.FileTimeToSystemTime(LocalFileTime, Result);
end;
function UTCFileTimeToDateTime(const FileTime: TFileTime): TDateTime;
begin
Result := SystemTimeToDateTime(UTCFileTimeToSystemTime(FileTime));
end;
You call LastWriteTime to get the last modified time in file time format. Then call UTCFileTimeToDateTime to convert into TDateTime accounting for the prevailing local time zone of the machine. You can then compare that value with Now.
As regards the formatting, you already appear to know how to do that. You basic approach will work and you just need to flesh out the details.
In the comments you say that
FormatDateTime('dd hh:nn:ss', 2.9);
shows a 1 for the day when you would expect a 2. The problem is that this function formats dates rather than time intervals. The value 2.9 is not treated as an elapsed time, rather it is treated as an absolute date/time, 2.9 days after the Delphi epoch. I would use Trunc and Frac to obtain number of days, and the part of days respectively, and work from there.
Days := Trunc(TimeDiff);
Time := Frac(TimeDiff);
The following code, extracted directly from my codebase, may give you some pointers. Note that its input is in seconds, but it should set you on the right path.
function CorrectPlural(const s: string; Count: Integer): string;
begin
Result := IntToStr(Count) + ' ' + s;
if Count<>1 then begin
Result := Result + 's';
end;
end;
function HumanReadableTime(Time: Double): string;
//Time is in seconds
const
SecondsPerMinute = 60;
SecondsPerHour = 60*SecondsPerMinute;
SecondsPerDay = 24*SecondsPerHour;
SecondsPerWeek = 7*SecondsPerDay;
SecondsPerYear = 365*SecondsPerDay;
var
Years, Weeks, Days, Hours, Minutes, Seconds: Int64;
begin
Try
Years := Trunc(Time/SecondsPerYear);
Time := Time - Years*SecondsPerYear;
Weeks := Trunc(Time/SecondsPerWeek);
Time := Time - Weeks*SecondsPerWeek;
Days := Trunc(Time/SecondsPerDay);
Time := Time - Days*SecondsPerDay;
Hours := Trunc(Time/SecondsPerHour);
Time := Time - Hours*SecondsPerHour;
Minutes := Trunc(Time/SecondsPerMinute);
Time := Time - Minutes*SecondsPerMinute;
Seconds := Trunc(Time);
if Years>5000 then begin
Result := IntToStr(Round(Years/1000))+' millennia';
end else if Years>500 then begin
Result := IntToStr(Round(Years/100))+' centuries';
end else if Years>0 then begin
Result := CorrectPlural('year', Years) + ' ' + CorrectPlural('week', Weeks);
end else if Weeks>0 then begin
Result := CorrectPlural('week', Weeks) + ' ' + CorrectPlural('day', Days);
end else if Days>0 then begin
Result := CorrectPlural('day', Days) + ' ' + CorrectPlural('hour', Hours);
end else if Hours>0 then begin
Result := CorrectPlural('hour', Hours) + ' ' + CorrectPlural('minute', Minutes);
end else if Minutes>0 then begin
Result := CorrectPlural('minute', Minutes);
end else begin
Result := CorrectPlural('second', Seconds);
end;
Except
Result := 'an eternity';
End;
end;
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Delphi Timer: Time before next event
How to get current timeleft when timer will be executed ?
for example:
I create a timer with interval 60 000
procedure TForm1.my_Timer(Sender: TObject);
begin
// do something
end;
Then I create another timer (interval 1000) which reason is to get the timeleft of the first timer
procedure TForm1.second_Timer(Sender: TObject);
begin
second_Timer.Interval := 1000;
Label1.Caption := IntToStr(my_Timer.timeleft); // How ???
end;
Thanks.
You can't get this information from the timer. The best you can do is make a note of when the timer last fired and work it out for yourself. For example you can use TStopwatch to do this.
TTimer itself does not have have this capability. One option would be to use the tag property of the first timer to store the remaining time like so:
procedure TForm1.MyTimerTimer(Sender: TObject);
begin
MyTimer.Tag := MyTimer.Interval;
// Do something
end;
procedure TForm1.Second_TimerTimer(Sender: TObject);
begin
MyTimer.Tag := MyTimer.Tag - Second_Timer.Interval;
Label1.Caption := 'Time Left '+IntToStr(MyTimer.Tag);
end;
Just created my hack way, I'm using two global vars
var
my_timer_interval_hack : integer;
my_timer_timeleft_hack : integer;
procedure TForm1.my_Timer(Sender: TObject);
begin
// do something
// hack, reset timer TimeLeft
my_timer_timeleft_hack := my_Timer.Interval;
end;
procedure TForm1.second_Timer(Sender: TObject);
begin
second_Timer.Interval := 1000;
// hack get timeleft from my_timer
my_timer_timeleft_hack := my_timer_timeleft_hack - 1000;
if my_timer_timeleft_hack <= 0 then
my_timer_timeleft_hack := my_timer.Interval;
Label1.Caption := IntToStr(my_timer_timeleft_hack);
end;
How can I code to see how long the computer has been on.
Simple examples of code if possible.
You use GetTickCount function see this example.
program Ticks;
{$APPTYPE CONSOLE}
uses
Windows,
SysUtils;
function TicksToStr(Ticks: Cardinal): string; //Convert Ticks to String
var
aDatetime : TDateTime;
begin
aDatetime := Ticks / SecsPerDay / MSecsPerSec;
Result := Format('%d days, %s', [Trunc(aDatetime), FormatDateTime('hh:nn:ss.z', Frac(aDatetime))]) ;
end;
begin
try
Writeln('Time Windows was started '+ TicksToStr(GetTickCount));
Readln;
except
on E:Exception do
Writeln(E.Classname, ': ', E.Message);
end;
end.
UPDATE
to get the info in other format just must edit this line,
Result := Format('%d days, %d hours %d minutes %d seconds ', [Trunc(aDatetime), HourOf(aDatetime),MinuteOf(aDatetime),SecondOf(aDatetime) ]) ;
and add the unit DateUtils.
Note that GetTickCount isn't really designed for accuracy.
For more reliable timing, use the QueryPerformanceCounter and QueryPerformanceFrequency api calls:
function SysUpTime : TDateTime;
var
Count, Freq : int64;
begin
QueryPerformanceCounter(count);
QueryPerformanceFrequency(Freq);
if (count<> 0) and (Freq <> 0) then
begin
Count := Count div Freq;
Result := Count / SecsPerDay;
end
else
Result := 0;
end;