DateTime2 vs DateTime en SQL Server


762

Cúal:

Cuál es la forma recomendada de almacenar la fecha y la hora en SQL Server 2008+?

Soy consciente de las diferencias en la precisión (y el espacio de almacenamiento probablemente), pero ignorando esas por ahora, ¿hay un documento de mejores prácticas sobre cuándo usar qué, o tal vez deberíamos usar datetime2solo?

Respuestas:


641

La documentación de MSDN para datetime recomienda usar datetime2 . Aquí está su recomendación:

Utilice los time, date, datetime2y datetimeoffsetlos tipos de datos para un nuevo trabajo. Estos tipos se alinean con el estándar SQL. Son más portables. time, datetime2y datetimeoffset proporciona más segundos de precisión. datetimeoffsetproporciona soporte de zona horaria para aplicaciones implementadas globalmente.

datetime2 tiene un rango de fechas más grande, una precisión fraccional predeterminada más grande y una precisión opcional especificada por el usuario. Además, dependiendo de la precisión especificada por el usuario, puede usar menos almacenamiento.


46
Si bien existe una mayor precisión con datetime2, algunos clientes no admiten date, time o datetime2 y lo obligan a convertir a un literal de cadena. Si le preocupa más la compatibilidad que la precisión, use datetime
FistOfFury el

44
Otra opción es usar una vista indizada con la columna convertida como fecha y hora para compatibilidad. Sin embargo, deberá poder apuntar la aplicación a la vista.
TamusJRoyce

99
El soporte de zona horaria con DATETIMEOFFSET es un nombre inapropiado. Solo almacena un desplazamiento UTC por un instante específico en el tiempo, no una zona horaria.
Suncat2000

66
@Porad: ¿Cuál es exactamente el beneficio en la práctica de ser "" más portátil "debido a que es" Estándar SQL "? Además de hacer que escriba mucho más código que sea significativamente menos legible / mantenible para un" puerto "a otro RDBMS que es probable que nunca ocurra durante la vida útil de ese código. Además de las herramientas y controladores SQL Server proporcionados por Microsoft (si es que existen), ¿hay alguna aplicación que realmente confíe en las representaciones específicas a nivel de bit del DateTime2tipo (o cualquier otro SQL Server tipo para el caso) Ver los Contras en mi 10.07.17 respuesta por debajo de eso que estoy pidiendo?.
Tom

2
@ Adam Porad: Además, es probable que todos esos beneficios sean innecesarios (fuera de las aplicaciones de ingeniería o científicas) y, por lo tanto, no valen mucho la pérdida de beneficios, MUCHO más probable es que sea necesaria: la capacidad mucho más fácil (incluso considerando soluciones alternativas) para convertir implícita / explícitamente a un valor numérico de coma flotante (número de días, si corresponde, días fraccionales desde la fecha y hora mín.) para sumas, restas, mínimos, máximos y promedios. Vea los Contras en mi 7/10/17 Respuesta a continuación para más detalles.
Tom

493

DATETIME2tiene un rango de fechas de "0001/01/01" a " DATETIME9999/12/31 ", mientras que el tipo solo admite el año 1753-9999.

Además, si lo necesita, DATETIME2puede ser más preciso en términos de tiempo; DATETIME está limitado a 3 1/3 milisegundos, mientras que DATETIME2puede ser preciso hasta 100ns.

Ambos tipos se asignan a System.DateTime.NET, no hay diferencia allí.

Si tiene la opción, le recomendaría usar DATETIME2siempre que sea posible. No veo ningún beneficio al usar DATETIME(excepto la compatibilidad con versiones anteriores): tendrá menos problemas (con fechas fuera de rango y problemas como ese).

Además: si solo necesita la fecha (sin parte de tiempo), use DATE, ¡es tan bueno como DATETIME2y también le ahorra espacio! :-) Lo mismo ocurre solo con el tiempo - uso TIME. ¡Para eso están estos tipos!


158
Tenga cuidado al agregar un valor .NET DateTime como parámetro a un SqlCommand, porque le gusta asumir que es el tipo de fecha y hora anterior, y obtendrá un error si intenta escribir un valor DateTime que esté fuera de ese rango de años 1753-9999 a menos que especifique explícitamente el tipo como System.Data.SqlDbType.DateTime2 para el SqlParameter. De todos modos, datetime2 es genial, porque puede almacenar cualquier valor que se pueda almacenar en el tipo .NET DateTime.
Triynko

10
@marc_s - ¿No es eso para lo que es nulo?
JohnFx

8
@JohnFX, un poco tarde aquí, pero no establecerías una fecha y hora como nula. usarías Nullable <datetime> o datetime? que maneja nulo bien, y en la asignación a un proceso simplemente haría param.value = someDateTime ?? DBValue.Null Su desafortunado que estamos atascados con un tipo de datos con un número después de que - sólo parece tan 'genérico':)
Adam Tuliper - MSFT

69
Lol, solo intenté votar mi propio comentario (arriba), antes de darme cuenta de que era mi propio comentario (hecho hace más de un año). Todavía estoy lidiando con la tonta decisión de diseño del marco .NET de TRUNCAR todos los valores de DateTime de forma predeterminada cuando se pasan como SqlParameters a menos que lo establezca explícitamente en el SqlDbType.DateTime2 más preciso. Demasiado para inferir automáticamente el tipo correcto. Realmente, deberían haber hecho el cambio transparente, reemplazando la implementación menos precisa, menos eficiente y de rango limitado, y manteniendo el nombre original del tipo "fecha y hora". Ver también stackoverflow.com/q/8421332/88409
Triynko

55
@marc_s ¿No es eso para lo que Nullable<DateTime>sirve?
ChrisW

207

datetime2 gana en la mayoría de los aspectos, excepto (compatibilidad de aplicaciones antiguas)

  1. mayor rango de valores
  2. mejor precisión
  3. menor espacio de almacenamiento (si se especifica la precisión opcional especificada por el usuario)

Comparación de tipos de datos de fecha y hora de SQL: datetime, datetime2, date, TIME

tenga en cuenta los siguientes puntos

  • Sintaxis
    • datetime2 [(precisión de segundos fraccionarios => Buscar debajo del tamaño de almacenamiento)]
  • Escala de precisión
    • 0 a 7 dígitos, con una precisión de 100ns.
    • La precisión predeterminada es de 7 dígitos.
  • Tamaño de almacenamiento
    • 6 bytes para precisión menor que 3;
    • 7 bytes para precisión 3 y 4.
    • Todas las demás precisiones requieren 8 bytes .
  • DateTime2 (3) tiene el mismo número de dígitos que DateTime pero usa 7 bytes de almacenamiento en lugar de 8 bytes ( SQLHINTS- DateTime Vs DateTime2 )
  • Encuentre más sobre datetime2 (artículo de Transact-SQL MSDN)

fuente de la imagen: MCTS Self-Pasion Training Kit (examen 70-432): Microsoft® SQL Server® 2008 - Implementación y mantenimiento Capítulo 3: Tablas -> Lección 1: Creación de tablas -> página 66


77
Gracias por mostrar que las estadísticas +1 por eso, datetime2es increíble (Ganador)
Pankaj Parkar

2
@Iman Abidi: Según el comentario de Oskar Berggren del 10 de septiembre de 2014 a las 3:51 pm en el artículo "SQLHINTS- DateTime Vs DateTime2" al que hizo referencia: "datetime2 (3) NO es lo mismo que datetime. Tendrán el mismo número de dígitos, pero la precisión de datetime es 3,33 ms, mientras que la precisión de datetime2 (3) es 1 ms ".
Tom

1
@PankajParkar: Woah, no tan rápido. Es posible que desee consultar la sección Contras de mi respuesta con fecha 7/10/17 a continuación.
Tom

¿Cómo datetime2utiliza menos espacio de almacenamiento datetimey ofrece un mayor alcance y una mayor precisión?
Dai

107

Estoy de acuerdo con @marc_s y @Adam_Poward: DateTime2 es el método preferido para avanzar. Tiene un rango más amplio de fechas, mayor precisión y utiliza igual o menos almacenamiento (dependiendo de la precisión).

Una cosa que la discusión se perdió, sin embargo ...
@Marc_s establece: Both types map to System.DateTime in .NET - no difference there. Esto es correcto, sin embargo, lo inverso no es cierto ... y es importante cuando se realizan búsquedas de rango de fechas (por ejemplo, "encuéntrame todos los registros modificados el 5/5/2010").

La versión de .NET Datetimetiene un rango y precisión similares a DateTime2. Cuando se asigna un .net Datetimeal antiguo SQL, se produceDateTime un redondeo implícito . El antiguo SQL DateTimetiene una precisión de 3 milisegundos. Esto significa que 11:59:59.997está lo más cerca posible al final del día. Algo más alto se redondea al día siguiente.

Prueba esto :

declare @d1 datetime   = '5/5/2010 23:59:59.999'
declare @d2 datetime2  = '5/5/2010 23:59:59.999'
declare @d3 datetime   = '5/5/2010 23:59:59.997'
select @d1 as 'IAmMay6BecauseOfRounding', @d2 'May5', @d3 'StillMay5Because2msEarlier'

Evitar este redondeo implícito es una razón importante para pasar a DateTime2. El redondeo implícito de fechas claramente causa confusión:


14
También puede evitar este redondeo si no intenta encontrar el "final" de un día de todos modos. > = 5 de mayo Y <6 de mayo es mucho más seguro y funcionará en cualquiera de los tipos de fecha / hora (excepto HORA, por supuesto). También sugiera evitar formatos regionales ambiguos como m / d / aaaa.
Aaron Bertrand

2
@AaronBertrand: totalmente de acuerdo, pero mirando la cantidad de preguntas que tenemos sobre el asunto, parecía que valía la pena describirlo.
EBarr

1
¿Por qué cambiaste de 20100505a 5/5/2010? El formato anterior funcionará con cualquier región en SQL Server. Este último se romperá: SET LANGUAGE French; SELECT Convert(datetime, '1/7/2015')oops:2015-07-01 00:00:00.000
ErikE

1
@EBarr: Re. "DateTime2 es el método preferido para avanzar. Tiene un rango más amplio de fechas, mayor precisión y utiliza igual o menos almacenamiento (dependiendo de la precisión": estoy totalmente en desacuerdo. Consulte la sección Contras de mi respuesta con fecha 7/10/17 a continuación En resumen, es probable que esos beneficios sean innecesarios (fuera de aplicaciones científicas / de ingeniería) y, por lo tanto, no valen la pérdida de beneficios MUCHO más probable que sea necesaria, la capacidad mucho más fácil (incluso teniendo en cuenta las soluciones alternativas) para convertir implícita / explícitamente a un valor numérico de punto flotante ( Número de días, si corresponde, valor de fracciones desde la fecha y hora mín. para +, - y promedio
Tom

20

Casi todas las respuestas y comentarios han sido pesados ​​en los pros y ligeros en los contras. Aquí hay un resumen de todos los pros y contras hasta ahora más algunos contras cruciales (en el n. ° 2 a continuación) que solo he mencionado una vez o no.

  1. PROS:

1.1. Más compatible con ISO (ISO 8601) (aunque no sé cómo esto entra en juego en la práctica).

1.2. Más rango (1/1/0001 a 31/12/9999 vs. 1/1 / 1753-12 / 31/9999) (aunque el rango adicional, todo antes del año 1753, probablemente no se utilizará, excepto por ej., en aplicaciones históricas, astronómicas, geológicas, etc.).

1.3. Coincide exactamente con el rango del rango de DateTimeTipo de .NET (aunque ambos se convierten de ida y vuelta sin una codificación especial si los valores están dentro del rango y la precisión del tipo de destino, excepto para Con # 2.1 a continuación, de lo contrario se producirá un error / redondeo).

1.4. Más precisión (100 nanosegundos, también conocido como 0.000,000,1 segundos, frente a 3,33 milisegundos, también conocido como 0,003,33 segundos) (aunque la precisión adicional probablemente no se utilizará, excepto, por ejemplo, en aplicaciones de ingeniería / científicas).

1.5. Cuando se configura para una precisión similar (como en 1 milisegundo no "igual" (como en 3.33 milisegundos) como Iman Abidi ha afirmado) DateTime, utiliza menos espacio (7 frente a 8 bytes), pero luego, por supuesto, estaría perdiendo el beneficio de precisión que probablemente sea uno de los dos (el otro es el rango) más promocionado, aunque probablemente beneficios innecesarios).

  1. CONTRAS:

2.1. Al pasar un parámetro a un .NET SqlCommand, debe especificar System.Data.SqlDbType.DateTime2si puede pasar un valor fuera del DateTimerango y / o precisión del SQL Server , ya que su valor predeterminado es System.Data.SqlDbType.DateTime.

2.2. No se puede convertir implícita / fácilmente a un valor numérico de coma flotante (# de días desde la fecha y hora mín.) Para hacer lo siguiente con / en las expresiones de SQL Server utilizando valores numéricos y operadores:

2.2.1. sumar o restar # de días o días parciales. Nota: Usar DateAddFunction como solución alternativa no es trivial cuando necesita considerar múltiples, si no todas, partes de la fecha y hora.

2.2.2. tome la diferencia entre dos fechas y horas para fines de cálculo de "edad". Nota: No puede simplemente usar la DateDifffunción de SQL Server en su lugar, porque no computa agecomo la mayoría de la gente esperaría si las dos fechas-hora cruzan un límite de fecha / hora de calendario / reloj de las unidades especificadas, aunque sea por una pequeña fracción de esa unidad, que va a devolver la diferencia como 1 de dicha unidad frente a 0. Por ejemplo, el DateDiffde Day's de dos pares fecha-hora a sólo 1 milisegundo aparte devolverá 1 vs 0 (días) si esos pares fecha-hora son en diferentes días calendario (es decir, "1999-12-31 23: 59: 59.9999999" y "2000-01-01 00: 00: 00.0000000"). La misma fecha y hora de diferencia de 1 milisegundo si se mueve para que no crucen un día calendario, devolverá un "DateDiff" en Day's de 0 (días).

2.2.3. tome la Avgfecha-hora (en una Consulta Agregada) simplemente convirtiendo a "Flotar" primero y luego nuevamente a DateTime.

NOTA: Para convertir DateTime2a un valor numérico, debe hacer algo como la siguiente fórmula, que aún asume que sus valores no son inferiores al año 1970 (lo que significa que está perdiendo todo el rango adicional más otros 217 años. Nota: puede no podrá simplemente ajustar la fórmula para permitir un rango adicional porque puede encontrarse con problemas de desbordamiento numérico.

25567 + (DATEDIFF(SECOND, {d '1970-01-01'}, @Time) + DATEPART(nanosecond, @Time) / 1.0E + 9) / 86400.0- Fuente: " https://siderite.dev/blog/how-to-translate-t-sql-datetime2-to.html "

Por supuesto, también podría Casta DateTimeprimera (y si es necesario volver de nuevo a DateTime2), pero se perdería la precisión y alcance (todos los anteriores a 1753 años) beneficios de la DateTime2frente DateTimeque son Prolly la 2 más grande y también, al mismo tiempo Prolly el 2 menos probable que sea necesario, lo que plantea la pregunta de por qué usarlo cuando pierde las conversiones implícitas / fáciles a números numéricos de punto flotante (# de días) para beneficio de suma / resta / "edad" (vs. DateDiff) / Avgcalcs, que es uno grande en mi experiencia.

Por cierto, la Avgfecha-hora es (o al menos debería ser) un caso de uso importante. a) Además del uso para obtener la duración promedio cuando las fechas-hora (ya que una fecha-hora base común) se usan para representar la duración (una práctica común), b) también es útil obtener una estadística de tipo panel de control sobre cuál es la fecha promedio- hora está en la columna de fecha y hora de un rango / grupo de Filas. c) Una consulta estándar (o al menos debería ser estándar) ad-hoc para monitorear / solucionar problemas en una columna que puede no ser válida alguna vez / por más tiempo y / o que deba ser desaprobada es enumerar para cada valor el recuento de ocurrencias y (si está disponible) los Min, Avgy Maxsellos de fecha y hora asociados con ese valor.


1
Al igual que la vista contraria, señala el lado c # de la ecuación. En combinación con todos los otros "profesionales", permitirá a las personas tomar una buena decisión en función de dónde quieren llevar su dolor.
EBarr

1
@EBarr: Solo la parte Contras # 1 de mi "'vista contraria'" "señala el lado c # de la ecuación". El resto (Cons # 's 2.2.1 - 2.2.3), que como dije son los beneficios necesarios (de DateTime) mucho más probables , están relacionados con los efectos en las consultas y declaraciones de SQL Server.
Tom

Re 2.2.1 - se considera una práctica insegura hacer aritmética en fechas, y la forma preferida es siempre usar DateAdd y funciones relacionadas. Esta es la mejor práctica. Hay responsabilidades serias para hacer aritmética de fechas, y la menor de ellas es que no funciona para la mayoría de los tipos de fechas. Algunos artículos: sqlservercentral.com/blogs/… sqlblog.org/2011/09/20/…
RBerman

@RBerman: Re. "inseguro": solo es inseguro con ciertos tipos de fechas (como el DateTime2que ya mencioné (debido a la alta probabilidad de desbordamiento)). Re. "no funciona para la mayoría de los tipos de fecha": solo necesita que funcione con uno, y la mayoría de las fechas en la mayoría de las aplicaciones probablemente nunca necesitarán convertirse a otro tipo de fecha para toda su vida (excepto tal vez, como también mencioné , DateTime2a DateTime(por ejemplo, para hacer "la aritmética de fechas"; P) Teniendo en cuenta que, no vale la pena todo el extra de codificación no sólo programar sino también consultas ad-hoc de investigación para utilizar un no-aritmética tipo de fecha amigable..
Tom

15

DateTime2 causa estragos si es un desarrollador de Access que intenta escribir Now () en el campo en cuestión. Acabo de hacer una migración de Access -> SQL 2008 R2 y puso todos los campos de fecha y hora como DateTime2. Agregar un registro con Now () como el valor bombardeado. Estuvo bien el 1/1/2012 2:53:04 PM, pero no el 1/10/2012 2:53:04 PM.

Una vez que el personaje hizo la diferencia. Espero que ayude a alguien.


15

Aquí hay un ejemplo que le mostrará las diferencias en el tamaño de almacenamiento (bytes) y la precisión entre smalldatetime, datetime, datetime2 (0) y datetime2 (7):

DECLARE @temp TABLE (
    sdt smalldatetime,
    dt datetime,
    dt20 datetime2(0),
    dt27 datetime2(7)
)

INSERT @temp
SELECT getdate(),getdate(),getdate(),getdate()

SELECT sdt,DATALENGTH(sdt) as sdt_bytes,
    dt,DATALENGTH(dt) as dt_bytes,
    dt20,DATALENGTH(dt20) as dt20_bytes,
    dt27, DATALENGTH(dt27) as dt27_bytes FROM @temp

que vuelve

sdt                  sdt_bytes  dt                       dt_bytes  dt20                 dt20_bytes  dt27                         dt27_bytes
2015-09-11 11:26:00  4          2015-09-11 11:25:42.417  8         2015-09-11 11:25:42  6           2015-09-11 11:25:42.4170000  8

Entonces, si quiero almacenar información hasta el segundo, pero no hasta el milisegundo, puedo guardar 2 bytes cada uno si uso datetime2 (0) en lugar de datetime o datetime2 (7).


10

Si bien existe una mayor precisión con datetime2 , algunos clientes no admiten date , time o datetime2 y lo obligan a convertir a un literal de cadena. Específicamente, Microsoft menciona problemas ODBC, OLE DB, JDBC y SqlClient de "nivel inferior" con estos tipos de datos y tiene un gráfico que muestra cómo cada uno puede mapear el tipo.

Si se compara el valor con la precisión, use datetime


10

Pregunta anterior ... Pero quiero agregar algo que no haya dicho nadie aquí ... (Nota: esta es mi propia observación, así que no solicite ninguna referencia)

Datetime2 es más rápido cuando se usa en criterios de filtro.

TLDR:

En SQL 2016 tenía una tabla con cien mil filas y una columna de fecha y hora ENTRY_TIME porque era necesario almacenar el tiempo exacto en segundos. Mientras ejecutaba una consulta compleja con muchas combinaciones y una subconsulta, cuando usaba la cláusula where como:

WHERE ENTRY_TIME >= '2017-01-01 00:00:00' AND ENTRY_TIME < '2018-01-01 00:00:00'

La consulta estuvo bien inicialmente cuando había cientos de filas, pero cuando aumentó el número de filas, la consulta comenzó a dar este error:

Execution Timeout Expired. The timeout period elapsed prior
to completion of the operation or the server is not responding.

Eliminé la cláusula where, e inesperadamente, la consulta se ejecutó en 1 segundo, aunque ahora se obtuvieron TODAS las filas para todas las fechas. Ejecuté la consulta interna con la cláusula where, y tardó 85 segundos, y sin la cláusula where tardó 0.01 segundos.

Encontré muchos hilos aquí para este problema como rendimiento de filtrado de fecha y hora

Optimicé la consulta un poco. Pero la velocidad real que obtuve fue cambiando la columna datetime a datetime2.

Ahora, la misma consulta que expiró anteriormente lleva menos de un segundo.

salud


9

La interpretación de las cadenas de fecha datetimey también datetime2puede ser diferente cuando se usan DATEFORMATconfiguraciones que no son de EE . UU . P.ej

set dateformat dmy
declare @d datetime, @d2 datetime2
select @d = '2013-06-05', @d2 = '2013-06-05'
select @d, @d2

Esto devuelve 2013-05-06(es decir, 6 de mayo) para datetimey 2013-06-05(es decir, 5 de junio) para datetime2. Sin embargo, con dateformatset to mdy, both @dy @d2return 2013-06-05.

El datetimecomportamiento parece estar en desacuerdo con la documentación de MSDN de SET DATEFORMATqué estados: Algunos formatos de cadenas de caracteres, por ejemplo ISO 8601, se interpretan independientemente de la configuración DATEFORMAT . ¡Obviamente no es cierto!

Hasta que me mordió esto, siempre pensé que las yyyy-mm-ddfechas se manejarían correctamente, independientemente de la configuración de idioma / configuración regional.


2
No Para ISO 8601, creo que querías decir AAAAMMDD (sin guiones). SET LANGUAGE FRENCH; DECLARE @d DATETIME = '20130605'; SELECT @d;Inténtalo de nuevo con los guiones.
Aaron Bertrand

1
El estándar permite los formatos AAAA-MM-DD y AAAAMMDD para representaciones de fecha calendario. ¡Creo que MSDN debería ser más específico sobre qué subconjunto de la especificación ISO 8601 se interpreta de forma independiente!
Richard Fawcett

1
Lo sé, pero en SQL Server solo la sintaxis sin guión es segura.
Aaron Bertrand

6

Según este artículo , si desea tener la misma precisión de DateTime usando DateTime2, simplemente debe usar DateTime2 (3). Esto debería darle la misma precisión, ocupar uno menos bytes y proporcionar un rango ampliado.


Para ser claros, es la misma precisión que SQL datetime, no un .NET DateTime.
Sam Rueby

Eso es correcto, supuse que todos entenderían el contexto pero vale la pena declararlo específicamente.
jKlaus

4

Me encontré con una ventaja más para DATETIME2: evita un error en el adodbapimódulo Python , que explota si se pasa un datetimevalor de biblioteca estándar que tiene microsegundos distintos de cero para una DATETIMEcolumna pero funciona bien si la columna se define como DATETIME2.


0
Select ValidUntil + 1
from Documents

El SQL anterior no funcionará con un campo DateTime2. Se devuelve y error "Choque de tipo de operando: datetime2 es incompatible con int"

Agregar 1 para obtener el día siguiente es algo que los desarrolladores han estado haciendo con fechas durante años. Ahora Microsoft tiene un campo súper nuevo datetime2 que no puede manejar esta funcionalidad simple.

"Usemos este nuevo tipo que es peor que el anterior", ¡no lo creo!


2
Sólo para que quede claro aquí el datetimey datetime2tipos de datos fueron ambos introducidos en SQL Server 2008. También puede obtener Operand type clash: date is incompatible with inta partir del datetipo que ha existido desde los días de puntos. Sin dateadd(dd, 1, ...)embargo, los tres tipos de datos funcionan bien .
Siempre aprendiendo el

2
Esto no está claro. Tengo una base de datos SQLServer 2005 con un campo de fecha y hora.
Paul McCarthy

0

Creo que DATETIME2es la mejor manera de almacenar el date, porque tiene más eficiencia que el DATETIME. En SQL Server 2008puede usar DATETIME2, almacena una fecha y hora, tarda 6-8 bytesen almacenar y tiene una precisión de 100 nanoseconds. Por lo tanto cualquier persona que necesite una mayor precisión el tiempo va a querer DATETIME2.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.