Percentuale di giorni lavorativi in ​​un mese


11

Dato un anno e un mese, scopri la percentuale di giorni lavorativi in ​​detto mese. I giorni lavorativi sono dal lunedì al venerdì, indipendentemente da festività o altre cose speciali. Viene utilizzato il calendario gregoriano.

Ingresso

Un anno e un mese in formato ISO 8601 (AAAA-MM). L'anno ha sempre quattro cifre, il mese ha sempre due cifre. L'anno dato non sarà prima del 1582.

Produzione

L'output è la percentuale di giorni lavorativi (secondo la definizione sopra) in un determinato mese, arrotondata a un numero intero. Non seguono segni di percentuale o cifre frazionarie.

Campione 1

Input                Output

2010-05              68

Campione 2

Input                Output

2010-06              73

Esempio 3

Input                Output

1920-10              68

Campione 4

Input                Output

2817-12              68

È passata una settimana, una risposta è stata accettata. Per i curiosi, le dimensioni delle proposte che abbiamo ricevuto nel nostro concorso:

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

Oltre alle nostre soluzioni (non classificate):

  75 - PowerShell
  93 - Ruby
112 - Bourne shell


2
Sono uno studente laureato, quindi ...echo 100
Amory,

Anche gli studenti laureati non possono sfuggire alle definizioni fondamentali nella loro linea di lavoro. E ho definito i giorni di lavoro in modo diverso ;-)
Joey,

Risposte:


4

64 bit Perl, 67 68

Perl 5.10 o successivo, esegui con perl -E 'code here'operl -M5.010 filename

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

Concessioni per la dimensione del codice:

  • sensibile alle impostazioni locali: conta come giorni lavorativi i giorni il cui dateoutput non inizia con una S maiuscola in LC_ALL=Ccaso di dubbio.
  • l'output è puro e ben formattato, ma c'è "immondizia" su stderr nei mesi più brevi di 31. 2> /dev/nullse turbato.
  • per qualche ragione, la mia versione di date2817-12 considera un mese non valido. Chi lo sapeva, la nuova apocalisse GNU è dovuta! Richiede una build a 64 bit dateper le date successive al 2038. (Grazie Joey)

1
Apparentemente fu abolito da "Siffo Hemes" durante il suo dominio. ref "Una nuova storia della Sacra Bibbia"
Martin York,

1
Ogni anno dopo il 2038 viene rotto? Quindi passare da una build a 64 bit potrebbe essere utile a causa di un po 'di coraggio con la gestione della data ;-)
Joey,

@Joey è esattamente così. Grazie per il consiglio!
JB,

JB: Era solo una supposizione e in realtà non mi aspettavo che nulla oltre a C usasse ancora numeri interi a 32 bit che contano i secondi da un'epoca strana. Comunque, a dire il vero, ho inserito il requisito relativo alle date> 2038 esattamente per questo scopo ;-)
Joey,

3

PHP - 135

L'ho fatto in PHP perché avevo un problema simile da trattare qualche giorno fa.

<?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)

(In qualche modo) Più leggibile e senza avvisi sulle costanti utilizzate come stringhe:

<?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);
?>

Ciò è reso possibile da un algoritmo molto semplice per calcolare il numero di giorni lavorativi in ​​un mese: verificare la durata del giorno 29, 30 e 31 (se tali date esistono) e aggiungere 20.


Ottimo algoritmo, scarso golf. Utilizzando il contemporaneo PHP 5.3.5 e -R, questo approccio può essere golfato fino a 86 byte (63,7%): $a="2333211";echo.5+min(-8+$m=date(t,$t=strtotime($argn)),20+$a[date(w,$t)])/$m*100|0; vedere i passi del golf.
Tito

80 byte:<?=.5+min(-8+$m=date(t,$t=strtotime($argn)),20+(5886>>date(w,$t)*2&3))/$m*100|0;
Tito

2

Personaggi di Python 152

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)

2

Bash + coreutils, 82 byte

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

2

Windows PowerShell, 80

$x=$args;1..31|%{"$x-$_"|date -u %u -ea 0}|%{$a++
$b+=!!($_%6)}
[int]($b*100/$a)

Sei [int]davvero sicuro ? Tenderei a crederci sui pavimenti.
zneak,

@zneak: PowerShell non è un linguaggio C o derivato C. Utilizza la modalità di arrotondamento predefinita di .NET che è »arrotondare al numero intero pari più vicino«. Provalo: entrambi [int]1.5e [int]2.5cedi 2. Questo comportamento esatto spesso causa problemi nei compiti in cui è necessaria la divisione del pavimento (che quindi richiede un extra [Math]::Floor()), ma in questo caso non fa male e "arrotondare a" si applica solo ai numeri che terminano in .5cui non può avvenire qui.
Joey,

Se sei sicuro, allora ti credo. Mi aspettavo invece che funzionasse come C #, e non ho alcuna macchina Windows su cui testare a casa.
zneak,

@zneak: No, sicuramente non funziona come in C #. Qualcosa come [int]in PowerShell è di solito più una conversione che un cast :-). Cose come [int[]][char[]]'abc'funzionano anche che non puoi lavorare in molte altre lingue.
Joey,

Necrobump ma $input -> $argssalva un byte.
Veskah,

1

D: 186 personaggi

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);}

Più leggibilmente:

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);
}

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)

Grazie a fR0DDY per il bit del calendario.


1

Rubino, 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

Richiede Ruby 1.9 a causa dello splatting dell'anno e del mese prima dell'argomento -1 "day" e ?-per "-". Per Ruby 1.8, dobbiamo aggiungere 2 caratteri:

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

Modifica : radere cinque personaggi in base all'aiuto di @ Dogbert.
Modifica : radere altri otto personaggi in base all'aiuto di @ steenslag.


Perché stai assegnando Date a D?
Dogbert,

@Dogbert Whoops! Holdover da un'epoca in cui avevo due Date.civils; Grazie!
Phrogz,

'-'potrebbe essere scritto come ?-in Ruby 1.9
Dogbert,

@Dogbert Nice. Lo aggiungerò anche io. Sento che ci deve essere un modo più breve per scegliere i giorni della settimana, ma non l'ho ancora trovato.
Phrogz,

e + 1 << 1 è tre più corto di ee.day + 1
steenslag

1

PHP 5.2, 88 byte

Anche se ho già giocato a golf con la soluzione di zneak fino a 85 byte (ne ho appena trovato uno), ecco il mio:
dubito di poter spremere altri tre byte qui.

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

accetta input da STDIN: esegui con echo <yyyy>-<mm> | php -nR '<code>' .

La stringa $amappa i giorni al mese ( date(t)) e il giorno della settimana del primo giorno del mese ( date(N): lunedì = 1, domenica = 7) alla percentuale di giorni lavorativi-67;strtotimeconverte l'input in un timestamp UNIX; il resto del codice esegue l'hash.

+1 byte per PHP 5 precedente: sostituisci Ncon we $a=_...;con $a="...".
altri +3 byte per PHP 4: inserire .-1dopo $argn.

-5 byte per PHP 5.5 o versioni successive (dopo la data della sfida):
rimuovi tutto prima echoe sostituiscilo $acon "4444444255555236666304777411".


Bene ... un byte: %7invece di %28.
Tito

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

Ungolfed:

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

0

C #, 158 byte

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);};

Metodo anonimo che restituisce la percentuale richiesta.

Programma completo con metodo non commentato, commentato e casi di test:

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
    }
}

Funzione alternativa, che aggiunge valori negativi al numero di giorni lavorativi, modificando il segno nella restituzione senza costi di byte aggiuntivi:

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);};

0

Oracle SQL, 110 byte

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

Funziona presupponendo che i dati di input siano memorizzati in una tabella utilizzando il datetipo di dati, ad es

with t as (select to_date('2010-06','yyyy-mm') x from dual)

0

APL (Dyalog Unicode) , SBCS da 55 byte

Funzione prefisso tacito anonimo.

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

 allegare la data per trattarla nel suo insieme

(2 5)2{...  applica la seguente funzione, ma con gli argomenti a sinistra [2,5]e 2:

⎕CY'dfns' copia la libreria "dfns"

⍵⊣ scartare il rapporto a favore della data

' '@5⊢ sostituire il 5 ° carattere ( -) con uno spazio

 eseguilo per ottenere un elenco di due elementi

cal chiama la funzione calendario su quella

' ', anteporre una colonna di spazi a quella

¯2⌽ ruota le ultime due colonne (sabato) in primo piano

⍺↓ elimina il numero dell'argomento sinistro di righe (2, intestazioni) e colonne (se specificato; 5 = Sat + Sun)

 dividere la matrice in un elenco di linee

 formato (appiattisce con l'inserimento della doppia spaziatura)

 esegui (trasforma i numeri dei giorni rimanenti in un elenco numerico piatto)

 conta quelli

2÷/ dividere ogni coppia (ce n'è solo una)

100× moltiplicare per cento

.5+ aggiungere una metà

 pavimento

Provalo online!


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.