Time of the day struct in C# - c#

I need to perform some calculations on times of days in 24 hours formatting in an application using schedules. For example, 17h + 12h = 5h. I tried to create my own Time struct and wrap around TimeSpan or DateTime. It's working ok but I'm getting stuck with the formatting to be done in ToString.
Indeed, I of course want my struct to be able to work under any culture. At first I was thinking of delegating the formatting to a DateTime struct and skip all custom formatting that are not strictly time-related. I can't delegate this task to a TimeSpan because it doesn't handle AM/PM used in some cultures for example. For example, in my Time struct I wrote:
public string Format(string format, IFormatProvider provider)
{
return TimeFormat.Format(this, format, DateTimeFormatInfo.GetInstance(provider), provider);
}
// ...
internal class TimeFormat
{
internal string Format(Time time, string format, DateTimeFormatInfo dtfi, IFormatProvider provider)
{
DateTime dt = new DateTime(DateTime.MinValue, 1, 1, time.Hours, time.Minutes, time.Seconds, time.Milliseconds);
/* Here I am checking what the 'format' string is made of
and filter-out anything that is not strictly-time related. */
return dt.Format(format, provider);
}
}
I had a look at how the ToString method of the DateTime struct is implemented into mscorlib. And waow... So many stuffs to take into account. I wanted to more or less rewrite what was in it but keeping only what is time-related but everything's mixed up in this and not easy to understand. And I noticed some definitely not straightforward stuffs to handle also such as Hebrew formatting etc. It would take me way more time than I was expecting to rewrite all this! Especially I don't want to do it without understanding clearly what I'm doing.
So to sum up:
How can I create an "all cultures compliant" formatting of time of the day?
One level above, am I handling the problem correctly using wrappers of DateTime or TimeSpan? I can't use directly a TimeSpan: It's working with negative times (I'm using modulos to have times strictly between 0 and 23:59:59.999), it doesn't handle AM/PM in some cultures, it uses Days. For me, a 'time of the day' is different from a 'span of time'.
I don't need to handle time zone or DST, nor leap second as I am in a calendar agnostic context but I want to be able to handle localized formatting of time of the day. (using 'H' (or any Hebrew, Chinese, Tamil or Russian character) instead of ':' or AM/PM instead for 24-hour format for example).
Any hint or help pointing me to some libraries or tutorials will be helpful!
I had a look at this post before asking this question and took a look at this library also but it looks too complex regarding what I want to achieve.

(I realize this is a plug for my own project, but hopefully folks will see it's entirely relevant :)
Any hint or help pointing me to some libraries or tutorials will be helpful!
I'm the main developer for the Noda Time library, which should do most of what you want. See the user guide for initial information - and feel free to ask more questions either here or on the mailing list.
The type you want is LocalTime.
And yes, you're right about both DateTime and TimeSpan being inappropriate here, and also that the formatting/parsing is complicated :)
In Noda Time you can use the ToString methods as with the normal BCL types, and there are currently static Parse/TryParse/ParseExact/TryParseExact methods - but those may well vanish this evening :) Instead, you should use a LocalTimePattern: define the pattern once (per format and culture), and then use it for both formatting and parsing.
In terms of formatting and parsing, you need to be very clear about what you need. Noda Time piggy-backs onto the BCL for things like what the AM/PM designators are, etc. The fact that you're only dealing with time-of-day makes it much simpler as there aren't month/day names (and different calendars) to worry about. You can use the t pattern for "short" and T for "long" for a particular culture - or specify a custom pattern. Custom patterns are generally more useful for machine-to-machine communication; standard ones are more useful for user-oriented formatting.
(Currently you have to "know" that the t and T patterns exist - although they're documented in the user guide, of course. At some point I'll create methods to make this simpler...)

Related

Is there a way to convert from .Net TimeZoneInfo to Java ZonedDateTime?

This is a follow-on to this question. I am building a RESTful server (that is in C# but the core engine is Java (via IKVM)). We are writing clients in C# Java, etc.
So for the specific case of the C# client, if they want a ZonedDateTime on the server side, I need to pass a string similar to "2018-10-21T08:11:55-06:00[America/Denver]". For this case I think I should have them pass me a (DateTime, TimeZoneInfo) and from that build the string.
To do this I need to get the Java "America/Denver" from the setting for TimeZoneInfo. Is there any way to get this conversion.
As I understand it, there's no ISO or other widely used setting for uniquely identifying time zones.
"America/Denver" is the IANA name for that time zone. The Time Zone Database (tzdb) contains these names and their rules (which can change over time). NodaTime uses tzdb data to perform its logic.
You can use the TimeZoneConverter package to convert a TimeZoneInfo to a tzdb time zone ID.
You've asked a few questions related to this and I'd like to piece some of that together for you here.
NodaTime All The Way Down
If it's feasible to do so, you should have the consumer pass you a ZonedDateTime. It's a single value with all the information needed by the Java-based core engine, which is exactly what you were asking for (here). Using a single value that has been validated in the domain (as opposed to a custom container for the constituent parts) defers error-proned activities to the consumer, who would be better suited than you to resolve them and must do before calling your client. Then you don't have to be responsible for any errors or bugs related to something that shouldn't concern you.
Provided that you have a ZonedDateTime instance, all you need now is a custom pattern that will give you a string in the format the Java side expects.
ZonedDateTimePattern customPattern = ZonedDateTimePattern.Create(
"uuuu'-'MM'-'dd'T'HH':'mm':'sso<Z-HH':'mm>'['z']'",
CultureInfo.InvariantCulture,
mapping => mapping.LocalDateTime.InZoneLeniently(mapping.Zone),
DateTimeZoneProviders.Tzdb,
default);
Based on your previous questions, it looks like you need a literal "Z" for UTC instead of "+00:00". The "Z-HH':'mm" subpattern does that. Take a look at the Offset patterns documentation if you need something different.
Now you can use customPattern to create the string you need to send.
string formatted = customPattern.Format(zonedDateTime);
The same pattern can be used to parse such a string back to ZonedDateTime, if necessary.
Using NodaTime Internally Only
If you can't expect the consumer to work with NodaTime types, that's okay. Receiving DateTimeOffset and TimeZoneInfo could also work. You can convert them to a ZonedDateTime within your client without much ceremony.
// Given: DateTimeOffset dateTimeOffset, TimeZoneInfo timeZoneInfo
DateTimeZone dateTimeZone = DateTimeZoneProviders.Tzdb[TZConvert.WindowsToIana(timeZoneInfo.Id)];
ZonedDateTime zonedDateTime = OffsetDateTime.FromDateTimeOffset(dateTimeOffset).InZone(dateTimeZone);
The potential problem with this is that the collective input hasn't been validated in the domain. Matt Johnson-Pint pointed out in his answer that an offset could be passed which is incorrect for the provided time zone. Be prepared to add validation or a try/catch so you can tell the consumer what they did wrong in very clear language.
Accepting Ambiguous Times
You could accept a DateTime but you would be forced to make assumptions about ambiguous times that may not be acceptable to you and/or the consumer. Then you become responsible for logic that has nothing to do with your API's functionality.
I'll include it here for completeness but this is not an endorsement of any kind.
// Given: DateTime dateTime, TimeZoneInfo timeZoneInfo
DateTimeZone dateTimeZone = DateTimeZoneProviders.Tzdb[TZConvert.WindowsToIana(timeZoneInfo.Id)];
ZonedDateTime zonedDateTime = LocalDateTime.FromDateTime(dateTime).InZoneLeniently(dateTimeZone);
InZoneLeniently is the big red flag there. It will "just do it" when an ambiguous time is encountered, and it might not be right. Remember, "just" is a 4-letter word.

Universal string to date conversion c#

Is there a universal string to date conversion method available. I have a situation where I get a datetime value as string but the value could be in any of the valid datetime format.
How do I parse it to to DateTime object in C#?
Edit
I have a possible list of formats but that can be quite large.. I have read about DateTime.Parse(string value, string[] format, culture, style) method where I can pass multiple formats in the format parameter and one of the possible format would be matched.. So how much performance impact would this approach have? –
Quick answer: universal: No.
Because there are written date formats that are ambiguous. Eg. to me today is "08/04/14", but in a historical context to the west of the Atlantic that could be the 4th August in an earlier century.1
If you have a subset of formats to process then DataTime.TryParseExact has an overload that takes an array of formats to try in turn.
Any method that you do not pass a date format to will use the current locale settings to start the parsing: which is likely to lead to invalid parses because you don't know the input format.
The best solution is to use ISO format (yyyy-MM-dd) the next best is to get the precise rules from whomever is specifying the system.
EDIT: Additional. While the best and next best may not be possible, the various DataTime parse, try-parse and -exact methods have overloads that accept an IFormatProvider (which CultureInfo implements) that would allow you to be explicit about the source of the data.
In terms of performance (from updated question): in an earlier version of the framework I looked at DateTime.TryParseExact in some detail, and I seem to recall it simply tried each format in turn finishing as soon as it got a successful parse. Thus if the most common format is first in the array its performance will be little different to the single format overload. But (1) if you need this functionality then you need it, whatever the overhead; (2) it seems unlikely your application's performance will be much affected by this (compared to, say, the cost of reading the data into and writing it out of the application).
1 Strictly speaking this is not true, I've worked too often with the, IMHO, horrible US format to know to write today as "2014-04-08".

Add 1 week to current date

I've got something like this DateTime.Now.ToString("dd.MM.yy"); In my code, And I need to add 1 week to it, like 5.4.2012 to become 12.4.2012 I tried to convert it to int and then add it up, but there is a problem when it's up to 30.
Can you tell me some clever way how to do it?
You want to leave it as a DateTime until you are ready to convert it to a string.
DateTime.Now.AddDays(7).ToString("dd.MM.yy");
First, always keep the data in it's native type until you are ready to either display it or serialize it (for example, to JSON or to save in a file). You wouldn't convert two int variables to strings before adding or multiplying them, so don't do it with dates either.
Staying in the native type has a few advantages, such as storing the DateTime internally as 8 bytes, which is smaller than most of the string formats. But the biggest advantage is that the .NET Framework gives you a bunch of built in methods for performing date and time calculations, as well as parsing datetime values from a source string. The full list can be found here.
So your answer becomes:
Get the current timestamp from DateTime.Now. Use DateTime.Now.Date if you'd rather use midnight than the current time.
Use AddDays(7) to calculate one week later. Note that this method automatically takes into account rolling over to the next month or year, if applicable. Leap days are also factored in for you.
Convert the result to a string using your desired format
// Current local server time + 7 days
DateTime.Now.AddDays(7).ToString("dd.MM.yy");
// Midnight + 7 days
DateTime.Now.Date.AddDays(7).ToString("dd.MM.yy");
And there are plenty of other methods in the framework to help with:
Internationalization
UTC and timezones (though you might want to check out NodaTime for more advanced applications)
Operator overloading for some basic math calcs
The TimeSpan class for working with time intervals
Any reason you can't use the AddDays method as in
DateTime.Now.AddDays(7)

A type for Date only in C# - why is there no Date type?

In our C# project we have the need for representing a date without a time.
I know of the existence of the DateTime, however, it incorporates a time of day as well.
I want to make explicit that certain variables and method-arguments are date-based.
Hence I can't use the DateTime.Date property
What are the standard approaches to this problem?
Why is there no Date class in C#?
Does anyone have a nice implementation using a struct and maybe some extensionmethods on DateTime and maybe implementing some operators such as == and <, > ?
Allow me to add an update to this classic question:
DateOnly (and TimeOnly) types have been added to .NET 6, starting with Preview 4. See my other answer here.
Jon Skeet's Noda Time library is now quite mature, and has a date-only type called LocalDate. (Local in this case just means local to someone, not necessarily local to the computer where the code is running.)
I've studied this problem significantly, so I'll also share several reasons for the necessity of these types:
There is a logical discrepancy between a date-only, and a date-at-midnight value.
Not every local day has a midnight in every time zone. Example: Brazil's spring-forward daylight saving time transition moves the clock from 11:59:59 to 01:00:00.
A date-time always refers to a specific time within the day, while a date-only may refer to the beginning of the day, the end of the day, or the entire range of the day.
Attaching a time to a date can lead to the date changing as the value is passed from one environment to another, if time zones are not watched very carefully. This commonly occurs in JavaScript (whose Date object is really a date+time), but can easily happen in .NET also, or in the serialization as data is passed between JavaScript and .NET.
Serializing a DateTime with XML or JSON (and others) will always include the time, even if it's not important. This is very confusing, especially considering things like birth dates and anniversaries, where the time is irrelevant.
Architecturally, DateTime is a DDD value-object, but it violates the Single Responsibly Principle in several ways:
It is designed as a date+time type, but often is used as date-only (ignoring the time), or time-of-day-only (ignoring the date). (TimeSpan is also often used for time-of-day, but that's another topic.)
The DateTimeKind value attached to the .Kind property splits the single type into three, The Unspecified kind is really the original intent of the structure, and should be used that way. The Utc kind aligns the value specifically with UTC, and the Local kind aligns the value with the environment's local time zone.
The problem with having a separate flag for kind is that every time you consume a DateTime, you are supposed to check .Kind to decide what behavior to take. The framework methods all do this, but others often forget. This is truly a SRP violation, as the type now has two different reasons to change (the value, and the kind).
The two of these lead to API usages that compile, but are often nonsensical, or have strange edge cases caused by side effects. Consider:
// nonsensical, caused by mixing types
DateTime dt = DateTime.Today - TimeSpan.FromHours(3); // when on today??
// strange edge cases, caused by impact of Kind
var london = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
var paris = TimeZoneInfo.FindSystemTimeZoneById("Romance Standard Time");
var dt = new DateTime(2016, 3, 27, 2, 0, 0); // unspecified kind
var delta = paris.GetUtcOffset(dt) - london.GetUtcOffset(dt); // side effect!
Console.WriteLine(delta.TotalHours); // 0, when should be 1 !!!
In summary, while a DateTime can be used for a date-only, it should only do so when when every place that uses it is very careful to ignore the time, and is also very careful not to try to convert to and from UTC or other time zones.
I suspect there is no dedicate pure Date class because you already have DateTime which can handle it. Having Date would lead to duplication and confusion.
If you want the standard approach look at the DateTime.Date property which gives just the date portion of a DateTime with the time value set to 12:00:00 midnight (00:00:00).
I've emailed refsrcfeedback#microsoft.com and that's their answer
Marcos, this is not a good place to ask questions like these. Try http://stackoverflow.com
Short answer is that you need a model to represent a point in time, and DateTime does that, it’s the most useful scenario in practice. The fact that humans use two concepts (date and time) to mark points in time is arbitrary and not useful to separate.
Only decouple where it is warranted, don’t do things just for the sake of doing things blindly. Think of it this way: what problem do you have that is solved by splitting DateTime into Date and Time? And what problems will you get that you don’t have now? Hint: if you look at DateTime usages across the .NET framework: http://referencesource.microsoft.com/#mscorlib/system/datetime.cs#df6b1eba7461813b#references
You will see that most are being returned from a method. If we didn’t have a single concept like DateTime, you would have to use out parameters or Tuples to return a pair of Date and Time.
HTH,
Kirill Osenkov
In my email I'd questioned if it was because DateTime uses TimeZoneInfo to get the time of the machine - in Now propriety. So I'd say it's because "the business rules" are "too coupled", they confimed that to me.
I created a simple Date struct for times when you need a simple date without worrying about time portion, timezones, local vs. utc, etc.
https://github.com/claycephus/csharp-date
System.DateOnly and System.TimeOnly types were recently added to .NET 6, and are available in the daily builds.
They were included with the .NET 6 Preview 4 release.
See https://github.com/dotnet/runtime/issues/49036
They are in the .NET source code here:
https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs
https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs
I've blogged about them here.
If you need to run date comparisons then use
yourdatetime.Date;
If you are displaying to the screen use
yourdatetime.ToShortDateString();
Allow me to speculate: Maybe it is because until SQL Server 2008 there was no Date datatype in SQL so it would be hard so store it in SQL server?? And it is after all a Microsoft Product?
Who knows why it's that way. There are lots of bad design decisions in the .NET framework. However, I think this is a pretty minor one. You can always ignore the time part, so even if some code does decide to have a DateTime refer to more than just the date, the code that cares should only ever look at the date part. Alternatively, you could create a new type that represents just a date and use functions in DateTime to do the heavy lifting (calculations).
Why? We can only speculate and it doesn't do much to help solve engineering problems. A good guess is that DateTime contains all the functionality that such a struct would have.
If it really matters to you, just wrap DateTime in your own immutable struct that only exposes the date (or look at the DateTime.Date property).
In addition to Robert's answer you also have the DateTime.ToShortDateString method. Also, if you really wanted a Date object you could always use the Adapter pattern and wrap the DateTime object exposing only what you want (i.e. month, day, year).
There is always the DateTime.Date property which cuts off the time part of the DateTime. Maybe you can encapsulate or wrap DateTime in your own Date type.
And for the question why, well, I guess you'll have to ask Anders Heljsberg.
Yeah, also System.DateTime is sealed. I've seen some folks play games with this by creating a custom class just to get the string value of the time as mentioned by earlier posts, stuff like:
class CustomDate
{
public DateTime Date { get; set; }
public bool IsTimeOnly { get; private set; }
public CustomDate(bool isTimeOnly)
{
this.IsTimeOnly = isTimeOnly;
}
public string GetValue()
{
if (IsTimeOnly)
{
return Date.ToShortTimeString();
}
else
{
return Date.ToString();
}
}
}
This is maybe unnecessary, since you could easily just extract GetShortTimeString from a plain old DateTime type without a new class
Because in order to know the date, you have to know the system time (in ticks), which includes the time - so why throw away that information?
DateTime has a Date property if you don't care at all about the time.
If you use the Date or Today properties to get only the date portion from the DateTime object.
DateTime today = DateTime.Today;
DateTime yesterday = DateTime.Now.AddDays(-1).Date;
Then you will get the date component only with the time component set to midnight.

Is it ok to use ticks to pass datetime using ticks between pages of an application

I'm currently passing some date-time info to a web page using url parameters, which are ticks of date times, and then converting the ticks back into date times when I need to at the other end.
Is there a better way to do this, and why.
for example
http://localhost:57765/dinners/updatedinner/38?startDate=633917664000000000
that's fine, in fact that the standard format for encoding dates for JSON. only concern is timezones, as your tickcount doesn't encode that. you can either always assume the timzone, and do offset calculations based on that, or encode the timezone in the value (eg sD=12343245345-0500)
Representing datetime in human readable format would be much better for developers (troubleshooting etc.) and potential customers of your site.
Given that Ticks is defined as the number of 100-nanosecond intervals that have passed since 12 midnight, January 1, 0001, I can't see there being any functional issues, as long as you either convert to UTC before passing (and from UTC after) or otherwise deal with timezone issues.
That said, there are more human-friendly ways to pass the information, for example passing it as yyyyMMddThhmmss.nnn will be more friendly if anyone wants to manually enter the URL, although it's not quite as precise (if you need better than millisecond precision).

Categories