yyyy parseexact parsear parse formato fecha ejemplos convertir convert delphi delphi-5

delphi - parseexact - Convertir una cadena a TDateTime en base a un formato arbitrario



iformatprovider datetime c# (8)

Aquí está la función, y sus dos ayudantes, escribí para analizar una cadena usando un formato de fecha y hora exacto . Y dado que Stackoverflow también es un código-wiki: aquí está todo el código:

class function TDateTimeUtils.TryStrToDateExact(const S, DateFormat: string; PivotYear: Integer; out Value: TDateTime): Boolean; var Month, Day, Year: Integer; Tokens: TStringDynArray; CurrentToken: string; i, n: Integer; Partial: string; MaxValue: Integer; nCurrentYear: Integer; function GetCurrentYear: Word; var y, m, d: Word; begin DecodeDate(Now, y, m, d); Result := y; end; begin Result := False; { M/dd/yy Valid pictures codes are d Day of the month as digits without leading zeros for single-digit days. dd Day of the month as digits with leading zeros for single-digit days. ddd Abbreviated day of the week as specified by a LOCALE_SABBREVDAYNAME* value, for example, "Mon" in English (United States). Windows Vista and later: If a short version of the day of the week is required, your application should use the LOCALE_SSHORTESTDAYNAME* constants. dddd Day of the week as specified by a LOCALE_SDAYNAME* value. M Month as digits without leading zeros for single-digit months. MM Month as digits with leading zeros for single-digit months. MMM Abbreviated month as specified by a LOCALE_SABBREVMONTHNAME* value, for example, "Nov" in English (United States). MMMM Month as specified by a LOCALE_SMONTHNAME* value, for example, "November" for English (United States), and "Noviembre" for Spanish (Spain). y Year represented only by the last digit. yy Year represented only by the last two digits. A leading zero is added for single-digit years. yyyy Year represented by a full four or five digits, depending on the calendar used. Thai Buddhist and Korean calendars have five-digit years. The "yyyy" pattern shows five digits for these two calendars, and four digits for all other supported calendars. Calendars that have single-digit or two-digit years, such as for the Japanese Emperor era, are represented differently. A single-digit year is represented with a leading zero, for example, "03". A two-digit year is represented with two digits, for example, "13". No additional leading zeros are displayed. yyyyy Behaves identically to "yyyy". g, gg Period/era string formatted as specified by the CAL_SERASTRING value. The "g" and "gg" format pictures in a date string are ignored if there is no associated era or period string. PivotYear The maximum year that a 1 or 2 digit year is assumed to be. The Microsoft de-factor standard for y2k is 2029. Any value greater than 29 is assumed to be 1930 or higher. e.g. 2029: 1930, ..., 2000, 2001,..., 2029 If the PivotYear is between 0 and 99, then PivotYear is assumed to be a date range in the future. e.g. (assuming this is currently 2010): Pivot Range 0 1911..2010 (no future years) 1 1912..2011 ... 98 2009..2108 99 2010..2099 (no past years) 0 ==> no years in the future 99 ==> no years in the past } if Length(S) = 0 then Exit; if Length(DateFormat) = 0 then Exit; Month := -1; Day := -1; Year := -1; Tokens := TDateTimeUtils.TokenizeFormat(DateFormat); n := 1; //input string index for i := Low(Tokens) to High(Tokens) do begin CurrentToken := Tokens[i]; if CurrentToken = ''MMMM'' then begin //Long month names, we don''t support yet (you''re free to write it) Exit; end else if CurrentToken = ''MMM'' then begin //Short month names, we don''t support yet (you''re free to write it) Exit; end else if CurrentToken = ''MM'' then begin //Month, with leading zero if needed if not ReadDigitString(S, n, 2{MinDigits}, 2{MaxDigits}, 1{MinValue}, 12{MaxValue}, {var}Month) then Exit; end else if CurrentToken = ''M'' then begin //months if not ReadDigitString(S, n, 1{MinDigits}, 2{MaxDigits}, 1{MinValue}, 12{MaxValue}, {var}Month) then Exit; end else if CurrentToken = ''dddd'' then begin Exit; //Long day names, we don''t support yet (you''re free to write it) end else if CurrentToken = ''ddd'' then begin Exit; //Short day names, we don''t support yet (you''re free to write it); end else if CurrentToken = ''dd'' then begin //If we know what month it is, and even better if we know what year it is, limit the number of valid days to that if (Month >= 1) and (Month <= 12) then begin if Year > 0 then MaxValue := MonthDays[IsLeapYear(Year), Month] else MaxValue := MonthDays[True, Month]; //we don''t know the year, assume it''s a leap year to be more generous end else MaxValue := 31; //we don''t know the month, so assume it''s the largest if not ReadDigitString(S, n, 2{MinDigits}, 2{MaxDigits}, 1{MinValue}, MaxValue{MaxValue}, {var}Day) then Exit; end else if CurrentToken = ''d'' then begin //days //If we know what month it is, and even better if we know what year it is, limit the number of valid days to that if (Month >= 1) and (Month <= 12) then begin if Year > 0 then MaxValue := MonthDays[IsLeapYear(Year), Month] else MaxValue := MonthDays[True, Month]; //we don''t know the year, assume it''s a leap year to be more generous end else MaxValue := 31; //we don''t know the month, so assume it''s the largest if not ReadDigitString(S, n, 1{MinDigits}, 2{MaxDigits}, 1{MinValue}, MaxValue{MaxValue}, {var}Day) then Exit; end else if (CurrentToken = ''yyyy'') or (CurrentToken = ''yyyyy'') then begin //Year represented by a full four or five digits, depending on the calendar used. { Thai Buddhist and Korean calendars have five-digit years. The "yyyy" pattern shows five digits for these two calendars, and four digits for all other supported calendars. Calendars that have single-digit or two-digit years, such as for the Japanese Emperor era, are represented differently. A single-digit year is represented with a leading zero, for example, "03". A two-digit year is represented with two digits, for example, "13". No additional leading zeros are displayed. } if not ReadDigitString(S, n, 4{MinDigits}, 4{MaxDigits}, 0{MinValue}, 9999{MaxValue}, {var}Year) then Exit; end else if CurrentToken = ''yyy'' then begin //i''m not sure what this would look like, so i''ll ignore it Exit; end else if CurrentToken = ''yy'' then begin //Year represented only by the last two digits. A leading zero is added for single-digit years. if not ReadDigitString(S, n, 2{MinDigits}, 2{MaxDigits}, 0{MinValue}, 99{MaxValue}, {var}Year) then Exit; nCurrentYear := GetCurrentYear; Year := (nCurrentYear div 100 * 100)+Year; if (PivotYear < 100) and (PivotYear >= 0) then begin //assume pivotyear is a delta from this year, not an absolute value PivotYear := nCurrentYear+PivotYear; end; //Check the pivot year value if Year > PivotYear then Year := Year - 100; end else if CurrentToken = ''y'' then begin //Year represented only by the last digit. if not ReadDigitString(S, n, 1{MinDigits}, 1{MaxDigits}, 0{MinValue}, 9{MaxValue}, {var}Year) then Exit; nCurrentYear := GetCurrentYear; Year := (nCurrentYear div 10 * 10)+Year; if (PivotYear < 100) and (PivotYear >= 0) then begin //assume pivotyear is a delta from this year, not an absolute value PivotYear := nCurrentYear+PivotYear; end; //Check the pivot year value if Year > PivotYear then Year := Year - 100; end else begin //The input string should contains CurrentToken starting at n Partial := Copy(S, n, Length(CurrentToken)); Inc(n, Length(CurrentToken)); if Partial <> CurrentToken then Exit; end; end; //If there''s still stuff left over in the string, then it''s not valid if n <> Length(s)+1 then begin Result := False; Exit; end; if Day > MonthDays[IsLeapYear(Year), Month] then begin Result := False; Exit; end; try Value := EncodeDate(Year, Month, Day); except Result := False; Exit; end; Result := True; end; class function TDateTimeUtils.TokenizeFormat(fmt: string): TStringDynArray; var i: Integer; partial: string; function IsDateFormatPicture(ch: AnsiChar): Boolean; begin case ch of ''M'',''d'',''y'': Result := True; else Result := False; end; end; begin SetLength(Result, 0); if Length(fmt) = 0 then Exit; //format is only one character long? If so then that''s the tokenized entry if Length(fmt)=1 then begin SetLength(Result, 1); Result[0] := fmt; end; partial := fmt[1]; i := 2; while i <= Length(fmt) do begin //If the characters in partial are a format picture, and the character in fmt is not the same picture code then write partial to result, and reset partial if IsDateFormatPicture(partial[1]) then begin //if the current fmt character is different than the running partial picture if (partial[1] <> fmt[i]) then begin //Move the current partial to the output //and start a new partial SetLength(Result, Length(Result)+1); Result[High(Result)] := partial; Partial := fmt[i]; end else begin //the current fmt character is more of the same format picture in partial //Add it to the partial Partial := Partial + fmt[i]; end; end else begin //The running partial is not a format picture. //If the current fmt character is a picture code, then write out the partial and start a new partial if IsDateFormatPicture(fmt[i]) then begin //Move the current partial to the output //and start a new partial SetLength(Result, Length(Result)+1); Result[High(Result)] := partial; Partial := fmt[i]; end else begin //The current fmt character is another non-picture code. Add it to the running partial Partial := Partial + fmt[i]; end; end; Inc(i); Continue; end; //If we have a running partial, then add it to the output if partial <> '''' then begin SetLength(Result, Length(Result)+1); Result[High(Result)] := partial; end; end; class function TDateTimeUtils.ReadDigitString(const S: string; var Pos: Integer; MinDigits, MaxDigits: Integer; MinValue, MaxValue: Integer; var Number: Integer): Boolean; var Digits: Integer; Value: Integer; Partial: string; CandidateNumber: Integer; CandidateDigits: Integer; begin Result := False; CandidateNumber := -1; CandidateDigits := 0; Digits := MinDigits; while Digits <= MaxDigits do begin Partial := Copy(S, Pos, Digits); if Length(Partial) < Digits then begin //we couldn''t get all we wanted. We''re done; use whatever we''ve gotten already Break; end; //Check that it''s still a number if not TryStrToInt(Partial, Value) then Break; //Check that it''s not too big - meaning that getting anymore wouldn''t work if (Value > MaxValue) then Break; if (Value >= MinValue) then begin //Hmm, looks good. Keep it as our best possibility CandidateNumber := Value; CandidateDigits := Digits; end; Inc(Digits); //try to be greedy, grabbing even *MORE* digits end; if (CandidateNumber >= 0) or (CandidateDigits > 0) then begin Inc(Pos, CandidateDigits); Number := CandidateNumber; Result := True; end; end;

¿Hay alguna forma en Delphi 5 de convertir una cadena a un TDateTime donde pueda especificar el formato real que se usará?

Estoy trabajando en un procesador de trabajo, que acepta tareas de varias estaciones de trabajo. Las tareas tienen un rango de parámetros, algunos de los cuales son fechas, pero (desafortunadamente, y fuera de mi control) se pasan como cadenas. Como los trabajos pueden provenir de diferentes estaciones de trabajo, el formato de fecha y hora real utilizado para formatear las fechas como una cadena podría (y, por supuesto, la diferencia real).

Buscando en Google, la única solución rápida que encontré fue cambiar la variable ShortDateFormat y restaurarla a su valor original después. Dado que ShortDateFormat es una variable global, y estoy trabajando en un entorno de subprocesos, la única forma en que esto funcionaría es sincronizando cada acceso a él, lo cual es completamente inaceptable (y no se puede deshacer).

Podría copiar el código de la biblioteca de la unidad SysUtils en mis propios métodos, y modificarlos para que funcionen con un formato específico en lugar de las variables globales, pero me pregunto si hay algo más adecuado que no haya visto.

Un cordial saludo, y gracias de antemano,

Willem

ACTUALIZAR

Para decirlo de forma más sucinta:

Necesito algo como StrToDate (o StrToDateTime ), con la opción agregada de especificar el formato exacto que debería usar para convertir la cadena a un TDateTime.


Creé tal rutina para la unidad de fechas de FreePascal, y debería ser fácil de transportar, si es que se necesita portar.

Código:

http://svn.freepascal.org/cgi-bin/viewvc.cgi/trunk/packages/rtl-objpas/src/inc/dateutil.inc?revision=30628&view=co

(el código es el último (enorme) procedimiento al final del archivo)

documentación:

http://www.freepascal.org/docs-html/rtl/dateutils/scandatetime.html

Tenga en cuenta que no es un inverso completo de formatdatetime y tiene algunas extensiones:

  • Un inverso de FormatDateTime no es 100% inverso, simplemente porque uno puede poner, por ejemplo, tokens de tiempo dos veces en la cadena de formato, y scandatetime no sabría qué hora elegir.

  • Cuerdas como hn no se pueden revertir de forma segura. Por ejemplo, 1: 2 (2 minutos después de 1) entrega 12, que se analiza como 12:00 y luego pierde caracteres para la parte "n".

    • Los caracteres finales son ignorados.
    • no es compatible con los caracteres de formato de Asia oriental, ya que solo son ventanas.
    • no hay soporte de MBCS.
  • Extensiones

    • # 9 come espacios en blanco.
    • el espacio en blanco al final de un patrón es opcional.
    • ? coincide con cualquier char.
    • Cita los caracteres anteriores para que coincidan con el personaje.

(Creo que estos comentarios están un poco desactualizados en el sentido de que luego se agregó algo de apoyo asiático, pero no estoy seguro)


Las versiones posteriores de Delphi pueden llevar un argumento TFormatSettings adicional a las funciones de conversión de cadenas. TFormatSettings es una estructura que contiene varias variables globales de formato (ShortDateFormat, LongDateFormat, etc.). Por lo tanto, puede anular ese valor de manera segura para subprocesos e incluso para una sola llamada.

No recuerdo en qué versión de Delphi se introdujo, pero estoy bastante seguro de que fue después de Delphi 5.

Así que sí, por lo que sé, debe sincronizar cada acceso a ShortDateFormat o usar una función diferente.


No estoy seguro de lo que quieres. Ya no uso Delphi 5 pero estoy bastante seguro de que la función StrToDateTime existe en él. Usándolo puedes convertir una cadena a TDateTime con ajustes de formato. Luego, puede convertir dicho TDateTime a cualquier formato usando FormatDateTime, lo que le permite usar cualquier formato de fecha que desee.


Si quieres saber cómo se resolvió esto más adelante en Delphi, puedes echar un vistazo a la fuente de un sysutils.pas un poco más moderno (se parece a Delphi 6) aquí:

http://anygen.googlecome.com/.../SysUtils.pas

Revise las versiones sobrecargadas de StrToDateTime que toman un parámetro TFormatSettings .

function StrToDateTime(const S: string; const FormatSettings: TFormatSettings): TDateTime; overload;


Utilice VarToDateTime en su lugar. Admite muchos más formatos de fecha en la cadena y los convierte automáticamente.

var DateVal: TDateTime; begin DateVal := VarToDateTime(''23 Sep 2010''); ShowMessage(DateToStr(DateVal)); end;

Veo que estás usando Delphi 5. Algunas versiones de Delphi necesitarán agregar Variantes a la cláusula de usos; La mayoría de las versiones posteriores lo agregan por ti. No recuerdo en qué categoría Delphi 5 cayó.


Utilice la biblioteca RegExpr ( https://github.com/masterandrey/TRegExpr )

var RE: TRegExpr; begin RE := TRegExpr.Create; try RE.Expression := ''^(/d/d/d/d)/(/d/d)/(/d/d)T(/d/d):(/d/d):(/d/d)$''; if RE.Exec( Value ) then begin try Result := EncodeDate( StrToInt( RE.Match[1] ), StrToInt( RE.Match[2] ), StrToInt( RE.Match[3] ) ) + EncodeTime( StrToInt( RE.Match[4] ), StrToInt( RE.Match[5] ), StrToInt( RE.Match[6] ), 0 ) except raise EConvertError.Create( ''Invalid date-time: '' + Value ) end end else raise EConvertError.Create( ''Bad format: '' + Value ) finally RE.Free end end;


Yo iría al revés. Como yo lo veo, tienes dos opciones de las que mencionaste una.

  • Ajuste el ShortDateFormat y mantenga todos los accesos sincronizados.
  • Si conoce el formato de las cadenas que está recibiendo (de alguna manera, tendrá que hacerlo), simplemente haga algunos malabares de cadenas para obtener primero las cadenas en su formato actual de shortdate . Después de eso, convierta la cadena (malabarizada) a un TDateTime .

Me pregunto cómo va a determinar el formato para, por ejemplo, 04/05/2010.

program DateTimeConvert; {$APPTYPE CONSOLE} uses SysUtils; function GetPart(const part, input, format: string): string; var I: Integer; begin for I := 1 to Length(format) do if Uppercase(format[I]) = Uppercase(part) then Result := Result + input[I]; end; function GetDay(const input, format: string): string; begin Result := GetPart(''d'', input, format); if Length(Result) = 1 then Result := SysUtils.Format(''0%0:s'', [Result]); end; function GetMonth(const input, format: string): string; begin Result := GetPart(''m'', input, format); if Length(Result) = 1 then Result := SysUtils.Format(''0%0:s'', [Result]); end; function GetYear(const input, format: string): string; begin Result := GetPart(''y'', input, format); end; function ConvertToMyLocalSettings(const input, format: string): string; begin Result := SysUtils.Format(''%0:s/%1:s/%2:s'', [GetDay(input, format), GetMonth(input, format), GetYear(input, format)]); end; begin Writeln(ConvertToMyLocalSettings(''05/04/2010'', ''dd/mm/yyyy'')); Writeln(ConvertToMyLocalSettings(''05-04-2010'', ''dd-mm-yyyy'')); Writeln(ConvertToMyLocalSettings(''5-4-2010'', ''d-m-yyyy'')); Writeln(ConvertToMyLocalSettings(''4-5-2010'', ''m-d-yyyy'')); Writeln(ConvertToMyLocalSettings(''4-05-2010'', ''M-dd-yyyy'')); Writeln(ConvertToMyLocalSettings(''05/04/2010'', ''dd/MM/yyyy'')); Readln; end.