17
\$\begingroup\$

I just had I genius idea for making the work-life easier - a countdown to a specific date which only counts workdays.


The basic task is to create a countdown to a specific date which only includes the workdays in the countdown.

As workday counts Monday, Tuesday, Wednesday, Thursday and Friday.

The Input should be a specific date in the "unofficial" European standard format dd.MM.yyyy and must be today or a day in the future.

The Output should only be the number of days left.

As it's the shortest code wins.


Test case:

  Today    |   Input    | Output
10.12.2018 | 17.12.2018 |    5
02.10.2018 | 16.10.2018 |   10

If I missed a few things in the question, please forgive me - it's my first question :)

EDIT:

  • You can use false as output instead of 0 <- but it's not beautiful
  • No need for respecting DST
\$\endgroup\$
  • 9
    \$\begingroup\$ Is there any specific reason behind this "unofficial" European input format? Our consensus is to allow flexible input whenever possible. \$\endgroup\$ – Arnauld Oct 1 '18 at 11:36
  • 6
    \$\begingroup\$ Is there really any point in adding the "extra challenge" of a hard to process date format? That just seems unfair w.r.t. languages that have flexible date formats... \$\endgroup\$ – Quintec Oct 1 '18 at 13:25
  • 3
    \$\begingroup\$ @Hille I did not say it was "hard", it just is an unnecessary hassle, especially in code-golf...do note the link that Arnauld posted above... generally flexible input is the norm... \$\endgroup\$ – Quintec Oct 1 '18 at 13:31
  • 6
    \$\begingroup\$ By the way, you note that this is your first challenge; I invite you to use The Sandbox for refinement before posting a challenge to main! Otherwise, nice work, and I'll enjoy seeing some more from you! \$\endgroup\$ – Giuseppe Oct 1 '18 at 16:49
  • 7
    \$\begingroup\$ Not really impressed by the strict input format but other than that a good challenge. \$\endgroup\$ – ElPedro Oct 1 '18 at 19:52

25 Answers 25

18
\$\begingroup\$

05AB1E, 130 128 133 131 124 123 bytes

žežfžg)V0[Y`UÐ3‹12*+>13*5÷s3‹Xα©т%D4÷®т÷©4÷®·()ćsO7%2@+Y`т‰0Kθ4ÖUD2Qi\28X+ë<7%É31α}‹iY¬>0ëY1¾ǝDÅsD12‹i>1ë\1Dǝ¤>2}}ǝVYI'.¡Q#

I'm out of my mind..

For the golfing language 05AB1E it doesn't matter at all whether the input is with . or -. However, 05AB1E doesn't have any builtins for Date objects or calculations. The only builtin regarding dates it has is today's year/month/day/hours/minutes/seconds/microseconds.

So because of that, almost all of the code you see are manual calculations to go to the next day, and calculating the day of the week.

+5 bytes due to a part I forgot in Zeller's formula (year-1 for months January and February)..

Try it online or Try it online with an emulated self-specified date of 'today'.

Explanation:

Wall of text incoming.

In general, the code follows the following pseudo-code:

1   Date currentDate = today;
2   Integer counter = 0;
3   Start an infinite loop:
4*    If(currentDate is NOT a Saturday and currentDate is NOT a Sunday):
5       Counter += 1;
6*    currentDate += 1; // Set currentDate to the next day in line
7     If(currentDate == parsed input-string):
8       Stop the infinite loop, and output the counter

1) Date currentDate = today; is this part of the 05AB1E program:

že          # Push today's day
  žf        # Push today's month
    žg      # Push today's year
      )     # Wrap them into a single list
       V    # Pop and store this list in variable `Y`

2) Integer counter = 0; and 3) Start an infinite loop: are straight-forward in the 05AB1E program:

0     # Push 0 to the stack
 [    # Start an infinite loop

4) If(currentDate is NOT a Saturday and currentDate is NOT a Sunday): is the first hard part with manual calculations. Since 05AB1E has no Date builtins, we'll have to calculate the Day of the Week manually.

The general formula to do this is:

$${\displaystyle h=\left(q+\left\lfloor {\frac {13(m+1)}{5}}\right\rfloor +K+\left\lfloor {\frac {K}{4}}\right\rfloor +\left\lfloor {\frac {J}{4}}\right\rfloor -2J\right){\bmod {7}},}$$

Where for the months March through December:

  • \$q\$ is the \$day\$ of the month ([1, 31])
  • \$m\$ is the 1-indexed \$month\$ ([3, 12])
  • \$K\$ is the year of the century (\$year \bmod 100\$)
  • \$J\$ is the 0-indexed century (\$\left\lfloor {\frac {year}{100}}\right\rfloor\$)

And for the months January and February:

  • \$q\$ is the \$day\$ of the month ([1, 31])
  • \$m\$ is the 1-indexed \$month + 12\$ ([13, 14])
  • \$K\$ is the year of the century for the previous year (\$(year - 1) \bmod 100\$)
  • \$J\$ is the 0-indexed century for the previous year (\$\left\lfloor {\frac {year-1}{100}}\right\rfloor\$)

Resulting in in the day of the week \$h\$, where 0 = Saturday, 1 = Sunday, ..., 6 = Friday.
Source: Zeller's congruence

We can see this in this part of the 05AB1E program:

Y             # Push variable `Y`
 `            # Push the day, month, and year to the stack
  U           # Pop and save the year in variable `X`
   Ð          # Triplicate the month
    3‹        # Check if the month is below 3 (Jan. / Feb.),
              # resulting in 1 or 0 for truthy/falsey respectively
      12*     # Multiply this by 12 (either 0 or 12)
         +    # And add it to the month
              # This first part was to make Jan. / Feb. 13 and 14

>             # Month + 1
 13*          # Multiplied by 13
    5÷        # Integer-divided by 5
s3‹           # Check if the month is below 3 again (resulting in 1 / 0)
   Xα         # Take the absolute difference with the year
     ©        # Store this potentially modified year in the register
      т%      # mYear modulo-100
D4÷           # mYear modulo-100, integer-divided by 4
®т÷©4÷        # mYear integer-divided by 100, and then integer-divided by 4
®·(           # mYear integer-divided by 100, doubled, and then made negative
)             # Wrap the entire stack into a list
 ć            # Extract the head (the counter variable that was also on the stack)
  s           # Swap so the calculated values above are as list at the top
   O          # Take the sum of this entire list
    7%        # And then take modulo-7 to complete the formula,
              # resulting in 0 for Saturday, 1 for Sunday, and [2, 6] for [Monday, Friday]

2@            # Check if the day is greater than or equal to 2 (so a working day)

5) Counter += 1; is straight-forward again:

     # The >=2 check with `2@` results in either 1 for truthy and 0 for falsey
+    # So just adding it to the counter variable is enough

6) currentDate += 1; // Set currentDate to the next day in line is again more complex, because we have to do it manually. So this will be expanded to the following pseudo-code:

a   Integer isLeapYear = ...;
b   Integer daysInCurrentMonth = currentDate.month == 2 ?
c                                 28 + isLeapYear
d                                :
e                                 31 - (currentDate.month - 1) % 7 % 2;
f   If(currentDate.day < daysInCurrentMonth):
g     nextDate.day += 1;
h   Else:
i     nextDate.day = 1;
j     If(currentDate.month < 12):
k       nextDate.month += 1;
l     Else:
m       nextDate.month = 1;
n       nextDate.year += 1;

Sources:
Algorithm for determining if a year is a leap year. (EDIT: No longer relevant, since I use an alternative method to check leap years which saved 7 bytes.)
Algorithm for determining the number of days in a month.

6a) Integer isLeapYear = ...; is done like this in the 05AB1E program:

Y             # Push variable `Y`
 `            # Push the days, month and year to the stack
  т‰          # Divmod the year by 100
    0K        # Remove all items "00" (or 0 when the year is below 100)
      θ       # Pop the list, and leave the last item
       4Ö     # Check if this number is visible by 4
         U    # Pop and save the result in variable `X`

Also used in this 05AB1E answer of mine, so there some example years are added to illustrate the steps.

6b) currentDate.month == 2 ? and 6c) 28 + isLeapYear are done like this:

D            # Duplicate the month that is now the top of the stack
 2Q          # Check if it's equal to 2
   i         # And if it is:
    \        #  Remove the duplicated month from the top of the stack
     28X+    #  Add 28 and variable `X` (the isLeapYear) together

6d) : and 6e) 31 - (currentDate.month - 1) % 7 % 2; are done like this:

ë           # Else:
 <          #  Month - 1
  7%        #  Modulo-7
    É       #  Is odd (shortcut for %2)
     31     #  Push 31
       α    #  Absolute difference between both
}           # Close the if-else

6f) If(currentDate.day < daysInCurrentMonth): is done like this:

‹     # Check if the day that is still on the stack is smaller than the value calculated
 i    # And if it is:

6g) nextDate.day += 1; is done like this:

Y       # Push variable `Y`
 ¬      # Push its head, the days (without popping the list `Y`)
  >     # Day + 1
   0    # Push index 0

        # (This part is done after the if-else clauses to save bytes)
}}      # Close the if-else clauses
  ǝ     # Insert the day + 1 at index 0 in the list `Y`
   V    # Pop and store the updated list in variable `Y` again

6h) Else: and 6i) nextDate.day = 1; are then done like this:

ë        # Else:
 Y       #  Push variable `Y`
  1      #  Push a 1
   ¾     #  Push index 0
    ǝ    #  Insert 1 at index 0 (days part) in the list `Y`

6j) If(currentDate.month < 12)::

D           # Duplicate the list `Y`
 Ås         # Pop and push its middle (the month)
   D12‹     # Check if the month is below 12
       i    # And if it is:

6k) nextDate.month += 1;:

>       # Month + 1
 1      # Push index 1

        # (This part is done after the if-else clauses to save bytes)
}}      # Close the if-else clauses
  ǝ     # Insert the month + 1 at index 1 in the list `Y`
   V    # Pop and store the updated list in variable `Y` again

6l) Else:, 6m) nextDate.month = 1; and 6n) nextDate.year += 1; are then done like this:

ë        # Else:
 \       #  Delete the top item on the stack (the duplicated month)
  1      #  Push 1
   D     #  Push index 1 (with a Duplicate)
    ǝ    #  Insert 1 at index 1 (month part) in the list `Y`

 ¤       #  Push its tail, the year (without popping the list `Y`)
  >      #  Year + 1
   2     #  Index 2

         # (This part is done after the if-else clauses to save bytes)
}}       # Close the if-else clauses
  ǝ      # Insert the year + 1 at index 2 in the list `Y`
   V     # Pop and store the updated list in variable `Y` again

And finally at 8) If(currentDate == parsed input-string): and 9) Stop the infinite loop, and output the counter:

Y          # Push variable `Y`
 I         # Push the input
  '.¡     '# Split it on dots
     Q     # Check if the two lists are equal
      #    # And if they are equal: stop the infinite loop
           # (And output the top of the stack (the counter) implicitly)
\$\endgroup\$
  • 5
    \$\begingroup\$ You're a madman ... have an upvote. \$\endgroup\$ – AdmBorkBork Oct 1 '18 at 17:54
  • 1
    \$\begingroup\$ Longest 05AB1E program ever? \$\endgroup\$ – Luis Mendo Oct 1 '18 at 18:31
  • 2
    \$\begingroup\$ @LuisMendo Close, but I'm afraid I have one 05AB1E answer myself that is even longer, and one that comes way too close.. ;) I'm sure I'll be able to golf a few bytes here an there and simplify parts of the pseudo-code implementation of the next day. Will look tomorrow morning, but just came back from sport and will go to bed soon. \$\endgroup\$ – Kevin Cruijssen Oct 1 '18 at 21:00
11
\$\begingroup\$

Excel 24 Bytes

Assumes input in Cell A1

=NETWORKDAYS(NOW()+1,A1)

Uses built-in function. Unfortunately the function includes both today and the end date. OP has since clarified to not count today, so I add one to NOW so as to not count today.

To address comments on format of number, again, this is Excel standard: enter image description here

\$\endgroup\$
  • \$\begingroup\$ While this works with date values, it fails to take input as stated. That is (at least in the US version) 10.12.2018 is a string when held in a cell rather than a date. The obvious but long solution to correct this would be to change A1 to DATE(RIGHT(A1,4),MID(A1,4,2),LEFT(A1,2)) in your solution \$\endgroup\$ – Taylor Scott Oct 1 '18 at 19:57
  • \$\begingroup\$ unfortunately, the community has decided that languages need to be run under their default settings to be valid (the one exception that I have seen to this is language - IE, if your language supports both English and Spanish, you may readily use either, but this must be noted.) Further, OP (@hille) has not stated that format is flexible, and has in fact stated quite the opposite (see second comment on this question) \$\endgroup\$ – Taylor Scott Oct 2 '18 at 18:40
  • 2
    \$\begingroup\$ The format is not standard, it's locale-based. Excel reads the format from the HKCU\Control Panel\International\sDecimal registry string. On a default US Windows installation that's MM/dd/yyyy. In most EU countries this would be the default. \$\endgroup\$ – Erik A Oct 3 '18 at 7:42
  • \$\begingroup\$ @luisMendo Yes, that works. I didn't see any clarification. If it had been to not count last day instead, I could have =NETWORKDAYS(NOW(),A1-1). I knew it would always be the same byte count no matter which clarification. \$\endgroup\$ – Keeta Oct 3 '18 at 16:02
  • \$\begingroup\$ Glad that works. I removed the downvote \$\endgroup\$ – Luis Mendo Oct 3 '18 at 16:28
11
\$\begingroup\$

R, 76 bytes

Please also have a look at this 72 byte R answer that is also locale-independent.

sum(!grepl("S",weekdays(seq(Sys.Date(),as.Date(scan(,""),"%d.%m.%Y"),1))))+1

Try it online!

weekdays gives text days of week, so we count the days in the sequence between today and the input that do not contain S, and add one to the result.

\$\endgroup\$
8
\$\begingroup\$

Java 10, 233 232 226 bytes

import java.util.*;d->{int r=0;var s=Calendar.getInstance();s.setTime(new Date());var e=s.getInstance();for(e.setTime(new java.text.SimpleDateFormat("dd.MM.yyyy").parse(d));!s.after(e);s.add(5,1))if(s.get(7)%7>1)r++;return r;}

Date always reminds me how verbose Java really is..

NOTE: There are now two shorter Java answers (below 175 bytes), one with smart use of deprecated methods from earlier Java versions by @LukeStevens, and one using the java.time.LocalDate that's new since Java 8 by @OlivierGrégoire.

Try it online.

Explanation:

import java.util.*;            // Required import for both Calendar and Date
d->{                           // Method with String parameter and integer return-type
  int r=0;                     //  Result-integer, starting at 0
  var s=Calendar.getInstance();//  Create a Calendar instance for the start-date
  s.setTime(new Date());       //  Set the start date to today
  var e=s.getInstance();       //  Create a Calendar instance for the end-date
  for(e.setTime(               //  Set the end date to:
        new java.text.SimpleDateFormat("dd.MM.yyyy")
                               //   Create a formatter for the "dd.MM.yyyy" format
         .parse(d));           //   And parse the input-String to a Date
      !s.after(e)              //  Loop as long as we haven't reached the end date yet
      ;                        //    After every iteration:
       s.add(5,1))             //     Increase the start-date by 1 day
    if(s.get(7)%7>1)           //   If the day of the week is NOT a Saturday or Sunday:
                               //   (SUNDAY = 1, MONDAY = 2, ..., SATURDAY = 7)
      r++;                     //    Increase the result-sum by 1
  return r;}                   //  Return the result-sum
\$\endgroup\$
  • \$\begingroup\$ Could you do e=s.clone()? \$\endgroup\$ – Quintec Oct 1 '18 at 18:51
  • 1
    \$\begingroup\$ We can also (I assume) do Calendar s=Calendar.getInstance(),e=s.getInstance(), which unfortunately ends up being exactly the same length. \$\endgroup\$ – Misha Lavrov Oct 2 '18 at 1:01
  • 1
    \$\begingroup\$ @MishaLavrov Ah, the static C indeed isn't necessary. It was from an old part of the code where I also used C somewhere else. Been able to golf 1 byte by using var s=Calendar.getInstance();var e=s.getInstance(); so thanks. :) \$\endgroup\$ – Kevin Cruijssen Oct 2 '18 at 6:50
  • 1
    \$\begingroup\$ 150 bytes, using java.time. \$\endgroup\$ – Olivier Grégoire Oct 2 '18 at 11:35
  • 1
    \$\begingroup\$ Done! It's very close in bytes to the other answer, but doesn't beat it yet. \$\endgroup\$ – Olivier Grégoire Oct 2 '18 at 11:58
7
\$\begingroup\$

JavaScript (ES6), 116 103 bytes

f=(d,n=+new Date)=>(D=new Date(n)).toJSON()<d.split`.`.reverse().join`-`&&(D.getDay()%6>0)+f(d,n+864e5)

Try it online!

How?

A JavaScript timestamp is defined as the number of milliseconds elapsed since the UNIX epoch. The current timestamp is hold in the variable \$n\$.

At each iteration, we use \$n\$ to generate a date \$D\$ (as a JavaScript object). This date is converted to ISO 8601 format with the .toJSON() method:

YYYY-MM-DDThh:mm:ss.sssZ

Since this string starts with YYYY-MM-DD, it is safe to perform a lexicographical comparison with another string in a similar format. This is why we want to convert the input string \$d\$ to YYYY-MM-DD rather than converting our computed date to the input string format DD.MM.YYYY (which cannot be sorted lexicographically):

d.split`.`.reverse().join`-`

Before each recursive call, we increment the final result if D.getDay() is neither \$0\$ (Sunday) nor \$6\$ (Saturday), i.e. if it's not congruent to \$0\$ modulo \$6\$:

(D.getDay() % 6 > 0) + f(d, n + 864e5)

We add \$86,400,000\$ milliseconds to \$n\$ to reach the next day.

\$\endgroup\$
6
\$\begingroup\$

MATL, 24 bytes

46tQZt24&YO:Z':X-9XO83-z

Try it online!

I don't want to have any input format so that specific code golf languages got a big advantage

You half succeeded :-)

Explanation

46      % Push 46 (ASCII for '.')
tQ      % Duplicate, add 1: gives 47 (ASCII for '/')
Zt      % Implicit input. Replace '.' by '/' in the input string
24&YO   % Convert string with date format 24 ('dd/mm/yyyy') to serial date number.
        % This is an integer representing day starting at Jan-1-0000
:       % Inclusive range from 1 to that
Z'      % Push current date and time as a serial number. Integer part is day;
        % decimal part represents time of the day
:       % Inclusive range from 1 to that
X-      % Set difference. Gives serial numbers of days after today, up to input
9XO     % Convert each number to date format 9, which is a letter for each day
        % of the week: 'M', 'T', 'W', 'T', ' F', 'S', 'S'
83-     % Subtract 83 (ASCII for 'S')
z       % Number of nonzeros. Implicit display
\$\endgroup\$
  • \$\begingroup\$ If I understood the challenge correctly, you only take one date input, and compare it to today's date. For example, 16.10.2018 would today (Monday 01-10-2018) result in 11, tomorrow in 10, etc. \$\endgroup\$ – Kevin Cruijssen Oct 1 '18 at 16:35
  • \$\begingroup\$ @KevinCruijssen Whoops. Thanks! Corrected now \$\endgroup\$ – Luis Mendo Oct 1 '18 at 16:47
  • 1
    \$\begingroup\$ And with the same byte-count. :) Nice, +1 from me. \$\endgroup\$ – Kevin Cruijssen Oct 1 '18 at 16:52
6
\$\begingroup\$

Wolfram Language (Mathematica), 64 56 bytes

DayCount[Today,""<>#~StringTake~{{4,6},3,-4},"Weekday"]&

Try it online!

DayCount[x,y,"Weekday"] counts the number of weekdays between x and y.

The inputs x and y can be many things, including a fancy DateObject like the one returned by Today, or a string in the format (unfortunately) mm.dd.yyyy.

My previous attempt tried to turn the dd.mm.yyyy input into a DateObject by telling Mathematica how to parse it; the new solution simply rearranges the string to put day and month in the order Mathematica expects.

It's worth noting that the 28-byte solution DayCount[Today,#,"Weekday"]& not only works perfectly for a month-day-year input format, but also correctly handles unambiguous day-month-year inputs such as 31.12.2018, which couldn't possibly mean "the 12th day of the 31st month". So it's correct more than 60% of the time :)

\$\endgroup\$
5
\$\begingroup\$

Perl 6, 61 bytes

{+grep 6>*.day-of-week,Date.today^..Date.new(|[R,] m:g/\d+/)}

Try it online!

\$\endgroup\$
5
\$\begingroup\$

Java (OpenJDK 8), 174 166 165 bytes

With a bit of inspiration from Kevin's answer and a good ol' trawl through the deprecated Date API, I've managed to get a more succinct Java solution.

-8 bytes thanks to Kevin's inventive regex date parsing

-1 bytes thanks to Nevay's clever bitwise operation

import java.util.*;d->{long r=0,s=new Date().getTime(),e=Date.parse(d.replaceAll("(..).(..).","$2/$1/"));for(;s<=e;s+=864e5)r-=-new Date(s).getDay()%6>>-1;return r;}

Try it online!

Explanation

import java.util.*;                         // Required import for Date 
long r=0,                                   // Initialise result variable
     s=new Date().getTime(),                // Current date in millis
     e=Date.parse(
         d.replaceAll("(..).(..).","$2/$1/")// Use regex to convert to MM/dd/yyyy
     );                                     // Parse date arg using deprecated API
for(;s<=e;                                  // Loop while current millis are less than date arg (e.g. date is before)       
    s+=864e5)                               // Add 86400000 ms to current date (equiv of 1 day)
    r-=-new Date(s).getDay()%6>>-1;        // If day is Sunday (0) or Saturday (6) don't increment, else add 1
return r;                                   // When loop finished return result
\$\endgroup\$
  • 1
    \$\begingroup\$ Nice answer! Smart use of the varargs with the d=d[0].split and the deprecated .parse with default format MM/dd/yyyy format. One small mistake in your post, you have import java.text.*; instead of import java.util.*; in your code, and // Required import for both Calendar and Date in your explanation (even though you don't use Calendar). \$\endgroup\$ – Kevin Cruijssen Oct 2 '18 at 9:02
  • \$\begingroup\$ @KevinCruijssen No idea why I had java.text but fixed now! Thanks! \$\endgroup\$ – Luke Stevens Oct 2 '18 at 9:10
  • 1
    \$\begingroup\$ Although I liked the d=d[0].split with the varargs, changing the input to a regular String, removing d=d[0].split("\\."); and changing d[1]+"/"+d[0]+"/"+d[2] to d.replaceAll("(..).(..).","$2/$1/") saves 7 bytes. \$\endgroup\$ – Kevin Cruijssen Oct 2 '18 at 12:05
  • 1
    \$\begingroup\$ And 1 more byte by changing r+=new Date(s).getDay()%6<1?0:1,s+=864e5); to s+=864e5)r+=new Date(s).getDay()%6<1?0:1;. :) \$\endgroup\$ – Kevin Cruijssen Oct 2 '18 at 12:07
  • 1
    \$\begingroup\$ -1 byte: r-=-new Date(s).getDay()%6>>-1; \$\endgroup\$ – Nevay Oct 3 '18 at 16:03
4
\$\begingroup\$

Red, 72 bytes

func[a][b: now/date s: 0 until[if b/weekday < 6[s: s + 1]a < b: b + 1]s]

Try it online!

Takes the date in format dd-mm-yyyy, for example 31-10-2018 (also works with 10-Oct-2018)

Strict input:

Red, 97 bytes

func[a][a: do replace/all a".""-"b: now/date s: 0 until[if b/weekday < 6[s: s + 1]a < b: b + 1]s]

Try it online!

Bonus:

Returns a list of the dates/weekdays of the working days up to the given date:

Red, 235 bytes

f: func [ a ] [
    b: now/date
    d: system/locale/days
    collect [ 
        until [ 
            if b/weekday < 6 [ 
                keep/only reduce [ b ":" d/(b/weekday) ]
            ]
            a < b: b + 1
        ]
    ]
]

Try it online!

\$\endgroup\$
  • \$\begingroup\$ Agh, no fair, in python I need to spend about 72 bytes processing this IO format... :P \$\endgroup\$ – Quintec Oct 1 '18 at 13:15
  • 1
    \$\begingroup\$ Usually my Red solutions are among the longest ones, but fortunately Red deals very well with dates :) \$\endgroup\$ – Galen Ivanov Oct 1 '18 at 13:17
  • 1
    \$\begingroup\$ 90 bytes to process python... i'm done, I quit until there's a more flexible input format :P \$\endgroup\$ – Quintec Oct 1 '18 at 13:20
4
\$\begingroup\$

JavaScript, 87 85 bytes

Returns false for 0.

s=>([d,m,y]=s.split`.`,g=_=>new Date<(x=new Date(y,~-m,d--))&&(x.getDay()%6>0)+g())()

Try it online or check the next 31 days

\$\endgroup\$
4
\$\begingroup\$

R, 72 chars

A variation on the answer provided by @ngm which avoids the grepl to save a few characters and works in non-English locales.

sum(strftime(seq(Sys.Date(),as.Date(scan(,""),"%d.%m.%Y"),1),'%u')<6)+1

\$\endgroup\$
  • \$\begingroup\$ Shorter and more general too. Nice answer and welcome to the asylum. \$\endgroup\$ – ngm Oct 4 '18 at 14:26
  • 1
    \$\begingroup\$ Welcome to PPCG! you can add a TIO link - it's easy and formats the answer for you :) \$\endgroup\$ – JayCe Oct 4 '18 at 19:32
3
\$\begingroup\$

Python 2, 163 156 149 147 bytes

lambda s:sum((date.today()+timedelta(x)).weekday()<5for x in range((date(*map(int,(s.split(".")[::-1])))-date.today()).days))
from datetime import*

Try it online!

-7 with thanks to @mypetlion

-7 more with thanks to @ovs

+30 due to the very restrictive input format which I only noticed just before I posted my previous code which took input as e.g. (2018,11,1) :-(

\$\endgroup\$
  • 2
    \$\begingroup\$ No need for this: (0,1)[t.weekday()<5]. Python booleans are a subclass of int and True, False can be used in arithmetic operations as 1,0. Replace it with c+=t.weekday()<5 to save 7 bytes. \$\endgroup\$ – mypetlion Oct 1 '18 at 15:50
  • 1
    \$\begingroup\$ 149 bytes as a lambda. \$\endgroup\$ – ovs Oct 1 '18 at 16:48
  • \$\begingroup\$ Thanks @mypetlion. I shouldn't have missed that one. \$\endgroup\$ – ElPedro Oct 1 '18 at 17:32
  • \$\begingroup\$ Thanks @ovs. Second time you have helped recently. Last time was a very impressive -30. Was trying to work out how to get this into a lambda. \$\endgroup\$ – ElPedro Oct 1 '18 at 17:37
3
\$\begingroup\$

Java (JDK 10), 171 bytes

s->{int c=0;for(var t=java.time.LocalDate.now();!t.parse(s.replaceAll("(..).(..).(.*)","$3-$2-$1")).equals(t);t=t.plusDays(1))c-=t.getDayOfWeek().getValue()/6-1;return c;}

Try it online!

Credits

\$\endgroup\$
  • 1
    \$\begingroup\$ You can change the (.*)\\.(.*)\\.(.*) to (..).(..).(.*). \$\endgroup\$ – Kevin Cruijssen Oct 2 '18 at 12:00
  • \$\begingroup\$ With your replaceAll technique his answer can be golfed by 7 bytes as well however, so yours is still slightly longer. ;) \$\endgroup\$ – Kevin Cruijssen Oct 2 '18 at 12:05
  • \$\begingroup\$ @KevinCruijssen Thanks for the regex! And no worries: I don't mind having a longer answer ;) \$\endgroup\$ – Olivier Grégoire Oct 2 '18 at 12:16
3
\$\begingroup\$

JavaScript (Node.js), 168 160 139 133 bytes

35 bytes less thanks to Quintec and Kevin Cruijssen

D=>{var i=D.split('.'),n=0;for(var d=new Date();d<=new Date(i[2],i[1]-1,i[0]);d.setDate(d.getDate()+1))n+=-~d.getDay()%7>1;return n;}

Try it online!

D=>{
  var i=D.split('.'),                 // Splits the date string by .
      n=0;                            // Counter variable
  for(var d=new Date();               // For the actual date
      d<=new Date(i[2],i[1]-1,i[0]);      // As long as the date is less or equal the requested date
      d.setDate(d.getDate()+1))           // Count the date one up
    n+=-~d.getDay()%7>1;                // If the date is not a Sunday or Saturday
  return n;                           // Return the counter variable
}
\$\endgroup\$
3
\$\begingroup\$

Python3 & Numpy, 96 bytes

I couldn't get smaller than the boring pre-made solution...

from numpy import*
d=datetime64
lambda s:busday_count(d('today'),d(f'{s[6:]}-{s[3:5]}-{s[:2]}'))

Try it online!

\$\endgroup\$
  • \$\begingroup\$ Must get into Python 3 ;) \$\endgroup\$ – ElPedro Oct 1 '18 at 21:25
  • \$\begingroup\$ Based on your import, you are not using Python 3, but rather Python 3 with numpy. \$\endgroup\$ – Jonathan Frech Oct 1 '18 at 23:30
  • \$\begingroup\$ @JonathanFrech should that be in the title? others using python also have used a library as python has no builtin datatype for dates or times. \$\endgroup\$ – Aaron Oct 2 '18 at 13:32
  • 1
    \$\begingroup\$ This depends on your definition of builtin -- modules like datetime are standard library modules and thus I would count them as being part of the core language. However, when one uses third-party modules like numpy, one enhances the language's capabilities and therefore I would see it as another language. \$\endgroup\$ – Jonathan Frech Oct 2 '18 at 15:57
2
\$\begingroup\$

PowerShell, 107 99 bytes

-8 bytes thanks to mazzy

$d,$m,$y=$args-split'\.';for($a=date;$a-lt(date "$y-$m-$d");$a=$a|% *ys 1){$o+=($a|% D*k)-in1..5}$o

Try it online!

Performs a regex -split on the input $args, stores the values into $days, $months, and $years, respectively. Then, enters a for loop, initializing $a to today's date. The loop continues while $a is -lessthan our input target date. Each iteration we're adding 1 days to $a, and checking whether the current D*k (short for DayOfWeek) is in the range 1..5 (i.e., Monday to Friday). That Boolean result is accumulated into $o and once we're out of the loop that value is left on the pipeline and output is implicit.

\$\endgroup\$
  • \$\begingroup\$ 100 bytes? $d,$m,$y=$args-split'\.';for($a=date;$a-lt(date "$y-$m-$d");$a=$a|% *ys 1){$o+=($a|% D*k)-in1..5};$o \$\endgroup\$ – mazzy Oct 1 '18 at 19:02
  • 1
    \$\begingroup\$ @mazzy Indeed. Plus, the semicolon between for(...){...} and $o can be removed, so we're now below 100! \$\endgroup\$ – AdmBorkBork Oct 1 '18 at 19:11
2
\$\begingroup\$

Python 2, 147 143 141 140 bytes

from datetime import*
lambda e,s=date.today():sum((s+timedelta(x+1)).weekday()<5for x in range((date(*map(int,e.split(".")[::-1]))-s).days))

Try it online!

Takes a string, e, which represents the end date in the format "dd.MM.YYYY". Optionally also takes the start date, but this is expected to be a datetime.date.

The start date, s, is defaulted to today's date as a datetime.date object in order to disregard time. The end time is parsed into a datetime.datetime object then converted to a date, since datetime.date objects do not have a parse method and datetimes cannot be added to/subtracted from dates. Iterates through every day in (start, end] and adds 1 to the total if its weekday number is < 5. ([0-4] are [Mon-Fri], [5-6] are [Sat-Sun]).

Datetime parsing is the worst, you guys.

EDIT: Stole ElPedro's map(int,thing) trick to save 4 bytes.

EDIT 2: ELECTRIC BOOGALOO: Saved 2 bytes by making it an anonymous function. (Thanks Aaron!)

EDIT 3: xrange -> range. (Thanks again Aaron!)

\$\endgroup\$
  • 1
    \$\begingroup\$ You're welcome! Nice answer :) \$\endgroup\$ – ElPedro Oct 1 '18 at 17:54
  • 1
    \$\begingroup\$ It is convention you can leave off the f= from lambda expressions here \$\endgroup\$ – Aaron Oct 1 '18 at 18:53
  • 1
    \$\begingroup\$ "Datetime parsing is the worst, you guys" Hahahaha feel my pain, you succeeded where I failed though :P \$\endgroup\$ – Quintec Oct 1 '18 at 18:54
  • \$\begingroup\$ @Aaron I'm never sure if that's okay with multiple functions or with import statements, thanks! \$\endgroup\$ – Triggernometry Oct 1 '18 at 18:57
  • 1
    \$\begingroup\$ You can also use range rather than xrange it should still work just fine. \$\endgroup\$ – Aaron Oct 1 '18 at 19:12
2
\$\begingroup\$

PHP, 66 bytes

for($t=time();$t<strtotime($argn);)$r+=date(N,$t+=86400)<6;echo$r;

empty output for 0; insert + between echo and $r to fix.

Run as pipe with -nr or try it online.


60 bytes with unary output:

for($t=time();$t<strtotime($argn);)echo date(N,$t+=86400)<6;
\$\endgroup\$
1
\$\begingroup\$

PHP (with Carbon), 107 bytes

function a($d){return Carbon\Carbon::parse($d)->diffInDaysFiltered(function($e){return!$e->isWeekend();});}
\$\endgroup\$
1
\$\begingroup\$

IBM/Lotus Notes Formula - 99 bytes

d:=i;c:=0;@While(d>@Today;@Set("c";c+@If(@Weekday(d)=1:7;0;1));@Set("d";@Adjust(d;0;0;-1;0;0;0)));c

Takes input from a date/time field i. The input format of i is set to . separated so there is no need to convert the input. Notes can take a date input with any separator as long as you tell it before what it is going to be (hope that's not cheating!). Formula is in computed numeric field o on the same form.

Interesting aside: Ever since @For and @While were introduced into the Formula language in (I think) R6 by the great Damien Katz the only use I have found for them is code golfing. I have never used them in a production app.

There is no TIO available for formula so here is a screenshot taken on 02.10.2018:

enter image description here

\$\endgroup\$
1
\$\begingroup\$

Ruby, 81 bytes

->e{require'date';(Date.today...Date.strptime(e,'%d.%m.%Y')).count{|d|6>d.cwday}}

Try it online!

\$\endgroup\$
1
\$\begingroup\$

K4, 40 bytes

Solution:

{+/1<.q.mod[d+!(."."/:|"."\:x)-d:.z.d]7}

Explanation:

Calculate the difference between the dates, use modulo 7 to ignore weekends, sum up.

{+/1<.q.mod[d+!(."."/:|"."\:x)-d:.z.d]7} / the solution
     .q.mod[                         ]7  / modulo with 7
                                 .z.d    / UTC date
                               d:        / save as d
                              -          / subtract from
               (             )           / do this together
                       "."\:x            / split input on "."
                      |                  / reverse
                 "."/:                   / join back with "."
                .                        / take the value
              !                          / range 0..the difference
            d+                           / add today's date to range
   1<                                    / is 1 less than the modulo (ie is this mon-fri)?
 +/                                      / sum up

Notes:

  • same byte alternative to the date parsing: "D"$,/|"."\:x
\$\endgroup\$
1
\$\begingroup\$

C (clang), 209 208 205 bytes

Compiler flags -DU=u=localtime(&b) -DW=tm_wday -DY=tm_year -DT=tm_yday (52 bytes).

#import<time.h>
i;f(char*a){long b;struct tm t,*u;time(&b);U;strptime(a,"%d.%m.%Y",&t);for(i=0;u->T^t.T|u->Y^t.Y;u->W&&u->W^6&&i++,b+=86400,U);return i;}

Try it online!

-1 byte thanks to @JonathanFrech

\$\endgroup\$
  • \$\begingroup\$ ?i++:0 -> &&++i. \$\endgroup\$ – Jonathan Frech Oct 1 '18 at 23:33
0
\$\begingroup\$

q, 52 79 bytes

{x:"D"$"."sv reverse"."vs x;i:0;while[x-.z.d;$[2>x mod 7;x-:1;[i+:1;x-:1]]];:i}

in q, each date has an underlying integer value, based on how many days have passed since the start of the millenium. Applying 'mod 7' to this, you get unique values for each day of the week (0 for Saturday, 6 for Friday). So when 2 > x mod 7, don't increment the counter, to avoid counting weekends.

EDIT: Missed strict date format, editing

EDIT2: Included

\$\endgroup\$
  • 1
    \$\begingroup\$ Best I've come up with is {sum 1<mod[d+til("D"$x 10 vs 67893401)-d:.z.d]7} for 48 bytes without resorting to K verbs. \$\endgroup\$ – streetster Oct 4 '18 at 20:03
  • \$\begingroup\$ Using the list indices is a lot more elegant than reverse, and rather than using a loop, applying mod to the list. Great answer +1 \$\endgroup\$ – Thaufeki Oct 5 '18 at 1:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.