How to Iterate Over a Timespan After Days, Hours, Weeks and Months

How to iterate over a timespan after days, hours, weeks and months?

Use dateutil and its rrule implementation, like so:

from dateutil import rrule
from datetime import datetime, timedelta

now = datetime.now()
hundredDaysLater = now + timedelta(days=100)

for dt in rrule.rrule(rrule.MONTHLY, dtstart=now, until=hundredDaysLater):
print dt

Output is

2008-09-30 23:29:54
2008-10-30 23:29:54
2008-11-30 23:29:54
2008-12-30 23:29:54

Replace MONTHLY with any of YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY, or SECONDLY. Replace dtstart and until with whatever datetime object you want.

This recipe has the advantage for working in all cases, including MONTHLY. Only caveat I could find is that if you pass a day number that doesn't exist for all months, it skips those months.

python - iteration by date and time - half hour

Use timedelta to generate a range of datetime objects:

from datetime import timedelta, datetime

start_date = datetime(2016, 6, 9, 5, 0, 0)
for td in (start_date + timedelta(minutes=30*it) for it in xrange(10)):
print td.strftime("%Y-%m-%d,%H:%M")

Output:

2016-06-09,05:00
2016-06-09,05:30
2016-06-09,06:00
2016-06-09,06:30
2016-06-09,07:00
2016-06-09,07:30
2016-06-09,08:00
2016-06-09,08:30
2016-06-09,09:00
2016-06-09,09:30

How do I loop through a date range?

Well, you'll need to loop over them one way or the other. I prefer defining a method like this:

public IEnumerable<DateTime> EachDay(DateTime from, DateTime thru)
{
for(var day = from.Date; day.Date <= thru.Date; day = day.AddDays(1))
yield return day;
}

Then you can use it like this:

foreach (DateTime day in EachDay(StartDate, EndDate))
// print it or whatever

In this manner you could hit every other day, every third day, only weekdays, etc. For example, to return every third day starting with the "start" date, you could just call AddDays(3) in the loop instead of AddDays(1).

Iterate over each day, calculate average price of first and last 3 hours and take difference of those averages in Python

As you showed, the hours are ordered, so you can groupby day, and the get the list of the prices of the 24 hours of the day, then, you can apply a function to do the difference. You could try something like this:

import pandas as pd
from statistics import mean
def getavg(ls):
mean3first=mean(ls[:3])
mean3last=mean(ls[len(ls)-3:])
return mean3first-mean3last


diff_means= df.groupby(['Date']).agg(list)['Price'].apply(getavg).reset_index()
diff_means.columns=['Date','Diff']
print(diff_means)

A Real Timespan Object With .Years & .Months

Here is the main answer with code, please note that you can get any number of dates/times accuracy, seconds & minutes, or seconds, minutes and days, anywhere up to years (which would contain 6 parts/segments). If you specify top two and it's over a year old, it will return "1 year and 3 months ago" and won't return the rest because you've requested two segments. if it's only a few hours old, then it will only return "2 hours and 1 minute ago". Of course, same rules apply if you specify 1, 2, 3, 4, 5 or 6 segmets (maxes out at 6 because seconds, minutes, hours, days, months, years only make 6 types). It will also correct grammer issues like "minutes" vs "minute" depending on if it's 1 minute or more, same for all types, and the "string" generated will always be grammatically correct.

Here are some examples for use:
bAllowSegments identifies how many segments to show... ie: if 3, then return string would be (as an example)... "3 years, 2 months and 13 days" (won't include hours, minutes and seconds as the top 3 time categories are returned), if however, the date was a newer date, such as something a few days ago, specifying the same segments (3) will return "4 days, 1 hour and 13 minutes ago" instead, so it takes everything into account!

if bAllowSegments is 2 it would return "3 years and 2 months" and if 6 (maximum value) would return "3 years, 2 months, 13 days, 13 hours, 29 minutes and 9 seconds", but, be reminded that it will NEVER RETURN something like this "0 years, 0 months, 0 days, 3 hours, 2 minutes and 13 seconds ago" as it understands there is no date data in the top 3 segments and ignores them, even if you specify 6 segments, so don't worry :). Of course, if there is a segment with 0 in it, it will take that into account when forming the string, and will display as "3 days and 4 seconds ago" and ignoring the "0 hours" part! Enjoy and please comment if you like.

 Public Function RealTimeUntilNow(ByVal dt As DateTime, Optional ByVal bAllowSegments As Byte = 2) As String
' bAllowSegments identifies how many segments to show... ie: if 3, then return string would be (as an example)...
' "3 years, 2 months and 13 days" the top 3 time categories are returned, if bAllowSegments is 2 it would return
' "3 years and 2 months" and if 6 (maximum value) would return "3 years, 2 months, 13 days, 13 hours, 29 minutes and 9 seconds"
Dim rYears, rMonths, rDays, rHours, rMinutes, rSeconds As Int16
Dim dtNow = DateTime.Now
Dim daysInBaseMonth = Date.DaysInMonth(dt.Year, dt.Month)

rYears = dtNow.Year - dt.Year
rMonths = dtNow.Month - dt.Month
If rMonths < 0 Then rMonths += 12 : rYears -= 1 ' add 1 year to months, and remove 1 year from years.
rDays = dtNow.Day - dt.Day
If rDays < 0 Then rDays += daysInBaseMonth : rMonths -= 1
rHours = dtNow.Hour - dt.Hour
If rHours < 0 Then rHours += 24 : rDays -= 1
rMinutes = dtNow.Minute - dt.Minute
If rMinutes < 0 Then rMinutes += 60 : rHours -= 1
rSeconds = dtNow.Second - dt.Second
If rSeconds < 0 Then rSeconds += 60 : rMinutes -= 1

' this is the display functionality
Dim sb As StringBuilder = New StringBuilder()
Dim iSegmentsAdded As Int16 = 0

If rYears > 0 Then sb.Append(rYears) : sb.Append(" year" & If(rYears <> 1, "s", "") & ", ") : iSegmentsAdded += 1
If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn

If rMonths > 0 Then sb.AppendFormat(rMonths) : sb.Append(" month" & If(rMonths <> 1, "s", "") & ", ") : iSegmentsAdded += 1
If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn

If rDays > 0 Then sb.Append(rDays) : sb.Append(" day" & If(rDays <> 1, "s", "") & ", ") : iSegmentsAdded += 1
If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn

If rHours > 0 Then sb.Append(rHours) : sb.Append(" hour" & If(rHours <> 1, "s", "") & ", ") : iSegmentsAdded += 1
If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn

If rMinutes > 0 Then sb.Append(rMinutes) : sb.Append(" minute" & If(rMinutes <> 1, "s", "") & ", ") : iSegmentsAdded += 1
If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn

If rSeconds > 0 Then sb.Append(rSeconds) : sb.Append(" second" & If(rSeconds <> 1, "s", "") & "") : iSegmentsAdded += 1

parseAndReturn:

' if the string is entirely empty, that means it was just posted so its less than a second ago, and an empty string getting passed will cause an error
' so we construct our own meaningful string which will still fit into the "Posted * ago " syntax...

If sb.ToString = "" Then sb.Append("less than 1 second")

Return ReplaceLast(sb.ToString.TrimEnd(" ", ",").ToString, ",", " and")

End Function

Of course, you will need a "ReplaceLast" function, which takes a source string, and an argument specifying what needs to be replaced, and another arg specifying what you want to replace it with, and it only replaces the last occurance of that string... i've included my one if you don't have one or dont want to implement it, so here it is, it will work "as is" with no modification needed. I know the reverseit function is no longer needed (exists in .net) but the ReplaceLast and the ReverseIt func are carried over from the pre-.net days, so please excuse how dated it may look (still works 100% tho, been using em for over ten years, can guarante they are bug free)... :). Also, if you are using VB6, you can use StrReverse (wrapping it around the string extended with the .ReverseIt extension method), instead of using the ReverseIt() function (provided as an extension method). So, instead of doing sReplacable.ReverseIt, you'd do StrReverse(sReplacable) as StrReverse() is a built in VB6 function (and does the exact same thing, reverses a given string, and does nothing more). If you use StrReverse() instead of my generic ReverseIt function, feel free to delete the ReverseIt function/extension. StrReverse() function should be available in .NET as long as you are importing the legacy ms-visualbasic-dll library. Makes no difference either way, I had written ReverseIt() before I even know a StrReverse() function had existed, and had been using it ever since out of habit (no real reason to use mine as opposed to the in-built generic function StrReverse) - in fact, I'm sure StrReverse (or a similar, newer .NET specific version of a string reversing function) would be written to be more efficient :). cheers.

<Extension()> _ 
Public Function ReplaceLast(ByVal sReplacable As String, ByVal sReplaceWhat As String, ByVal sReplaceWith As String) As String
' let empty string arguments run, incase we dont know if we are sending and empty string or not.
sReplacable = sReplacable.ReverseIt
sReplacable = Replace(sReplacable, sReplaceWhat.ReverseIt, sReplaceWith.ReverseIt, , 1) ' only does first item on reversed version!
Return sReplacable.ReverseIt.ToString
End Function

<Extension()> _
Public Function ReverseIt(ByVal strS As String, Optional ByVal n As Integer = -1) As String
Dim strTempX As String = "", intI As Integer

If n > strS.Length Or n = -1 Then n = strS.Length

For intI = n To 1 Step -1
strTempX = strTempX + Mid(strS, intI, 1)
Next intI

ReverseIt = strTempX + Right(strS, Len(strS) - n)

End Function

Finding Days Per Month From Now Until End Date

Use the datetime library to get the number of days between two dates

>>> import calendar
>>> from dateutil import rrule
>>> from datetime import datetime
>>> end = datetime.strptime('12/31/2022', '%m/%d/%Y')
>>> start = datetime.now()
>>> [calendar.monthrange(dt_i.year, dt_i.month)[1] for dt_i in rrule.rrule(rrule.MONTHLY, dtstart=start, until=end)]
[30, 31, 30, 31, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]

Return Months & days from TimeSpan asp.net

It might be easier to use embedded methods :

DateTime startDate = new DateTime(1970, 01, 01);
DateTime endDate = DateTime.Now.ToUniversalTime();
TimeSpan diff = (endDate - startDate);

Console.WriteLine("Number of seconds:" + diff.TotalSeconds);
Console.WriteLine("Number of minutes:" + diff.TotalDays);
Console.WriteLine("Number of hours:" + diff.TotalHours );
Console.WriteLine("Number of days:" + diff.TotalDays);

//months
int nbMonths = ((endDate.Year - startDate.Year) * 12) + endDate.Month - startDate.Month;
Console.WriteLine("Number of months:" + nbMonths);
//years
int nbYears = endDate.Year - startDate.Year;
Console.WriteLine("Number of years:" + nbYears);
Console.ReadKey();

Grouping Months of a particular Time span together using DateTime

Using LINQ:

DateTime startDate = new DateTime(2010, 1, 1);
DateTime endDate = new DateTime(2010, 12, 31);

int monthCount =
(endDate.Month - startDate.Month + 1) +
(endDate.Year - startDate.Year) * 12;

Enumerable
.Range(0, monthCount)
.Select(x => new DateTime(startDate.Year, startDate.Month, 1).AddMonths(x))
.ToList()
.ForEach(d1 =>
{
string month = d1.ToString("MMMM");
// here should be your code
// to work with months

Enumerable
.Range(0, d1.AddMonths(1).AddDays(-1).Day)
.Select(x => d1.AddDays(x))
.ToList()
.ForEach(d2 =>
{
string dayOfWeek = d2.ToString("ddd");
string day = d2.Day.ToString();
// here should be your code
// to work with days
});
});

OK, the next variant without LINQ:

StringBuilder sb = new StringBuilder();

DateTime startDate = new DateTime(2010, 1, 1);
DateTime endDate = new DateTime(2012, 12, 31);

int monthCount =
(endDate.Month - startDate.Month + 1) +
(endDate.Year - startDate.Year) * 12;

for (int i = 0; i < monthCount; i++)
{
DateTime d1 = new DateTime(startDate.Year, startDate.Month, 1).AddMonths(i);
string month = d1.ToString("MMMM");

sb.AppendFormat("<p>{0}</p>", month);

int daysInMonth = d1.AddMonths(1).AddDays(-1).Day;
StringBuilder daysOfWeekRow = new StringBuilder();
StringBuilder daysRow = new StringBuilder();
for (int j = 0; j < daysInMonth; j++)
{
DateTime d2 = d1.AddDays(j);
string dayOfWeek = d2.ToString("ddd");
string day = d2.Day.ToString();

daysOfWeekRow.AppendFormat("<td>{0}</td>", dayOfWeek);
daysRow.AppendFormat("<td>{0}</td>", day);
}
sb.AppendFormat(
"<table><tr>{0}</tr><tr>{1}</tr></table>",
daysOfWeekRow.ToString(),
daysRow.ToString()
);
}

string result = sb.ToString();

You may change output formatting as you want I provided the basic example only.

The main thing is to iterate through the necessary dates (to use or not to use LINQ is you option, but you could agree solution with LINQ is more elegant) and add custom formatting in the necessary places (I put comments where to do it with the first example).

Looping through the days of the week inside of C# TimeSpan Class

This will loop through all days from the start date to the end date and get the day of week for each.

DateTime startDate = new DateTime(2010, 1, 1);
DateTime endDate = new DateTime(2011, 12, 12);
for (DateTime date = startDate; date <= endDate; date = date.AddDays(1))
{
DayOfWeek dw = date.DayOfWeek;
// ...
}

Unless you're really worried about optimization, I wouldn't spring for anything more complicated.



Related Topics



Leave a reply



Submit