LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   any lang: week number (https://www.linuxquestions.org/questions/programming-9/any-lang-week-number-937140/)

ezekieldas 03-29-2012 03:09 PM

any lang: week number
 
I don't do much programming and I've never tried working out a calendar problem. I'm trying to
do this in python and if I had the background, then maybe I could find the answer using datetime() or something. Anyhow, here's my issue...

I'd like to get a week number. That is, starting with Sunday, ending with Saturday. Looking at UNIX cal I see today, 3/29 would be week 5. I purloined this code from stack overflow and it generally works fine. However when I ran this through a number of tests I found some issues with some weeks where the 21st and 22nd are in the 4th week. March 2011 is an example.

Here's what I'm using. Any suggestions are appreciated.

Code:

print "which date?",
number = int(raw_input())
week_number = (number - 1) // 7 + 1

print "The date you gave was %r, and the calculation is %r." % (number, week_number)


evo2 03-29-2012 04:45 PM

Hi,

in a shell the current week can be obtained with:
Code:

date +%W
Or if you want it for a specfic date, Eg 5th October2011:
Code:

date +%W --date 2011-10-05
In python
Code:

import datetime
datetime.datetime(2011,10,5).strftime('%W')

Cheers,

Evo2.

ezekieldas 03-29-2012 04:53 PM

Thanks evo2. I should have been more clear. What I'm after is a week number 1-5. The first week being 1, starting on Sundays. I can see this with UNIX cal but to get a proper calculation has been difficult.

Dark_Helmet 03-29-2012 05:23 PM

You still need a little more clarity ;)

Quote:

What I'm after is a week number 1-5. The first week being 1, starting on Sundays.
By limiting your range to 1-5, I'm inferring that you want a week number within a given month. My assumption is that your "which date?" prompt really asks "which day of the current month?"

When I execute cal on my machine, I get:
Code:

    March 2012
Su Mo Tu We Th Fr Sa
            1  2  3
 4  5  6  7  8  9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31

So, if the "first week being 1, starting on Sundays," what week contains March 1, 2, and 3?

Perhaps you would be interested in isocalendar(). From Python 2.7.2 documentation:
Code:

date.isocalendar()

    Return a 3-tuple, (ISO year, ISO week number, ISO weekday).

    The ISO calendar is a widely used variant of the Gregorian calendar. See http://www.phys.uu.n
    /~vgent/calendar/isocalendar.htm for a good explanation.

    The ISO year consists of 52 or 53 full weeks, and where a week starts on a Monday and ends on a
    Sunday. The first week of an ISO year is the first (Gregorian) calendar week of a year containing
    a Thursday. This is called week number 1, and the ISO year of that Thursday is the same as its
    Gregorian year.

    For example, 2004 begins on a Thursday, so the first week of ISO year 2004 begins on Monday, 29
    Dec 2003 and ends on Sunday, 4 Jan 2004, so that date(2003, 12, 29).isocalendar() == (2004, 1,
    1) and date(2004, 1, 4).isocalendar() == (2004, 1, 7).

You would, of course, have to massage the results because the ISO definition for a week starts on a Monday rather than a Sunday.

evo2 03-29-2012 05:23 PM

Hi,

"week number" normally means the week of the year 0-53 (man 1 date). Perhaps what you want
is the week of the month?

Could post the invocation, output and version of cal that provides this information.

Cheers,

Evo2.

Nominal Animal 03-29-2012 05:54 PM

Like evo2 and Dark_Helmet above stated, the problem is that we do not have any definition for your "week number".

For example, consider February 2012. The first and last days of February 2012 fell on Wednesdays.

Which dates are the first week of February 2012?

There are many, all equally valid, answers:
  • 2012-01-29 to 2012-02-04 (Sun to Sat, the week that contains the first of February)
  • 2012-01-29 to 2012-02-04 (Sun to Sat, the week that contains more than half its days in February)
  • 2012-01-05 to 2012-02-11 (Sun to Sat, the first full week in February)
  • 2012-01-30 to 2012-02-05 (Mon to Sun, the week that contains the first of February)
  • 2012-01-30 to 2012-02-05 (Mon to Sun, the week that contains more than half its days in February)
  • 2012-01-06 to 2012-02-12 (Mon to Sun, the first full week in February)
Which one the asker is looking for?

Asking for week number 1-5 is like asking for the NominalAnimalNumber of February 2012. Nobody can answer it correctly before you define exactly what it is.

I'm trying hard to not be a dickweed here. If you want the calendar definitions used in your region, just say so; they're very well defined -- but there are large differences between various regions in the world. See ISO week date and Seven-day week articles at Wikipedia, to see how complex (or simple, depending on how familiar you are with human cultural differences) issue week numbering really is.

Dark_Helmet 03-29-2012 06:03 PM

I'm just throwing this out there as an example of how I might use the isocalendar() function I referenced. Perhaps it will spur you in the right direction.

This is from an interpreter session:
Code:

Python 2.6.5 (r265:79063, Apr 16 2010, 13:57:41)
[GCC 4.4.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import datetime
>>> monthStart = datetime.date(2012, 3, 1)
>>> currentDay = datetime.date(2012, 3, 29)
>>> monthStart.isocalendar()
(2012, 9, 4)
>>> currentDay.isocalendar()
(2012, 13, 4)
>>> weekNum = currentDay.isocalendar()[1] - monthStart.isocalendar()[1]
>>> weekNum
4

So, basically, the code above calculates the ISO week number for the first day of the month, calculates the ISO week number for the current day, subtracts the two, and the result is (roughly) a 0-based week number within the current month.

And of course, you can always substitute the user-supplied value from your prompt for the literal '29' I used in the above.

But again, that's using the Monday-based ISO calendar format.

Tinkster 03-29-2012 06:06 PM

All the theory aside; assuming he uses a linux distro which' locale has the definitions
he desires, these should do the job:
Code:

$ cal
    March 2012
Su Mo Tu We Th Fr Sa
            1  2  3
 4  5  6  7  8  9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
$ date "+%w"
5
$ perl -e 'use POSIX; my $date=strftime("%w",localtime); print $date;'
5


Nominal Animal 03-29-2012 06:14 PM

Quote:

Originally Posted by Tinkster (Post 4640113)
All the theory aside; assuming he uses a linux distro which' locale has the definitions
he desires, these should do the job

No!

First, for a Finnish calendar (with LC_ALL=fi_FI.UTF-8 LANG=fi_FI.UTF-8) I need to run ncal -bM to get the standard calendar,
Code:

  Maaliskuu 2012   
ma ti ke to pe la su 
          1  2  3  4 
 5  6  7  8  9 10 11 
12 13 14 15 16 17 18 
19 20 21 22 23 24 25 
26 27 28 29 30 31

It does not get all the data (like which day starts the week) from the locale, apparently. Therefore, for date manipulations, I do need to use different fields depening on whether I consider the first day of week to be Sunday or Monday, when using e.g. strftime().

Second, '%w' yields the day of week number, 0 = Sun, 1 = Mon, ..., 6 = Sat, not a week number.

Tinkster 03-29-2012 06:18 PM

Aight. It's settled. I need a holiday

ezekieldas 03-29-2012 06:20 PM

Thanks for the comments. Sorry for the lack of clarity. I've never worked with this type of problem before. There's a couple of posts on Stack Overflow that discuss this (I'm appending URL below).

I think what Dark_Helmet points out above, using isocalendar(), might do the trick. I'm going to play with this and come back to this post. Visually, this is what I have in mind:

Code:

    March 2012
Su Mo Tu We Th Fr Sa
            1  2  3  = week 1
 4  5  6  7  8  9 10  = week 2
11 12 13 14 15 16 17  = week 3
18 19 20 21 22 23 24  = week 4
25 26 27 28 29 30 31  = week 5

The larger problem involves trying to find 2nd or 4th Monday or Tuesday of each month. For further illustration this is to notify car owners of street cleaning days in Oakland, California :-)

The week_number I posted earlier comes from these posts.

http://stackoverflow.com/questions/7...eek-in-a-month
http://stackoverflow.com/questions/3...r-of-the-month

Nominal Animal 03-29-2012 07:19 PM

Quote:

Originally Posted by ezekieldas (Post 4640132)
The larger problem involves trying to find 2nd or 4th Monday or Tuesday of each month. For further illustration this is to notify car owners of street cleaning days in Oakland, California :-)

But.. but.. that has nothing to do with week numbers, or even which day starts a week!

Use the datetime Python module for this. Consider something like the following:
Code:

import datetime

# Remember: Monday = 0, Tuesday = 1, .., Sunday = 6.

# Return the date of weeknum'th weekday of year-month.
def day(year, month, weeknum, weekday):

    # Start at the first of that month.
    theday = datetime.date(year, month, 1)

    # Adjust to the first 'weekday' weekday of that month.
    if theday.weekday() < weekday:
        theday += datetime.timedelta(weekday - theday.weekday())
    elif theday.weekday() > weekday:
        theday += datetime.timedelta(7 + weekday - theday.weekday())

    # Adjust weeknum-1 weeks forward.
    return theday + datetime.timedelta(7 * (weeknum - 1))

Using the above, the second(2) Friday(4) of May(5) 2012 is
Code:

print day(2012, 5, 2, 4)
i.e. 2012-05-11, AKA Fri 11 May 2012.

Does this not solve your larger problem rather simply?

ezekieldas 03-30-2012 02:39 AM

Quote:

Originally Posted by Nominal Animal (Post 4640167)
But.. but.. that has nothing to do with week numbers, or even which day starts a week!
Does this not solve your larger problem rather simply?

It might if I understood it better. I am in a bit over my head with this one. That is, I don't understand the issue very well + I don't know the language that well. If you could break down that example a bit further I might be able to use it.

Prior to reading these additional posts I found I was reasoning with the issue incorrectly. That is, this isn't a *horizontal* calendar issue (like I posted earlier) but rather a *vertical* one. To find the 2nd and 4th Mon and Tue from March 2011:

Code:

Su Mo Tu We Th Fr Sa
      1  2  3  4  5
 6  7  8  9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31

If we took all the Mondays we'd have (7,14,21,28). Note: I'm reading down, vertically on that calendar from Mo. So with perl (I think), 2nd/4th Mondays could be extracted with @Mondays[1,3] (that's the 2nd and 4th element of the array) and we get 14th and 28th. Tuesdays is (1,8,15,22,29) So we take @Tuesdays[1,3] and get the 8th and 22nd. But I'm unsure how to do this with python, which is the language used to develop this prior to finding the trouble involved with this 'week_number = (number - 1) // 7 + 1'

FWIW, here is the old code:

Code:

import time, datetime

today = datetime.date.today()

day_of_week = datetime.datetime.isoweekday(today)
day_of_month = datetime.datetime.now().day
week_number = (day_of_month - 1) // 7 + 1

print "day of week is:", day_of_week
print "week number is:", week_number
print "day of month is:", day_of_month

if (( day_of_week == 7 ) or ( day_of_week == 1 )) and (( week_number == 2 ) or ( week_number == 4 )):
 print "do it."
else:
 print "do not do it."


Nominal Animal 03-30-2012 03:50 PM

Quote:

Originally Posted by ezekieldas (Post 4640397)
It might if I understood it better. I am in a bit over my head with this one. That is, I don't understand the issue very well + I don't know the language that well. If you could break down that example a bit further I might be able to use it.

Okay.

First, the function assumes you specify the desired date as year, month, weeknum (from 1 to 5, for first to fifth, respectively), and weekday (0 for Monday, 1 for Tuesday, and so on, up to 6 for Sunday).

It will then return the datetime.date object that matches that date.

First, we create a datetime.date object that matches the first day of the desired month:
Code:

    theday = datetime.date(year, month, 1)
Next, we check which weekday started the month, and adjust the datetime.date object forward to the (first) weekday that matches the weekday parameter. If the desired weekday is later on in the same week, we can just add the days in between:
Code:

    if theday.weekday() < weekday:
        theday += datetime.timedelta(weekday - theday.weekday())

otherwise we'll have to skip to next week (to be able to count backwards):
Code:

    elif theday.weekday() > weekday:
        theday += datetime.timedelta(7 + weekday - theday.weekday())

The effect of the above is the same if you wrote a loop that advances the object day by day until its weekday matches,
Code:

    while (theday.weekday() != weekday):
              theday += datetime.timedelta(1)

except that doing the addition directly is obviously much cleaner and faster.

At this point, theday is the first desired weekday weekday of the desired month and year. All we need to is adjust theday forwards by weeknum-1 weeks -- as the desired date asked is the weeknum'th weekday:
Code:

    return theday + datetime.timedelta(7 * (weeknum - 1))
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

Your example code indicates you actually use the inverse; that is, you want to know whether this is the first, second, third, fourth, or fifth occurrence of this weekday this month.

This is even simpler, and depends only on the day of month, and is, as you posted, 1 + ((day - 1) // 7) :
Code:

import datetime

# Return 1 to 5, depending on whether the date is the first, second,
# third, fourth, or fifth same weekday of that month.
#
def nth(theday):
    return 1 + ((theday.day - 1) // 7)


# Fixed names for the example below.
# Remember that lists in Python start at index 0.

monthname = [ '',
              'January', 'February', 'March', 'April',
              'May', 'June', 'July', 'August',
              'September', 'October', 'November', 'December' ]
dayname = [ 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday' ]
nthname = [ '', 'first', 'second', 'third', 'fourth', 'fifth' ]

# Example:

theday = datetime.date.today()

print 'Today is the %s %s of %s, %d.' % ( nthname[nth(theday)], dayname[theday.weekday()], monthname[theday.month], theday.year )

Honestly, if it was more complex than that, do you really expect humans would use the concept at all? :p
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

Why does 1 + ((day - 1) // 7) then work?

In Python, // is integer division operator. It means the result will be an integer, with all the decimals removed. The key point here is to understand this operation, i.e. that
Code:

      0 // 7 == 0      10 // 7 == 1      20 // 7 == 2
      1 // 7 == 0      11 // 7 == 1      21 // 7 == 3
      2 // 7 == 0      12 // 7 == 1      22 // 7 == 3
      3 // 7 == 0      13 // 7 == 1      23 // 7 == 3
      4 // 7 == 0      14 // 7 == 2      24 // 7 == 3
      5 // 7 == 0      15 // 7 == 2      25 // 7 == 3
      6 // 7 == 0      16 // 7 == 2      26 // 7 == 3
      7 // 7 == 1      17 // 7 == 2      27 // 7 == 3
      8 // 7 == 1      18 // 7 == 2      28 // 7 == 4
      9 // 7 == 1      19 // 7 == 2      29 // 7 == 4
                                          30 // 7 == 4

The answer is simple. The first seven days, 1 to 7, of each month are the first weekdays of that month. They have to be! It does not matter which day of week starts the week, or which weekday the first day of the month might be.

The next seven days, 8 to 14, are the second occurrences of their respective weekdays. The next seven days, 15 to 21, are the third occurrences. The next, 22 to 28, the fourth. Obviously the 29 to 31 are the fifth occurrences.

Now, if you read the two paragraphs above, and look at the integer division table above, the answer should be obvious. Substract one from the day-of-month, integer divide by seven, and add one, and you have your desired result, 1 to 5.

Or, you could even use a fixed list (remembering that Python starts list indices from zero):
Code:

dayname = [ 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday' ]

nthname = [ 'none',
            'first',  'first',  'first',  'first',  'first',  'first',  'first',
            'second', 'second', 'second', 'second', 'second', 'second', 'second',
            'third',  'third',  'third',  'third',  'third',  'third',  'third',
            'fourth', 'fourth', 'fourth', 'fourth', 'fourth', 'fourth', 'fourth',
            'fifth',  'fifth',  'fifth' ]

import datetime

theday = datetime.date.today()

print 'This is the %s %s of this month.' % (nthname[theday.day], dayname[theday.weekday()])

Hope this helps,


All times are GMT -5. The time now is 03:29 AM.