Create array of months between two dates - c#

I have the following snippet that I use to get the individual dates between two dates:
DateTime[] output = Enumerable.Range(0, 1 + endDate.Subtract(startDate).Days)
.Select(offset => startDate.AddDays(offset))
.ToArray();
However, the following section
endDate.Subtract(startDate).Days
does not have a .Months to return the months in the date range.
For example, if I provide 1/1/2010 and 6/1/2010 I would expect to return 1/1/2010, 2/1/2010, 3/1/2010, 4/1/2010, 5/1/2010 and 6/1/2010.
Any ideas?

Try this:
static IEnumerable<DateTime> monthsBetween(DateTime d0, DateTime d1)
{
return Enumerable.Range(0, (d1.Year - d0.Year) * 12 + (d1.Month - d0.Month + 1))
.Select(m => new DateTime(d0.Year, d0.Month, 1).AddMonths(m));
}
This includes both the starting month and the ending month. This finds how many months there is, and then creates a new DateTime based on d0´s year and month. That means the months are like yyyy-MM-01. If you want it to includes the time and day of d0 you can replace new DateTime(d0.Year, d0.Month, 1).AddMonths(m) by d0.AddMonths(m).
I see you need an array, in that case you just use monthsBetween(..., ...).ToArray() or put .ToArray() inside the method.

Since I just needed the year and month in between two dates I modified Lasse Espeholt answer a little. suppose:
d0 = 2012-11-03
d1 = 2013-02-05
The result will be something like this:
2012-11
2012-12
2013-01
2013-02
private List<Tuple<int,int>> year_month_Between(DateTime d0, DateTime d1)
{
List<DateTime> datemonth= Enumerable.Range(0, (d1.Year - d0.Year) * 12 + (d1.Month - d0.Month + 1))
.Select(m => new DateTime(d0.Year, d0.Month, 1).AddMonths(m)).ToList();
List<Tuple<int, int>> yearmonth= new List<Tuple<int,int>>();
foreach (DateTime x in datemonth)
{
yearmonth.Add(new Tuple<int, int>(x.Year, x.Month));
}
return yearmonth;
}

Is this what you are looking for? The requirement is very ambiguous.
DateTime[] calendarMonthBoundaries = Enumerable.Range(0, 1 + endDate.Subtract(startDate).Days)
.Select(offset => startDate.AddDays(offset))
.Where(date => date.Day == 1)
.ToArray();

You could enumerate increments of months with:
private static IEnumerable<DateTime> ByMonths(DateTime startDate, DateTime endDate)
{
DateTime cur = startDate;
for(int i = 0; cur <= endDate; cur = startDate.AddMonths(++i))
{
yield return cur;
}
}
and then call ToArray() on that if you want an array. It's reasonably good about having values that are likely to be what is wanted; e.g. if you start at Jan 31st you'll next get Feb 28th (or 29th on leap years), then Mar 31st, then Apr 30th and so on.

Related

Last 3 Months in linq

I have this line in a stored procedure like this :
DATENAME(MONTH, tblReg.StartDate) as [Month],
Now I want to convert this line in linq
var b = sd.tblReg;
foreach (var c in b)
{
res += "'" + c.StartDate + "',";
}
res = res.Substring(0, res.Length - 1);
res += "]";
and want to get last 3 months.. i.e. current month is Aug so with Aug i want to get last 3 months same if current month is Jan then Dec Nov Oct.. in res like this
['May' ,'June','July','Aug']
You could do something like this to find previous 3 months using Linq. DateTimeFormat.GetMonthName will help you to get month name.
int month = ..; // given a month
var result = Enumerable
.Range(-2,18) // Compute +/- 3 months for original 12 months.
.TakeWhile(x=>x <=month) // Take months until the current month
.Reverse() // Reverse the order as we need backword months.
.Take(4) // Take top 4 months (including current month)
.Select(x=>CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x<=0?x+12: x==12? 12 : (x+12)%12))
Check this Demo

Get all months and years between two datetime

What I need is to get logic on how to get monthname-year between two dates.
Dictionary<Monthname,year> GetMonthsandYear(Datetime d1,Datetime d2)
or
List<Tuple<string,int> GetMonthsandYear(Datetime d1,Datetime d2)
example : jan-1-2013 to mar-3-2013
should return January-2013,february-2013,march-2013 or in reverse format by list.reverse
If your actual requirement is "the previous 24 months" then it's much simpler. Just start off with the current month, and get the 1st day of it - then iterate and add 1 month 24 times.
Personally I'd return an IEnumerable<DateTime> rather than anything else - you can format each element however you want - but it's pretty simple:
public static IEnumerable<DateTime> GetMonths(int count)
{
// Note: this uses the system local time zone. Are you sure that's what
// you want?
var today = DateTime.Today;
// Always return the 1st of the month, so we don't need to worry about
// what "March 30th - 1 month" means
var startOfMonth = new DateTime(today.Year, today.Month, 1);
for (int i = 0; i < count; i++)
{
yield return startOfMonth;
startOfMonth = startOfMonth.AddMonths(-1);
}
}
Then if you want a List<string> of these values as "February 2014" etc for example, you could have:
var monthYears = GetMonths(24).Select(dt => dt.ToString("MMMM yyyy"))
.ToList();
Note that a Dictionary<...> would not be appropriate unless you really don't care about the order - and I suspect you do. You shouldn't rely on the order in which items are returned from a Dictionary<TKey, TValue> when you view it as a sequence - it's not intended to be an ordered collection.
I don't understand why you need Dictionary or List<Tuple<string,int> but one solution could be;
DateTime dt1 = new DateTime(2013, 1, 1);
DateTime dt2 = new DateTime(2013, 3, 3);
while (dt1 < dt2)
{
Console.WriteLine(dt1.ToString("MMMM-yyyy"));
dt1 = dt1.AddMonths(1);
}
Result will be;
January-2013
February-2013
March-2013
Even if you need, you can add these values to a List<string> in while loop.
But be carefull about what Jon said, this solution will generate only January and February if your dt1.Day is greater than dt2.Day.

Getting a days collection from System.DateTime for a given month in custom format in WPF/C#

I am looking to get a list of days in the following format: Jan 1, Jan 3.....Jan 30, Jan 31.
I can generate this collection by my self by calling DateTime.DaysInMonth(2013,01); But this just gives me "31" so i will have to loop through them and generate collection. But will there be any custom methods already that does this easily or in simple way.
Thanks
Try to use next code snippet. Consider using the MMM DateTime pattern to extract month name
Enumerable.Range(1, DateTime.DaysInMonth(2012, 1))
.Select(num => num +" Jan");
returns
1 Jan
2 Jan
3 Jan
...
30 Jan
31 Jan
Edit:
To extract month name you can use next code snippet
var start = new DateTime(2012, 1, 1);
Enumerable.Range(0, DateTime.DaysInMonth(2012, 1))
.Select(days => start.AddDays(days))
.Select(day => day.ToString("d MMM")); // returns the same
You can use Linq:
DateTime date = new DateTime(2013, 1, 1);
var cal = System.Globalization.CultureInfo.CurrentCulture.Calendar;
List<String> monthDates = Enumerable.Range(0, cal.GetDaysInMonth(date.Year, date.Month))
.Select(i => date.AddDays(i).ToString("MMM d"))
.ToList();
Demo
I don't think there are any built in methods to do this, but you could easily do something like:
DateTime.DaysInMonth(2013, 01).Select(x=> "Jan " + x.Tostring()).ToList();
You can see how you can easily parameterize and make your own function out of this to get you the different months as well.
Based on Ilya Ivanov's answer above, if you wanted the list to show the format "Jan ??", you could do the following:
var list = Enumerable.Range(1, DateTime.DaysInMonth(2012, 1)).Select(num => "Jan " + num);

Linq list for month+year for the whole year

Looking for linq query to fill a list with month + year for example (January 2012)
starting form current month
var currentdate = System.DateTime.Now
If Dec 2011 is the current month
then list should be like this
December 2011
January 2012
......
November 2012
var months =
Enumerable.Range(0, 12).
Select(n => DateTime.Today.AddMonths(n)).
Select(d = new { Year = d.Year, Month = d.Month });
I'm editing to turn my sample code into a method that I might almost use in production because it's more testable and culture-aware:
public IEnumerable GetMonths(DateTime currentDate, IFormatProvider provider)
{
return from i in Enumerable.Range(0, 12)
let now = currentDate.AddMonths(i)
select new
{
MonthLabel = now.ToString("MMMM", provider),
Month = now.Month,
Year = now.Year
};
}
This outputs (on a French computer):

Convert a two digit year to a four digit year

This is a question of best practices. I have a utility that takes in a two digit year as a string and I need to convert it to a four digit year as a string. right now I do
//DOB's format is "MMM (D)D YY" that first digit of the day is not there for numbers 1-9
string tmpYear = rowIn.DOB.Substring(rowIn.DOB.Length - 3, 2); //-3 because it is 0 indexed
if (Convert.ToInt16(tmpYear) > 50)
tmpYear = String.Format("19{0}", tmpYear);
else
tmpYear = String.Format("20{0}", tmpYear);
I am sure I am doing it horribly wrong, any pointers?
The .NET framework has a method that does exactly what you want:
int fourDigitYear = CultureInfo.CurrentCulture.Calendar.ToFourDigitYear(twoDigitYear)
That way you will correctly adhere to current regional settings as defined in Control Panel (or group policy):
Given that there are people alive now born before 1950, but none born after 2010, your use of 50 as the flipping point seems broken.
For date of birth, can you not set the flip point to the 'year of now' (i.e. 10) in your app? Even then you'll have problems with those born before 1911...
There's no perfect way to do this - you're creating information out of thin air.
I've assumed DOB = date-of-birth. For other data (say, maturity of a financial instrument) the choice might be different, but just as imperfect.
You can also use the DateTime.TryParse method to convert your date. It uses the current culture settings to define the pivot year (in my case it is 2029)
DateTime resultDate;
Console.WriteLine("CultureInfo.CurrentCulture.Calendar.TwoDigitYearMax : {0}", System.Globalization.CultureInfo.CurrentCulture.Calendar.TwoDigitYearMax);
DateTime.TryParse("01/01/28", out resultDate);
Console.WriteLine("Generated date with year=28 - {0}",resultDate);
DateTime.TryParse("01/02/29",out resultDate);
Console.WriteLine("Generated date with year=29 - {0}", resultDate);
DateTime.TryParse("01/03/30", out resultDate);
Console.WriteLine("Generated date with year=30 - {0}", resultDate);
The output is:
CultureInfo.CurrentCulture.Calendar.TwoDigitYearMax : 2029
Generated date with year=28 - 01/01/2028 00:00:00
Generated date with year=29 - 01/02/2029 00:00:00
Generated date with year=30 - 01/03/1930 00:00:00
If you want to change the behavior you can create a culture with the year you want to use as pivot. This thread shows an example
DateTime.TryParse century control C#
But as martin stated, if you want to manage a time period that spans more than 100 year, there is no way to do it with only 2 digits.
I think Java has a good implementation of this:
http://java.sun.com/javase/6/docs/api/java/text/SimpleDateFormat.html#year
People rarely specify years far into the future using a two-digit code. The Java implementation handles this by assuming a range of 80 years behind and 20 years ahead of the current year. So right now, 30 would be 2030, while 31 would be 1931. Additionally, this implementation is flexible, modifying its ranges as time goes on, so that you don't have to change the code every decade or so.
I just tested, and Excel also uses these same rules for 2-digit year conversion. 1/1/29 turns into 1/1/2029. 1/1/30 turns into 1/1/1930.
The implementation of
System.Globalization.CultureInfo.CurrentCulture.Calendar.ToFourDigitYear
is
public virtual int ToFourDigitYear(int year)
{
if (year < 0)
throw new ArgumentOutOfRangeException("year", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
if (year < 100)
return (this.TwoDigitYearMax / 100 - (year > this.TwoDigitYearMax % 100 ? 1 : 0)) * 100 + year;
else
return year;
}
Hope this helps!
It might be smarter to check tmpYear > currentYear%100. If it is, then it's 19XX, otherwise 20XX.
This solution we use for Expiration Dates, the user enters MM and YY into separate fields. This results in dates being the 31st or 30th and 28th or 29th also for February.
/// <summary>
/// Creates datetime for current century and sets days to end of month
/// </summary>
/// <param name="MM"></param>
/// <param name="YY"></param>
/// <returns></returns>
public static DateTime GetEndOfMonth(string MM, string YY)
{
// YY -> YYYY #RipVanWinkle
// Gets Current century and adds YY to it.
// Minus 5 to allow dates that may be expired to be entered.
// eg. today is 2017, 12 = 2012 and 11 = 2111
int currentYear = DateTime.Now.Year;
string thisYear = currentYear.ToString().Substring(0, 2) + YY;
int month = Int32.Parse(MM);
int year = Int32.Parse(thisYear);
if ((currentYear - 5) > year)
year += 100;
return new DateTime(year, month, DateTime.DaysInMonth(year, month));
}
This Method can convert the credit card last two year digits to four year
private static int ToFourDigitYear(int year)
{
string stringYear = year.ToString("00");
if (stringYear.Length == 2)
{
int currentYear = DateTime.Now.Year;
string firstTwoDigitsOfCurrentYear = currentYear.ToString().Substring(0, 2);
year = Convert.ToInt32(firstTwoDigitsOfCurrentYear + stringYear);
if (year < currentYear)
year = year + 100;
}
return year;
}
Out of curiosity, from where do you get this data? From a form? In that case; I would simply ask the user to fill in (or somehow select) the year with four digits or get the users age and month/day of birth, and use that data to figure out what year they were born. That way, you wouldn't have to worry about this problem at all :)
Edit: Use DateTime for working with this kind of data.
Try this simple code
//Invoke TextBoxDateFormat method with date as parameter.
Method
public void TextBoxDateFormat(string str1)
{
// Takes the current date format if MM/DD/YY or MM/DD/YYYY
DateTime dt = Convert.ToDateTime(str1);
//Converts the requested date into MM/DD/YYYY and assign it to textbox field
TextBox = String.Format("{0:MM/dd/yyyy}", dt.ToShortDateString());
//include your validation code if required
}
Had a similar issue, and came up with this... HTH!
value = this.GetDate()
if (value.Length >= 6)//ensure that the date is mmddyy
{
int year = 0;
if (int.TryParse(value.Substring(4, 2), out year))
{
int pastMillenium = int.Parse(DateTime.Now.ToString("yyyy").Substring(0, 2)) - 1;
if (year > int.Parse(DateTime.Now.ToString("yy")))//if its a future year it's most likely 19XX
{
value = string.Format("{0}{1}{2}", value.Substring(0, 4), pastMillenium, year.ToString().PadLeft(2, '0'));
}
else
{
value = string.Format("{0}{1}{2}", value.Substring(0, 4), pastMillenium + 1, year.ToString().PadLeft(2, '0'));
}
}
else
{
value = string.Empty;
}
}
else
{
value = string.Empty;
}
My answer will not match your question but for credit cards I just add 2 digits of current year
private int UpconvertTwoDigitYearToFour(int yearTwoOrFour)
{
try
{
if (yearTwoOrFour.ToString().Length <= 2)
{
DateTime yearOnly = DateTime.ParseExact(yearTwoOrFour.ToString("D2"), "yy", null);
return yearOnly.Year;
}
}
catch
{
}
return yearTwoOrFour;
}
If you calculate for a person he will probably not be more than 100 years...
Eg: 751212
var nr = "751212";
var century = DateTime.Now.AddYears(-100).Year.ToString().Substring(0, 2);
var days = (DateTime.Now - DateTime.Parse(century + nr)).Days;
decimal years = days / 365.25m;
if(years>=99)
century = DateTime.Now.Year.ToString().Substring(0, 2);
var fullnr = century+nr;
To change a 2-digit year to 4-digit current or earlier -
year = year + (DateTime.Today.Year - DateTime.Today.Year%100);
if (year > DateTime.Today.Year)
year = year - 100;
My two cents,
Given an age range=[18, 100+], two digits year=90, I can do
current year - twoDigitsYear = 2018 - 90 = 1928, I got 19, 28
hence 19 is the first two digits of year of born, and 28 is the age, which is
year=1990, age=28
But it won't work when age 0 and 100 both included in the range, same to some of the other answers here.
Based on above solutions, here is mine, i used in android while using java
it takes current year in two digit format then checks for if input
year length is equal to 2, if yes then it get current year and from
this year it splits first two digits of century, then it adds this
century with year user input. to make it 4 digit year.
public static int getConvertedYearFromTwoToFourDigits(String year) {
if (year.length() == 2) {
int currentYear = Calendar.getInstance().get(Calendar.YEAR);
String firstTwoDigitsOfCurrentYear = String.valueOf(currentYear).substring(0, 2);
year = firstTwoDigitsOfCurrentYear + year;
}
return Integer.parseInt(year);
}
int fYear = Convert.ToInt32(txtYear.Value.ToString().Substring(2, 2));
My answer will not match your question but for credit cards I just add 2 digits of current year
private int UpconvertTwoDigitYearToFour(int yearTwoOrFour)
{
try
{
if (yearTwoOrFour.ToString().Length <= 2)
{
DateTime yearOnly = DateTime.ParseExact(yearTwoOrFour.ToString("D2"), "yy", null);
return yearOnly.Year;
}
}
catch
{
}
return yearTwoOrFour;
}

Categories