Procent dni roboczych w miesiącu

11

Biorąc pod uwagę rok i miesiąc, sprawdź procent dni roboczych w tym miesiącu. Dni robocze są od poniedziałku do piątku bez względu na święta lub inne specjalne rzeczy. Używany jest kalendarz gregoriański.

Wejście

Rok i miesiąc w formacie ISO 8601 (RRRR-MM). Rok zawsze ma cztery cyfry, miesiąc zawsze ma dwie cyfry. Dany rok nie będzie wcześniejszy niż 1582.

Wynik

Wyjście to procent dni roboczych (zgodnie z powyższą definicją) w danym miesiącu, w zaokrągleniu do liczby całkowitej. Nie następuje znak procentu ani cyfry ułamkowe.

Próbka 1

Input                Output

2010-05              68

Próbka 2

Input                Output

2010-06              73

Próbka 3

Input                Output

1920-10              68

Próbka 4

Input                Output

2817-12              68

Minął tydzień, odpowiedź została zaakceptowana. Dla ciekawskich rozmiary zgłoszeń, które otrzymaliśmy w naszym konkursie:

129 - Z shell
174 - VB.NET
222 - C
233 - C
300 - C

Jak również nasze własne (nierankingowe) rozwiązania:

  75 - PowerShell
  93 - Ruby
112 - Powłoka Bourne'a

Joey
źródło
2
Jestem studentem, więc ...echo 100
Amory
Nawet studenci nie mogą uniknąć podstawowych definicji w swojej pracy. I zdefiniowałem dni pracy inaczej ;-)
Joey

Odpowiedzi:

4

64-bitowy Perl, 67 68

Perl 5.10 lub nowszy, uruchom z perl -E 'code here'lubperl -M5.010 filename

map{$d++,/^S/||$w++if$_=`date -d@ARGV-$_`}1..31;say int.5+100*$w/$d

Koncesje na rozmiar kodu:

  • wrażliwe na ustawienia regionalne: liczy się jako dni robocze dni, których dateprodukcja nie zaczyna się od dużej litery S. W LC_ALL=Crazie wątpliwości należy przejść poniżej .
  • dane wyjściowe są czyste i dobrze sformatowane, ale na stderr występuje „śmieci” w miesiącach krótszych niż 31., 2> /dev/nulljeśli są zdenerwowane.
  • z jakiegoś powodu moja wersja dateuważa 2817-12 za nieprawidłowy miesiąc. Kto wiedział, nadchodzi nowa apokalipsa GNU! Wymaga 64-bitowej wersji datedla dat po 2038 r. (Dzięki Joey)
JB
źródło
1
Najwyraźniej został zniesiony przez „Siphousa Hemesa” za jego rządów. ref „Nowa historia Pisma Świętego”
Martin York,
1
Czy co roku po 2038 roku jest zepsuty? Wtedy przełączenie tej 64-bitowej kompilacji może pomóc ze względu na braki w obsłudze daty ;-)
Joey
@Joey to wszystko. Dzięki za wskazówkę!
JB
JB: Tylko zgadywałem i właściwie nie spodziewałem się, że cokolwiek poza C nadal będzie używać wyłącznie 32-bitowych liczb całkowitych, które liczą sekundy od dziwnej epoki. Chociaż, szczerze mówiąc, umieściłem tam wymóg dotyczący dat> 2038 właśnie w tym celu ;-)
Joey
3

PHP - 135

Zrobiłem to w PHP, ponieważ kilka dni temu miałem podobny problem do rozwiązania .

<?php $a=array(2,3,3,3,2,1,1);$t=strtotime($argv[1]);$m=date(t,$t);echo round((20+min($m-28,$a[date(w,strtotime('28day',$t))]))/$m*100)

(Trochę) Bardziej czytelnie i bez powiadomienia o stałych używanych jako łańcuchy:

<?php
date_default_timezone_set('America/New_York');
$additionalDays = array(2, 3, 3, 3, 2, 1, 1);
$timestamp = strtotime($argv[1]);
$daysInMonth = date('t', $timestamp);
$limit = $daysInMonth - 28;
$twentyNinthDayIndex = date('w', strtotime("+28 days", $timestamp));
$add = $additionalDays[$twentyNinthDayIndex];
$numberOfWorkDays = 20 + min($limit, $add);
echo round($numberOfWorkDays / $daysInMonth * 100);
?>

Jest to możliwe dzięki bardzo prostemu algorytmowi obliczania liczby dni roboczych w miesiącu: sprawdź dni tygodnia 29, 30 i 31 (jeśli te daty istnieją) i dodaj 20.

skradać się
źródło
Świetny algorytm, słaba gra w golfa. Korzystając ze współczesnego PHP 5.3.5 i -Rtego podejścia, można zastosować golf do 86 bajtów (63,7%): $a="2333211";echo.5+min(-8+$m=date(t,$t=strtotime($argn)),20+$a[date(w,$t)])/$m*100|0; Zobacz etapy gry w golfa.
Tytus
80 bajtów:<?=.5+min(-8+$m=date(t,$t=strtotime($argn)),20+(5886>>date(w,$t)*2&3))/$m*100|0;
Tytus
2

Python 152 znaków

from calendar import*
y,m=map(int,raw_input().split('-'))
c=r=monthrange(y,m)[1]
for d in range(1,r+1):
 if weekday(y,m,d)>4:c-=1
print '%.f'%(c*100./r)
fR0DDY
źródło
2

Bash + coreutils, 82 bajty

f()(cal -NMd$1|sed -n "s/^$2.//p"|wc -w)
dc -e`f $1 "[^S ]"`d`f $1 S`+r200*r/1+2/p
Cyfrowa trauma
źródło
2

Windows PowerShell, 80

$x=$args;1..31|%{"$x-$_"|date -u %u -ea 0}|%{$a++
$b+=!!($_%6)}
[int]($b*100/$a)
Joey
źródło
Czy na pewno [int]naprawdę się kręcisz? Chciałbym wierzyć, że to podłogi.
zneak
@zneak: PowerShell nie jest językiem C ani językiem pochodnym C. Używa domyślnego trybu zaokrąglania .NET, który jest »zaokrąglony do najbliższej parzystej liczby całkowitej«. Po prostu wypróbuj: Zarówno [int]1.5i [int]2.5wydaj 2. To dokładne zachowanie często powoduje problemy w zadaniach, w których konieczne jest dzielenie zmiennoprzecinkowe (co następnie wymaga dodatkowego [Math]::Floor()), ale w tym przypadku nie boli, a „zaokrąglenie do parzystości” dotyczy tylko liczb, których koniec .5nie może się tutaj zdarzyć.
Joey
Jeśli jesteś pewien, wierzę ci. Spodziewałem się, że zamiast tego będzie działał jak C # i nie mam żadnej maszyny z systemem Windows, na której można by przetestować w domu.
zneak
@zneak: Nie, zdecydowanie nie działa jak w C #. Coś jak [int]w PowerShell jest zwykle bardziej konwersją niż obsadą :-). Rzeczy takie jak [int[]][char[]]'abc'praca, których nie można dostać do pracy w wielu innych językach.
Joey
Necrobump, ale $input-> $argszapisuje bajt.
Veskah
1

D: 186 znaków

auto f(S)(S s){auto d=Date.fromISOExtendedString(s~"-28"),e=d.endOfMonth;int n=20;while(1){d+=dur!"days"(1);if(d>e)break;int w=d.dayOfWeek;if(w>0&&w<6)++n;}return rndtol(n*100.0/e.day);}

Bardziej czytelnie:

auto f(S)(S s)
{
    auto d = Date.fromISOExtendedString(s ~ "-28"), e = d.endOfMonth;
    int n = 20;

    while(1)
    {
        d += dur!"days"(1);

        if(d > e)
            break;

        int w = d.dayOfWeek;

        if(w > 0 && w < 6)
            ++n;
    }

    return rndtol(n * 100.0 / e.day);
}
Jonathan M. Davis
źródło
1

Python - 142

from calendar import*
y,m=map(int,raw_input().split('-'))
f,r=monthrange(y,m)
print'%.f'%((r-sum(weekday(y,m,d+1)>4for d in range(r)))*100./r)

Dzięki fR0DDY za bit kalendarza.

Juan
źródło
1

Ruby, 124 119 111

require 'date'
e=Date.civil *$*[0].split(?-).map(&:to_i),-1
p ((e+1<<1..e).count{|d|d.cwday<6}*1e2/e.day).round

Wymaga Ruby 1.9 z powodu rozpryskiwania się rok i miesiąc przed argumentem -1 „dzień” i ?-dla "-". W Ruby 1.8 musimy dodać 2 znaki:

require 'date'
e=Date.civil *$*[0].split('-').map(&:to_i)<<-1
p ((e+1<<1..e).count{|d|d.cwday<6}*1e2/e.day).round

Edycja : Ogol pięć znaków na podstawie pomocy @ Dogberta.
Edycja : Ogol osiem kolejnych znaków na podstawie pomocy @ steenslag.

Phrogz
źródło
Dlaczego przypisujesz datę do D?
Dogbert
@Dogbert Whoops! Holdover z czasów, kiedy miałem dwa Date.civils; dzięki!
Phrogz
'-'można napisać jak ?-w Ruby 1.9
Dogbert
@Dogbert Nice. Też to wrzucę. Wydaje mi się, że musi istnieć krótszy sposób na wybranie dni tygodnia, ale jeszcze go nie znalazłem.
Phrogz
e + 1 << 1 jest trzy razy krótszy niż ee.day + 1
steenslag
1

PHP 5.2, 88 bajtów

Chociaż już grałem w golfa rozwiązanie zajadłego do 85 bajtów (właśnie znalazłem jeszcze jeden), oto moje własne:
wątpię, czy uda mi się wycisnąć tutaj jeszcze trzy bajty.

$a=_4444444255555236666304777411;echo$a[date(t,$t=strtotime($argn))%28*7+date(N,$t)]+67;

pobiera dane wejściowe z STDIN: Uruchom z echo <yyyy>-<mm> | php -nR '<code>'.

Ciąg $aodwzorowuje date(t)liczbę dni w miesiącu ( ) i dzień tygodnia pierwszego dnia miesiąca ( date(N): poniedziałek = 1, niedziela = 7) na procent dni roboczych-67; strtotimekonwertuje dane wejściowe na znacznik czasu UNIX; reszta kodu robi haszowanie.

+1 bajt dla starszych PHP 5: Wymień Nsię wi $a=_...;z $a="...".
kolejne +3 bajty dla PHP 4: wstaw .-1po $argn.

-5 bajty dla PHP w wersji 5.5 lub nowszej (późniejsza wyzwanie):
usunąć wszystko przed echoi wymienić $az "4444444255555236666304777411".

Tytus
źródło
Cóż ... jeden bajt: %7zamiast %28.
Tytus
0

Rebol - 118 113

w: b: 0 o: d: do join input"-01"while[d/2 = o/2][if d/7 < 6[++ w]++ b d: o + b]print to-integer round w / b * 100

Nie golfowany:

w: b: 0 
o: d: do join input "-01"
while [d/2 = o/2] [
    if d/7 < 6 [++ w]
    ++ b
    d: o + b
]
print to-integer round w / b * 100
draegtun
źródło
0

C #, 158 bajtów

s=>{var d=DateTime.Parse(s);int i=0,t=DateTime.DaysInMonth(d.Year,d.Month),w=0;while(i<t)w-=-(int)d.AddDays(i++).DayOfWeek%6>>31;return Math.Round(1e2*w/t);};

Anonimowa metoda, która zwraca wymagany procent.

Pełny program z niepoznaną, komentowaną metodą i przypadkami testowymi:

using System;

class WorkingDayPercentage
{
    static void Main()
    {
        Func <string, double> f =
        s =>
        {
            var d = DateTime.Parse(s);                      // extracts a DateTime object from the supplied string
            int i = 0,                                      // index variable
                t = DateTime.DaysInMonth(d.Year, d.Month),  // number of total number of days in the specified month
                w = 0;                                      // number of working days in the month

            while (i < t)                                   // iterates through the days of the month
                w -= -(int)d.AddDays(i++).DayOfWeek%6 >> 31;// d.AddDays(i) is the current day
                                                            // i++ increments the index variable to go to the next day
                                                            // .DayOfWeek is an enum which hold the weekdays
                                                            // (int)..DayOfWeek gets the days's index in the enum
                                                            // note that 6 is Saturday, 0 is Sunday, 1 is Monday etc.
                                                            // (int)DayOfWeek % 6 converts weekend days to 0
                                                            // while working days stay strictly positive
                                                            // - changes the sign of the positive numbers
                                                            // >> 31 extracts the signum
                                                            // which is -1 for negative numbers (working days)
                                                            // weekend days remain 0
                                                            // w -= substracts the negative numbers
                                                            // equivalent to adding their modulus

            return Math.Round(1e2 * w / t);                 // the Math.round function requires a double or a decimal
                                                            // working days and total number of days are integers
                                                            // also, a percentage must be returned
                                                            // multiplying with 100.0 converts the expression to a double
                                                            // however, 1e2 is used to shorten the code
        };

        // test cases:
        Console.WriteLine(f("2010-05")); // 68
        Console.WriteLine(f("2010-06")); // 73
        Console.WriteLine(f("1920-10")); // 68
        Console.WriteLine(f("2817-12")); // 68
    }
}

Alternatywna funkcja, która dodaje wartości ujemne do liczby dni roboczych, zmieniając znak w zwrocie bez dodatkowych kosztów bajtu:

s=>{var d=DateTime.Parse(s);int i=0,t=DateTime.DaysInMonth(d.Year,d.Month),w=0;while(i<t)w+=-(int)d.AddDays(i++).DayOfWeek%6>>31;return-Math.Round(1e2*w/t);};
adrianmp
źródło
0

Oracle SQL, 110 bajtów

select round(100*sum(1-trunc(to_char(x+level-1,'d')/6))/sum(1))from dual,t connect by level<=add_months(x,1)-x

Działa przy założeniu, że dane wejściowe są przechowywane w tabeli przy użyciu datetypu danych, np

with t as (select to_date('2010-06','yyyy-mm') x from dual)
Dr Y Wit
źródło
0

APL (Dyalog Unicode) , 55 bajtów SBCS

Anonimowa ukryta funkcja prefiksu.

.5+100×2÷/(2 5)2{≢⍎⍕↓⍺↓¯2' ',cal' '@5⊢⍵⊣⎕CY'dfns'}¨⊂

 dołącz datę, aby traktować ją jako całość

(2 5)2{ Zastosuj do tego następującą funkcję, ale z lewymi argumentami [2,5]i 2:

⎕CY'dfns' skopiuj bibliotekę „dfns”

⍵⊣ odrzuć raport na korzyść daty

' '@5⊢ zamień 5. znak ( -) na spację

 wykonaj to, aby uzyskać listę dwóch elementów

cal w tym celu wywołać funkcję kalendarza

' ', dodaj do tego kolumnę spacji

¯2⌽ obróć dwie ostatnie kolumny (sobota) do przodu

⍺↓ upuść lewy argument liczby wierszy (2, nagłówki) i kolumn (jeśli podano; 5 = Sat + Sun)

 podziel macierz na listę linii

 format (spłaszcza się z wstawieniem podwójnych odstępów)

 wykonaj (zamienia pozostałe liczby dni w płaską listę numeryczną)

 zliczyć te

2÷/ podziel każdą parę (jest tylko jedna)

100× pomnóż przez sto

.5+ dodaj połowę

 piętro

Wypróbuj online!

Adám
źródło
0

Perl 6 , 78 bajtów

{round 100*@_.grep(6>*.day-of-week)/@_}o{Date.new("$_-01")...^*.month-*.month}

Wypróbuj online!

Jo King
źródło