• (unregistered)

    <FONT style="BACKGROUND-COLOR: #efefef">The least thing that could have been done is to start daysInMonth at 28.  </FONT>

  • (cs) in reply to

    My shortcut, when a library function is unavailable, is to go to the first of the next month and subtract a day and check the day number.... (And yes, I handle december correctly. :)


  • (cs)

    Doin' it the Angry way...

     

    CREATE FUNCTION dbo.DaysInMonth
     (
     @iYear smallint, @iMonth tinyint
     )
    RETURNS tinyint
    AS


    BEGIN
     DECLARE @iDays tinyint
     DECLARE @vDate varchar(10), @dtDate smalldatetime
     SET @vDate = CONVERT(varchar(4), @iYear) + '-' + RIGHT('00' + CONVERT(varchar(2), @iMonth), 2) + '-01'

     IF ISDATE(@vDate) < 1
      BEGIN
       /*Undefined*/
       SET @iDays = NULL
      END
     ELSE
      BEGIN
       SET @dtDate = CONVERT(smalldatetime, @vDate)
       SET @iDays = CONVERT(tinyint, DATEDIFF(day, @dtDate, DATEADD(month, 1, @dtDate)))
      END

     RETURN @iDays

    END

  • (cs) in reply to Mr. Angry DBA

    Pretty cool, angry... I like the month handling "RIGHT('00'" part, that's a nice solution.  Was kinda suprised not to see something similar with the @iYear...

    Of course, since you're checking with ISDATE anyway you could probably skip that step..




  • (unregistered)

    $daysInMonth=1;

    Where does this constant 1 come from? You may as well choose any number in the range 1 through 28 for this value. The only difference is the running time.

  • (cs) in reply to Mr. Angry DBA

    Blue,

    See what happens when you hurry and type stuff up...

    Nice catch; I meant to have it on both. Not formatting the year properly leaves open numerous "new" WTF's...such as how should "4-02-01" be interpreted...LOL

    Here's the amended version...

    CREATE FUNCTION dbo.DaysInMonth
     (
     @iYear smallint, @iMonth tinyint
     )
    RETURNS tinyint
    AS


    BEGIN
     DECLARE @iDays tinyint
     DECLARE @vDate varchar(10), @dtDate smalldatetime
     SET @vDate = RIGHT('0000' + CONVERT(varchar(4), @iYear), 4) + '-' + RIGHT('00' + CONVERT(varchar(2), @iMonth), 2) + '-01'

     IF ISDATE(@vDate) < 1
      BEGIN
       /*Undefined*/
       SET @iDays = NULL
      END
     ELSE
      BEGIN
       SET @dtDate = CONVERT(smalldatetime, @vDate)
       SET @iDays = CONVERT(tinyint, DATEDIFF(day, @dtDate, DATEADD(month, 1, @dtDate)))
      END

     RETURN @iDays

    END

     

  • (unregistered)

    Here's my way :

    (it's in Java, but its easy to code in whatever)

    The month here is zero based... That's kind of odd, but that's the Java API way...

    [code language="c#"]
    /**
    * Returns the actual maximum number of days in a month (0 based).
    *
    * @param iYear the Year
    * @param izMonth the zero based month number
    * @return the maximum number of days for the month
    */
    public static int getMaximumDays(int iYear, int izMonth) {
    int daysInMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    int i = daysInMonth[izMonth];

    if (isLeapYear(iYear) && i == 28) i++; // Feb 29

    return i;
    }

    /**
    * Returns true if the year is a leap year.
    *
    * @param year the year
    * @return true if the year is a leap year, false otherwise
    */
    public static final boolean isLeapYear(int year) {
    if ((year % 100) == 0)
    return ((year % 400) == 0);

    return ((year % 4) == 0);
    }
    [/code]

  • (unregistered) in reply to Mr. Angry DBA
    Mr. Angry DBA:

    Blue,

    See what happens when you hurry and type stuff up...

    Nice catch; I meant to have it on both. Not formatting the year properly leaves open numerous "new" WTF's...such as how should "4-02-01" be interpreted...LOL

    Here's the amended version...

    CREATE FUNCTION dbo.DaysInMonth
     (
     @iYear smallint, @iMonth tinyint
     )
    RETURNS tinyint
    AS


    BEGIN
     DECLARE @iDays tinyint
     DECLARE @vDate varchar(10), @dtDate smalldatetime
     SET @vDate = RIGHT('0000' + CONVERT(varchar(4), @iYear), 4) + '-' + RIGHT('00' + CONVERT(varchar(2), @iMonth), 2) + '-01'

     IF ISDATE(@vDate) < 1
      BEGIN
       /*Undefined*/
       SET @iDays = NULL
      END
     ELSE
      BEGIN
       SET @dtDate = CONVERT(smalldatetime, @vDate)
       SET @iDays = CONVERT(tinyint, DATEDIFF(day, @dtDate, DATEADD(month, 1, @dtDate)))
      END

     RETURN @iDays

    END

     



    Or you could just use (assuming it's T-SQL):

    CREATE FUNCTION dbo.DaysInMonth
     (
     @iYear smallint, @iMonth tinyint
     )
    RETURNS tinyint
    AS BEGIN
    RETURN DATEPART(day, DATEADD(year, @iYear - 1900, DATEADD(month, @iMonth, -1)))
    END

  • (unregistered)

    sigh

    You mean start at 29.  28 always returns true and thus is a useless test.  Of course, other elements would need to be adjusted.

  • (cs) in reply to

    Anonymous,

    "You're fired!".

    Your code is totally broken, and if I have to tell you why, your employer is probably the one crying.

    The cutesy solution is not always best.[N]

    Mr. Angry DBA

     

  • (cs)

    int days_in_month( int month, int year )  /* month goes from 1 to 12 */
        {
        const long long c = 1151794505154789279;

        return ( c >> ( ( month - 1 ) * 5 ) ) & 0x1f +
               ( month == 2 && is_leap_year( year ) ? 1 : 0 );
        }

  • (cs)

    I really like the original code - sure, it loops a lot, but it's only gonna loop about 30 times, which is peanuts (how long can checkdate take, anyway?) And it's trivial to see what it's doing. It took about 2 minutes to write, and there's no reason to change it unless the profiler indicates it's a performance problem, which I doubt unless it turns out you're calling it in a tight loop elsewhere - in which case the correct solution might even be to hoist it out of the loop instead of having to rewrite the function.

  • (cs) in reply to JoeNotCharles

    JoeNotCharles:
    I really like the original code - sure, it loops a lot, but it's only gonna loop about 30 times, which is peanuts (how long can checkdate take, anyway?) And it's trivial to see what it's doing. It took about 2 minutes to write, and there's no reason to change it unless the profiler indicates it's a performance problem, which I doubt unless it turns out you're calling it in a tight loop elsewhere - in which case the correct solution might even be to hoist it out of the loop instead of having to rewrite the function.

    LOL. [:D]

    Yeah, this makes sense. Especially since the first 28 iterations are pointless.

    Mr. Angry DBA

  • (cs) in reply to jef

    jef:
    int days_in_month( int month, int year )  /* month goes from 1 to 12 */
        {
        const long long c = 1151794505154789279;

        return ( c >> ( ( month - 1 ) * 5 ) ) & 0x1f +
               ( month == 2 && is_leap_year( year ) ? 1 : 0 );
        }

    Interesting method, but false economy.

    const long long c = 1151794505154789279;   takes 8 bytes.

    const char c[12] = {31,28,31,30,31,30,31,31,30,31,30,31}; takes 12, but...

    return ( c >> ( ( month - 1 ) * 5 ) ) & 0x1f + ....

    will be at least 4 bytes longer than

    return (c[month-1] + ...)

    In fact, subtracting 1 from month, probably takes more than one byte, so the best, in both size and speed (and possibly readability) would be:

    const char c[12] = {0, 31,28,31,30,31,30,31,31,30,31,30,31};

    return (c[month] + ...)

  • (cs) in reply to Mr. Angry DBA

    jef,

    I'm too lazy to load your code into the IDE and run it, but damn.... If it does work, you've gone to the extreme of brevity at the expense of maintainability for those that come after you!


  • (cs)

    And shouldn't that be "return $daysInMonth -1;".  As it is, it should return one higher than the number of days in the month.

  • (unregistered) in reply to

    <font face="Lucida Console, Courier" size="2">public static int getMaximumDays(int iYear, int izMonth) {
    int daysInMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    </font>

    Make that array a private static array in the class and it won't have to be defined every time you execute this method.

  • (unregistered) in reply to JamesCurran
    JamesCurran:

    [image] jef wrote:
    int days_in_month( int month, int year )  /* month goes from 1 to 12 */
        {
        const long long c = 1151794505154789279;

        return ( c >> ( ( month - 1 ) * 5 ) ) & 0x1f +
               ( month == 2 && is_leap_year( year ) ? 1 : 0 );
        }

    Interesting method, but false economy.

    const long long c = 1151794505154789279;   takes 8 bytes.

    const char c[12] = {31,28,31,30,31,30,31,31,30,31,30,31}; takes 12, but...

    return ( c >> ( ( month - 1 ) * 5 ) ) & 0x1f + ....

    will be at least 4 bytes longer than

    return (c[month-1] + ...)

    In fact, subtracting 1 from month, probably takes more than one byte, so the best, in both size and speed (and possibly readability) would be:

    const char c[12] = {0, 31,28,31,30,31,30,31,31,30,31,30,31};

    return (c[month] + ...)

    As a matter of fact, your zero-padded array is 13 bytes long, so it should be c[13]... [8-|]
  • (cs) in reply to

    :
    As a matter of fact, your zero-padded array is 13 bytes long, so it should be c[13]... [8-|]

    OOOps... You're right... I was thinking that I had to change it when I did the copy'n'paste, but apparently I forgot.......

     

  • (unregistered) in reply to Mr. Angry DBA
    Mr. Angry DBA:

    Anonymous,

    "You're fired!".

    Your code is totally broken, and if I have to tell you why, your employer is probably the one crying.

    The cutesy solution is not always best.No

    Mr. Angry DBA

     

  • (unregistered) in reply to Mr. Angry DBA
    Mr. Angry DBA:

    Anonymous,

    "You're fired!".

    Your code is totally broken, and if I have to tell you why, your employer is probably the one crying.

    The cutesy solution is not always best.No

    Mr. Angry DBA

     

    SET DATEFORMAT ymd
    SELECT dbo.DaysInMonth(2005, 2) -- Your version, not mine
    -- 28

    SET DATEFORMAT ydm
    SELECT dbo.DaysInMonth(2005, 2)
    -- 31

  • (unregistered)

    This is already built into PHP anyway and can be done in one line:

    $days_in_month = date('t',mktime(0,0,0,$month,1,$year));

  • (cs) in reply to jef

    jef:
    int days_in_month( int month, int year )  /* month goes from 1 to 12 */
        {
        const long long c = 1151794505154789279;

        return ( c >> ( ( month - 1 ) * 5 ) ) & 0x1f +
               ( month == 2 && is_leap_year( year ) ? 1 : 0 );
        }

    WTF! This has to be some of the least readable code I've ever seen. The original WTF was retarded, but at least you knew what it was doing. Congratulations, jef, because wherever you're working, you have acheived total job security. [:D]

     

  • (cs) in reply to
    :
    [image] Mr. Angry DBA wrote:

    Anonymous,

    "You're fired!".

    Your code is totally broken, and if I have to tell you why, your employer is probably the one crying.

    The cutesy solution is not always best.No

    Mr. Angry DBA

     

    SET DATEFORMAT ymd
    SELECT dbo.DaysInMonth(2005, 2) -- Your version, not mine
    -- 28

    SET DATEFORMAT ydm
    SELECT dbo.DaysInMonth(2005, 2)
    -- 31

    If was referring to the fact that your code returns integer values between 28 and 31 for months that do not exist, such as (2004, 17).

    An option such as the interpretation if string literals into dates is usually a system-wide decision made at design time, and functions will be written with this in mind as a matter of course. It is generally not something that is 'tweaked' on each method call. Even so, I didn't have to toggle any option bits to break your algorithm, so...what month is 17, anyhow?

    Mr. Angry DBA

     

  • (cs)

    Behold, a friend of mine worked out a way to do this entirely in bitshifting. The leap year handling could be improved, and two months are wrong, but I'm sure a few more tweaks would make it work.

     #define BIT(value, x) ((value & (1 << x)) >> x)
      
      int num_days(int m) {
        int days;
        days = (((BIT(m,3) ^ BIT(m,2)) & BIT(m,1) & BIT(m,0)) | (!BIT(m,3) & BIT(m,2) & !BIT(m,0))) ? 30 : 31;
        days = m == 2 ? 28 : days;
        return days;
      }

    btw, has anyone run the original function? It always returns one more day than the month has! You need to subtract one somewhere every time. (Maybe it was made to be more like java's Calendar wtfs.)

  • (unregistered) in reply to Mr. Angry DBA
    Mr. Angry DBA:

    If was referring to the fact that your code returns integer values between 28 and 31 for months that do not exist, such as (2004, 17).

    An option such as the interpretation if string literals into dates is usually a system-wide decision made at design time, and functions will be written with this in mind as a matter of course. It is generally not something that is 'tweaked' on each method call. Even so, I didn't have to toggle any option bits to break your algorithm, so...what month is 17, anyhow?

    Mr. Angry DBA

    Point taken - it doesn't validate input. My revised version:

    <!--StartFragment --> CREATE FUNCTION dbo.DaysInMonth
     (
     @iYear smallint, @iMonth tinyint
     )
    RETURNS tinyint
    AS BEGIN
    RETURN CASE WHEN @iMonth <= 12 THEN
      DATEPART(day, DATEADD(year, @iYear - 1900, DATEADD(month, @iMonth, -1)))
    END
    END

    It's my personal opinion that:

    1. A task this simple shouldn't require 27 lines of code

    2. If you have integers and have to use string operations to turn them into a date, there's something missing in the language. A DateSerial function like what Access/VBA have would be useful here.

    You obviously disagree, which is fine. My revised function still doesn't handle invalid year values while yours does, so at least in that respect it has an advantage.

  • (cs) in reply to
    :
    <FONT face="Lucida Console, Courier" size=2>public static int getMaximumDays(int iYear, int izMonth) {
    int daysInMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    </FONT>


    Make that array a private static array in the class and it won't have to be defined every time you execute this method.

    int year;
    int month;

    year = 2005;
    month = 2;

    Console.WriteLine(DateTime.DaysInMonth(year, month).ToString());

    why do you guys make it so complicated?  

     

  • (cs)

    Mr. Angry DBA -- the quick 1 line solution is the best solution in T-SQL.  you can add a check to ensure months from 1-12 pretty easily if you wish, obviously.  Your method, as demonstrated, relies on an implicit datetime conversion that is dependant on the sytem setting and is overall much longer and more complex than it needs to be.

    Also, it is often desirable (depending on the specs) to allow for function to handle months <1 or >12.  (i.e., the DateSerial() function in VB).  This greatly simplifies "date math" to not worry about crossing year boundaries -- you can safely add 6 to a particular month to get that date plus 6 months w/o special handling for months after June.

  • (cs) in reply to

    hey Anonymous -- sign up for an account so we know which posts are yours! (or sign them).    As stated in my last post, I completely 100% agree with your method.  (my blog has very similiar examples)

    Very often in computer science, the simpliest solution is the best.

  • (cs) in reply to KraGiE

    Obviously, if there is a library available which already has the function, then it makes sense to use it.   Kind of implied.  But then again, after seeing all the WTF's at this site (both the highlighted items and about 75% of the responses) you can't take anything for granted, I suppose! [:(]

  • (cs) in reply to Jeff S

    Jeff & the rest,

    It's always best to know the benefits and limits of the language and technology you choose for any task.  If you don't have a choice, and you're stuck using that language or technology, it's best to research as much as possible so you're more familar with it. 

    But these WTF's are funny.  Makes my day go by so much better.

    Kay

  • (unregistered)

    In .NET it's simple:

    System.Globalization.Calendar.GetDaysInMonth

    You can even localize it for specific calendars supported in .NET.
    System.Globalization.TaiwanCalendar
    System.Globalization.KoreanCalendar
    System.Globalization.HebrewCalendar
    System.Globalization.HijriCalendar
    System.Globalization.ThaiBuddhistCalendar

    .NET 2.0 has some new fancy calendars.

    The only reason I know is my company is developing a product to calc holidays all arround the world.

    Schneider

  • (cs)

    Of course, any method which can't handle months outside the range 1..12 will have problems when you're dealing with a company that uses 13-month years.

     

  • (unregistered) in reply to FrostCat

    Can you explain 13-month issue more? Is this a business logic or localization issue?

    Schneider

  • (unregistered)

    I'm one of those anonymous guys who doesn't want to get clowned.

    I have absolutely nothing to contribute to this conversation...but I wish I did. This stuff is way over my head.

    I don't even understand this particular WTF.

    How embarassing is that?

    I just felt like posting and taking up space in this forum.

  • (cs) in reply to FrostCat
    FrostCat:

    Of course, any method which can't handle months outside the range 1..12 will have problems when you're dealing with a company that uses 13-month years.

    We're talking calendar months, obviously ... this is a function to return the # of days in a month!   What does that have to do with accounting periods ??

  • (unregistered)

    To the anonymous poster who doesn't understand the WTF:

    When you visit this site and look at the code, do you understand any of it? I mean, does your mind start to swim and your thoughts get cloudy when you see a line of code?

    It really shouldn't hurt your brain to look at code and dissect it for its meaning. Most of the programmers in here like to take apart code and see how it works.

    Based on your comments, it sounds like you weren't meant to program. You sound like the type of person who would find for loops "confusing"...

    If you search the internet you may be able to find some kiddie programmer sites to visit and contribute to. Some where you won't get clowned on.

    Cause you sure clowned yourself here! hahaha

  • (cs)

    Not only is this function brain-dead, it is wrong. Off by one - unless it is the case that checkdate uses a zero-based day, which is unlikely.

    Consider July.

    Checkdate Jul-31-2005 succeeds, so one gets added to $daysInMonth.

    Checkdate Jul-32-2005 fails. The loop quits, and 32 is returned as the number of days in July. Wrong.

  • (unregistered)

    The VB6 way:

    Public Function DaysInMonth(ByVal Year As Integer, ByVal Month As Integer) As Integer
        DaysInMonth = Day(DateSerial(Year, Month + 1, 0))
    End Function

  • (unregistered) in reply to
    :

    The VB6 way:

    Public Function DaysInMonth(ByVal Year As Integer, ByVal Month As Integer) As Integer
        DaysInMonth = Day(DateSerial(Year, Month + 1, 0))
    End Function

    But that's VB!  It must be a WTF!  Everybody knows VB is shit and people who use it are morons!  Oh, wait, my mistake...

    (Man, this forum software bites ass.)

  • (cs) in reply to jef
    jef:
    int days_in_month( int month, int year )  /* month goes from 1 to 12 */
        {
        const long long c = 1151794505154789279;

        return ( c >> ( ( month - 1 ) * 5 ) ) & 0x1f +
               ( month == 2 && is_leap_year( year ) ? 1 : 0 );
        }



    I think 24 bits should be enough information for this one.
    We could write this easily and a bit more unreadable something like this:

    int days_of_month(int m, int y) {
      return 28+((15662003+((!(y%4)&&y%100)||!(y%400)?4:0)>>(2*(m-1)))&3);
    }

  • (unregistered) in reply to tipu
    tipu:
    [image] jef wrote:
    int days_in_month( int month, int year )  /* month goes from 1 to 12 */
        {
        const long long c = 1151794505154789279;

        return ( c >> ( ( month - 1 ) * 5 ) ) & 0x1f +
               ( month == 2 && is_leap_year( year ) ? 1 : 0 );
        }



    I think 24 bits should be enough information for this one.
    We could write this easily and a bit more unreadable something like this:

    int days_of_month(int m, int y) {
      return 28+((15662003+((!(y%4)&&y%100)||!(y%400)?4:0)>>(2*(m-1)))&3);
    }

  • (unregistered)

     *weeping*  [:'(]  This is my fifth time trying to post my comment.  Trying in IE this time...

    Why use 2* at this point?  Might as well use <<1

    int days_of_month(int m, int y) {
      return 28+((15662003+((!(y%4)&&y%100)||!(y%400)?4:0)>>((m-1)<<1))&3);
    }

  • (unregistered) in reply to

    I'm not even sure this is right, but... here's my.... contribution. [6]

    int days_of_month(int m, int y) {
      return m==2?y%4&&!(y%100)||y%400?29:28:30+(5550>>m&1);
    }

  • (unregistered) in reply to

    oh... assuming a 1 to 12 value for month.

  • (unregistered)

    Uh...does the original wtf code even work? I mean - if I'm not mistaken, it checks the given date, then increments the day until the check fails. That means, after the loop, $dayInMonth equals the first day that does not pass the check, which should be one more than the month actually has. Does checkdate expect 2005,0,0 for Jan 1st, 2005 or would this function actually claim that January has 32 days?

  • (unregistered) in reply to JamesCurran
    JamesCurran:

    And shouldn't that be "return $daysInMonth -1;".  As it is, it should return one higher than the number of days in the month.

     

    That's what I thought too. Nonsense code with a wrong result :-)

  • (cs)

    Guh, foon and anonymous, I already mentioned that - it's not so much an error as a design flaw, similar to how you have to add one to java's month to get a useful number. How on earth the creator decided that subtracting one from every single instance of the function was easier than returning $daysInMonth - 1; I'll never know.

    I am, possibly incorrectly, assuming that its clients aren't trying to pass off 32-day months to the world. That wtf would awe me. ("We can't figure out where the error is. Just mentally subtract one when you see the dates.")

  • (cs)

    Alex Papadimoulis:
    I wonder what the functional specifications discussion was like for debating whether or not to include date_sunrise() and date_sunset().

    Well, I imagine applications dealing with religious time spans (fasting, resting, etc.) would benefit greately from those functions, and I can also think of some scientific applications that could make use of those as well.

  • (unregistered)

    I was working at a local university some time ago, and a student developer in our employ had a very clever way of determining the number of days in the month.

    His logic was as follows (pseudo-code):

    if MonthNumber <=7 then
          if MonthNumber = 2 then
             if isLeapYear(Year) then return 29 else return 28
          else
             if isOdd(MonthNumber) then return 31 else return 30
    else
          if isOdd(MonthNumber) then return 30 else return 31

    The number of days flips between 31 and 30 depending upon whether the month is before or after July.

    In a way, the number of days in a month is now "programmatically hard-coded" into the application.

     

Leave a comment on “Looking for a Date”

Log In or post as a guest

Replying to comment #:

« Return to Article