다른 두 날짜 사이에 임의의 날짜를 생성


138

주어진 다른 두 날짜 사이에있는 임의의 날짜를 어떻게 생성합니까?

함수의 서명은 다음과 같아야합니다.

random_date("1/1/2008 1:30 PM", "1/1/2009 4:50 AM", 0.34)
                   ^                       ^          ^

            date generated has  date generated has  a random number
            to be after this    to be before this

다음과 같은 날짜를 반환합니다. 2/4/2008 7:20 PM


현재 질문이 표시되는 방식은 날짜 또는 시간 만 무작위로 원하는지 명확하지 않습니다. 귀하의 예에서는 시간을 찾고 있다고 제안합니다. 두 날짜 사이에 있어야한다면 지금까지 제공된 답변을 필요에 맞게 수정하고 종료 및 시작 시간을 제외 할 수 있습니다. 마지막으로 허용되는 답변과 같은 대부분의 답변에서 코드는 int로 자르기 때문에 종료 시간을 제외한 날짜 시간을 출력합니다. 응답에 끝을 포함 할 수있는 시간을 생성하려면 코드를 다음과 같이 변경하십시오.ptime = stime + prop * (etime - stime) + 0.5
tortal

답변:


149

두 문자열을 타임 스탬프 (예 : 선택한 해상도, 밀리 초, 초, 시간, 일 등)로 변환하고 나중에 더 이른 것을 빼고 임의의 숫자 (에 분포되어 있다고 가정 range [0, 1])에 그 차이를 곱한 다음 이전 것. 타임 스탬프를 다시 날짜 문자열로 변환하면 해당 범위의 임의 시간이 있습니다.

파이썬 예제 (출력은 0패딩 이외의 거의 지정한 형식으로되어 있습니다 -미국 시간 형식 규칙을 비난하십시오) :

import random
import time

def str_time_prop(start, end, format, prop):
    """Get a time at a proportion of a range of two formatted times.

    start and end should be strings specifying times formated in the
    given format (strftime-style), giving an interval [start, end].
    prop specifies how a proportion of the interval to be taken after
    start.  The returned time will be in the specified format.
    """

    stime = time.mktime(time.strptime(start, format))
    etime = time.mktime(time.strptime(end, format))

    ptime = stime + prop * (etime - stime)

    return time.strftime(format, time.localtime(ptime))


def random_date(start, end, prop):
    return str_time_prop(start, end, '%m/%d/%Y %I:%M %p', prop)

print(random_date("1/1/2008 1:30 PM", "1/1/2009 4:50 AM", random.random()))

이 방법은 1970 년 이전에 시작된 날짜를 지원하지 않습니다.
Cmbone

114
from random import randrange
from datetime import timedelta

def random_date(start, end):
    """
    This function will return a random datetime between two datetime 
    objects.
    """
    delta = end - start
    int_delta = (delta.days * 24 * 60 * 60) + delta.seconds
    random_second = randrange(int_delta)
    return start + timedelta(seconds=random_second)

정밀도는 초입니다. 원하는 경우 최대 정밀도를 마이크로 초까지 늘리거나 30 분으로 줄일 수 있습니다. 이를 위해 마지막 줄의 계산을 변경하십시오.

예제 실행 :

from datetime import datetime

d1 = datetime.strptime('1/1/2008 1:30 PM', '%m/%d/%Y %I:%M %p')
d2 = datetime.strptime('1/1/2009 4:50 AM', '%m/%d/%Y %I:%M %p')

print(random_date(d1, d2))

산출:

2008-12-04 01:50:17

3
start경우 변수를 사용하는 것이 완벽합니다. 코드에서 볼 수있는 유일한 문제 seconds는 결과 의 속성을 사용하는 것입니다 delta. 전체 간격에서 총 초 수를 반환하지는 않습니다. 대신 'time'구성 요소의 초 수 (0에서 60 사이)입니다. timedelta목적은 보유 total_seconds대신 표기 방법.
emyller

7
@emyller : 아니요, (delta.days * 24 * 60 * 60) + delta.seconds총 초 결과를 사용하고 있습니다. 이 total_seconds()방법은 Python 2.7의 새로운 기능이며 2009 년에 질문에 대답했을 때 존재하지 않았습니다. 파이썬 2.7이 있다면 대신 사용해야하지만 코드는 정상적으로 작동합니다.
nosklo

나는이 방법이 2.7-에서 다시 존재한다는 것을 알지 못했습니다. 방금 timedelta 객체가 기본적으로 일과 초로 구성되어 있는지 확인 했으므로 맞습니다. :-)
emyller

@emyller : 완전성을 위해 timedelta 객체는 일, 초 및 마이크로 초로 구성 됩니다. 위의 임의 날짜 생성 코드의 정밀도는 최대 초이지만 답변에서 언급 한 것처럼 변경 될 수 있습니다.
nosklo

83

작은 버전.

import datetime
import random


def random_date(start, end):
    """Generate a random datetime between `start` and `end`"""
    return start + datetime.timedelta(
        # Get a random amount of seconds between `start` and `end`
        seconds=random.randint(0, int((end - start).total_seconds())),
    )

모두 참고 start하고 end인수해야 datetime객체. 대신 문자열이 있으면 변환하기가 쉽습니다. 다른 답변은 그렇게하는 몇 가지 방법을 가리 킵니다.


54

업데이트 된 답변

Faker를 사용하면 훨씬 간단 합니다.

설치

pip install faker

용법:

from faker import Faker
fake = Faker()

fake.date_between(start_date='today', end_date='+30y')
# datetime.date(2025, 3, 12)

fake.date_time_between(start_date='-30y', end_date='now')
# datetime.datetime(2007, 2, 28, 11, 28, 16)

# Or if you need a more specific date boundaries, provide the start 
# and end dates explicitly.
import datetime
start_date = datetime.date(year=2015, month=1, day=1)
fake.date_between(start_date=start_date, end_date='+30y')

이전 답변

레이더를 사용하는 것은 매우 간단합니다

설치

pip install radar

용법

import datetime

import radar 

# Generate random datetime (parsing dates from str values)
radar.random_datetime(start='2000-05-24', stop='2013-05-24T23:59:59')

# Generate random datetime from datetime.datetime values
radar.random_datetime(
    start = datetime.datetime(year=2000, month=5, day=24),
    stop = datetime.datetime(year=2013, month=5, day=24)
)

# Just render some random datetime. If no range is given, start defaults to 
# 1970-01-01 and stop defaults to datetime.datetime.now()
radar.random_datetime()

3
가짜 모듈 제안에 대한 공감. 프로필을 생성하는 데 사용했지만 날짜 유틸리티를 사용하지 않았습니다. 가짜는 테스트하는 동안 매우 좋은 모듈입니다.
Gahan

이 형식으로 출력을 받고 있지만 다음 datetime.date(2039, 3, 16)과 같은 출력을 원합니다 2039-03-16. 그렇게하는 방법?
Ayush Kumar

당신은 문자열을 원하십니까? 매우 쉽습니다 (그에 따라 형식을 지정하십시오) fake.date_between(start_date='today', end_date='+30y').strftime('%Y-%m-%d').
Artur Barseghyan

1
설치해야하는 경우에도 놀라운 라이브러리 사용에 대한 찬성. 이것은 구현의 복잡성을 본질적으로 4 줄로 줄입니다.
Blairg23 23

1
@ KubiK888 : 물론, 내 업데이트 답변을 참조하십시오. start_date를 명시 적으로 제공해야합니다.
Artur Barseghyan

24

이것은 다른 접근법입니다-그런 종류의 작품 ..

from random import randint
import datetime

date=datetime.date(randint(2005,2025), randint(1,12),randint(1,28))

더 나은 접근법

startdate=datetime.date(YYYY,MM,DD)
date=startdate+datetime.timedelta(randint(1,365))

1
첫 번째 접근 방식은 29, 30 또는 31 일로 끝나는 날짜를 절대 선택하지 않으며 두 번째 접근 방식은 연도가 366 일인 윤년을 고려하지 않습니다. 즉 startdate, 윤년에 12 월 31 일까지 + 1 년이 지나면 코드는 정확히 1 년 후 같은 날짜를 선택하지 않습니다. 두 가지 방법 모두 시작 날짜와 몇 년 후에 만 ​​지정할 수있는 반면, 두 날짜를 지정하는 것에 대한 질문과 내 의견으로는 더 유용한 API입니다.
Boris

15

Python 3 timedelta은 float와의 곱셈을 지원하므로 이제 다음을 수행 할 수 있습니다.

import random
random_date = start + (end - start) * random.random()

주어진 startend유형의 수 있습니다 datetime.datetime. 예를 들어 다음 날에 임의의 날짜 시간을 생성하려면 다음을 수행하십시오.

import random
from datetime import datetime, timedelta

start = datetime.now()
end = start + timedelta(days=1)
random_date = start + (end - start) * random.random()

6

팬더 기반 솔루션을 칩킹하려면 다음을 사용하십시오.

import pandas as pd
import numpy as np

def random_date(start, end, position=None):
    start, end = pd.Timestamp(start), pd.Timestamp(end)
    delta = (end - start).total_seconds()
    if position is None:
        offset = np.random.uniform(0., delta)
    else:
        offset = position * delta
    offset = pd.offsets.Second(offset)
    t = start + offset
    return t

나는 pd.Timestamp다른 물건과 형식을 던질 수 있는 멋진 기능 때문에 그것을 좋아 합니다. 다음 몇 가지 예를 고려하십시오 ...

당신의 서명.

>>> random_date(start="1/1/2008 1:30 PM", end="1/1/2009 4:50 AM", position=0.34)
Timestamp('2008-05-04 21:06:48', tz=None)

무작위 위치.

>>> random_date(start="1/1/2008 1:30 PM", end="1/1/2009 4:50 AM")
Timestamp('2008-10-21 05:30:10', tz=None)

다른 형식.

>>> random_date('2008-01-01 13:30', '2009-01-01 4:50')
Timestamp('2008-11-18 17:20:19', tz=None)

팬더 / 날짜 시간 객체를 직접 전달합니다.

>>> random_date(pd.datetime.now(), pd.datetime.now() + pd.offsets.Hour(3))
Timestamp('2014-03-06 14:51:16.035965', tz=None)

그리고 어떻게 무작위 날짜 시간 시리즈를 우아하게 만들 것입니까 (즉, 각 요소에 대해 기능을 반복하지 않고)?
dmvianna

글쎄, 함수를 수정하여 delta값 의 배열을 생성하고 한 번에 모두 타임 스탬프에 매핑 할 수 있습니다. 개인적으로, 나는 단지 같은 것을하고 싶습니다 pd.Series([5] * 10, [random_date('2014-01-01', '2014-01-30') for i in range(10)]).
metakermit

3

다음은이 질문의 본문이 아닌 제목의 문자 적 ​​의미에 대한 답변입니다.

import time
import datetime
import random

def date_to_timestamp(d) :
  return int(time.mktime(d.timetuple()))

def randomDate(start, end):
  """Get a random date between two dates"""

  stime = date_to_timestamp(start)
  etime = date_to_timestamp(end)

  ptime = stime + random.random() * (etime - stime)

  return datetime.date.fromtimestamp(ptime)

이 코드는 허용되는 답변을 기반으로합니다.


포괄적 인 범위를 생성 ptime = random.randint(stime, etime)하기 때문에 마지막 두 번째 줄을 조금 더 정확하게 변경할 수 있습니다 randint.
보리스

3

을 사용할 수 있습니다 Mixer.

pip install mixer

과,

from mixer import generators as gen
print gen.get_datetime(min_datetime=(1900, 1, 1, 0, 0, 0), max_datetime=(2020, 12, 31, 23, 59, 59))

1
문법은 약간 바뀌었고, 위의 방법을 잘 모르겠지만, 장고 오브젝트는 다음과 같이 임의의 날짜를 client = mixer.blend(Client, date=mixer.RANDOM)
채 웁니다

@tutuDajuju : 고객이 무엇을 의미합니까?
Nima Soroush

그들의 문서 에 따르면 Django, SQLAlchemy 또는 Mongoengine 모델 클래스가 될 수 있습니다.
tutuDajuju

2
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""Create random datetime object."""

from datetime import datetime
import random


def create_random_datetime(from_date, to_date, rand_type='uniform'):
    """
    Create random date within timeframe.

    Parameters
    ----------
    from_date : datetime object
    to_date : datetime object
    rand_type : {'uniform'}

    Examples
    --------
    >>> random.seed(28041990)
    >>> create_random_datetime(datetime(1990, 4, 28), datetime(2000, 12, 31))
    datetime.datetime(1998, 12, 13, 23, 38, 0, 121628)
    >>> create_random_datetime(datetime(1990, 4, 28), datetime(2000, 12, 31))
    datetime.datetime(2000, 3, 19, 19, 24, 31, 193940)
    """
    delta = to_date - from_date
    if rand_type == 'uniform':
        rand = random.random()
    else:
        raise NotImplementedError('Unknown random mode \'{}\''
                                  .format(rand_type))
    return from_date + rand * delta


if __name__ == '__main__':
    import doctest
    doctest.testmod()

2

날짜를 타임 스탬프로 변환하고 타임 스탬프를 호출 random.randint한 다음 임의로 생성 된 타임 스탬프를 다시 날짜로 변환하십시오.

from datetime import datetime
import random

def random_date(first_date, second_date):
    first_timestamp = int(first_date.timestamp())
    second_timestamp = int(second_date.timestamp())
    random_timestamp = random.randint(first_timestamp, second_timestamp)
    return datetime.fromtimestamp(random_timestamp)

그러면 이렇게 사용할 수 있습니다

from datetime import datetime

d1 = datetime.strptime("1/1/2018 1:30 PM", "%m/%d/%Y %I:%M %p")
d2 = datetime.strptime("1/1/2019 4:50 AM", "%m/%d/%Y %I:%M %p")

random_date(d1, d2)

random_date(d2, d1)  # ValueError because the first date comes after the second date

시간대에 관심이 있다면 다른 답변에서 이미 제안 했듯이이 코드를 가져온 라이브러리 date_time_between_dates에서 사용해야 합니다.Faker


1
  1. 입력 날짜를 숫자로 변환하십시오 (int, float, 용도에 가장 적합한 것)
  2. 두 날짜 숫자 중에서 숫자를 선택하십시오.
  3. 이 숫자를 다시 날짜로 변환하십시오.

날짜를 숫자로 변환하거나 숫자로 변환하는 많은 알고리즘이 이미 많은 운영 체제에서 사용 가능합니다.


1

난수는 무엇이 필요합니까? 일반적으로 (언어에 따라) 날짜로부터 Epoch에서 초 / 밀리 초 수를 얻을 수 있습니다. 따라서 startDate와 endDate 사이의 임의의 날짜에 대해 다음을 수행 할 수 있습니다.

  1. startDate와 endDate 사이의 시간을 ms 단위로 계산합니다 (endDate.toMilliseconds ()-startDate.toMilliseconds ())
  2. 0과 1에서 얻은 숫자 사이의 숫자를 생성하십시오.
  3. 시간 오프셋 = startDate.toMilliseconds () + 2에서 얻은 숫자로 새 날짜 생성

1

가장 쉬운 방법은 두 숫자를 타임 스탬프로 변환 한 다음 난수 생성기에서 최소 및 최대 경계로 설정하는 것입니다.

빠른 PHP 예제는 다음과 같습니다.

// Find a randomDate between $start_date and $end_date
function randomDate($start_date, $end_date)
{
    // Convert to timetamps
    $min = strtotime($start_date);
    $max = strtotime($end_date);

    // Generate random number using above bounds
    $val = rand($min, $max);

    // Convert back to desired date format
    return date('Y-m-d H:i:s', $val);
}

이 함수는 strtotime()날짜 시간 설명을 Unix 타임 스탬프로 변환하고 date()생성 된 임의의 타임 스탬프에서 유효한 날짜를 만드는 데 사용합니다.


누구나 파이썬으로 쓸 수 있다면 도움이 될 것입니다.
퀼비

1

다른 것을 추가하기 만하면됩니다.

datestring = datetime.datetime.strftime(datetime.datetime( \
    random.randint(2000, 2015), \
    random.randint(1, 12), \
    random.randint(1, 28), \
    random.randrange(23), \
    random.randrange(59), \
    random.randrange(59), \
    random.randrange(1000000)), '%Y-%m-%d %H:%M:%S')

하루 처리에는 몇 가지 고려 사항이 필요합니다. 28을 사용하면 보안 사이트에 있습니다.


1

다음은 모든 해상도에서 임의의 날짜 배열을 반환하는 emyller의 접근 방식으로 수정 된 솔루션입니다.

import numpy as np

def random_dates(start, end, size=1, resolution='s'):
    """
    Returns an array of random dates in the interval [start, end]. Valid 
    resolution arguments are numpy date/time units, as documented at: 
        https://docs.scipy.org/doc/numpy-dev/reference/arrays.datetime.html
    """
    start, end = np.datetime64(start), np.datetime64(end)
    delta = (end-start).astype('timedelta64[{}]'.format(resolution))
    delta_mat = np.random.randint(0, delta.astype('int'), size)
    return start + delta_mat.astype('timedelta64[{}]'.format(resolution))

이 방법에 대한 좋은 점 중 하나 np.datetime64는 날짜에 일을 강제하는 것입니다. 시작 / 종료 날짜를 문자열, 날짜 시간, 팬더 타임 스탬프로 지정할 수 있습니다. 거의 모든 것이 작동합니다.


0

개념적으로는 매우 간단합니다. 사용하는 언어에 따라 해당 날짜를 참조 32 또는 64 비트 정수로 변환 할 수 있으며 일반적으로 다른 임의의 날짜 이후 "Unix time"또는 밀리 초라고 알려진 에포크 (1970 년 1 월 1 일) 이후의 초를 나타냅니다. 이 두 값 사이에 임의의 32 또는 64 비트 정수를 생성하면됩니다. 이것은 모든 언어에서 하나의 라이너 여야합니다.

일부 플랫폼에서는 시간을 이중으로 생성 할 수 있습니다 (날짜는 정수 부분, 시간은 소수 부분은 하나의 구현 임). 단정도 또는 배정도 부동 소수점 숫자 (C, Java 및 기타 언어의 "floats"또는 "doubles")를 처리하는 경우를 제외하고 동일한 원칙이 적용됩니다. 차이를 빼고 난수 (0 <= r <= 1)를 곱한 다음 시작 시간에 더하고 완료합니다.


0

파이썬에서 :

>>> from dateutil.rrule import rrule, DAILY
>>> import datetime, random
>>> random.choice(
                 list(
                     rrule(DAILY, 
                           dtstart=datetime.date(2009,8,21), 
                           until=datetime.date(2010,10,12))
                     )
                 )
datetime.datetime(2010, 2, 1, 0, 0)

(파이썬 dateutil라이브러리 필요 – pip install python-dateutil)


0

ApacheCommonUtils를 사용하여 지정된 범위 내에서 임의의 long을 생성 한 다음 해당 long을 벗어난 Date를 작성하십시오.

예:

import org.apache.commons.math.random.RandomData;

import org.apache.commons.math.random.RandomDataImpl;

공개 날짜 nextDate (날짜 최소, 최대 날짜) {

RandomData randomData = new RandomDataImpl();

return new Date(randomData.nextLong(min.getTime(), max.getTime()));

}


1
질문은 "python"
David Marx

0

나는 무작위와 시간을 사용하여 다른 프로젝트를 위해 이것을 만들었습니다. 난 당신이 문서를 볼 수있는 시간에서 일반 형식을 사용 여기 의 strftime의 첫 번째 인수 (). 두 번째 부분은 random.randrange 함수입니다. 인수 사이의 정수를 리턴합니다. 원하는 문자열과 일치하는 범위로 변경하십시오. 당신은 두 번째 arugment의 튜플에 좋은 주장이 있어야합니다.

import time
import random


def get_random_date():
    return strftime("%Y-%m-%d %H:%M:%S",(random.randrange(2000,2016),random.randrange(1,12),
    random.randrange(1,28),random.randrange(1,24),random.randrange(1,60),random.randrange(1,60),random.randrange(1,7),random.randrange(0,366),1))

0

팬더 + numpy 솔루션

import pandas as pd
import numpy as np

def RandomTimestamp(start, end):
    dts = (end - start).total_seconds()
    return start + pd.Timedelta(np.random.uniform(0, dts), 's')

dts는 초 단위의 타임 스탬프 (float) 차이입니다. 그런 다음 시작 타임 스탬프에 추가되는 0에서 dts 사이의 팬더 타임 델타를 만드는 데 사용됩니다.


0

mouviciel의 답변을 바탕으로 numpy를 사용한 벡터화 솔루션이 있습니다. 시작 날짜와 종료 날짜를 정수로 변환하고 그 사이에 임의의 숫자 배열을 생성 한 다음 전체 배열을 날짜로 다시 변환합니다.

import time
import datetime
import numpy as np

n_rows = 10

start_time = "01/12/2011"
end_time = "05/08/2017"

date2int = lambda s: time.mktime(datetime.datetime.strptime(s,"%d/%m/%Y").timetuple())
int2date = lambda s: datetime.datetime.fromtimestamp(s).strftime('%Y-%m-%d %H:%M:%S')

start_time = date2int(start_time)
end_time = date2int(end_time)

random_ints = np.random.randint(low=start_time, high=end_time, size=(n_rows,1))
random_dates = np.apply_along_axis(int2date, 1, random_ints).reshape(n_rows,1)

print random_dates

0

@ (Tom Alsberg)의 수정 된 방법입니다. 밀리 초로 날짜를 얻도록 수정했습니다.

import random
import time
import datetime

def random_date(start_time_string, end_time_string, format_string, random_number):
    """
    Get a time at a proportion of a range of two formatted times.
    start and end should be strings specifying times formated in the
    given format (strftime-style), giving an interval [start, end].
    prop specifies how a proportion of the interval to be taken after
    start.  The returned time will be in the specified format.
    """
    dt_start = datetime.datetime.strptime(start_time_string, format_string)
    dt_end = datetime.datetime.strptime(end_time_string, format_string)

    start_time = time.mktime(dt_start.timetuple()) + dt_start.microsecond / 1000000.0
    end_time = time.mktime(dt_end.timetuple()) + dt_end.microsecond / 1000000.0

    random_time = start_time + random_number * (end_time - start_time)

    return datetime.datetime.fromtimestamp(random_time).strftime(format_string)

예:

print TestData.TestData.random_date("2000/01/01 00:00:00.000000", "2049/12/31 23:59:59.999999", '%Y/%m/%d %H:%M:%S.%f', random.random())

산출: 2028/07/08 12:34:49.977963


0
start_timestamp = time.mktime(time.strptime('Jun 1 2010  01:33:00', '%b %d %Y %I:%M:%S'))
end_timestamp = time.mktime(time.strptime('Jun 1 2017  12:33:00', '%b %d %Y %I:%M:%S'))
time.strftime('%b %d %Y %I:%M:%S',time.localtime(randrange(start_timestamp,end_timestamp)))

보내다


0
    # needed to create data for 1000 fictitious employees for testing code 
    # code relating to randomly assigning forenames, surnames, and genders
    # has been removed as not germaine to the question asked above but FYI
    # genders were randomly assigned, forenames/surnames were web scrapped,
    # there is no accounting for leap years, and the data stored in mySQL

    import random 
    from datetime import datetime
    from datetime import timedelta

    for employee in range(1000):
        # assign a random date of birth (employees are aged between sixteen and sixty five)
        dlt = random.randint(365*16, 365*65)
        dob = datetime.today() - timedelta(days=dlt)
        # assign a random date of hire sometime between sixteenth birthday and yesterday
        doh = datetime.today() - timedelta(days=random.randint(1, dlt-365*16))
        print("born {} hired {}".format(dob.strftime("%d-%m-%y"), doh.strftime("%d-%m-%y")))

0

다른 방법은 사용하여 두 날짜 사이의 임의의 날짜를 만들 수 있습니다 np.random.randint(), pd.Timestamp().valuepd.to_datetime()for loop:

# Import libraries
import pandas as pd

# Initialize
start = '2020-01-01' # Specify start date
end = '2020-03-10' # Specify end date
n = 10 # Specify number of dates needed

# Get random dates
x = np.random.randint(pd.Timestamp(start).value, pd.Timestamp(end).value,n)
random_dates = [pd.to_datetime((i/10**9)/(60*60)/24, unit='D').strftime('%Y-%m-%d')  for i in x]

print(random_dates)

산출

['2020-01-06',
 '2020-03-08',
 '2020-01-23',
 '2020-02-03',
 '2020-01-30',
 '2020-01-05',
 '2020-02-16',
 '2020-03-08',
 '2020-02-09',
 '2020-01-04']
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.