todatetime - .NET Date to string da cadenas inválidas en pseudo-culturas de Vista
iformatprovider (2)
Este error específico se debe a la transformación de algunos caracteres especiales que no se escapan en los patrones como ShortDatePattern
.
ShortDatePattern = "d//MM//yyyy";
/
en un patrón significa "insertar el separador de fecha" pero aquí la expansión ya está hecha (al menos en mi sistema) cuando la cadena se copia del sistema a la estructura DateTimeFormat
. Lamentablemente, falta un escape (Obviamente, no es visible en ningún idioma que no use un carácter especial como separador y no se ve en inglés, ya que se reemplaza a sí mismo)
La única solución parece ser escapar de los separadores en todos los patrones de la instancia de DateTimeFormat
:
var c = new System.Globalization.CultureInfo("qps-ploc", true);
c.DateTimeFormat.ShortDatePattern =
c.DateTimeFormat.ShortDatePattern.Replace("/", "''/''");
c.DateTimeFormat.LongTimePattern =
c.DateTimeFormat.LongTimePattern.Replace(":", "'':''");
Console.WriteLine(DateTime.Now.ToString(c));
Aquí hay ejemplos de código completo para los tres casos comunes
Fecha a la cadena
/// <summary>Convert a date to the short date string in the current locale (e.g. 30//11//2011)</summary>
/// <param name="value">A DateTime to be converted to a short date string</param>
/// <returns>A string containing the localized version of the date</returns>
public static String DateToStr(DateTime value)
{
String format = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern;
//The bug in .NET is that it assumes "/" in a date pattern means "the date separator"
//What .NET doesn''t realize is that the locale strings returned by Windows are the Windows format strings.
//The bug is exposed in locale''s that use two slashes as for their date separator:
// dd//MM//yyyy
// Which .NET misinterprets to give:
// 30////11////2011
// when really it should be taken literally to be:
// dd''//''MM''//''yyyy
//which is what this fix does
format = format.Replace("/", "''/''");
return value.ToString(format);
}
Tiempo de cadena
/// <summary>
/// Convert a time to string using the short time format in the current locale(e.g. 7::21 AM)
/// </summary>
/// <param name="value">A DateTime who''s time portion will be converted to a localized string</param>
/// <returns>A string containing the localized version of the time</returns>
public static String TimeToStr(DateTime value)
{
String format = CultureInfo.CurrentCulture.DateTimeFormat.ShortTimePattern;
//The bug in .NET is that it assumes ":" in a time pattern means "the time separator"
//What .NET doesn''t realize is that the locale strings returned by Windows are the Windows format strings.
//The bug is exposed in locale''s that use two colons as their time separator:
// h::mm::ss tt
// Which .NET misinterprets to give:
// 11::::39::::17 AM
// when really it should be taken literally to be:
// h''::''mm''::''ss tt
//which is what this fix does
format = format.Replace(":", "'':''");
return value.ToString(format);
}
Fecha a cadena
/// <summary>
/// Convert a datetime to a string in the current locale (e.g. 30//11//2001 7::21 AM)
/// </summary>
/// <param name="datetime">A DateTime to be converted to a general string in the current locale</param>
/// <returns>A string containing the localized version of the datetime</returns>
public static String DateTimeToStr(DateTime datetime)
{
return DateToStr(datetime)+" "+TimeToStr(datetime);
}
Mi computadora está configurada con una cultura que no es en-US
.
Cuando uso la función Win32 GetDateFormat
nativa, obtengo fechas formateadas correctamente:
- 22 // 11 // 2011 4 :: 42 :: 53 P̰̃M]
Esto es correcto; Y es también como Windows lo hace:
la barra de tareas
Configuración regional y de idioma
Explorador de Windows
panorama
Cuando intento convertir una fecha en una cadena en .NET usando mi ubicación actual, por ejemplo:
DateTime.Now.ToString();
DateTime.Now.ToString(CultureInfo.CurrentCulture);
me sale una fecha incorrecta:
- 22 //// 11 //// 2011 4 :::: 42 :::: 53 P̰̃M]
Este error en .NET es evidente en cualquier lugar de Windows que utiliza el código .NET con errores:
Visor de eventos de Windows:
Programador de tareas:
SQL Server Management Studio:
¿Cómo hago .NET no buggy?
¿Cómo convierto fechas y horas a cadenas usando la cultura actual (correctamente)?
Nota : El usuario tiene permiso para configurar su Windows en cualquier configuración regional que desee. Como está ahora, mi programa no manejará las configuraciones válidas correctamente. Decirle al usuario, "No hagas eso" es bastante mezquino.
Un ejemplo similar proviene de Delphi, que asume que un separador de fecha nunca puede tener más de un carácter. Cuando Windows está configurado con una configuración regional que usa varios caracteres para el separador de fecha, por ejemplo:
- SK-SK (Eslovaco - Eslovaquia):.
donde las fechas deben ser formateadas como:
22. 11. 2011
la biblioteca de códigos no acepta un separador de fecha con más de un carácter y recurre a:
22/11/2011
En el pasado, algunos podrían sugerir que no se moleste con tales casos de ventaja . Tales sugerencias no tienen peso conmigo.
Evitaré meterme en una pelea con alguien que quiera alterar el significado de mi pregunta cambiando el título. Pero la pregunta no se limita a los pseudo-locales, específicamente diseñados para encontrar errores en las aplicaciones.
Bonus Chatter
Aquí hay una lista única de formatos de fecha de todo el mundo:
- 11.11.25
- 11.25.2011
- 25/11/2011
- 2011.11.25
- 2011.11.25.
- 2011/11/25
- 2011-11-25
- 2011
- 25.11.11
- 25.11.2011
- 25.11.2011 г.
- 25.11.2011.
- 25 // 11 // 2011
- 25/11 2011
- 25/11/2011
- 25/11/2554
- 25-11-11
- 25-11-2011
- 29/12/32
De particular interés es el último ejemplo que no usa el calendario gregoriano :
- Árabe (Arabia Saudita)
ar-SA
: 29/12/32 02:03:07 م - Divehi (Maldivas)
dv-MV
: 29/12/32 14:03:07 - Dari / Pashto (Afganistán)
prf-AF / ps-AF
: 29/12/32 2:03:07 .و
Aunque esos son casos extremos que nunca tendrías que preocuparte.
Actualización 14 // 12 // 2011:
Otra demostración del error es que Datetime.Parse
no puede analizar DateTime.ToString
:
String s = DateTime.Today.ToString("d"); //returns "14////12////2011"
DateTime d = DateTime.Parse(s); //expects "dd//MM//yyyy"
El .Parse
lanza una excepción.
Actualización 02 // 8, 2012 09 :: 56''12:
Cualquier uso de un separador de fecha está restringido, además de ser incorrecto. Desde MSDN:
LOCALE_SDATE
Windows Vista y versiones posteriores: esta constante está en desuso. Utilice
LOCALE_SSHORTDATE
enLOCALE_SSHORTDATE
lugar. Es posible que una configuración regional personalizada no tenga un único carácter separador uniforme. Por ejemplo, un formato como "12/31, 2006" es válido.LOCALE_STIME
Windows Vista y versiones posteriores: esta constante está en desuso. Use
LOCALE_STIMEFORMAT
enLOCALE_STIMEFORMAT
lugar. Es posible que una configuración regional personalizada no tenga un único carácter separador uniforme. Por ejemplo, un formato como "03: 56''23" es válido.
Lo mejor es registrar el error en MS y luego crear un método de extensión que detecte estos casos de borde y los maneje.
Algo como esto (de la parte superior de mi cabeza):
public static class DateTimeFix
{
public static string FixedToString(this DateTime value)
{
if (IsEdgeCase())
return FixEdgeCase(value);
else
return value.ToString();
}
// Edge case logic below
}
Entonces usas:
DateTime.Now.FixedToString()
en su codigo