DateTime in C# where the precision is represented - c#

I have a scenario where I need to parse strings representing datetimes.
The length of the string implies the precision:
"2012-11-11" represents any moment in "november 11 of 2012"
"2012-11" represents any moment in "november of 2012"
"2012-11-11 10:11" represents any seconds in that minute
I would need to parse these strings as ranges of time.
Is there a datastructure that can help representing this or do I need to parse it manually and use 2 DateTimes.

You will have to write your own class to manage this.
Let's assume that your date strings are always in one of the following formats:
yyyy-M-d HH:mm:ss
yyyy-M-d HH:mm
yyyy-M-d HH
yyyy-M-d
yyyy-M
yyyy
Then you can write a class to encapsulate this as follows:
public sealed class TimeRange
{
public TimeRange(string dateTimeString)
{
DateTime dateTime;
if (DateTime.TryParseExact(dateTimeString, #"yyyy-M-d HH\:mm\:ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
{
_start = dateTime;
_end = dateTime.AddSeconds(1);
}
else if (DateTime.TryParseExact(dateTimeString, #"yyyy-M-d HH\:mm", CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
{
_start = dateTime;
_end = dateTime.AddMinutes(1);
}
else if (DateTime.TryParseExact(dateTimeString, #"yyyy-M-d HH", CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
{
_start = dateTime;
_end = dateTime.AddHours(1);
}
else if (DateTime.TryParseExact(dateTimeString, #"yyyy-M-d", CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
{
_start = dateTime;
_end = dateTime.AddDays(1);
}
else if (DateTime.TryParseExact(dateTimeString, #"yyyy-M", CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
{
_start = dateTime;
_end = dateTime.AddMonths(1);
}
else if (DateTime.TryParseExact(dateTimeString, #"yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
{
_start = dateTime;
_end = dateTime.AddYears(1);
}
else
{
throw new ArgumentException("date/time is invalid: " + dateTimeString, "dateTimeString");
}
}
public DateTime Start
{
get
{
return _start;
}
}
public DateTime ExclusiveEnd
{
get
{
return _end;
}
}
private readonly DateTime _start;
private readonly DateTime _end;
}
Note that for simplicity the end of the range, ExclusiveEnd, is expressed as an exclusive range. That means you'd make comparisons like:
if (timeRange.Start <= targetDateTime && targetDateTime < timeRange.ExclusiveEnd)
...
rather than the following, which would be incorrect:
if (timeRange.Start <= targetDateTime && targetDateTime <= timeRange.ExclusiveEnd)
...
Note the difference between < timeRange.ExclusiveEnd and <= timeRange.ExclusiveEnd
To avoid this subtlety you could add to the class a Contains(DateTime) method like:
public bool Contains(DateTime target)
{
return (_start <= target) && (target < _end);
}

A DateTime doesn't represent a period of time, it represents a specific moment in time.
You could use DateTime.TryParse() to parse the string however the interpretation of that is up to you.
https://msdn.microsoft.com/en-us/library/system.datetime.tryparse%28v=vs.110%29.aspx
Example
string foo = "11 August 2016";
DateTime bar = DateTime.Now;
DateTime.TryParse(foo, out bar);
Console.WriteLine(bar.ToString());
will output 11/08/2016 00:00:00
so you could assume this represents any time from 11/08/2016 to 11/08/2016 23:59:59 although it will miss out the case where the time specified was specifically 11/08/2016 00:00:00. Is this a likely input??
Could you gave a bit more info on why you need to interpret it this way?

Related

How to find the valid time if StartTime and EndTime is given?

How can the method be implemented in C#?
string StartTime = "06:10 PM";
string Endtime = "08:10 PM";
DateTime current_time = DateTime.Now;
bool validTime = validTimeFindout(StartTime,Endtime,current_time);
bool validTimeFindout(string StartTime, string Endtime,DateTime current_time){
// This method should return true
// when the current_time>= StartTime && current_time<=Endtime
// otherwise false
}
I tried to find out the valid time in the specific range and for that validTimeFindout method will help and here the method is comparing the time get from local pc and compare them with StartTime and Endtime
Starting with .NET 6, you can use the TimeOnly Struct:
static bool IsTimeBetween(string startTime, string endTime, DateTime dateTime)
{
if (TimeOnly.TryParse(startTime, out var t1) &&
TimeOnly.TryParse(endTime, out var t2))
{
return TimeOnly.FromDateTime(dateTime).IsBetween(t1, t2);
}
return false;
}
Note that TimeOnly.IsBetween supports time ranges that span midnight such as 23:00-01:00.
You can use DateTime.TryParse() method to parse a string into the DateTime datatype.
DateTime datatypes can be compared as numeric datatypes with <, <=, ==, !=, >=, >
bool validTimeFindout(string StartTime, string Endtime, DateTime current_time){
DateTime start;
DateTime.TryParse(StartTime, out start);
DateTime end;
DateTime.TryParse(Endtime, out end);
return current_time >= start && current_time <= end;
}
You just need to convert the inputs from string to datetime and do the calculations: datetime.parse()
bool validTimeFindout(string StartTime, string Endtime, DateTime current_time)
{
DateTime _startTime = DateTime.Parse(StartTime);
DateTime _endTime = DateTime.Parse(Endtime);
if (current_time >= _startTime && current_time <= _endTime)
return true;
else
return false;
}
First you need to convert those strings to DateTime objects.
Then you should compare those DateTime objects and current time.
using System.Globalization;
string StartTime = "06:10 PM";
string Endtime = "08:10 PM";
DateTime current_time = DateTime.Now;
try {
bool validTime = validTimeFindout(StartTime, Endtime, current_time);
Console.WriteLine(validTime);
}catch(Exception exc) {
Console.WriteLine(exc.Message);
}
bool validTimeFindout(string StartTime, string Endtime, DateTime current_time) {
DateTime start = ParseTimeString(StartTime);
DateTime end = ParseTimeString(Endtime);
if(current_time.CompareTo(start) >= 0 && current_time.CompareTo(end) <= 0) {
return true;
} else {
return false;
}
}
DateTime ParseTimeString(string timeString) {
string format = "hh:mm tt";
DateTime result;
if (DateTime.TryParseExact(timeString, format, CultureInfo.GetCultureInfo("en-US"), DateTimeStyles.None, out result)) {
return result;
} else {
throw new Exception("Cannot parse time!");
}
}
There are also several things you can rethink and fix.
Naming pattern - IMHO is valid is not a good name for method checking if a DateTime is between other DateTimes.
Use single naming convention and stick to it. The most popular convention in C# is cammel case (like startTime).
Is it really needed to store those DateTimes in strings formatted like "01:10 PM"? Why doesn't you simply store DateTime objects?

How could I remove this FormatException problem

I wrote a code for converting string value to TimeSpan value.
Sometimes it has FormatException not always. It is usually works well.
To subtract string as time, I used DateTime.Parse for each string value.
TimeSpan timespan = DateTime.Parse(logs_temp[i + 1].time).Subtract(DateTime.Parse(logs_temp[i].time));
It is my part of code.
public class log
{
[XmlElement("command")]
public int command { get; set; }
[XmlElement("param")]
public int param { get; set;}
[XmlElement("time")]
public string time { get; set; }
}
List<log> logs = logs_temp.ToList();
// logs and logs_temp have same command and param item. However add timespan bewteewn two commands in
// time item
for (int i = 0; i <= logs_temp.Count - 1; i++)
{
logs[i].command = logs_temp[i].command;
logs[i].param = logs_temp[i].param;
//Get a timespan between two sequencial command log
if (i + 1 < logs_temp.Count)
{ // I could find Format exception there
TimeSpan timespan = DateTime.Parse(logs_temp[i + 1].time).Subtract(DateTime.Parse(logs_temp[i].time));
//add second value as string but cannot
logs[i].time = timespan.TotalSeconds.ToString();
}
}
DateTime.Parse tries to parse it using the current PC culture. If it can't it will throw the format exception. So I guess your supplied time string is different sometimes. So set a break point to check.
Best would be to use DateTime.TryParseExact or the
dateString = "05/01/2009 01:30:42 PM -05:00";
if (DateTime.TryParseExact(dateString, "MM/dd/yyyy hh:mm:ss tt zzz", enUS, DateTimeStyles.None, out dateValue))
Console.WriteLine("Converted '{0}' to {1} ({2}).", dateString, dateValue, dateValue.Kind);
else
Console.WriteLine("'{0}' is not in an acceptable format.", dateString);

Net Core 2 StartOfWeek [duplicate]

How do I find the start of the week (both Sunday and Monday) knowing just the current time in C#?
Something like:
DateTime.Now.StartWeek(Monday);
Use an extension method:
public static class DateTimeExtensions
{
public static DateTime StartOfWeek(this DateTime dt, DayOfWeek startOfWeek)
{
int diff = (7 + (dt.DayOfWeek - startOfWeek)) % 7;
return dt.AddDays(-1 * diff).Date;
}
}
Which can be used as follows:
DateTime dt = DateTime.Now.StartOfWeek(DayOfWeek.Monday);
DateTime dt = DateTime.Now.StartOfWeek(DayOfWeek.Sunday);
The quickest way I can come up with is:
var sunday = DateTime.Today.AddDays(-(int)DateTime.Today.DayOfWeek);
If you would like any other day of the week to be your start date, all you need to do is add the DayOfWeek value to the end
var monday = DateTime.Today.AddDays(-(int)DateTime.Today.DayOfWeek + (int)DayOfWeek.Monday);
var tuesday = DateTime.Today.AddDays(-(int)DateTime.Today.DayOfWeek + (int)DayOfWeek.Tuesday);
A little more verbose and culture-aware:
System.Globalization.CultureInfo ci =
System.Threading.Thread.CurrentThread.CurrentCulture;
DayOfWeek fdow = ci.DateTimeFormat.FirstDayOfWeek;
DayOfWeek today = DateTime.Now.DayOfWeek;
DateTime sow = DateTime.Now.AddDays(-(today - fdow)).Date;
Using Fluent DateTime:
var monday = DateTime.Now.Previous(DayOfWeek.Monday);
var sunday = DateTime.Now.Previous(DayOfWeek.Sunday);
Ugly but it at least gives the right dates back
With start of week set by system:
public static DateTime FirstDateInWeek(this DateTime dt)
{
while (dt.DayOfWeek != System.Threading.Thread.CurrentThread.CurrentCulture.DateTimeFormat.FirstDayOfWeek)
dt = dt.AddDays(-1);
return dt;
}
Without:
public static DateTime FirstDateInWeek(this DateTime dt, DayOfWeek weekStartDay)
{
while (dt.DayOfWeek != weekStartDay)
dt = dt.AddDays(-1);
return dt;
}
Let's combine the culture-safe answer and the extension method answer:
public static class DateTimeExtensions
{
public static DateTime StartOfWeek(this DateTime dt, DayOfWeek startOfWeek)
{
System.Globalization.CultureInfo ci = System.Threading.Thread.CurrentThread.CurrentCulture;
DayOfWeek fdow = ci.DateTimeFormat.FirstDayOfWeek;
return DateTime.Today.AddDays(-(DateTime.Today.DayOfWeek- fdow));
}
}
This would give you the preceding Sunday (I think):
DateTime t = DateTime.Now;
t -= new TimeSpan ((int) t.DayOfWeek, 0, 0, 0);
For Monday
DateTime startAtMonday = DateTime.Now.AddDays(DayOfWeek.Monday - DateTime.Now.DayOfWeek);
For Sunday
DateTime startAtSunday = DateTime.Now.AddDays(DayOfWeek.Sunday- DateTime.Now.DayOfWeek);
This may be a bit of a hack, but you can cast the .DayOfWeek property to an int (it's an enum and since its not had its underlying data type changed it defaults to int) and use that to determine the previous start of the week.
It appears the week specified in the DayOfWeek enum starts on Sunday, so if we subtract 1 from this value that'll be equal to how many days the Monday is before the current date. We also need to map the Sunday (0) to equal 7 so given 1 - 7 = -6 the Sunday will map to the previous Monday:-
DateTime now = DateTime.Now;
int dayOfWeek = (int)now.DayOfWeek;
dayOfWeek = dayOfWeek == 0 ? 7 : dayOfWeek;
DateTime startOfWeek = now.AddDays(1 - (int)now.DayOfWeek);
The code for the previous Sunday is simpler as we don't have to make this adjustment:-
DateTime now = DateTime.Now;
int dayOfWeek = (int)now.DayOfWeek;
DateTime startOfWeek = now.AddDays(-(int)now.DayOfWeek);
using System;
using System.Globalization;
namespace MySpace
{
public static class DateTimeExtention
{
// ToDo: Need to provide culturaly neutral versions.
public static DateTime GetStartOfWeek(this DateTime dt)
{
DateTime ndt = dt.Subtract(TimeSpan.FromDays((int)dt.DayOfWeek));
return new DateTime(ndt.Year, ndt.Month, ndt.Day, 0, 0, 0, 0);
}
public static DateTime GetEndOfWeek(this DateTime dt)
{
DateTime ndt = dt.GetStartOfWeek().AddDays(6);
return new DateTime(ndt.Year, ndt.Month, ndt.Day, 23, 59, 59, 999);
}
public static DateTime GetStartOfWeek(this DateTime dt, int year, int week)
{
DateTime dayInWeek = new DateTime(year, 1, 1).AddDays((week - 1) * 7);
return dayInWeek.GetStartOfWeek();
}
public static DateTime GetEndOfWeek(this DateTime dt, int year, int week)
{
DateTime dayInWeek = new DateTime(year, 1, 1).AddDays((week - 1) * 7);
return dayInWeek.GetEndOfWeek();
}
}
}
Putting it all together, with Globalization and allowing for specifying the first day of the week as part of the call we have
public static DateTime StartOfWeek ( this DateTime dt, DayOfWeek? firstDayOfWeek )
{
DayOfWeek fdow;
if ( firstDayOfWeek.HasValue )
{
fdow = firstDayOfWeek.Value;
}
else
{
System.Globalization.CultureInfo ci = System.Threading.Thread.CurrentThread.CurrentCulture;
fdow = ci.DateTimeFormat.FirstDayOfWeek;
}
int diff = dt.DayOfWeek - fdow;
if ( diff < 0 )
{
diff += 7;
}
return dt.AddDays( -1 * diff ).Date;
}
Step 1:
Create a static class
public static class TIMEE
{
public static DateTime StartOfWeek(this DateTime dt, DayOfWeek startOfWeek)
{
int diff = (7 + (dt.DayOfWeek - startOfWeek)) % 7;
return dt.AddDays(-1 * diff).Date;
}
public static DateTime EndOfWeek(this DateTime dt, DayOfWeek startOfWeek)
{
int diff = (7 - (dt.DayOfWeek - startOfWeek)) % 7;
return dt.AddDays(1 * diff).Date;
}
}
Step 2: Use this class to get both start and end day of the week
DateTime dt = TIMEE.StartOfWeek(DateTime.Now ,DayOfWeek.Monday);
DateTime dt1 = TIMEE.EndOfWeek(DateTime.Now, DayOfWeek.Sunday);
var now = System.DateTime.Now;
var result = now.AddDays(-((now.DayOfWeek - System.Threading.Thread.CurrentThread.CurrentCulture.DateTimeFormat.FirstDayOfWeek + 7) % 7)).Date;
This would give you midnight on the first Sunday of the week:
DateTime t = DateTime.Now;
t -= new TimeSpan ((int) t.DayOfWeek, t.Hour, t.Minute, t.Second);
This gives you the first Monday at midnight:
DateTime t = DateTime.Now;
t -= new TimeSpan ((int) t.DayOfWeek - 1, t.Hour, t.Minute, t.Second);
Try with this in C#. With this code you can get both the first date and last date of a given week. Here Sunday is the first day and Saturday is the last day, but you can set both days according to your culture.
DateTime firstDate = GetFirstDateOfWeek(DateTime.Parse("05/09/2012").Date, DayOfWeek.Sunday);
DateTime lastDate = GetLastDateOfWeek(DateTime.Parse("05/09/2012").Date, DayOfWeek.Saturday);
public static DateTime GetFirstDateOfWeek(DateTime dayInWeek, DayOfWeek firstDay)
{
DateTime firstDayInWeek = dayInWeek.Date;
while (firstDayInWeek.DayOfWeek != firstDay)
firstDayInWeek = firstDayInWeek.AddDays(-1);
return firstDayInWeek;
}
public static DateTime GetLastDateOfWeek(DateTime dayInWeek, DayOfWeek firstDay)
{
DateTime lastDayInWeek = dayInWeek.Date;
while (lastDayInWeek.DayOfWeek != firstDay)
lastDayInWeek = lastDayInWeek.AddDays(1);
return lastDayInWeek;
}
I tried several, but I did not solve the issue with a week starting on a Monday, resulting in giving me the coming Monday on a Sunday. So I modified it a bit and got it working with this code:
int delta = DayOfWeek.Monday - DateTime.Now.DayOfWeek;
DateTime monday = DateTime.Now.AddDays(delta == 1 ? -6 : delta);
return monday;
The same for end of the week (in style of Compile This's answer):
public static DateTime EndOfWeek(this DateTime dt)
{
int diff = 7 - (int)dt.DayOfWeek;
diff = diff == 7 ? 0 : diff;
DateTime eow = dt.AddDays(diff).Date;
return new DateTime(eow.Year, eow.Month, eow.Day, 23, 59, 59, 999) { };
}
Thanks for the examples. I needed to always use the "CurrentCulture" first day of the week and for an array I needed to know the exact Daynumber.. so here are my first extensions:
public static class DateTimeExtensions
{
//http://stackoverflow.com/questions/38039/how-can-i-get-the-datetime-for-the-start-of-the-week
//http://stackoverflow.com/questions/1788508/calculate-date-with-monday-as-dayofweek1
public static DateTime StartOfWeek(this DateTime dt)
{
//difference in days
int diff = (int)dt.DayOfWeek - (int)CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek; //sunday=always0, monday=always1, etc.
//As a result we need to have day 0,1,2,3,4,5,6
if (diff < 0)
{
diff += 7;
}
return dt.AddDays(-1 * diff).Date;
}
public static int DayNoOfWeek(this DateTime dt)
{
//difference in days
int diff = (int)dt.DayOfWeek - (int)CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek; //sunday=always0, monday=always1, etc.
//As a result we need to have day 0,1,2,3,4,5,6
if (diff < 0)
{
diff += 7;
}
return diff + 1; //Make it 1..7
}
}
Here is a correct solution. The following code works regardless if the first day of the week is a Monday or a Sunday or something else.
public static class DateTimeExtension
{
public static DateTime GetFirstDayOfThisWeek(this DateTime d)
{
CultureInfo ci = System.Threading.Thread.CurrentThread.CurrentCulture;
var first = (int)ci.DateTimeFormat.FirstDayOfWeek;
var current = (int)d.DayOfWeek;
var result = first <= current ?
d.AddDays(-1 * (current - first)) :
d.AddDays(first - current - 7);
return result;
}
}
class Program
{
static void Main()
{
System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("en-US");
Console.WriteLine("Current culture set to en-US");
RunTests();
Console.WriteLine();
System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("da-DK");
Console.WriteLine("Current culture set to da-DK");
RunTests();
Console.ReadLine();
}
static void RunTests()
{
Console.WriteLine("Today {1}: {0}", DateTime.Today.Date.GetFirstDayOfThisWeek(), DateTime.Today.Date.ToString("yyyy-MM-dd"));
Console.WriteLine("Saturday 2013-03-02: {0}", new DateTime(2013, 3, 2).GetFirstDayOfThisWeek());
Console.WriteLine("Sunday 2013-03-03: {0}", new DateTime(2013, 3, 3).GetFirstDayOfThisWeek());
Console.WriteLine("Monday 2013-03-04: {0}", new DateTime(2013, 3, 4).GetFirstDayOfThisWeek());
}
}
Modulo in C# works bad for -1 mod 7 (it should be 6, but C# returns -1)
so... a "one-liner" solution to this will look like this :)
private static DateTime GetFirstDayOfWeek(DateTime date)
{
return date.AddDays(
-(((int)date.DayOfWeek - 1) -
(int)Math.Floor((double)((int)date.DayOfWeek - 1) / 7) * 7));
}
I did it for Monday, but with similar logic for Sunday.
public static DateTime GetStartOfWeekDate()
{
// Get today's date
DateTime today = DateTime.Today;
// Get the value for today. DayOfWeek is an enum with 0 being Sunday, 1 Monday, etc
var todayDayOfWeek = (int)today.DayOfWeek;
var dateStartOfWeek = today;
// If today is not Monday, then get the date for Monday
if (todayDayOfWeek != 1)
{
// How many days to get back to Monday from today
var daysToStartOfWeek = (todayDayOfWeek - 1);
// Subtract from today's date the number of days to get to Monday
dateStartOfWeek = today.AddDays(-daysToStartOfWeek);
}
return dateStartOfWeek;
}
The following method should return the DateTime that you want. Pass in true for Sunday being the first day of the week, false for Monday:
private DateTime getStartOfWeek(bool useSunday)
{
DateTime now = DateTime.Now;
int dayOfWeek = (int)now.DayOfWeek;
if(!useSunday)
dayOfWeek--;
if(dayOfWeek < 0)
{// day of week is Sunday and we want to use Monday as the start of the week
// Sunday is now the seventh day of the week
dayOfWeek = 6;
}
return now.AddDays(-1 * (double)dayOfWeek);
}
You could use the excellent Umbrella library:
using nVentive.Umbrella.Extensions.Calendar;
DateTime beginning = DateTime.Now.BeginningOfWeek();
However, they do seem to have stored Monday as the first day of the week (see the property nVentive.Umbrella.Extensions.Calendar.DefaultDateTimeCalendarExtensions.WeekBeginsOn), so that previous localized solution is a bit better. Unfortunate.
Edit: looking closer at the question, it looks like Umbrella might actually work for that too:
// Or DateTime.Now.PreviousDay(DayOfWeek.Monday)
DateTime monday = DateTime.Now.PreviousMonday();
DateTime sunday = DateTime.Now.PreviousSunday();
Although it's worth noting that if you ask for the previous Monday on a Monday, it'll give you seven days back. But this is also true if you use BeginningOfWeek, which seems like a bug :(.
Following on from Compile This' answer, use the following method to obtain the date for any day of the week:
public static DateTime GetDayOfWeek(DateTime dateTime, DayOfWeek dayOfWeek)
{
var monday = dateTime.Date.AddDays((7 + (dateTime.DayOfWeek - DayOfWeek.Monday) % 7) * -1);
var diff = dayOfWeek - DayOfWeek.Monday;
if (diff == -1)
{
diff = 6;
}
return monday.AddDays(diff);
}
This will return both the beginning of the week and the end of the week dates:
private string[] GetWeekRange(DateTime dateToCheck)
{
string[] result = new string[2];
TimeSpan duration = new TimeSpan(0, 0, 0, 0); //One day
DateTime dateRangeBegin = dateToCheck;
DateTime dateRangeEnd = DateTime.Today.Add(duration);
dateRangeBegin = dateToCheck.AddDays(-(int)dateToCheck.DayOfWeek);
dateRangeEnd = dateToCheck.AddDays(6 - (int)dateToCheck.DayOfWeek);
result[0] = dateRangeBegin.Date.ToString();
result[1] = dateRangeEnd.Date.ToString();
return result;
}
I have posted the complete code for calculating the begin/end of week, month, quarter and year on my blog
ZamirsBlog
Calculating this way lets you choose which day of the week indicates the start of a new week (in the example I chose Monday).
Note that doing this calculation for a day that is a Monday will give the current Monday and not the previous one.
//Replace with whatever input date you want
DateTime inputDate = DateTime.Now;
//For this example, weeks start on Monday
int startOfWeek = (int)DayOfWeek.Monday;
//Calculate the number of days it has been since the start of the week
int daysSinceStartOfWeek = ((int)inputDate.DayOfWeek + 7 - startOfWeek) % 7;
DateTime previousStartOfWeek = inputDate.AddDays(-daysSinceStartOfWeek);
I work with a lot of schools, so correctly using Monday as the first day of the week is important here.
A lot of the most terse answers here don't work on Sunday -- we often end up returning the date of tomorrow on Sunday, which is not good for running a report on last week's activities.
Here's my solution, which returns last Monday on Sunday, and today on Monday.
// Adding 7 so remainder is always positive; Otherwise % returns -1 on Sunday.
var daysToSubtract = (7 + (int)today.DayOfWeek - (int)DayOfWeek.Monday) % 7;
var monday = today
.AddDays(-daysToSubtract)
.Date;
Remember to use a method parameter for "today" so it's unit testable!!
Here is a combination of a few of the answers. It uses an extension method that allows the culture to be passed in. If one is not passed in, the current culture is used. This will give it maximum flexibility and reuse.
/// <summary>
/// Gets the date of the first day of the week for the date.
/// </summary>
/// <param name="date">The date to be used</param>
/// <param name="cultureInfo">If none is provided, the current culture is used</param>
/// <returns>The date of the beggining of the week based on the culture specifed</returns>
public static DateTime StartOfWeek(this DateTime date, CultureInfo cultureInfo=null) =>
date.AddDays(-1 * (7 + (date.DayOfWeek - (cultureInfo ?? CultureInfo.CurrentCulture).DateTimeFormat.FirstDayOfWeek)) % 7).Date;
Example Usage:
public static void TestFirstDayOfWeekExtension() {
DateTime date = DateTime.Now;
foreach(System.Globalization.CultureInfo culture in CultureInfo.GetCultures(CultureTypes.UserCustomCulture | CultureTypes.SpecificCultures)) {
Console.WriteLine($"{culture.EnglishName}: {date.ToShortDateString()} First Day of week: {date.StartOfWeek(culture).ToShortDateString()}");
}
}
If you want Saturday or Sunday or any day of week, but not exceeding the current week (Sat-Sun), I got you covered with this piece of code.
public static DateTime GetDateInCurrentWeek(this DateTime date, DayOfWeek day)
{
var temp = date;
var limit = (int)date.DayOfWeek;
var returnDate = DateTime.MinValue;
if (date.DayOfWeek == day)
return date;
for (int i = limit; i < 6; i++)
{
temp = temp.AddDays(1);
if (day == temp.DayOfWeek)
{
returnDate = temp;
break;
}
}
if (returnDate == DateTime.MinValue)
{
for (int i = limit; i > -1; i++)
{
date = date.AddDays(-1);
if (day == date.DayOfWeek)
{
returnDate = date;
break;
}
}
}
return returnDate;
}
We like one-liners: Get the difference between the current culture's first day of week and the current day, and then subtract the number of days from the current day:
var weekStartDate = DateTime.Now.AddDays(-((int)now.DayOfWeek - (int)DateTimeFormatInfo.CurrentInfo.FirstDayOfWeek));

Date time value parsing for specific format C#

There is a string 2020-12-27 20:00:00. An application must parse it to DateTime structure. Expected format is yyyy-MM-dd hh:mm:ss.
I use:
DateTime.TryParseExact(timeString, "yyyy-MM-dd hh:mm:ss",
CultureInfo.InvariantCulture, DateTimeStyles.None, out time)
but it doesn't work. TryParseExact returns false.
Anyone knows why?
You need to use HH instead of hh specifier.
HH specifier is for 24-hour clock format (00 to 23) but hh specifier is for 12-hour clock format (01 to 12).
string s = "2020-12-27 20:00:00";
DateTime dt;
if(DateTime.TryParseExact(s, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture,
DateTimeStyles.None, out dt))
{
// 27.12.2020 20:00:00
}
hh is 12 hours format, you should use HH for 24 hours
So your example becomes:
DateTime.TryParseExact(timeString, "yyyy-MM-dd HH:mm:ss",
CultureInfo.InvariantCulture, DateTimeStyles.None, out time)
You can use method I wrote for purpose of converting DTs from string of any format:
public static DateTime? parseDate(this string date, string format = #"ddMMyyyy")
{
format += "HHmmss";
DateTime dt;
try
{
string day = date.Substring(format.IndexOf('d'), 2);
string month = date.Substring(format.IndexOf('M'), 2);
string year = date.Substring(format.IndexOf('y'), 4);
string hour = date.Length - 1 > format.IndexOf('H')? date.Substring(format.IndexOf('H'), 2) : "00";
string minute = date.Length - 1 > format.IndexOf('m')? date.Substring(format.IndexOf('m'), 2) : "00";
string second = date.Length - 1 > format.IndexOf('s')? date.Substring(format.IndexOf('s'), 2) : "00";
dt = DateTime.Parse(year + "/" + month + "/" + day + " " + hour + ":" + minute + ":" + second);
}
catch { return null; }
return dt;
}
Usage:
string date = "2014ASDA04QWER05zxc";
DateTime? dt = date.parseDate("yyyyxxxxMMxxxxdd");
Result: 2014/04/05 00:00
However you are right maybe you should just use DateTime.ParseExact with HH instead of hh as my method is overcomplicated for this.
Little comparison of two methods using 20 000 random dates:

Unable to parse Oracle timestamp in C#

I have timestamp of Oracle:
string timestamp = "23-JUN-14 09.39.04.000000000 AM";
I am not able to parse it into system date time object. I used:
CultureInfo provider = CultureInfo.InvariantCulture;
String format = "yy-MMM-dd hh:mm:ss:fffffff";
string timestamp = "10-DEC-07 10.32.47.797201123 AM";
{
var date = DateTime.ParseExact(timestamp, format, provider);
DateTime dateTime = DateTime.ParseExact(timestamp.ToString(), "dd-MMM-y HH:mm:ss", CultureInfo.InvariantCulture);
}
It is still passing error. It is working 7 f after m but not more than that. I used try Parse, try ParseExact - is there any way?
According to https://stackoverflow.com/a/23198962/328864, there is no way to skip parts of an exact pattern, so i guess you could do something like this:
CultureInfo provider = CultureInfo.InvariantCulture;
string timestamp = "10-DEC-07 10.32.47.797201123 AM";
String format = String.Format("yy-MMM-dd hh.mm.ss.fffffff{0} tt", timestamp.Substring(26,2));
DateTime date = DateTime.ParseExact(timestamp, format, provider);
Console.WriteLine(date);
Not very pretty though.
Once we started to use ODP.NET, we had to implement an extension like below:
public static T ConvertOracleValue<T>(this object value)
{
if (value != null)
{
Type typeOfValue = value.GetType();
if (typeOfValue.Namespace.Contains("Oracle.DataAccess"))
{
if (typeOfValue.Name.Equals("OracleTimeStamp"))
{
int tempInt = 0;
Oracle.DataAccess.Types.OracleTimeStamp ots = (Oracle.DataAccess.Types.OracleTimeStamp)value;
tempInt = Int32.TryParse(ots.Millisecond.ToString("000").Substring(0, 3), out tempInt) ? tempInt : 0;
DateTime ret = new DateTime(ots.Year, ots.Month, ots.Day, ots.Hour, ots.Minute, ots.Second, tempInt);
return ConvertHelper.ConvertValue<T>(ret);
}
if (typeOfValue.Name.Equals("OracleTimeStampLTZ"))
{
int tempInt = 0;
Oracle.DataAccess.Types.OracleTimeStampLTZ ots = (Oracle.DataAccess.Types.OracleTimeStampLTZ)value;
tempInt = Int32.TryParse(ots.Millisecond.ToString("000").Substring(0, 3), out tempInt) ? tempInt : 0;
DateTime ret = new DateTime(ots.Year, ots.Month, ots.Day, ots.Hour, ots.Minute, ots.Second, tempInt);
return ConvertHelper.ConvertValue<T>(ret);
}
if (typeOfValue.Name.Equals("OracleTimeStampTZ"))
{
int tempInt = 0;
Oracle.DataAccess.Types.OracleTimeStampTZ ots = (Oracle.DataAccess.Types.OracleTimeStampTZ)value;
tempInt = Int32.TryParse(ots.Millisecond.ToString("000").Substring(0, 3), out tempInt) ? tempInt : 0;
DateTime ret = new DateTime(ots.Year, ots.Month, ots.Day, ots.Hour, ots.Minute, ots.Second, tempInt);
return ConvertHelper.ConvertValue<T>(ret);
}
string temp = value.ToString();
return ConvertHelper.ConvertValue<T>(temp);
}
}
else
{
return default(T);
}
return ConvertHelper.ConvertValue<T>(value);
}
where ConvertHelper.ConvertValue is another extension:
public static class ConvertHelper
{
public static T ConvertValue<T>(object value)
{
Type typeOfT = typeof(T);
if (typeOfT.BaseType != null && typeOfT.BaseType.ToString() == "System.Enum")
{
return (T)Enum.Parse(typeOfT, Convert.ToString(value));
}
if ((value == null || value == Convert.DBNull) && (typeOfT.IsValueType))
{
return default(T);
}
if (value is IConvertible)
{
return (T)Convert.ChangeType(value, typeOfT, new CultureInfo("en-GB"));
}
return (T)Convert.ChangeType(value, typeOfT);
}
}
This worked like a charm in our test, integration and production environments.
.NET DateTime structure has a precision of tick - 100 nanoseconds - 0.0000001 of second - 7 decimal positions after the point.
Oracle TimeStamp has a precision of up to nanosecond - 0.000000001 - 9 decimal positions after the point.
That is why standard DateTime cannot store all possible oracle TimeStamps. And its parsing function simply fail on more precise string representations of TimeStamp.
So, what could be tried:
Format your TimeStamps in query to some format parseable by DataTime(with loss of precision if necessary) - http://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements004.htm
Or create your own more precise CustomTimeStamp .Net structure and parse to it manually

Categories

Resources