Mostrando entradas con la etiqueta Format. Mostrar todas las entradas
Mostrando entradas con la etiqueta Format. Mostrar todas las entradas

viernes, 21 de junio de 2019

Formato de fecha y hora en SQLite.

En SQLite el formato de la fecha/hora es YYYY-MM-DD HH:MM.SS.MMM por ejemplo: 2019-06-22 01:00:27.123

Si bien Free Pascal tiene muchas funciones para el tratamiento de fecha y hora, a veces por la configuración regional hay que hacer conversiones.

Usando TFormatSettings:

aFormatSettings.LongDateFormat:='yyyy-mm-dd hh:nn:ss';
aFormatSettings.DateSeparator := '-';
aFormatSettings.TimeSeparator := ':';


Donde aFormatSettings es una variable del tipo TFormatSettings y luego utilizando la función FormatDateTime que devuelve un string:

function FormatDateTime(

const FormatStr: string;

DateTime: TDateTime;

Options: TFormatDateTimeOptions = []

):string;

function FormatDateTime(

const FormatStr: string;

DateTime: TDateTime;

const FormatSettings: TFormatSettings;

Options: TFormatDateTimeOptions = []

):string;


Por ejemplo en una sentencia SQL en WHERE:

sqlWhere:='WHERE regfecha BETWEEN '+QuotedStr(FormatDateTime('YYYY-MM-DD',edFechaDesde.Date))+
' AND '+QuotedStr(FormatDateTime('YYYY-MM-DD',edFechaHasta.Date))';


En este caso no fue necesario TFormatSettings y solo fue fecha, sin hora.

Con TZquery.FieldByName y utilizando componentes TDateTimePicker esto funciona:

ZQReg.FieldByName('regfechahora').AsDateTime:=dtpFecha.Date+dtpHora.Time;

En cuanto los campos del tipo TIME en SQLite; StrToTime('00:00:00') evita el valor nulo que muchas veces conviene evitar.

En SQLite la fecha debe ir entre comillas simples.

Y ante cualquier complicación siempre podemos hacer una función como la siguiente:

function FormatoFechaHoraSQLite(lafechahora: TDateTime): String;
var
  d,m,a,h,mi,s,ms:Word;
  dd,mm,hh,mmi,ss,mms:String;
  lfecha, lhora:String;
begin
  DecodeDate(lafechahora,a,m,d);
  if d < 10 then dd:='0'+IntToStr(d) else dd:=IntToStr(d);

  if m < 10 then mm:='0'+IntToStr(m) else mm:=IntToStr(m);
  lfecha:=IntToStr(a)+'-'+mm+'-'+dd;
  DecodeTime(lafechahora,h,mi,s,ms);
  if h < 10 then hh:='0'+IntToStr(h) else hh:=IntToStr(h);
  if mi < 10 then mmi:='0'+IntToStr(mi) else mmi:=IntToStr(mi);
  if s < 10 then ss:='0'+IntToStr(s) else ss:=IntToStr(s);
  if ms < 10 then mms:='00'+IntToStr(ms) else
    if ms < 100 then mms:='0'+IntToStr(ms) else
      mms:=IntToStr(ms);
  lhora:=hh+':'+mmi+':'+ss+'.'+mms;
  Result:=lfecha+' '+lhora;
end;

O más sencillo usando la función AddChar que se encuentra en la unidad strutils.

function FormatoFechaHoraSQLite2(lafechahora: TDateTime): String;
var
  d,m,a,h,mi,s,ms:Word;
  dd,mm,hh,mmi,ss,mms:String;
  lfecha, lhora:String;
begin
  DecodeDate(lafechahora,a,m,d);
  dd:=AddChar('0',IntToStr(d),2);
  mm:=AddChar('0',IntToStr(m),2);
  lfecha:=IntToStr(a)+'-'+mm+'-'+dd;
  DecodeTime(lafechahora,h,mi,s,ms);
  hh:=AddChar('0',IntToStr(h),2);
  mmi:=AddChar('0',IntToStr(mi),2);
  ss:=AddChar('0',IntToStr(s),2);
  mms:=AddChar('0',IntToStr(ms),3);
  lhora:=hh+':'+mmi+':'+ss+'.'+mms;
  Result:=lfecha+' '+lhora;
end;


DecodeDate y DecodeTime son procedimientos y utiliza variables del tipo Word que son enteros sin signo entre 0 y 65535. Le enviamos un TDateTime y las variables del tipo Word donde se escribirán el año, mes y día (en DecodeDate). No es necesario inicializar las variables, el procedimiento utiliza out:

procedure DecodeDate(

Date: TDateTime;

out Year: Word;

out Month: Word;

out Day: Word

);

martes, 18 de junio de 2019

Número a texto con formato.

Pasar de número a string utilizando la función Format y el registro TFormatSettings. También se puede usar Format para cadenas de texto (strings), básicamente para alinearlas y/o recortarlas, no obstante su potencia radica en lo numérico, no solo enteros y decimales, también hexadecimales, exponenciales y hasta punteros. Ejemplos de todos ellos en: https://www.freepascal.org/docs-html/rtl/sysutils/format.html la cual recomiendo leer antes de continuar. Como también las referidas a TFormatSettings y DefaultFormatSettings.

Como lo indica su nombre, DefaultFormatSettings, es una variable global del tipo TFormatSettings que contiene los valores actuales de formato, si se altera, afectará a todo el proyecto, por ende debe usarse con algunos recaudos, como ser, por ejemplo, si necesito que en una función se utilice el separador decimal punto pero luego continuar utilizando la coma, entonces una opción es, antes de llamar a la función usar DefaultFormatSettings.DecimalSeparator:='.' e inmediatamente en la siguiente línea de código que sucede a la función DefaultFormatSettings.DecimalSeparator:=',' o, lo mismo pero dentro de la función.

Otra opción, y según las necesidades, es crear una variable del tipo TFormatSettings (que es del tipo record) y especificar en ella únicamente los formatos que vamos a utilizar.

Recordemos que Format devuelve un string así que puede usarse casi para cualquier cosa, pero, siempre hay un pero, a nivel IDE, más precisamente en el componente TMemo, no produce siempre los resultados deseados, el problema es con los espacios y el ancho del caracter, a no ser que exista alguna Font de tamaño fijo que desconozco.

En mi caso, necesitaba imprimir unas tablas (no tablas de bases de datos) y no tenía ganas de recurrir a LazReport, porque era algo muy simple, por eso mi primera opción fue TMemo. Pero por qué no aprovechar lo que ya está hecho, por ejemplo un editor de texto plano que cualquier sistema operativo posee.
Fue entonces que opté por TStringList que dispone de dos procedimientos elementales: LoadFromFile y SaveToFile. Finalmente utilizo OpenDocument y listo, de ahí en más se encarga el sistema operativo.

procedure TForm1.BTablaRetGcias2019Click(Sender: TObject);
var
  mes:Integer=0;
  ind:Integer=0;
  FS:TFormatSettings; //Creo la variable del tipo TFormatSettings
  sl:TStringList; //Creo la variable del tipo TStringList
begin
  sl:=TStringList.Create;  //Creo la instancia porque TStringList es una clase
  FS.CurrencyFormat:=1;  //FS es un type record y no necesita instanciasión
  FS.CurrencyDecimals:=2;
  FS.DecimalSeparator:=',';
  FS.ThousandSeparator:='.';
  FS.CurrencyString:='';
  sl.Add('Año 2019');
  sl.Add('');
  sl.Add('Mes Fijo Alíc.% Exedente');
  for mes:=1 to 12 do
  begin
    sl.Add('--------------------------------------');
    for ind:=1 to 9 do
      sl.Add(
      Format('%2.0d',[mes])+
      Format('%14.2m',[TablaGcias19[mes,ind].fijo],FS)+
      Format('%6.0m',[TablaGcias19[mes,ind].alicuota],FS)+
      Format('%16.2m',[TablaGcias19[mes,ind].exedente],FS));
      //%16 cantidad de caracteres .2 es 2 decimales y m es que el argumento es currency
  end;
  sl.SaveToFile('SL_Tabla_2019.txt');
  OpenDocument('SL_Tabla_2019.txt');
  sl.Free;
end;


Algunas aclaraciones:

FS.CurrencyFormat: en este caso no importa el valor, porque lo anulo con FS.CurrencyString:=''. Pero ya que estamos, lo valores para este campo (CurrencyFormat) pueden ser:
0 : $1
1 : 1$
2 : $ 1
3 : 1 $

En este caso TablaGcias19 es una matriz del registro TTablaGcias

TTablaGcias = record
  fijo:Currency;
  exedente:Currency;
  alicuota:Currency;
end;


Y con muy poco código se obtiene esto:



Al final se libera el TStringList y de la variable FS que usamos para FormatSettings nos olvidamos ya que es local a la función y del tipo record.