python에서 datetime class를 이용할 때, javascript에서 Date class를 이용할 때 그리고 java에서 Date class를 이용할 때 종종 보았던 날짜 표시 형식과 그 의미에 관해서 간략하게 알아본다.


UTC와 GMT

UTC

UTC는 Coordinated Universal Time의 줄임말로 1972년 1월 1일부터 시행된 국제 표준시입니다. 이 UTC는 국제 원자시와 윤초 보정을 기반으로 표준화 되어있습니다. 즉, 세계에서 시간을 표현하는 표준시라고 보면됩니다.

GMT

GMT는 Greenwich Mean Time의 줄임말로 그리니치 평균시라고 불리며 런던을 기점으로 하고 웰링턴에서 종점으로 설정 되는 협정 세계시입니다.

UTC vs GMT?

결론부터 말하면 UTC와 GMT는 일상에서는 공통으로 같은 시간을 나타냅니다.(정확하게 들어가면 UTC가 더 정확한 시간을 표현합니다.) 1972년 1월 1일부터 UTC라는 세계 협정시가 사용되었으며 이는 먼저 사용되던 GMT와 기준시가 같기에 요즘날에 혼용해서 사용하게 되었습니다.


UTC를 기준으로 한 시간의 표현과 Timezone

Timezone

이제 위에서 UTC라는 국제표준시로 시간을 정한다는 부분을 설명드렸습니다. 그런데 국가마다 사용하는 시간이 다르기 때문에 공통된 시간대로 묶은 특정 지역을 Timezone이라고 합니다.

우리나라의 경우에는 ISO 8601에 따르면 2017-03-16T17:40:00+09:00와 같은 형식으로 표시된다고 합니다.

아래서 설명을 하겠지만 뒤에 +09:00부분을 통해서 UTC기준시로 9시간이 추가된 시간대라는 것을 표현합니다.

Timezone에 관련된 링크는 다음과 같습니다. Wikipedia - Timezone


ISO 8601

정의

UTC 그리고 Timezone과 함께 문자열의 형태로 시간을 표현하는 방법을 기술해놓은 표준입니다.

몇가지 원칙

  • 년/월/일T시:분:초 처럼 왼쪽에서 오른쪽으로 갈수록 작은 단위어야 합니다.
  • 날짜와 시간은 포맷을 맞추기 위해 0을 붙입니다. EX) 4월 -> 04월
  • 위의 2가지가 중요하다고 생각하며 나머지 3가지는 링크를 참고하시길 바랍니다.Wiki

형태

가장 기본적인 형식(날짜와 시간)은 아래와 같습니다.

2017-03-16T17:40:00+09:00
  • 날짜: 년-월-일의 형태로 나와있습니다.
  • T: 날짜 뒤에 시간이 오는것을 표시해주는 문자입니다.
  • 시간: 시:분:초의 형태로 나와있으며 프로그래밍 언어에 따라서 초 뒤에 소수점 형태로 milliseconds가 표시되기도 합니다.
  • Timezone Offset: 시간 뒤에 ±시간:분의 형태로 나와있으며 UTC기준시로부터 얼마만큼 차이가 있는지를 나타냅니다. 현재 위의 예시는 한국시간을 나타내며 UTC기준시로부터 9시간 +된 시간임을 나타냅니다
  • (Z or +00:00): UTC기준시를 나타내는 표시이며 “+00:00”으로 나타내기도 합니다.


Python에서 datetime과 timezone표시

파이썬에서 간단하게 ISO 8601에 따라서 시간을 표시해보는 간단한 코드를 보겠습니다. datetime class를 이용합니다.

특별한 명시 혹은 timezone을 설정해주지 않으면 System의 시간과 timezone에 따라서 시간을 나타내게 됩니다.

test.py

import datetime

#.now() -> 현재 local time
print(str(datetime.datetime.now()))
#.utcnow() -> utc 기준시
print(str(datetime.datetime.utcnow()))
Taewooui-MacBook-Pro:Documents taewoo$ python test.py
2017-03-16 19:55:28.084217
2017-03-16 10:55:28.084274

위와 같이 현재 시간과 UTC 시간이 나오지만 UTC에 대한 정보가 생략되어있습니다. 즉, +00:00과 같은 포맷이 없는데다가 T가 없고 띄어쓰기가 있는걸로 보아 완전한 ISO 8601의 형태가 아닙니다.


test_datetime_in_iso_format.py

import datetime

print(str(datetime.datetime.now().isoformat()))
print(str(datetime.datetime.utcnow().isoformat()))
Taewooui-MacBook-Pro:Documents taewoo$ python test_datetime_in_iso_format.py
2017-03-16T20:02:57.224509
2017-03-16T11:02:57.224592

T도 있고 붙여써서 ISO 8601 형태에 맞지만 아직 timezone의 정보가 들어있지 않습니다.

파이썬 datetime 자체로는 timezone을 지원하지 않습니다. 그래서 pytz와 같은 모듈을 써야합니다.


test_datetime_with_pytz.py

import datetime
import pytz

print(str(datetime.datetime.now(tz=pytz.utc).isoformat()))
Taewooui-MacBook-Pro:Documents taewoo$ python test_datetime_with_pytz.py
2017-03-16T11:08:12.216857+00:00


JSON string date format => Python datetime

JSON은 string의 형태로 datetime의 값이 날아오게 됩니다. 이 때 ISO 8601에 따른 형식일 때 어떻게 python datetime 객체로 바꾸는지에 대한 코드입니다. dateutil.parser를 이용하면 비교적 쉽게 파싱이 가능합니다 :)

import dateutil.parser

print(str(dateutil.parser.parse("2017-03-16T11:08:12.216857+00:00")))
print(str(dateutil.parser.parse("2017-03-16T11:08:12.216857Z")))
2017-03-16 11:08:12.216857+00:00
2017-03-16 11:08:12.216857+00:00

위처럼 UTC 시간으로 나오며 뒤에 타임존 부분도 추가되어있음을 확인 할 수 있습니다. 실제로 테스트 코드도 pytest로 돌려본 결과 같다고 assert를 실행하였을 때 결과가 나왔습니다


하지만 UTC 표준시로 나와있으며 타임존이 표시가 되어있지만 현재 제 지역의 타임존이 추가 되어있지 않습니다ㅠㅠ

이제 현재 컴퓨터에 따른 Timezone을 추가해봅시다.

버전에 따라서 여러 방법이 있지만 pytz를 통해서 2와 3에서 다 사용 할 수 있는 코드는 아래와 같습니다.

test.py

import dateutil.parser
import pytz

# JSON으로 들어온 문자열을 python datetime으로 변환
date=dateutil.parser.parse("2017-03-16T11:08:12.216857+00:00")

# Timezone을 설정
local_timezone = pytz.timezone('Asia/Seoul')

# Timezone에 따라서 새로운 date형식을 변경
local_date = date.replace(tzinfo=pytz.utc).astimezone(local_timezone)

# 출력
print(local_date.isoformat())
Taewooui-MacBook-Pro:Documents taewoo$ python test.py
2017-03-16T20:08:12.216857+09:00


왜 UTC와 ISO 8601을 따라야 할까?

ISO 8601에 따르면 파싱을 할 수 있는 라이브러리가 많으며 전체적으로 호환이 되는 부분이 많습니다. 무엇보다 현재 적용하고 있는 프로젝트와 이전과 비교되면서 느낀거지만 timestamp나 정수형으로 저장이 되어있다면 어디 기준으로 기록된 시간인지에 대한 정보가 없습니다. 이러한 부분에서 보면 불편함이 있더라도 DB에 저장하거나 할 때 각 언어가 지원해주는 날짜 및 시간관련 코드들을 적극 사용하는 부분이 여러 호환에 있어서 이득이라 여겨집니다.

Circle CI에서 빌드를 할 때 터미널에 값을 입력하는 방법을 알아보자


배경

안드로이드로 개발 환경을 만들고 Circle CI에서 빌드를 하는데 [y/n]로 물어보는 부분에서 넘어가지 못하고 있는 상황이 있었는데 구글링을 좀 하고 결과를 찾아서 공유하고자 포스팅을 한다.

방법은 의외로 매우 간단한데… echo 명령어를 써주면 끝난다...

echo 명령어는 echo 다음에 오는 문자열들을 터미널에 출력해주는 역할을 한다.

처음에는 “;y;”와 같이 직접 입력하는 시도를 해봤는데 되지 않았다…


방법

다음 아래처럼 입력하고자 하는 값들 혹은 문자열들을 echo를 통해서 입력하고 |로 다음 명령어를 입력하면 된다.

  • echo y | commands-you-want!

이는 Circle CI뿐만 아니라 다른 CI에서도 적용이 가능하다(쉘 명령어니까!)

circle.yml

general:
  artifacts:
    - /home/ubuntu/Unopen.Lab-Alarm/app/build/outputs/apk/

machine:
  java:
    version: openjdk8

dependencies:
  override:
     - chmod +x gradlew

# 다음 아래처럼 "echo y |" 문만 앞에! 추가하면 됩니다.
test:
  pre:
    - echo y | android update sdk --no-ui --filter build-tools-25.0.2,android-25,extra-android-m2repository

업데이트(2021.10.11): pyenv 설정 관련 값 추가

python으로 개발을 할 때 가상환경을 설정하는 상황이 꽤나 많아서 직접 포스팅을 하기로 하였습니다.


배경

파이썬으로 개발 혹은 어떠한 소프트웨어를 실행 할 때, 파이썬으로 된 환경에서 실행을 하려면 파이썬버전부터 해서 pip모듈까지 환경에 따라 설정해야 할 부분이 많은데 pyenv, virtualenv 그리고 autoenv를 이용하면 개발 하거나 파이썬 환경 세팅을 더 편하게 할 수 있다.


우선 요약

  • pyenv: python의 가상환경을 설정 할 수 있도록 해주는 도구로 여러 버전의 파이썬을 사용 할 수 있다.

  • virtualenv: python의 가상환경으로 정해진 버전에서 pip모듈들을 각 가상환경에서 설치하고 실행 할 수 있도록 해준다.

  • autoenv: 특정 디렉토리에 들어갔을 때 virtualenv를 자동으로 실행시켜줍니다.


pyenv

pyenv란 python 버전에 따라서 가상환경 설정을 할 수 있도록 도와주는 도구입니다.

설치는 맥에서 아래와 같습니다.

$ brew install pyenv


그 다음 pyenv init~/.bash_profile에 추가합니다. bash_profile에 추가하는것은 현재 OS(Mac이나 Linux)에 환경변수를 설정하는 부분이며 아래와 같습니다.

$ echo 'eval "$(pyenv init --path)"' >> ~/.bash_profile
$ echo 'eval "$(pyenv init -)"' >> ~/.bash_profile
$ source ~/.bash_profile


다음 아래와 같은 명령어로 현재 pyenv에서 사용 가능한 파이썬 버전들을 볼 수 있습니다.

$ pyenv versions
* system (set by /Users/taewoo/.pyenv/version)
  3.5.2
  3.5.2/envs/LinkU
  3.5.2/envs/pytest_practice
  3.5.2/envs/tdd_book_practice
  3.6.0
  3.6.0/envs/FoodGroup
  3.6.0/envs/LinkU
  3.6.0/envs/djangogirls
  FoodGroup
  LinkU
  LinkU_deprecated
  djangogirls
  pytest_practice
  tdd_book_practice


다음 아래와 같이 pyenv install –list를 통해서 pyenv에 설치 할 수 있는 파이썬 버전들을 볼 수 있으며 해당 버전을 설치하면 됩니다.

$ pyenv install --list

...

$ pyenv install [version]

예시

$ pyenv install 3.5.2


최신 python 버전들을 사용하기 위해서는 pyenv를 git pull하거나 update를 해줘야하는데 update의 명령어가 더 편합니다.

$ pyenv update


virtualenv

virtualenv를 통해서 가상환견을 설정하고 그 가상환경 설정마다 pip 모듈을 설치해 현재 컴퓨터의 환경과 독립적으로 파이썬만의 환경을 설정 할 수 있습니다.

다음 아래처럼 설치를 해줍니다.

$ brew install pyenv-virtualenv


pyenv와 똑같이 아래처럼 ~/.bash_profile에 추가합니다.

$ 'eval "$(pyenv virtualenv-init -)"' >> ~/.bash_profile
$ source ~/.bash_profile


이제 실제로 가상환경을 만들어 보겠습니다. 사용예제는 아래 형태와 같으며 실제 명령어는 그 아래와 같습니다.

$ pyenv virtualenv [python version] [virtualenv name]
$ pyenv virtualenv 3.5.2 virtualenv-test


실제로 가상환경이 만들어지면 아래와 같이 “pyenv activate [virtualenv name]”을 명시하시면 되며 나갈 때는 반대로 “pyenv deactivate”을 통해서 나갈 수 있습니다.

  • pyenv activate [virtualenv name]: [virtualenv name]라는 이름의 가상환경 실행(사전에 가상환경이 만들어져 있어야함)
  • pyenv deactivate: 현재 shell에서 실행된 가상환경을 종료

가상환경이 세팅되면 커맨드 라인 제일 앞줄에 (virtualenv name)와 같이 가상환경이 실행되어 있음을 자동으로 나타내줍니다.

$  pyenv activate virtualenv-test
$ (virtualenv-test)...
$ (virtualenv-test)...pyenv deactivate
//원상태로의 복귀


autoenv

autoenv는 특정 디렉토리에 들어갔을 때 virtualenv가 자동으로 실행 될 수 있도록 도와주는 도구입니다.

설치는 아래와 같습니다.

$ brew install autoenv


똑같이 이제 ~/.bash_profile에 추가를 합니다.

$ echo 'source /usr/local/opt/autoenv/activate.sh' >> ~/.bash_profile
$ source ~/.bash_profile


사용하는 방법은 가상환경을 실행하려는 디렉토리에서 다음과 같이 .env파일을 만들고 .env 파일 안에 아래 명령어처럼 원하는 가상환경을 입력해주시면 됩니다.

$ touch .env
$ echo "pyenv activate [virtualenv name]" > .env

예시는 아래와 같습니다.

$ touch .env
$ echo "pyenv activate virtualenv-test" > .env

이제 해당 디렉토리에 들어갈때마다 virtualenv가 실행이 됩니다.(해당 환경이 본 글과 같은 경우 virtualenv-test로 나옵니다.)

하지만 deactivate는 꼭 수동으로 해줘야합니다.


참고자료

ReactJS에서 생긴 오류에 대해서 하나씩 기억하기 위해 포스팅하려고 한다.


문제

React를 Webpack을 통해서 컴파일 할 때 다음과 같은 오류가 뜬적이 있는데

ERROR in multi (webpack)-dev-server/client?http://0.0.0.0:7777 webpack/hot/dev-server ./src/index.js

Module not found: Error: Can't resolve 'babel' in '/Users/taewoo/Documents/ReactJS projects/react-tutorial'

BREAKING CHANGE: It's no longer allowed to omit the '-loader' suffix when using loaders.

“It’s no longer allowed to omit the ‘-loader’ suffix when using loaders.”를 봐서 더 이상 “-loader”를 생략하면 안된다라는 의미같다.


해결

구글링을 통해 보니 아래와 같이 webpack.config.js에서 ‘babel’을 ‘babel-loader’로 고쳐주면된다.

  • loader: 'babel' -> loader: 'babel-loader'

webpack.config.js

module: {
        loaders: [
            {
                test: /\.js$/,
                loader: 'babel-loader',
                exclude: /node_modules/,
                query: {
                    cacheDirectory: true,
                    presets: ['es2015', 'react']
                }
            }
        ]
    }


참고자료

onDragListener가 아닌 onTouchListener를 이용해서 드래그를 구현할 때 생겼던 문제점이 있어서 기억해두고자 포스팅한다.


배경

안드로이드 프로젝트를 작게 진행하고 있는데 onTouchListener에서 ACTION_UP에 관련된 코드를 작성하였지만 작동하지 않은 사례가 있어서 기억하고자 포스팅한다.


코드 상황

아래와 같은 코드로 처음에 제작을 하였으며 onTouch안에다가 함수가 실행되고 Action Value가 어떤것인지 알 수 있도록 로그 코드를 작성하였으며 각 Action Value에 따라서도 case문을 타고 들어갔음을 알기위해 로그 코드를 작성하였습니다.

imageView_Lv1alarmClockCancel.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {

                Log.e("onTouchTest", "onTouch Entered");

                int action = motionEvent.getAction();

                Log.e("onTouchTest", "Action Value: " + action);
                switch (action) {

                    case MotionEvent.ACTION_DOWN:
                        Log.e("onTouchTest", "ACTION_DOWN");
                        // ...Dragging View Codes
                        break;

                    case MotionEvent.ACTION_UP:
                        Log.e("onTouchTest", "ACTION_UP");
                        // ...
                        break;

                    default:
                        break;
                }
                return true;

            }

        });


결과는 아래와 같습니다. 화면을 터치하는 순간 ACTION_DOWN(Action Value = 0)이 되면서 case문을 타고 가지만 드래그를 시행하고 모든 작업을 마쳤을 때 화면에서 손을 떼었을 때 ACTION_UP이 아닌 ACTION_CANCEL(Action Value = 3)이 들어오는 것을 확인 할 수 있습니다.

즉, onTouch의 함수에서는 화면을 터치했는지 안했는지만을 확인하며 그에 대한 값들만 ACTION 값으로 받아옴을 확인 할 수 있습니다.

03-09 20:01:31.198 7117-7117/com.unopenlab.iamalarm E/onTouchTest: onTouch Entered
03-09 20:01:31.198 7117-7117/com.unopenlab.iamalarm E/onTouchTest: Action Value: 0
03-09 20:01:31.198 7117-7117/com.unopenlab.iamalarm E/onTouchTest: ACTION_DOWN
03-09 20:01:31.241 7117-7117/com.unopenlab.iamalarm E/onTouchTest: onTouch Entered
03-09 20:01:31.242 7117-7117/com.unopenlab.iamalarm E/onTouchTest: Action Value: 3


참고자료