Is it possible to get (a TDateTime) starttime (or endtime) from a TStopWatch in Delphi 2010?
Clarification from comments: I do not need to account for multiple stop/restarts just want date & time that the stopwatch was started.
There's no property that will return the time at which the stopwatch started. So I think you'll have to do a little arithmetic to work this out.
Divide ElapsedTicks by Frequency to obtain the number of seconds since the stop watch started.
Subtract this value from the time now to obtain the time when the stop watch started.
The code might look like this:
uses
Diagnostics, DateUtils;
function GetStopwatchStart(const Stopwatch: TStopwatch): TDateTime;
var
ElapsedSeconds: Double;
begin
ElapsedSeconds := Stopwatch.ElapsedTicks / Stopwatch.Frequency;
Result := IncMillisecond(Now, -Round(ElapsedSeconds*1000));
end;
Note that this assumes that the stop watch is running, and has never been paused.
Related
I want to perform background tasks (Updates, Backups, Calculations, ...) at a time when nobody is using my application.
Therefore, I want to determine the time since the last keystroke and/or mouse move in my application. If there is no user activity for more than a specific time, the chance is high not to disturb a user. Multithreading is not an option for me.
I want to avoid touching every single OnMouseDown-/OnKeyPress-Event of every component in my application because this doesn't make any sense.
How can I get
a) The time since last input in Windows
b) The time since last input in my application
This solution works for problem
a) The time since last input in Windows
Every mouse move or keyboard input resets the time to zero.
function GetTimeSinceLastUserInputInWindows(): TTimeSpan;
var
lastInput: TLastInputInfo;
currentTickCount: DWORD;
millisecondsPassed: Double;
begin
lastInput := Default(TLastInputInfo);
lastInput.cbSize := SizeOf(TLastInputInfo);
Win32Check( GetLastInputInfo(lastInput) );
currentTickCount := GetTickCount();
if (lastInput.dwTime > currentTickCount) then begin // lastInput was before 49.7 days but by now, 49.7 days have passed
millisecondsPassed :=
(DWORD.MaxValue - lastInput.dwTime)
+ (currentTickCount * 1.0); // cast to float by multiplying to avoid DWORD overflow
Result := TTimeSpan.FromMilliseconds(millisecondsPassed);
end else begin
Result := TTimeSpan.FromMilliseconds(currentTickCount - lastInput.dwTime );
end;
end;
https://www.delphipraxis.net/1504414-post3.html
I have a datetimepicker component in a Delphi form and I would like to just get the Time. When i look at the date in debug mode I see 42544.621701, and I would like just to get 0.621701 without the date value.
You can use the Frac() function:
var
Time: TTime;
...
Time := Frac(DateTimePicker1.DateTime);
Or, you can use the System.DateUtils.TimeOf() function, which is merely an inlined wrapper around Frac() with a more descriptive name:
uses
..., DateUtils;
var
Time: TTime;
...
Time := TimeOf(DateTimePicker1.DateTime);
The question is not actually about a date time picker. The control returns you a date time value. You are looking for a way to extract just the time portion. Do that with the TimeOf function from the System.DateUtils unit.
MyTime := TimeOf(MyDateTime);
EditHoo.Text:=timetostr(DateTimePicker2.Time);
i have create a coding to set a 20 millisecond faster than windows system. i'm using encodetime.
here's the code
procedure TForm1.Button1Click(Sender: TObject);
Var
delphi_datetime :tDateTime;
t_date : tdatetime ;
windows_datetime : tSystemTime;
begin
t_date := dATE;
delphi_datetime := encodetime(8,44,59,980);
delphi_datetime := incmillisecond(delphi_datetime, 20);
replacedate(t_date , delphi_datetime);
datetimetosystemtime( delphi_datetime , windows_datetime );
setlocaltime( windows_datetime );
showmessage('time now = ' + timetostr(delphi_datetime));
end;
aftr i run it, show the correct time. but the date goes to 30 dec 1899.. but i want to the current today date but with the time faster 20 milliseconds. any help.. please...
You have the arguments of ReplaceDate backward. It reads the date of the second parameter and assigns the date portion of the first parameter. The date portion of delphi_datetime is 0 because that's how EncodeTime works. You take that zero value and assign it to t_date, but then you continue working with delphi_datetime.
Reverse the arguments of ReplaceDate, and you should see that your current system time gets set to 8:45:00.000 with the current date.
ReplaceDate(delphi_datetime, t_date);
You could have noticed the mistake sooner if you hadn't used a separate t_date variable. If you'd called Date directly, your code would have failed to compile:
ReplaceDate(Date, delphi_datetime); // can't pass function result as "var" parameter
This works:
ReplaceDate(delphi_datetime, Date);
Rob has identified problems with your existing code. However your existing approach is needlessly complex. If you chose a simpler approach you would find it easier to get the code right.
If you want a date time that is 20 milliseconds greater than now, do it like this:
MyDateTime := IncMillisecond(Now, 20);
If you want a date time representing 0845 today, then you write:
MyDateTime := Date + EncodeTime(8, 45, 0, 0);
I need to make a simple alarm clock application that, instead of playing a sound, will upload a file to the ftp (got the latter figured out). Timers have proved to be ineffective when it comes to executing a thread.
Here's what I got so far:
var
ttime : tDateTime;
timerstr : string;
timealarm : string;
aThread : TMyThread;
begin
aThread := tMyThread.Create(false);
ttime := Now;
timestr := FormatDateTime('hh:nn:ss', ttime);
timealarm := '09:30:30';
if timestr = timealarm then
aThread.Resume; //The thread will execute once and terminate;
end;
Can you guys think of another way to make that command happen once a day in a more effective way?
Thank you.
Solution found: CRON Scheduler
Thank you LU RD and Runner.
Here's my sample code. It's important to note that the comparison is between TDateTime values instead of strings, the comparison is >= rather than =, I'm careful to exclude the day portion when I don't want it, and I'm keeping track of the last day it ran.
procedure TForm1.Timer1Timer(Sender: TObject);
var
currentTime: TDateTime;
alarmTime: TDateTime;
begin
// Time is a floating point value with everything to the left of the zero
// representing the days, and everything to the right of the decimal
// point representing time of day
// Date() gets just the day portion, and lastDateExecuted is a global TDateTime
// variable where we store the last day the program ran
// We only go further if it didn't execute today
if (Date > lastDateExecuted) then
begin
// Use Time() instead of Now() to get just the time portion and leave the day
// portion at 0
currentTime := Time;
// Covert our alarm string to TDateTime instead of TDateTime to string
alarmTime := EncodeTime(9, 30, 30, 0);
// Is it time to run?
// Greater than or equal comparison makes the application still run in case
// our timer happens to miss the exact millisecond of the target time
if currentTime >= alarmTime then
begin
// Immediately set the last run date to keep the next timer event
// from repeating this
lastDateExecuted := Date;
// Do your work here
DoMyThing;
end;
end;
end;
Be sure to initialize the lastDateExecuted value to something like 0.
Use the timer interval for your granularity. If you want it to run within a minute of the target time, set the Interval to a minute. If you want it to try to run within a second, set the timer interval to a second.
If I understand you right, all you need to know is how to recognize when a particular time has been reached in order to execute this thread. Indeed, a timer isn't necessarily an ideal tool to use for this, as timers don't actually trigger in real-time (you can have the interval on 500 msec, but over after 1 minute, or 60,000 msec, it will not be perfectly lined up to an exact 120 executions, as you would wish). However, that doesn't mean we can't use timers.
Inside the timer (or you can make another repeating thread for this too), you simply get the current date/time. IMPORTANT: Make sure the interval of this timer or thread is less than half a second - this will ensure that your time won't be missed. So you would read it like this...
uses
DateUtils;
.....
var
HasExecuted: Bool;
.....
constructor TForm1.Create(Sender: TObject);
begin
HasExecuted:= False;
end;
procedure TimerOnTimer(Sender: TObject);
var
N, A: TDateTime;
Thread: TMyThread;
begin
N := Now;
A := StrToDateTime('11/20/2011 15:30:30'); //24 hour time
//If now is within 1 second over/under alarm time...
if (N >= IncSecond(A, -1)) and (N <= IncSecond(A, 1)) then
begin
if not HasExecuted then begin
HasExecuted:= True;
aThread := tMyThread.Create(true);
aThread.Resume; //The thread will execute once and terminate;
end;
end;
end;
It probably doesn't work right for you because you are using a = operator. What if this timer skips that 1 second? One execution could be the second before, and the next execution could be the second answer, in which case it won't evaluate this expression to equal true.
On another note, your TMyThread constructor - are you overriding this? If so, is the False still the original CreateSuspended parameter? If so, then you are telling this thread to execute immediately upon creation. Pass True in this parameter to make it suspended upon creation, because I see you are also calling Thread.Resume below that (which if the parameter remains false, it's already resumed). On the other hand, you also do not need to create that thread (at least I'm assuming) unless the time has been reached. Why are you creating it before you even check? It's creating one of these for each and every time this timer is executed (I'm presuming this is the thread that will take care of the uploading, as needed). Also, make sure that the thread is properly free'ing its self when it's done, and doesn't just get stuck...
constructor TMyThread.Create(CreateSuspended: Bool);
begin
inherited Create(CreateSuspended);
FreeOnTerminate:= True; //Make sure it free's its self when it's terminated
end;
EDIT:
I missed something - if, let's say, the interval of this timer was 1 (it should be more like 200-400), then it could very well execute many times in a row, during this time period. I modified the code above to also make sure it's only executed once. NOTE: This code was typed by memory, not in a delphi environment.
I would like a code sample for a function that takes a tDateTime and an integer as input and sets the system time using setlocaltime after advancing that tDateTime by (int) months. The time should stay the same.
pseudo code example
SetNewTime(NOW,2);
The issues I'm running into are rather frustrating. I cannot use incmonth or similar with a tDateTime, only a tDate, etc.
Below is a complete command-line program that works for me. Tested in Delphi 5 and 2007. Why do you say IncMonth does not work for TDateTime?
program OneMonth;
{$APPTYPE CONSOLE}
uses
SysUtils,
Windows,
Messages;
procedure SetLocalSystemTime(settotime: TDateTime);
var
SystemTime : TSystemTime;
begin
DateTimeToSystemTime(settotime,SystemTime);
SetLocalTime(SystemTime);
//tell windows that the time changed
PostMessage(HWND_BROADCAST,WM_TIMECHANGE,0,0);
end;
begin
try
SetLocalSystemTime(IncMonth(Now,1));
except on E:Exception do
Writeln(E.Classname, ': ', E.Message);
end;
end.
IncMonth should work with a TDateTime:
function IncMonth ( const StartDate : TDateTime {; NumberOfMonths : Integer = 1} ) : TDateTime;
Keep in mind a TDate is really just a TDateTime that by convention your ignore the fraction on.
Based on your pseudocode:
procedure SetNewTime(aDateTime: TDateTime; aMonths: Integer);
var
lSystemTime: TSystemTime;
begin
DateTimeToSystemTime(aDateTime, lSystemTime);
Inc(lSystemTime.wMonth, aMonths);
setSystemTime(lSystemTime);
end;
setSystemTime uses UTC time, so you have to adjust for your time zone. The bias is the number of minutes your machine's timezone differs from UTC. This adjusts the date properly on my system:
procedure SetNewTime(aDateTime: TDateTime; aMonths: Integer);
var
lSystemTime: TSystemTime;
lTimeZone: TTimeZoneInformation;
begin
GetTimeZoneInformation(lTimeZone);
aDateTime := aDateTime + (lTimeZone.Bias / 1440);
DateTimeToSystemTime(aDateTime, lSystemTime);
Inc(lSystemTime.wMonth, aMonths);
setSystemTime(lSystemTime);
end;
There isn't enough information to provide a definitive answer to your question.
Consider what you would want to happen if the day of the current month doesn't exist in your future month. Say, January 31 + 1 month. (7 months of the year have 31 days and the rest have fewer.) You have the same problem if you increment the year and the starting date is February 29 on a leap year. So there can't be a universal IncMonth or IncYear function that will work consistantly on all dates.
For anyone interested, I heartily recommend Julian Bucknall's article on the complexities that are inherent in this type of calculation
on how to calculate the number of months and days between two dates.
The following is the only generic date increment functions possible that do not introduce anomolies into generic date math. But it only accomplishes this by shifting the responsibility back onto the programmer who presumably has the exact requirements of the specific application he/she is programming.
IncDay - Add a or subtract a number of days.
IncWeek - Add or subtract a number of weeks.
But if you must use the built in functions then at least be sure that they do what you want them to do. Have a look at the DateUtils and SysUtils units. Having the source code to these functions is one of the coolest aspects of Delphi. Having said that, here is the complete list of built in functions:
IncDay - Add a or subtract a number of days.
IncWeek - Add or subtract a number of weeks.
IncMonth - Add a or subtract a number of months.
IncYear - Add a or subtract a number of years.
As for the second part of your question, how to set the system date & time using a TDatetime, the following shamelessly stolen code from another post will do the job once you have a TDatetime that has the value you want:
procedure SetSystemDateTime(aDateTime: TDateTime);
var
lSystemTime: TSystemTime;
lTimeZone: TTimeZoneInformation;
begin
GetTimeZoneInformation(lTimeZone);
aDateTime := aDateTime + (lTimeZone.Bias / 1440);
DateTimeToSystemTime(aDateTime, lSystemTime);
setSystemTime(lSystemTime);
end;