I have the following if statement:
if date.weekday() != 0:
How can I change this if statement to check every other Monday (one day every other week)?
Thanks in advance for your help!
You post doesn't mention what language you're using. That would be useful to know!
At any rate, a function like weekday probably returns a number from 0-6, which isn't going to give you enough info.
Most languages have a function that turns a date into a millisecond value, typically milliseconds since 1970-01-01 00:00 UTC.
If you take such a value and divide it by 86400000 (number of milliseconds in a day), you get a day number. Day 0 (January 1, 1970) happens to be a Thursday.
In JavaScript, this day number can be obtained from a Date object like this:
let dayNum = Math.floor(someDate.getTime() / 86400000)
From this, dayNum % 7 gives you a number from 0-6 for Thursday through Wednesday. You can add an offset to change what 0 means. (dayNum + 4) % 7 produces 0-6 for Sunday through Saturday.
You want a 14-day cycle for every other week, so dayNum % 14 is what you need, and you then just have to decide which value from 0 to 13 represents your target dates.
Take any sample date that is one of your desired dates, compute the remainder value you get for that date, and then test for that value to be able to match any qualifying date.
Two caveats:
For dates before 1970, the % operator might produces negative numbers. Python is one of the few languages that doesn't do this.
The timezone offset from UTC might be important to your task. And if that's important, Daylight Saving Time changes and/or historical changes in UTC offset can complicate matters.
Python update:
Let's say this coming Monday (July 5, 2021) is one of the every-other-week days that you want. This:
date(2021, 7, 5).toordinal() % 14
...produces a value of 8. Every other Monday before or after will also be 8. The toordinal() method goes straight to a day number, without having to fuss with milliseconds, 86400000, or having to worry about timezone offsets.
Related
The behaviour of freq = "W-SUN" etc. seems confusing and inconsistent. For example, d.date_range(pd.Timestamp('2019-07-09'), pd.Timestamp('2019-11-11'), freq='W-SUN') produces a sequence of Sundays, but pd.Index([pd.Timestamp('2019-07-09')]).to_period('W-SUN').to_timestamp() produces a Monday. What is going on here?
This has come up because I have an index of dates that I want to round to some frequency, while also generating a date_range with the same frequency and phase. It seems like index.to_period(freq).to_timestamp() and pd.date_range(start, end, freq=freq) should work for this, but it doesn't when freq is "W-DAY".
This is a little counter-intuitive, but here's the logic. When you use .to_period(), Pandas calculates the period of time within which the date you supplied falls. The way Pandas calculates this period is to find the next day that matches your specified frequency and extending the period backwards to include your chosen day. In other words, the period is end-inclusive, not start-inclusive.
To find the Sunday-anchored week for a given Tuesday, it finds the next Sunday after that Tuesday and adds the previous six days. When you convert to timestamp, however, it selects the first day of that period, which in this case will be a Monday. If you asked for the Sunday-anchored period of a Sunday, it would give you that day plus the previous six days, not the following six days.
If you want your period to start rather than end on a particular day of the week, just set the frequency string to the day prior. In your case, pd.Index([pd.Timestamp('2019-07-09')]).to_period('W-SAT').to_timestamp() should do the trick.
Some hopefully helpful demonstrations:
pd.Index([pd.Timestamp('2019-07-09')]).to_period('W-SUN') gives:
PeriodIndex(['2019-07-08/2019-07-14'], dtype='period[W-SUN]', freq='W-SUN
Note that this period ends on a Sunday. When you run pd.Index([pd.Timestamp('2019-07-09')]).to_period('W-SUN').to_timestamp() it gives you the first day of this period:
DatetimeIndex(['2019-07-08'], dtype='datetime64[ns]', freq=None)
You can observe how the days are chosen by running:
for f in ['W-SUN', 'W-MON', 'W-TUE', 'W-WED', 'W-THU', 'W-FRI', 'W-SAT']:
print(f, pd.Index([pd.Timestamp('2019-07-09')]).to_period(f))
Which gives:
PeriodIndex(['2019-07-08/2019-07-14'], dtype='period[W-SUN]', freq='W-SUN')
PeriodIndex(['2019-07-09/2019-07-15'], dtype='period[W-MON]', freq='W-MON')
PeriodIndex(['2019-07-03/2019-07-09'], dtype='period[W-TUE]', freq='W-TUE')
PeriodIndex(['2019-07-04/2019-07-10'], dtype='period[W-WED]', freq='W-WED')
PeriodIndex(['2019-07-05/2019-07-11'], dtype='period[W-THU]', freq='W-THU')
PeriodIndex(['2019-07-06/2019-07-12'], dtype='period[W-FRI]', freq='W-FRI')
PeriodIndex(['2019-07-07/2019-07-13'], dtype='period[W-SAT]', freq='W-SAT')
Note that the start of the chosen period jumps in the middle, but the logic remains consistent.
I am trying to get a date based on a number of the week, but there are some annoyances.
The date.weekday() returns the day of the week where 0 in Monday and 6 is Sunday.
The %w directive of date.strftime() and date.strptime() uses the 0 for Sunday and 6 for Saturday.
This causes some really annoying issues when trying to figure out a date given a week number from date.weekday().
Is there a better way of getting a date from a week number?
EDIT:
Added the example.
import datetime
original_date = datetime.date(2014, 8, 24)
week_of_the_date = original_date.isocalendar()[1] # 34
day_of_the_date = original_date.isocalendar()[2] # 7
temp = '{0} {1} {2}'.format(*(2014, week_of_the_date, day_of_the_date-1))
date_from_week = datetime.datetime.strptime(temp, '%Y %W %w')
week_from_new_date = date_from_week.isocalendar()[1] # 35!!
EDIT 2:
I ultimately put the date stuff in the view (using jQuery UI), it has more consistent notions of weeks.
I think the Sunday vs. Monday distinction between weekday and strftime using %W is moot - you could use isoweekday to get those to line up, or %U in strftime if you wanted Sunday as the first day of the week. The real problem is that strftime, based on the underlying C function, determines the first week of the year differently than the ISO definition. With %W the docs say: " All days in a new year preceding the first Monday are considered to be in week 0". ISO calendars count the week containing the first Thursday as the first week, for reasons I do not understand.
Two ways I found to work with ISO weeks, either just getting datetime.date instances back or supporting a variety of operations, are:
this answer with a simple timedelta approach:
What's the best way to find the inverse of datetime.isocalendar()?
this third-party library: https://pypi.python.org/pypi/isoweek/
The following code gets the start of the week of a date argument:
weekstart = datetime.datetime.strptime(sys.argv[1], '%m/%d/%Y')
if weekstart.strftime('%A') != 'Sunday':
print "Start date should be a Sunday"
while weekstart.strftime('%A') != 'Sunday':
#weekstart = weekstart - datetime.timedelta(a)
a=a+1
print "Using %s as start date instead" %weekstart.strftime('%m/%d/%Y')
I receive the following error only when the date entered is 7/10/2012, 7/11/2012, 7/12/2012, or 7/13/2012:
while weekstart.strftime('%A') != 'Sunday':
ValueError: year=1899 is before 1900; the datetime strftime() methods require year >= 1900
What is wrong with this?
Yes, this is a bug in your concept. You are incorrectly incrementing a and decreasing weekstart by that a. So it does not go day by day, but by 1, 2, 3, 4,… days.
7/10/2012 is Tuesday. Substract 1 day…
7/9/2012 is Monday. Substract 2 days…
7/7/2012 is Saturday. Substract 3 days…
7/4/2012 is Wednesday. Substract 4 days…
6/30/2012 is Saturday. Substract 5 days…
6/25/2012 is Monday. Substract 6 days…
6/19/2012 is Tuesday. Substract 7 days, i.e. one week and the loop goes on faster and faster until the year 1899 when it breaks.
It means that if your first day is Tuesday, it will never reach Sunday this way.
It works for 7/11/2012 (and any Wednesday), it breaks for 7/12/2012 (and any Thursday), it breaks for 7/13/2012 (and any Friday), it works for 7/14/2012 (and any Saturday) and it works for 7/16/2012 (and any Monday) and of course it stops immediately for any Sunday.
But it is not what you want.
If you want to get the previous (or today's) Sunday for any day without loops, just use this:
weekstart = weekstart - datetime.timedelta((weekstart.weekday() - 6) % 7)
Or, if you want to avoid mathematics and use libraries (dateutil):
from dateutil import relativedelta
weekstart = weekstart + relativedelta.relativedelta(weekday=relativedelta.SU(-1))
So I have list of events that are sort of like alarms. They're defined by their start and end time (in hours and minutes), a range of days (ie 1-3 which is sunday through wed.), and a range of months (ie 1-3, january through march). The format of that data is largely unchangeable. I need to, not necessarily sort the list, but I need to find the next upcoming event based on the current time. There's just so many different ways to do this and so many different corner cases. This is my pseudo code:
now = time()
diff = []
# Start difference between now and start times
for s in schedule #assuming appending to diff
diff.minutes = s.minutes - time.minutes #
diff.hours = s.hours - time.hours
diff.days = s.days - time.days
diff.months = s.months - time.months
for d in diff
if d < 0
d = period + d
# period is the maximum period of the attribute. ie minutes is 60, hours is 24
# repeat for event end times
So now I have a list of tuples of differences in hours, minutes, days, and weeks. This tuple already takes into account if it's passed the start time, but before the end time. So let's say it's in August and the start month of the event is July and the end month is September, so diff.month == 0.
Now this specific corner case is giving me trouble:
Let's say a schedule runs from 0 to 23:59 thursdays in august. And it's Friday the 27th. Running my algorithm, the difference in months would be 0 when in reality it won't run again until next august, so it should be 12. And I'm stuck. The month is the only problem I think because the month is the only attribute that directly depends on what the date of the specific month is (versus just the day). Is my algorithm OK and I can just deal with this special case? Or is there something better out there for this?
This is the data I'm working with
map['start_time']=''
map['end_time']=''
map['start_moy']=''
map['end_moy']=''
map['start_dow']=''
map['end_dow']=''
The schedule getAllSchedules method just returns a list to all of the schedules. I can change the schedule class but I'm not sure what difference I can make there. I can't add/change the format of the schedules I'm given
Convert the items from the schedule into datetime objects. Then you can simply sort them
from datetime import datetime
events = sorted(datetime(s.year, s.month, s.day, s.hour, s.minute) for s in schedule)
Since your resolution is in minutes, and assuming that you don't have many events, then I'd simply scan all the events every minute.
Filter your events so that you have a new list where the event range match the current month and day.
Then for each of those events declare that they are active or inactive according to whether the current time matches the event's range.
The primary issue seems to be with the fact that you're using the day of the week, instead of explicit days of the month.
While your cited edge case is one example, does this issue not crop up with all events scheduled in any month outside of the current one?
I think the most robust approach here would be to do the work to get your scheduled events into datetime format, then use #gnibbler's suggestion of sorting the datetime objects.
Once you have determined that the last event for the current month has already passed, calculate the distance to the next month the event occurs in (be it + 1 year, or just + 1 month), then construct a datetime object with that information:
first_of_month = datetime.date(calculated_year, calculated_month, 1)
By using the first day of the month, you can then use:
day_of_week = first_of_month.strftime('%w')
To give you what day of the week the first of that month falls on, which you can then use to calculate how many days to add to get to the first, second, third, etc. instance of a given day of the week, for that month. Once you have that day, you can construct a valid datetime object and do whatever comparisons you wish with now().
I couldn't figure out how to do it using only datetimes. But I found a module and used this. It's perfect
http://labix.org/python-dateutil
Python and Matlab quite often have integer date representations as follows:
733828.0
733829.0
733832.0
733833.0
733834.0
733835.0
733836.0
733839.0
733840.0
733841.0
these numbers correspond to some dates this year. Do you guys know which function can convert them back to YYYYMMDD format?
thanks a million!
The datetime.datetime class can help you here. The following works, if those values are treated as integer days (you don't specify what they are).
>>> from datetime import datetime
>>> dt = datetime.fromordinal(733828)
>>> dt
datetime.datetime(2010, 2, 25, 0, 0)
>>> dt.strftime('%Y%m%d')
'20100225'
You show the values as floats, and the above doesn't take floats. If you can give more detail about what the data is (and where it comes from) it will be possible to give a more complete answer.
Since Python example was already demonstrated, here is the matlab one:
>> datestr(733828, 'yyyymmdd')
ans =
20090224
Also, note that while looking similar these are actually different things in Matlab and Python:
Matlab
A serial date number represents the whole and fractional number of days
from a specific date and time, where datenum('Jan-1-0000 00:00:00') returns
the number 1. (The year 0000 is merely a reference point and is not intended
to be interpreted as a real year in time.)
Python, datetime.date.fromordinal
Return the date corresponding to the proleptic Gregorian ordinal, where January 1 of year 1 has ordinal 1.
So they would differ by 366 days, which is apparently the length of the year 0.
Dates like 733828.0 are Rata Die dates, counted from January 1, 1 A.D. (and decimal fraction of days). They may be UTC or by your timezone.
Julian Dates, used mostly by astronomers, count the days (and decimal fraction of days) since January 1, 4713 BC Greenwich noon. Julian date is frequently confused with Ordinal date, which is the date count from January 1 of the current year (Feb 2 = ordinal day 33).
So datetime is calling these things ordinal dates, but I think this only makes sense locally, in the world of python.
Is 733828.0 a timestamp? If so, you can do the following:
import datetime as dt
dt.date.fromtimestamp(733828.0).strftime('%Y%m%d')
PS
I think Peter Hansen is right :)
I am not a native English speaker. Just trying to help. I don't quite know the difference between a timestamp and an ordinal :(