The symmetry of months

32

1

Introduction

Some months are completely symmetric, meaning they have central symmetry as well as reflection symmetry, like February of 2010:

     February 2010
┌──┬──┬──┬──┬──┬──┬──┐ 
│  │  │  │  │  │  │  │ 
├──┼──┼──┼──┼──┼──┼──┤ 
│  │  │  │  │  │  │  │ 
├──┼──┼──┼──┼──┼──┼──┤ 
│  │  │  │  │  │  │  │ 
├──┼──┼──┼──┼──┼──┼──┤ 
│  │  │  │  │  │  │  │ 
└──┴──┴──┴──┴──┴──┴──┘ 

Some months have only central symmetry, like February of 1996 or current month, the April of 2018:

      February 1996
          ┌──┬──┬──┬──┐
          │  │  │  │  │
 ┌──┬──┬──┼──┼──┼──┼──┤
 │  │  │  │  │  │  │  │
 ├──┼──┼──┼──┼──┼──┼──┤
 │  │  │  │  │  │  │  │
 ├──┼──┼──┼──┼──┼──┼──┤
 │  │  │  │  │  │  │  │
 ├──┼──┼──┼──┼──┴──┴──┘
 │  │  │  │  │
 └──┴──┴──┴──┘

       April 2018  ┌──┐
                   │  │
 ┌──┬──┬──┬──┬──┬──┼──┤
 │  │  │  │  │  │  │  │
 ├──┼──┼──┼──┼──┼──┼──┤
 │  │  │  │  │  │  │  │
 ├──┼──┼──┼──┼──┼──┼──┤
 │  │  │  │  │  │  │  │
 ├──┼──┼──┼──┼──┼──┼──┤
 │  │  │  │  │  │  │  │
 ├──┼──┴──┴──┴──┴──┴──┘
 │  │
 └──┘

And some are asymmetric, like the previous month, the March of 2018:

      March 2018
         ┌──┬──┬──┬──┐
         │  │  │  │  │
┌──┬──┬──┼──┼──┼──┼──┤
│  │  │  │  │  │  │  │
├──┼──┼──┼──┼──┼──┼──┤
│  │  │  │  │  │  │  │
├──┼──┼──┼──┼──┼──┼──┤
│  │  │  │  │  │  │  │
├──┼──┼──┼──┼──┼──┼──┘
│  │  │  │  │  │  │
└──┴──┴──┴──┴──┴──┘

Task

Take an input in form of a date, e.g.:

  • 2018.04
  • 2018.03
  • 2010.02
  • 1996.02

Output the corresponding symmetry, e.g.

  • 2018.04 -> centrally symmetric
  • 2018.03 -> asymmetric
  • 2010.02 -> symmetric
  • 1996.02 -> centrally symmetric

Rules

  • This is code golf, so the smallest number of bytes wins.
  • Standard loopholes are obviously not allowed.
  • Assume that the week starts with Monday (thanks to Angs and Arnauld for suggestion).
  • Consider only years between 1900 and 2100 (inclusive).
  • The input and output formatting rules are permissive, meaning you can use any equivalent format that is native to the language of your choice.
  • Base your solution on the Gregorian calendar.

mkierc

Posted 2018-04-01T07:41:00.893

Reputation: 423

7

Consider that dates are weird, you may want to specify exactly the rules, or limit the possible input to a small range (say, 1901-2099)

– user202729 – 2018-04-01T08:05:39.883

2Things to avoid when writing challenges/Adding unnecessarily stuff includes "Making answers calculate f(x) for every x in a list." What about "take an input in form of a date"? – user202729 – 2018-04-01T08:08:04.363

6

Welcome to PPCG, and nice first challenge! Although this challenge is good, in the future if you want some feedback on the challenge before posting, you can post it in the sandbox.

– user202729 – 2018-04-01T08:11:50.353

@user202729 Fair points, I'll clarify that and change the input form, thanks! – mkierc – 2018-04-01T08:16:16.960

2Should the output be strictly the mentioned strings, or any 3 distinct values? – Uriel – 2018-04-01T09:03:57.963

1Could you add (links to) images for those on mobile, which stupidly doesn't use a monospaced font for code? – Shaggy – 2018-04-01T09:08:02.840

1@Uriel I've added details about the input/output format – mkierc – 2018-04-01T09:12:03.570

I believe it's your choice to make, however I'd just make a note that strict output is usually frowned upon on the site – Uriel – 2018-04-01T09:31:07.977

@Uriel I guess you're right, the length of program depends strongly on that, so I think I'll allow for permissive output as well. – mkierc – 2018-04-01T09:36:07.163

1Does between 1900 and 2100 include 1900 and 2100 ? (important for leap year reasons. I'd obviously prefer "no") – Ton Hospel – 2018-04-01T19:07:36.980

What timezone and country should the months be calculated for? Months are a weird concept – Ferrybig – 2018-04-02T11:17:49.183

@Ferrybig Gregorian calendar. AFAIK that's not affected by timezone/country. – user202729 – 2018-04-02T16:10:20.837

2(wait a minute, Gregorian calendar or Julian calendar? I suggested [1901-2099] but you decide to use [1900-2100] so they're different for some inputs) – user202729 – 2018-04-02T16:14:40.370

@mkierc May we use Sunday as first day of the week? – Adám – 2018-04-03T14:36:59.143

Answers

20

JavaScript (ES6), 55 bytes

Saved 6 bytes thanks to @Neil

Takes input in currying syntax (year)(month). Returns false for asymmetric, true for centrally symmetric and 0 for completely symmetric.

y=>m=>(n=(g=_=>new Date(y,m--,7).getDay())()+g())&&n==7

Try it online!

How?

We define the function g() which returns the weekday of yyyy/mm/01, as an integer between 0 = Monday and 6 = Sunday.

g = _ => new Date(y, m--, 7).getDay()

Because getDay() natively returns 0 = Sunday to 6 = Saturday, we shift the result to the expected range by querying for the 7th day instead.

Then we define:

n = g() + g()

Because the constructor of Date expects a 0-indexed month and because g() decrements m after passing it to Date, we actually first compute the weekday of the first day of the next month and then add that of the current one.

Completely symmetric months

Completely symmetric months start with a Monday and are followed by a month which also starts with a Monday. This is only possible for February of a non-leap year.

- Feb --------------    - Mar --------------
Mo Tu We Th Fr Sa Su    Mo Tu We Th Fr Sa Su
--------------------    --------------------
01 02 03 04 05 06 07    01 02 03 04 05 06 07
08 09 10 11 12 13 14    08 09 10 11 12 13 14
15 16 17 18 19 20 21    15 16 17 18 19 20 21
22 23 24 25 26 27 28    22 23 24 25 26 27 28
                        29 30 31

This leads to n = 0.

Centrally symmetric months

Centrally symmetric months are months for which the sum of the weekday of their first day and that of the next month is 7.

- M ----------------    - M+1 --------------
Mo Tu We Th Fr Sa Su    Mo Tu We Th Fr Sa Su
--------------------    --------------------
 0  1 [2] 3  4  5  6     0  1  2  3  4 [5] 6
--------------------    --------------------
      01 02 03 04 05                   01 02
06 07 08 09 10 11 12    03 04 05 06 07 07 09
13 14 15 16 17 18 19    ...
20 21 22 23 24 25 26
27 28 29 30 31

Hence the second test: n == 7.


No built-in, 93 bytes

Uses Zeller's congruence. Same I/O format as the other version.

y=>m=>(n=(g=_=>(Y=y,((m+(m++>2||Y--&&13))*2.6|0)+Y+(Y>>2)-6*~(Y/=100)+(Y>>2))%7)()+g())&&n==7

Try it online!

Arnauld

Posted 2018-04-01T07:41:00.893

Reputation: 111 334

I thought it was true, false and filenotfound instead of 0 – Angs – 2018-04-01T10:00:56.760

g=m=>new Date(y,m,7).getDay() saves 6 bytes. – Neil – 2018-04-01T21:18:54.807

7

T-SQL, 213 bytes (strict I/O rules)

SET DATEFIRST 1SELECT CASE WHEN a+b<>8THEN'a'WHEN a=1THEN''ELSE'centrally 'END+'symetric'FROM(SELECT DATEPART(DW,f)a,DATEPART(DW,DATEADD(M,1,f)-1)b FROM (SELECT CONVERT(DATETIME,REPLACE(s,'.','')+'01')f FROM t)y)x

The above query considers the strict input/output formatting rules.

The input is taken from the column s of a table named t:

CREATE TABLE t (s CHAR(7))
INSERT INTO t VALUES ('2018.04'),('2018.03'),('2010.02'),('1996.02')

Ungolfed:

SET DATEFIRST 1
SELECT *, CASE WHEN a+b<>8 THEN 'a' WHEN a=1 AND b=7 THEN '' ELSE 'centrally ' END+'symetric'
FROM (
    SELECT *,DATEPART(WEEKDAY,f) a, 
        DATEPART(WEEKDAY,DATEADD(MONTH,1,f)-1) b 
    FROM (SELECT *,CONVERT(DATETIME,REPLACE(s,'.','')+'01')f FROM t)y
) x

SQLFiddle 1

T-SQL, 128 bytes (permissive I/O rules)

SET DATEFIRST 1SELECT CASE WHEN a+b<>8THEN 1WHEN a=1THEN\END FROM(SELECT DATEPART(DW,d)a,DATEPART(DW,DATEADD(M,1,d)-1)b FROM t)x

If the format of the input and of the output can be changed, I would choose to input the first day of the month, in a datetime column named d:

CREATE TABLE t (d DATETIME)
INSERT INTO t VALUES ('20180401'),('20180301'),('20100201'),('19960201')

The output would be 1 for asymetric, 0 for symetric, NULL for centrally symetric.

If we can run it on a server (or with a login) configured for BRITISH language, we can remove SET DATEFIRST 1 saving 15 more bytes.

SQLFiddle 2

Razvan Socol

Posted 2018-04-01T07:41:00.893

Reputation: 341

1Nice work. Not sure if it'll work in all versions, but on SQL 2012 I was able to save 15 bytes by using CONVERT(DATETIME,s+'.01') instead of REPLACE. You can also drop the space in FROM (SELECT – BradC – 2018-04-03T14:58:17.100

1It works, but it's dependant on the DATEFORMAT setting. For example, if we use SET LANGUAGE BRITISH, then CONVERT(DATETIME,'2018.02.01') would be January 2nd, instead of February 1st. – Razvan Socol – 2018-04-03T16:46:23.567

5

Haskell, 170 bytes

import Data.Time.Calendar
import Data.Time.Calendar.WeekDate
a%b=((\(_,_,a)->a).toWeekDate.fromGregorian a b$1)!gregorianMonthLength a b
1!28=2
4!29=1
7!30=1
3!31=1
_!_=0

Returns 2 for centrally symmetric, 1 for symmetric and 0 for asymmetric

Angs

Posted 2018-04-01T07:41:00.893

Reputation: 4 825

@TuukkaX Sorry for confusion - this is my first challenge, I've changed the rules so they also allow permissive output formats so it can be more "in spirit" of the code-golf. – mkierc – 2018-04-01T09:38:30.450

5

Python 2, 118 104 bytes

Thanks to Jonathan Allan and Dead Possum for the improvements!

from calendar import*
def f(*d):_=monthcalendar(*d);print all(sum(_,[]))+(_[0].count(0)==_[-1].count(0))

Python 3, 122 105 bytes

from calendar import*
def f(*d):_=monthcalendar(*d);print(all(sum(_,[]))+(_[0].count(0)==_[-1].count(0)))

Input

  • First is the year
  • Second is the month


Output

  • 0 = no symmetry
  • 1 = central symmetry
  • 2 = complete symmetry

Jack of all Spades

Posted 2018-04-01T07:41:00.893

Reputation: 151

3Welcome to the site! You may not assume that input is stored in a variable (such as Y or M), so this is currently a snippet and invalid. If you change the variables to calls to input() however, this will be perfectly fine. – caird coinheringaahing – 2018-04-01T17:45:57.290

1@cairdcoinheringaahing Thanks for the welcome! Fixed user input :) – Jack of all Spades – 2018-04-01T18:11:39.217

Welcome! Tweaks for -9 bytes here - all import, unpacked input, _[0]+_[-1]->sum(..)

– Dead Possum – 2018-04-02T12:37:20.953

1

Some tricks to get it down 13 bytes here

– Jonathan Allan – 2018-04-02T13:04:35.793

1

...and another byte using Dead Possum's sum trick - here

– Jonathan Allan – 2018-04-02T13:19:27.740

4

Red, 199, 168 161 bytes

func[d][t: split d"."y: do t/1 m: do t/2 a: to-date[1 m y]b: a + 31
b/day: 1 b: b - 1 if(1 = s: a/weekday)and(7 = e: b/weekday)[return 1]if 8 - e = s[return 2]0]

Try it online!

0 - asymmetric

1 - symmetric

2 - centrally symmetric

More readable:

f: func[d][                  ; Takes the input as a string
    t: split d "."           ; splits the string at '.'
    y: do t/1                ; stores the year in y 
    m: do t/2                ; stores the month in m
    a: to-date[1 m y]        ; set date a to the first day of the month
    b: a + 31                ; set date b in the next month  
    b/day: 1                 ; and set the day to 1st
    b: b - 1                 ; find the end day of the month starting on a
    s: a/weekday             ; find the day of the week of a 
    e: b/weekday             ; find the day of the week of b
    if(s = 1) and (e = 7)    ; if the month starts at Monday and ends on Sunday
        [return 1]           ; return 1 fo symmetric
    if 8 - e = s             ; if the month starts and ends on the same day of the week
        [return 2]           ; return 2 for centrally symmetric  
    0                        ; else return 0 for assymetric
]

Galen Ivanov

Posted 2018-04-01T07:41:00.893

Reputation: 13 815

2

Mathematica, 137 bytes

a=#~DateValue~"DayName"&;b=a/@{2}~DateRange~{3};Which[{##}=={0,6},1,+##>5,0,1>0,-1]&@@(Position[b,a@#][[1,1]]~Mod~7&)/@{{##},{#,#2+1,0}}&

Pure function. Takes the year and the month as input and returns -1 for asymmetric months, 0 for centrally symmetric months, and 1 for fully symmetric months. Not sure why this language can't convert from a weekday to a number by default...

LegionMammal978

Posted 2018-04-01T07:41:00.893

Reputation: 15 731

2

Bash + GNU utilities, 70

date -f- +%u<<<"$1/1+1month-1day
$1/1"|dc -e??sad8*la-55-rla+8-d*64*+p

Input is formatted as YYYY/MM.

Output is numeric, as follows:

  • less than 0: centrally symmetric
  • exactly 0: symmetric
  • greater than 0: asymmetric

I assume this output format is acceptable for this question.

Try it online!

Digital Trauma

Posted 2018-04-01T07:41:00.893

Reputation: 64 644

1

C, 111 bytes

a;g(y,m){y-=a=m<3;return(y/100*21/4+y%100*5/4+(13*m+16*a+8)/5)%7;}f(y,m){a=g(y,m)+g(y,m+1);return(a>0)+(a==7);}

Invoke f(year, month), 0 for completely symmetric, 1 for asymmetric, 2 for centrally symmetric.

tsh

Posted 2018-04-01T07:41:00.893

Reputation: 13 072

IIRC you can abuse UB on GCC by replacing return with y= (the first parameter) and falling out of the function. – Quentin – 2018-04-03T10:48:54.233

1

Perl 6, 74 bytes

{{{$_==30??2!!$_%7==2}(2*.day-of-week+.days-in-month)}(Date.new("$_-01"))}

Bare block, implicitly a function of 1 argument, a string like "2012-02". Returns:

2     # Fully symmetric
True  # Centrally symmetric
False # Asymmetric

When the pattern is symmetric, as .day-of-week increases by 1, .days-in-month would need to move by 2 in order to still match (the month would be starting a day later but need to end a day earlier), so 2 * .day-of-week + .days-in-month gives us a measure of that gap. Modulo 7 it should be 1 to get symmetry, but we can first cheaply check for the non-leap February by checking that total before modulo (Monday and 28 days per month is the minimum possible combination).

I'm surprised that this takes so many bytes, but fully 36 bytes are needed for just making a date and getting the day of the week and days in that month.

Phil H

Posted 2018-04-01T07:41:00.893

Reputation: 1 376