LinuxQuestions.org
Latest LQ Deal: Linux Power User Bundle
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 04-10-2011, 08:30 PM   #16
Nominal Animal
Senior Member
 
Registered: Dec 2010
Location: Finland
Distribution: Xubuntu, CentOS, LFS
Posts: 1,723
Blog Entries: 3

Rep: Reputation: 946Reputation: 946Reputation: 946Reputation: 946Reputation: 946Reputation: 946Reputation: 946Reputation: 946

My approach differs a bit from wje_lq's. My workdays() takes the two dates as unix timestamps (time_t), and a 7-bit mask describing which days of week are work days. The function returns the number of work days between the two dates in the local time zone, inclusive. My version is thread-safe, and does not modify the environment (or locale or time zone) during the calculation. It does expect the time zone not to change while it runs, though.

My workdays() function makes the following assumptions:
  • localtime_r() converts a time_t to struct tm in the local timezone, and sets the tm_wday field to 0 for Sunday, 1 for Monday, .., 6 for Saturday
    (localtime_t() is also thread-safe, whereas localtime() is not.)
  • mktime() converts a struct tm in local timezone to time_t
  • difftime() returns the number of seconds between two time_t timestamps, and it is about 86400 seconds for each day
    (In any interval, there should be less than 28800 seconds (eight hours) of drift, including DST and other adjustments)
The logic in the calculation is simple: Calculate the number of days in the initial fractional week (zero if the interval is full weeks), plus the number of weeks in the interval multiplied by the number of workdays in a week.

The initial fractional week is best described using a bit pattern, since the work week is also described using a bit pattern. The left side in the & expression is a string of one bits the fractional week long, and the right side is the work week adjusted to start from the initial date in the interval. (Since the string of one bits is at most 6 bits long, it does not matter if there are a few extra bits in the right side.) The result is the correct bit pattern for the initial fractional week, and it will be empty (zero) if the interval is full weeks.

I've only tested the code lightly, so there may still be some bugs lurking in it.

Code:
#define  _XOPEN_SOURCE
#include <time.h>
#include <stdint.h>
#include <stdio.h>

/* These are the bit masks for workdays().
*/
#define   SUNDAY     (1U)
#define   MONDAY     (2U)
#define   TUESDAY    (4U)
#define   WEDNESDAY  (8U)
#define   THURSDAY  (16U)
#define   FRIDAY    (32U)
#define   SATURDAY  (64U)

/* This is the constant for Monday to Friday work week.
*/
#define   MON_FRI   (62U)

/* Return the number of bits set in mask, AKA __builtin_popcount().
*/
static inline uint32_t popcount(uint32_t value)
{
    uint32_t const  mask1 = 0x55555555;
    uint32_t const  mask2 = 0xc30c30c3;

    value -= (value >> 1U) & mask1;
    value  = (value & mask2)
           + ((value >> 2U) & mask2)
           + ((value >> 4U) & mask2);
    value += (value >> 6U);

    return (value + (value >> 12U) + (value >> 24U)) & 63U;
}

/* Return the number of workdays between the two dates, inclusive.
 * This uses the current time zone settings.
*/
int workdays(time_t from_time, time_t to_time, unsigned int const work_week)
{
    uint32_t const      week = (uint32_t)(work_week & 127U);
    uint32_t const      fortnight = week | (week << 7U);
    struct tm           from_tm, to_tm;
    int                 days;

    if (from_time < to_time) {
        localtime_r(&from_time, &from_tm);
        localtime_r(&to_time, &to_tm);
    } else {
        localtime_r(&from_time, &to_tm);
        localtime_r(&to_time, &from_tm);
    }

    /* Use 8 hours of buffer in any direction */
    from_tm.tm_hour  = 8;  to_tm.tm_hour  = 16;
    from_tm.tm_min   = 0;  to_tm.tm_min   =  0;
    from_tm.tm_sec   = 0;  to_tm.tm_sec   =  0;
    from_tm.tm_isdst = 0;  to_tm.tm_isdst =  0;

    from_time = mktime(&from_tm);
    to_time = mktime(&to_tm);

    days = 1 + (int)(difftime(to_time, from_time) / 86400.0);

    return (int)popcount( (127U >> (7 - (days % 7))) & (fortnight >> from_tm.tm_wday) )
         + (int)popcount( week ) * (int)(days / 7);
}

int local_noon(char const *date, time_t *const where)
{
    struct tm   tm;

    if (!date) {
        if (where) {
            time_t  now = time(NULL);
            localtime_r(&now, &tm);
            tm.tm_hour  = 12;
            tm.tm_min   = 0;
            tm.tm_sec   = 0;
            tm.tm_isdst = -1;
            *where = mktime(&tm);
        }
        return 0;
    }

    if (strptime(date, "%Y-%m-%d", &tm) ||
        strptime(date, "%Y%m%d", &tm) ||
        strptime(date, "%m/%d/%Y", &tm) ||
        strptime(date, "%d.%m.%Y", &tm)) {
        if (where) {
            tm.tm_hour  = 12;
            tm.tm_min   = 0;
            tm.tm_sec   = 0;
            tm.tm_isdst = -1;
            *where = mktime(&tm);
        }
        return 0;
    }

    return -1;
}

int main(int argc, char *argv[])
{
    time_t      time1, time2;

    if (argc < 2 || argc > 3) {
        fprintf(stderr, "Usage: yyyy-mm-dd [ yyyy-mm-dd ]\n");
        return (argc == 1) ? 0 : 1;
    }

    if (local_noon(argv[1], &time1)) {
        fprintf(stderr, "%s: Invalid date.\n", argv[1]);
        return 1;
    }
    if (argc > 2) {
        if (local_noon(argv[2], &time2)) {
            fprintf(stderr, "%s: Invalid date.\n", argv[2]);
            return 1;
        }
    } else
        local_noon(NULL, &time2);

    printf("%d\n", workdays(time1, time2, MON_FRI));

    return 0;
}
 
Old 04-10-2011, 11:04 PM   #17
ted_chou12
Member
 
Registered: Aug 2010
Location: Zhongli, Taoyuan
Distribution: slackware, windows, debian (armv4l GNU/Linux)
Posts: 430
Blog Entries: 28

Rep: Reputation: 2
Quote:
Originally Posted by paulsm4 View Post
wge_lq -


No. Think about it!

Why *not* define a "day" as a "period of time lasting exactly 86400 seconds"? It all depends on exactly what we're trying to measure. Often times, it's better ("more accurate", "more meaningful", "more precise") to NOT get caught up in minutia like "leap seconds" and "daylight savings time".

The "correct answer" to ANY question is seldom "yes" or "no". It's usually "It depends"
I was going to have a try at this b4, but when I thought about this and some other uncertainties, eg. in the first post, he didnt count the day started on. I gave up. Probably need more information to go with afterall.
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Havin a Tough Time With linux? Manish87 Linux - Newbie 5 06-30-2010 01:55 PM
Subnetting tough time seeing it. Class B example. networkingnub Linux - Networking 3 07-19-2009 11:13 PM
having a super tough time getting my tv tuner to work WickedAwsome Linux - Hardware 5 06-16-2006 05:55 PM
Having a tough time with K-shell pipeline pjz Programming 4 03-24-2005 04:05 PM
I'm having a tough time communcating through a serial port using java. Cobra133 Linux - Software 1 06-28-2004 10:34 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 08:58 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration