decodedate - formatdatetime delphi
En Delphi: ¿Cómo redondeo un TDateTime al segundo, minuto, minuto, etc. más cercano? (6)
Algo así (completamente no probado, escrito directamente en el navegador):
function RoundToNearest(time, interval: TDateTime): TDateTime;
var
time_sec, int_sec, rounded_sec: int64;
begin
time_sec := Round(time * SecsPerDay);
int_sec := Round(interval * SecsPerDay);
rounded_sec := (time_sec div int_sec) * int_sec;
if (rounded_sec + int_sec - time_sec) - (time_sec - rounded_sec) then
rounded_sec := rounded_sec + time+sec;
Result := rounded_sec / SecsPerDay;
end;
El código supone que desea redondear con una segunda precisión. Milisegundos son desechados.
¿Existe una rutina en Delphi que redondea un valor de TDateTime al segundo más cercano, a la hora más cercana, a los 5 minutos más cercanos, a la media hora más cercana, etc.?
ACTUALIZAR:
Gabr dio una respuesta. Hubo algunos errores pequeños, posiblemente debido a la falta total de pruebas ;-)
Lo limpié un poco y lo probé, y aquí está la versión final (?):
function RoundDateTimeToNearestInterval(vTime : TDateTime; vInterval : TDateTime = 5*60/SecsPerDay) : TDateTime;
var
vTimeSec,vIntSec,vRoundedSec : int64;
begin
//Rounds to nearest 5-minute by default
vTimeSec := round(vTime * SecsPerDay);
vIntSec := round(vInterval * SecsPerDay);
if vIntSec = 0 then exit(vTimeSec / SecsPerDay);
vRoundedSec := round(vTimeSec / vIntSec) * vIntSec;
Result := vRoundedSec / SecsPerDay;
end;
Aquí hay un código no probado con precisión ajustable.
Type
TTimeDef = (tdSeconds, tdMinutes, tdHours, tdDays)
function ToClosest( input : TDateTime; TimeDef : TTimeDef ; Range : Integer ) : TDateTime
var
Coeff : Double;
RInteger : Integer;
DRInteger : Integer;
begin
case TimeDef of
tdSeconds : Coeff := SecsPerDay;
tdMinutes : Coeff := MinsPerDay;
tdHours : Coeff := MinsPerDay/60;
tdDays : Coeff := 1;
end;
RInteger := Trunc(input * Coeff);
DRInteger := RInteger div Range * Range
result := DRInteger / Coeff;
if (RInteger - DRInteger) >= (Range / 2) then
result := result + Range / Coeff;
end;
Pruebe la unidad DateUtils.
Pero para redondear en un minuto, hora o incluso segundo, simplemente decodifique y luego codifique el valor de la fecha, con milisegundos, segundos y minutos configurados en cero. Redondear a múltiplos de minutos u horas solo significa: decodificar, redondear hacia arriba o hacia abajo las horas o minutos, luego codificar de nuevo.
Para codificar / decodificar valores de tiempo, use EncodeTime / DecodeTime de SysUtils. Use EncodeDate / DecodeDate para las fechas. Debería ser posible crear sus propias funciones de redondeo con todo esto.
Además, la función SysUtils tiene constantes como MSecsPerDay, SecsPerDay, SecsPerMin, MinsPerHour y HoursPerDay. Un tiempo es básicamente el número de milisegundos pasada la medianoche. Puede miliplicar Frac (Tiempo) con MSecsPerDay, que es la cantidad exacta de milisegundos.
Desafortunadamente, dado que los valores de tiempo son flotantes, siempre existe la posibilidad de pequeños errores de redondeo, por lo que es posible que no obtenga el valor esperado ...
¡Guauu! chicos, ¿cómo complicar demasiado algo tan simple ... también la mayoría de ustedes pierden la opción de redondear a 1/100 segundo más cercano, etc ...
Este es mucho más simple y también puede redondear partes de milisenconds:
function RoundToNearest(TheDateTime,TheRoundStep:TDateTime):TdateTime;
begin
if 0=TheRoundStep
then begin // If round step is zero there is no round at all
RoundToNearest:=TheDateTime;
end
else begin // Just round to nearest multiple of TheRoundStep
RoundToNearest:=Round(TheDateTime/TheRoundStep)*TheRoundStep;
end;
end;
Puedes simplemente probarlo con este ejemplo común o no tan común:
// Note: Scroll to bottom to see examples of round to 1/10 of a second, etc
// Round to nearest multiple of one hour and a half (round to 90''=1h30'')
ShowMessage(FormatDateTime(''hh:nn:ss.zzz''
,RoundToNearest(EncodeTime(15,31,37,156)
,EncodeTime(1,30,0,0))
)
);
// Round to nearest multiple of one hour and a quarter (round to 75''=1h15'')
ShowMessage(FormatDateTime(''hh:nn:ss.zzz''
,RoundToNearest(EncodeTime(15,31,37,156)
,EncodeTime(1,15,0,0))
)
);
// Round to nearest multiple of 60 minutes (round to hours)
ShowMessage(FormatDateTime(''hh:nn:ss.zzz''
,RoundToNearest(EncodeTime(15,31,37,156)
,EncodeTime(1,0,0,0))
)
);
// Round to nearest multiple of 60 seconds (round to minutes)
ShowMessage(FormatDateTime(''hh:nn:ss.zzz''
,RoundToNearest(EncodeTime(15,31,37,156)
,EncodeTime(0,1,0,0))
)
);
// Round to nearest multiple of second (round to seconds)
ShowMessage(FormatDateTime(''hh:nn:ss.zzz''
,RoundToNearest(EncodeTime(15,31,37,156)
,EncodeTime(0,0,1,0))
)
);
// Round to nearest multiple of 1/100 seconds
ShowMessage(FormatDateTime(''hh:nn:ss.zzz''
,RoundToNearest(EncodeTime(15,31,37,141)
,EncodeTime(0,0,0,100))
)
);
// Round to nearest multiple of 1/100 seconds
ShowMessage(FormatDateTime(''hh:nn:ss.zzz''
,RoundToNearest(EncodeTime(15,31,37,156)
,EncodeTime(0,0,0,100))
)
);
// Round to nearest multiple of 1/10 seconds
ShowMessage(FormatDateTime(''hh:nn:ss.zzz''
,RoundToNearest(EncodeTime(15,31,37,151)
,EncodeTime(0,0,0,10))
)
);
// Round to nearest multiple of 1/10 seconds
ShowMessage(FormatDateTime(''hh:nn:ss.zzz''
,RoundToNearest(EncodeTime(15,31,37,156)
,EncodeTime(0,0,0,10))
)
);
Espero que esto ayude a las personas como yo, que necesitan redondear a 1/100, 1/25 o 1/10 segundos.
Si quieres RoundUp o RoundDown ... como Ceil y Floor ...
Aquí hay (no olvides agregar la unidad de Matemáticas a tu cláusula de uso):
function RoundUpToNearest(TheDateTime,TheRoundStep:TDateTime):TDateTime;
begin
if 0=TheRoundStep
then begin // If round step is zero there is no round at all
RoundUpToNearest:=TheDateTime;
end
else begin // Just round up to nearest bigger or equal multiple of TheRoundStep
RoundUpToNearest:=Ceil(TheDateTime/TheRoundStep)*TheRoundStep;
end;
end;
function RoundDownToNearest(TheDateTime,TheRoundStep:TDateTime):TDateTime;
begin
if 0=TheRoundStep
then begin // If round step is zero there is no round at all
RoundDownToNearest:=TheDateTime;
end
else begin // Just round down to nearest lower or equal multiple of TheRoundStep
RoundDownToNearest:=Floor(TheDateTime/TheRoundStep)*TheRoundStep;
end;
end;
Y, por supuesto, con un pequeño cambio (use el tipo Float en lugar del tipo TDateTime) si también se puede usar para los valores Round / RoundUp y RoundDown decimal / float a un paso decimal / flotante.
Aquí están:
function RoundUpToNearest(TheValue,TheRoundStep:Float):Float;
begin
if 0=TheRoundStep
then begin // If round step is zero there is no round at all
RoundUpToNearest:=TheValue;
end
else begin // Just round up to nearest bigger or equal multiple of TheRoundStep
RoundUpToNearest:=Ceil(TheValue/TheRoundStep)*TheRoundStep;
end;
end;
function RoundToNearest(TheValue,TheRoundStep:Float):Float;
begin
if 0=TheRoundStep
then begin // If round step is zero there is no round at all
RoundToNearest:=TheValue;
end
else begin // Just round to nearest multiple of TheRoundStep
RoundToNearest:=Floor(TheValue/TheRoundStep)*TheRoundStep;
end;
end;
function RoundDownToNearest(TheValue,TheRoundStep:Float):Float;
begin
if 0=TheRoundStep
then begin // If round step is zero there is no round at all
RoundDownToNearest:=TheDateTime;
end
else begin // Just round down to nearest lower or equal multiple of TheRoundStep
RoundDownToNearest:=Floor(TheValue/TheRoundStep)*TheRoundStep;
end;
end;
Si desea utilizar ambos tipos (TDateTime y Float) en la misma unidad ... agregue la directiva de sobrecarga en la sección de la interfaz, por ejemplo:
function RoundUpToNearest(TheDateTime,TheRoundStep:TDateTime):TDateTime;overload;
function RoundToNearest(TheDateTime,TheRoundStep:TDateTime):TDateTime;overload;
function RoundDownToNearest(TheDateTime,TheRoundStep:TDateTime):TDateTime;overload;
function RoundUpToNearest(TheValue,TheRoundStep:Float):Float;overload;
function RoundToNearest(TheValue,TheRoundStep:Float):Float;overload;
function RoundDownToNearest(TheValue,TheRoundStep:Float):Float;overload;
Este es un fragmento de código muy útil. Utilizo esto porque me parece que la fecha y la hora tiende a "derivar" si la incrementa varias horas o minutos, lo que puede complicar las cosas si está trabajando en una serie temporal estricta. por ejemplo, 00: 00: 00,000 pasa a ser 23: 59: 59.998 Implementé la versión Sveins del código de Gabrs, pero sugiero algunas enmiendas: el valor predeterminado no me funcionó, también el ''(vTimeSec / SecsPerDay)'' después de la salida Creo que es un error, no debería estar allí. Mi código con correcciones y comentarios es:
Procedure TNumTool.RoundDateTimeToNearestInterval
(const ATime:TDateTime; AInterval:TDateTime{=5*60/SecsPerDay}; Var Result:TDateTime);
var //Rounds to nearest 5-minute by default
vTimeSec,vIntSec,vRoundedSec : int64; //NB datetime values are in days since 12/30/1899 as a double
begin
if AInterval = 0 then
AInterval := 5*60/SecsPerDay; // no interval given - use default value of 5 minutes
vTimeSec := round(ATime * SecsPerDay); // input time in seconds as integer
vIntSec := round(AInterval * SecsPerDay); // interval time in seconds as integer
if vIntSec = 0 then
exit; // interval is zero -cannot round the datetime;
vRoundedSec := round(vTimeSec / vIntSec) * vIntSec; // rounded time in seconds as integer
Result := vRoundedSec / SecsPerDay; // rounded time in days as tdatetime (double)
end;