I'm trying to parse incoming date from data source (which cannot be changed). It gives me time in ISO 8601 format example: 2007-04-05T24:00.
How ever in .Net it fails to parse this as valid time.
The wikipedia states that it should be valid format. Wikipedia ISO 8601
Example from https://stackoverflow.com/a/3556188/645410
How can I do this without a nasty string check hack?
Example (fiddle: http://dotnetfiddle.net/oB7EZx):
var strDate = "2007-04-05T24:00";
Console.WriteLine(DateTime.Parse(strDate, null, DateTimeStyles.RoundtripKind));
Throws:
The DateTime represented by the string is not supported in calendar
System.Globalization.GregorianCalendar.
Yes, .NET doesn't support this as far as I'm aware.
My Noda Time project does, but only partially: it can parse the value, but the value is just parsed to midnight at the start of the next day, and is never formatted as 24:00. There's nothing in the Noda Time conceptual model to represent "the end of the day".
Sample code to show what's possible:
using System;
using NodaTime;
using NodaTime.Text;
class Test
{
static void Main()
{
string text = "2007-04-05T24:00";
var pattern = LocalDateTimePattern.CreateWithInvariantCulture
("yyyy-MM-dd'T'HH:mm");
var dateTime = pattern.Parse(text).Value;
Console.WriteLine(pattern.Format(dateTime)); // 2007-04-06T00:00
}
}
If you don't mind losing the difference between inputs of "2007-04-05T24:00" and "2007-04-05T00:00" then you're probably okay.
Here is one simple solution — it updates this kind of end-of-the-day values to the start-of-the-next-day:
using System;
using System.Text.RegularExpressions;
namespace Iso8601ToDateTime
{
class Program
{
string text = "2021-12-31T24:00:00+01:00";
var pattern = #"^([-\d]+)(T24)";
var replaced = Regex.Replace(text, pattern,
m => DateTime.Parse(m.Groups[1].Value)
.AddDays(1).ToString("yyyy-MM-dd") + "T00");
Console.WriteLine(replaced); // 2022-01-01T00:00:00+01:00
}
}
UPDATED: Fixed bug based on raznagul's comment.
Related
I have a timestamp from a server of the form 20220505 17:36:29 - it has 2 whitespaces, and I do not trust the sender to always send the same number of whitespaces in future revisions - ideally would like to handle any number of whitespaces the same.
I tried this with DateTime.ParseExact but failed:
var horribleTimestamp = "20220505 17:36:29";
var timestamp = DateTime.ParseExact(horribleTimestamp, "yyyyMMdd hh:mm:ss", CultureInfo.InvariantCulture)
// throws `System.FormatException: String '20220505 17:36:29' was not recognized as a valid DateTime.`
To save my headaches with timezones later how can I achieve this with Nodatime as i think makes sense to switch to that already.
The time is local from my PC and I would like to convert this to a global timestamp (which I believe should be Instant?) for a given local timezone?
If you want to handle any amount of whitespace, there are two options:
Use a regular expression (or similar) to get it into a canonical format with a single space
Split on spaces and then parse the first and last parts separately. (Or split on spaces, recombine the first and last parts and parse...)
In Noda Time, the value you've got represents a LocalDateTime, so that's what you should parse it to. Here's a complete example using the regex approach:
using NodaTime;
using NodaTime.Text;
using System.Text.RegularExpressions;
// Lots of spaces just to check the canonicalization
string text = "20220505 17:36:29";
// Replace multiple spaces with a single space.
string canonicalized = Regex.Replace(text, " +", " ");
// Note: patterns are immutable; you should generally store them in
// static readonly fields. Note that "uuuu" represents an absolute year number,
// whereas "yyyy" would be "year of era".
LocalDateTimePattern pattern =
LocalDateTimePattern.CreateWithInvariantCulture("uuuuMMdd HH:mm:ss");
ParseResult<LocalDateTime> result = pattern.Parse(canonicalized);
// Note: if you're happy with an exception being thrown on a parsing failure,
// juse use result.Value unconditionally. The approach below shows what to do
// if you want to handle parse failures without throwing an exception (or with
// extra behavior).
if (result.Success)
{
LocalDateTime value = result.Value;
Console.WriteLine(value);
}
else
{
// You can also access an exception with more information
Console.WriteLine("Parsing failed");
}
You can pass multiple formats to ParseExact as an array
var horribleTimestamp = "20220505 17:36:29";
var formats = new[]{"yyyyMMdd HH:mm:ss","yyyyMMdd HH:mm:ss","yyyyMMdd HH:mm:ss"};
var timestamp = DateTime.ParseExact(horribleTimestamp, formats, CultureInfo.InvariantCulture, 0);
dotnetfiddle
You have an error in your format. use HH instead of hh. See updated code below
var horribleTimestamp = "20220505 17:36:29";
var timestamp = DateTime.ParseExact(horribleTimestamp, "yyyyMMdd HH:mm:ss", CultureInfo.InvariantCulture)
Here is y link that explains what you can use in a format -> https://www.c-sharpcorner.com/blogs/date-and-time-format-in-c-sharp-programming1
You can solve the problem with the whitespaces in this way:
var horribleTimestamp = "20220505 17:36:29";
var date = horribleTimestamp.Substring(0, 8);
var index = horribleTimestamp.LastIndexOf(' ') + 1;
var time = horribleTimestamp.Substring(index, horribleTimestamp.Length - index);
var timestamp = DateTime.ParseExact($"{date} {time}", "yyyyMMdd HH:mm:ss", CultureInfo.InvariantCulture);
I suppose that date has always 8 characters and that space is always present. In other case, check index == -1.
I have this code that when the button is clicked it returns the current time. However it returns the time in a format that's pretty unreadable. How do I format this to look better?
using System;
using Microsoft.AspNetCore.Mvc;
using Clockwork.API.Models;
namespace Clockwork.API.Controllers
{
[Route("api/[controller]")]
public class CurrentTimeController : Controller
{
// GET api/currenttime
[HttpGet]
public IActionResult Get()
{
var utcTime = DateTime.UtcNow;
var serverTime = DateTime.Now;
var ip = this.HttpContext.Connection.RemoteIpAddress.ToString();
var returnVal = new CurrentTimeQuery
{
UTCTime = utcTime,
ClientIp = ip,
Time = serverTime
};
using (var db = new ClockworkContext())
{
db.CurrentTimeQueries.Add(returnVal);
var count = db.SaveChanges();
Console.WriteLine("{0} records saved to database", count);
Console.WriteLine();
foreach (var CurrentTimeQuery in db.CurrentTimeQueries)
{
Console.WriteLine(" - {0}", CurrentTimeQuery.UTCTime);
}
}
return Ok(returnVal);
}
}
}
It returns like this :
{"currentTimeQueryId":15,"time":"2018-08-19T11:44:54.3348267-04:00","clientIp":"127.0.0.1","utcTime":"2018-08-19T15:44:54.3348267Z"}
You shouldn't try to change it at all.
That's an ISO-8601-formatted date and time including a UTC offset. That's good - it's easy to parse for pretty much any system out there. It's unambiguous, and doesn't have any culture-specific details like day and month names. It's great - for machines to read.
You're producing JSON, which is intended to be machine-readable, not human-readable. Sure, as an interchange format it is pretty readable, but it's not intended to be displayed to an end user. Instead, whatever is presenting the data to the end user should perform whatever formatting is appropriate for the date/time value. It's really important not to try to do that in the data transport format intended for machines (JSON in this case) as it can often lead to ambiguity or pointless culture-sensitivity.
Note that this isn't just for dates and times. Take a numeric value, for example: while 10.5 may be "human-readable" to you, it's not how someone in France would want to see "10 and a half". They'd format that as "10,5". But I wouldn't recommend using that string in the JSON: I'd make it 10.5 (as a number, not a string1) and then perform the "number to human-readable-format" conversion in Javascript.
Basically, keep data as presentation-agnostic as possible all the way through your data pipeline until it reaches the presentation layer, which should have all the cultural information required to format it ideally for the actual user. Very often you don't have all that cultural information elsewhere in the pipeline.
1 Unless you're trying to account for JSON being binary floating point when you're trying to represent a strictly decimal number. It's unfortunate that there's no real decimal format in JSON.
You can try
DateTime.Now.ToShortTimeString();
Say you have a string 5/9/2010 and you want to rearrange it to read 2010/5/9. How would you go about doing that?
I want to sort a list by a string that happens to be a date. While I could make it into a date, I want to stick with a string, if possible because the time part of the datetime is hard to eliminate. (This is being used in an sqlite database for a Unity3d App.)
Forgive me if this is a duplicate.
If you can guarantee that the string will always be the same input format, you can split the string on the /:
string input = "5/9/2010";
string[] inputSections = input.Split('/');
string output = string.Format("{0}/{1}/{2}", inputSections[2], inputSections[0], inputSections[1]);
Working Fiddle
My code is very verbose, you can certainly simplify it for your needs. I would also utilize the string inerpolation feature of C# 6 if it is available to you:
string input = "5/9/2010";
string[] inputSections = input.Split('/');
string output = $"{inputSections[2]}/{inputSections[1]}/{inputSections[0]}";
I would recommend parsing the date, in the off chances that the input date is not exactly in the format you were expecting, but was indeed a valid date. This is the situations parsing is for.
CultureInfo us = CultureInfo.GetCultureInfo("en-US");
string input = "5/9/2010";
DateTime date = DateTime.Parse(input, us);
Console.WriteLine(date.ToString("yyyy/MM/dd", us));
You can test here
I have a string that contains a date & time in some format. For example:
13:53:56 20.08.2014
This string is parsed from a file that user uploads to my service. The exact date & time format is not known. It can change to pretty much any date & time format you know. I do not have a list a expected formats or anything like that.
I want to develop an algorithm that somehow extracts the format from the initial string and applies it to, say, DateTime.Now.
Is there any way to do this simply and elegantly?
If you know the list of expected formats, then define them and use DateTime.TryParseExact() to find out the matching format of your input string. Once you have the matching format, you can simply use the matching format with DateTime.Now.ToString(format).
If this is a web application, perhaps you can "cheat a bit" by sniffing the user's regional settings before they input the string.
The MSDN article "How to: Display Localized Date and Time Information to Web Users" has useful information.
Inspired by a good suggestion by Mani, I've created the following extension method for my needs:
public static bool TryFormatLike(this DateTime dateTime, string another, out string result)
{
var allCultures = CultureInfo.GetCultures(CultureTypes.AllCultures | CultureTypes.UserCustomCulture | CultureTypes.ReplacementCultures);
foreach (var culture in allCultures)
{
var allPatterns = culture.DateTimeFormat.GetAllDateTimePatterns();
foreach (var pattern in allPatterns)
{
DateTime parsedAnother;
if (DateTime.TryParseExact(another, pattern, culture.DateTimeFormat, DateTimeStyles.AllowWhiteSpaces, out parsedAnother))
{
result = dateTime.ToString(pattern);
return true;
}
}
}
result = string.Empty;
return false;
}
You can use it like that:
string formattedNow;
if (DateTime.Now.TryFormatLike("13.02.2015 16:14:43", out formattedNow))
{
Console.WriteLine(formattedNow);
}
This will print
10.03.2015 23:37:08
Unfortunately, some date & time formats cannot be parsed by all patters in all cultures (for instance, string 16:14:43 13.02.2015 will not be parsed).
Anyway, thank you for your comments & answers. Maybe this method will be helpful to someone.
Is there any way to make
TypeDescriptor.GetConverter.ConvertFromString convert to DateTime using a custom format e.g. "2011-04-21 13-03-56"?
If not, is there any DateTime-format out there that can be applied to a folder name (/,\,:,etc. are not allowed as you know)?
Help is very much appreciated. Thanks in advance
Random-I-Am
Edit:
Since my request still seems to not be understood correctly I am again trying to elaborate on my question. My users are creating folders with specific names. For example "1000_Link_Topic1_2011-01-25 14-12-10". They are free to combine their information as they like and omit information where needed. They don't even have to care about case sensitivity. So I could face another folder named "1000_link_Topic2".
What I have is a single class with a single property for each possible fragment of information. In this case I would have (Since I had to find a way of checking each properties default value regardless of the property type I am using nullable types):
Short? short_val;
EContentType? enum_val;
String string_val;
DateTime? datetime_val;
My code obviously splits the folder name at "_" and then tells for each fragment to which of the above property types it belongs. As soon as I know the corresponding type I am trying to convert to the Type, lets say t, using TypeDescriptor.GetConverter(t).ConvertFromString(info_frag[i]). I hope you now understand why I cannot use another conversion Method.
The code works for all the types mentioned above. My problem is to find a custom DateTime format that can be used on a folder name. All of the formats I know are using colons to separate hours from minutes and seconds.
What I want is a way to convert from a custom DateTime-format to a DateTime-object using TypeDescriptor.GetConverter.ConvertFromString. If that is not possible I either need to find a standard DateTime-format which can be assigned as a folder name without any further conversion or somehow extend the built-in DateTime-formats by my custom format. If it is going to be the latter I do not care about being able to use seconds or minutes. Hours will do the job.
Thanks again for being as patient as you are and helping me out with this one. Feel free to ask as many further questions as you like. I hope you can help me getting this one to work.
Random-I-Am
I have looked into your problem a little and found that the standard DateTimeTypeConverter uses DateTime.Parse internally which doesn't listen to CurrentCulture info at all. Instead you can trick the conversion process with your own TypeConverter!
I do not know how to attach the new typeconverter to the existing DateTime structure, if you even can, so I made a miniture AdvancedDateTime structure .. which is empty. Noone said your custom TypeConvert should even return such a thing! This one returns a regular DateTime. I provided the code below:
public class CustomDateTimeTypeConverter : TypeConverter
{
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
return DateTime.ParseExact(value.ToString(), "yyyy-MM-dd HH-mm-ss", culture);
}
}
[TypeConverter(typeof(CustomDateTimeTypeConverter))]
struct AdvancedDateTime
{
}
[TestFixture]
public class DateTime
{
[Test]
public void TypeConvert_StrangeFormat_ConvertsWithoutProblem()
{
string datetime = "2011-04-21 23-12-13";
TypeConverter converter = TypeDescriptor.GetConverter( typeof (AdvancedDateTime) );
var convertedFromString = converter.ConvertFromString(datetime);
Assert.AreEqual(new DateTime(2011,4,21, 23,12,13), convertedFromString);
}
}
Try this
string m_strDate = DateTime.Now.ToString("MM/dd/yyyy");
m_strDate = m_strDate.Replace("/", "");
Append this m_strDate to your folder
A sample i used for text file is as follows
strPath += "/FileHeader_" + m_strDate + ".txt";
Check this sample
DateTime dt=new DateTime(1990,5,6);
Console.WriteLine(TypeDescriptor.GetConverter(dt).ConvertTo(dt, typeof(string)));
string myStr="1991-10-10";
Console.WriteLine(TypeDescriptor.GetConverter(dt).ConvertFrom(myStr));
The sample code i written just to display as per you need try as per your requirement
string s = "Folder";
DateTime dt = new DateTime(1990, 5, 6);
string str = TypeDescriptor.GetConverter(dt).ConvertTo(dt, typeof(string)).ToString();
string myStr = "1991-10-10";
string str1 = TypeDescriptor.GetConverter(dt).ConvertFrom(myStr).ToString();
s = s + str1.Replace("/", "").Replace(":", "");
textBox1.Text = s;