Can anyone help me how to format an int variable in delphi into a minute:seconds??
sample:
myVar := 19;
my label caption should display 00:19
any idea anyone? thanks
This will avoid any errors for seconds values that overflow into hours.
var
secs: integer;
str: string;
begin
secs := 236;
// SecsPerDay comes from the SysUtils unit.
str := FormatDateTime('nn:ss', secs / SecsPerDay));
// If you need hours, too, just add "hh:" to the formatting string
secs := 32236;
str := FormatDateTime('hh:nn:ss', secs / SecsPerDay));
end;
Assuming the myVar contains number of seconds:
label1.Caption := Format('%.2d:%.2d', [myVar div 60, myVar mod 60]);
You should use FormatDateTime method like this:
procedure TForm1.FormCreate(Sender: TObject);
const MyConst: Integer = 19;
begin
Caption:=FormatDateTime('nn:ss', EncodeTime(0, MyConst div 60, MyConst mod 60, 0));
end;
Expanding onto Brad's answer, I've wrapped this into a function which detects if the time is over an hour, and automatically shows hours if so. Otherwise, if it's less than an hour, it doesn't show the hours. It also has an optional parameter to define whether to show a leading zero on the hours and minutes, depending on your preference (i.e. 03:06:32 vs 3:6:32). This makes it a little more human-readable.
function SecsToTimeStr(const Secs: Integer; const LeadingZero: Boolean = False): String;
begin
if Secs >= SecsPerHour then begin
if LeadingZero then
Result := FormatDateTime('hh:nn:ss', Secs / SecsPerDay)
else
Result := FormatDateTime('h:n:ss', Secs / SecsPerDay)
end else begin
if LeadingZero then
Result := FormatDateTime('nn:ss', Secs / SecsPerDay)
else
Result := FormatDateTime('n:ss', Secs / SecsPerDay)
end;
end;
However, there are many different possible preferences with displaying a time period, which is up to you to decide. I won't cover all those possible ways here.
If you are sure you only want minutes and seconds - a quick solution could be:
Format('%d:%d',[(myVar div 60), (myVar mod 60)]);
Same solution as already proposed ... :-)
Related
I tried :
procedure TDataModule2.JournalLCalcFields(DataSet: TDataSet);
begin
JOURNAL.FieldByName('TIME').Value:= FormatDateTime('hh:mm:ss', JORNAL.FieldByName('end_date').AsDateTime - ZURNAL.FieldByName('start_date').AsDateTime);
end;
It kind of gives me the right answer at first when I run it but when I test it (change the end_date by a whole day on the sql server) then the result is totally wrong.
Any clues as to why
the oncalculate event fails?
TIME field is text.
FormatDateTime() is meant for formatting a specific date/time value, not a duration between two date/time values.
You can easily write your own code to format a duration, eg:
uses
..., DateUtils, SysUtils;
procedure TDataModule2.JournalLCalcFields(DataSet: TDataSet);
var
duration, hours, minutes, seconds: Int64;
begin
duration := SecondsBetween(JORNAL.FieldByName('end_date').AsDateTime, ZURNAL.FieldByName('start_date').AsDateTime);
hours := duration div 3600;
duration := duration mod 3600;
minutes := duration div 60;
duration := duration mod 60;
seconds := duration;
JOURNAL.FieldByName('TIME').Value := Format('%.2d:%.2d:%.2d', [hours, minutes, seconds]);
end;
Or, you can use the RTL's TTimeSpan type to help you, eg:
uses
..., System.TimeSpan, SysUtils;
procedure TDataModule2.JournalLCalcFields(DataSet: TDataSet);
var
ts: TTimeSpan;
begin
ts := TTimeSpan.Subtract(JORNAL.FieldByName('end_date').AsDateTime, ZURNAL.FieldByName('start_date').AsDateTime);
JOURNAL.FieldByName('TIME').Value := Format('%.2d:%.2d:%.2d', [ts.Hours, ts.Minutes, ts.Seconds]);
end;
Can someone advise me on the best way to convert a duration formated HH:MM:SS to seconds or minutes only?
ie. 00:01:49 = 109secs
I´m using this code but not work
var
i: real;
j: integer;
begin
i := frac(real(StrToTime('00:00:01')));
j := trunc(frac(real(StrToTime('01:00:00'))) / i );
memo2.lines.add(inttostr(j));
when I try the code with 00:10:00 return 599
thanks
Using the DateUtils unit:
WriteLn(SecondOfTheDay(StrToTime('00:10:00')));
WriteLn(MinuteOfTheDay(StrToTime('00:10:00')));
Outputs:
600
10
The reason why your code is not working is that floating point values often can not be exactly represented. To avoid all implementation details about how a TDateTime represents the time, use the built in functions in SysUtils and DateUtils, see Date and Time Support.
A very flexibel tool for handling durations is TTimeSpan found in unit System.TimeSpan. You can get the result in different units (second, minutes, hours, ...) and format that to your needs.
var
timeSpan: TTimeSpan;
begin
timeSpan := TTimeSpan.Parse('00:01:49');
memo2.lines.add(Format('%1.1f min', [timeSpan.TotalMinutes]));
end;
Use DecodeTime:
http://docwiki.embarcadero.com/Libraries/XE2/en/System.SysUtils.DecodeTime
So your code should look like this:
DecodeTime(StrToTime('00:00:01'), Hour, Min, Sec, MSec);
A function that returns the seconds should look something like this:
function GetSeconds(ATimeString: string): Integer;
var
Hour, Min, Sec, MSec: Word;
begin
DecodeTime(StrToTime(ATimeString), Hour, Min, Sec, MSec);
Result := Hour * 3600 + Min * 60 + Sec;
end;
This is not a standalone answer but just an extension to Uwe's answer. Upvote his answer.
{ Converts a string formatted like 'hh:mm:ss' to seconds.
Returns -1 is the string does not contain a valid time.
StringToSeconds('00:01:30') // returns 90 (sec)
StringToSeconds('01:30') // returns 5400 (sec)
StringToSeconds('10') // returns 864000 (sec)
StringToSeconds('1.30') // returns -1
StringToSeconds('x') // returns -1 }
function StringToSeconds(CONST s: String): integer;
VAR
TimeSpan: TTimeSpan;
begin
TRY
TimeSpan:= System.TimeSpan.TTimeSpan.Parse(s);
Result := round(TimeSpan.TotalSeconds);
EXCEPT
Result:= -1;
end;
end;
I'm a noob so please don't assume I know much. Feel free to let me know if I use incorrect termanology.
I have a function in a PAL script (based on Pascal / Delphi) in SAM broadcaster, radio automation software.
The function returns the time in milliseconds of Cue Point 1 in a database record related to a music file.
I wish to call this function's output in the body of my script as you might a variable. But it needs to be expressed as a hh:mm:ss timestamp.
Here is the function, which might have an output of 20000, for 20 seconds.
var CP : Integer = 0;
function ExtractCP(Song : TSongInfo):Integer;
var
P : Integer;
XFade : String;
begin
Result := -1;
XFade := Trim(Song['xfade']);
WriteLn('Decoding XFade string');
WriteLn('XFade: '+XFade);
if XFade = '' then
Result := -1
else
begin
P := Pos('ct0=',XFade); {Where 0 is the Custom Cue Point Number}
if (P > 0) then
begin
Delete(XFade,1,P+2);
P := Pos('&',XFade);
if (P>0) then
Delete(XFade,P,Length(XFade));
Result := StrToIntDef(XFade,-1);
WriteLn('CP time detected: '+XFade);
end;
end;
end;
Here is the implementation component.
while (Song['songtype']='S') and (not Skip) do
begin
VAR DT : DateTime;
VAR frac : Float;
VAR hours, minutes, seconds, milliseconds : Integer;
hours := 24;
minutes := 60;
seconds := 60;
milliseconds := 1000;
// 1 millisecond as fractional part of a day
frac := 1.0 / hours / minutes / seconds / milliseconds;
frac := frac * cp;
dt := Now + DateTime (frac); {Wait for Cue Point 0}
WriteLn(DateTimeToStr(dt));
PAL.WaitForTime(DT);
Skip := True;
end;
I guess my question is a simple one.
Calculated variable 'cp' is not being imported, how to I correctly call the result of the function in the line...
frac := frac * cp;
I have had help here (for full background, including timestamp calculation methodology and entire script)...
http://support.spacialaudio.com/forums/viewtopic.php?f=23&t=40795&start=15
Assuming XFade contains a String like 'ABC ct0=1234&'
Delete(XFade,1,P+2); will deliver '=1234&'
P := Pos('&',XFade);
if (P>0) then
Delete(XFade,P,Length(XFade));
will deliver '=1234' which can not be convertet to an integer
So at least you will have to change Delete(XFade,1,P+2); to Delete(XFade,1,P+3);
the generation of dt can be shortened to
{ if not defined
Const
MSecsPerDay= 24*60*60*1000;
}
cp := ExtractCP(Song);
if cp>-1 then
begin
dt := Now + CP / MSecsPerDay;
.....
end;
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;
Using DelphiXE, I'm trying to show the length of a wav file on a label. This is a wav file at fixed bit rate of 64kbps that is loaded into a tMediaPlayer.
A previous SO post on the task is HERE. But no code is shown and the link to Devhood no longer appears to work so I was unable to try that method.
I also tried the code from HERE but it gives incorrect results as follows.
type
HMSRec = record
Hours: byte;
Minutes: byte;
Seconds: byte;
NotUsed: byte;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
TheLength: LongInt;
begin
{ Set time format - note that some devices don’t support tfHMS }
MediaPlayer1.TimeFormat := tfHMS;
{ Store length of currently loaded media }
TheLength := MediaPlayer1.Length;
with HMSRec(TheLength) do { Typecast TheLength as a HMSRec record }
begin
Label1.Caption := IntToStr(Hours); { Display Hours in Label1 }
Label2.Caption := IntToStr(Minutes); { Display Minutes in Label2 }
Label3.Caption := IntToStr(Seconds); { Display Seconds in Label3 }
end;
end;
This code gives a value of 24:23:4, when it should be 0:04:28.
Is there an obvious problem with that code, or is there some more elegant way to accomplish this?
As always, thanks for your help.
Why not just do some simple elementary-school math?
var
sec,
min,
hr: integer;
begin
MediaPlayer1.TimeFormat := tfMilliseconds;
sec := MediaPlayer1.Length div 1000;
hr := sec div SecsPerHour;
min := (sec - (hr * SecsPerHour)) div SecsPerMin;
sec := sec - hr * SecsPerHour - min * SecsPerMin;
Caption := Format('%d hours, %d minutes, and %d seconds', [hr, min, sec]);
But why don't HMS work? Well, according to the official documentation:
MCI_FORMAT_HMS
Changes the time format
to hours, minutes, and seconds.
Recognized by the vcr and videodisc
device types.
MCI_FORMAT_MILLISECONDS
Changes the
time format to milliseconds.
Recognized by all device types.