@rvoh/dream 2.3.0-alpha.5 → 2.3.0-alpha.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (305) hide show
  1. package/dist/cjs/src/Dream.js +2 -40
  2. package/dist/cjs/src/cli/index.js +4 -0
  3. package/dist/cjs/src/dream/Query.js +14 -11
  4. package/dist/cjs/src/dream/QueryDriver/Base.js +0 -14
  5. package/dist/cjs/src/dream/QueryDriver/Kysely.js +86 -74
  6. package/dist/cjs/src/dream/QueryDriver/Postgres.js +10 -2
  7. package/dist/cjs/src/dream/internal/extractAssignableAssociationAttributes.js +9 -0
  8. package/dist/cjs/src/errors/db/DataIncompatibleWithDatabaseField.js +1 -3
  9. package/dist/cjs/src/helpers/areEqual.js +5 -0
  10. package/dist/cjs/src/helpers/cli/ASTBuilder.js +54 -2
  11. package/dist/cjs/src/helpers/cli/ASTKyselyCodegenEnhancer.js +84 -0
  12. package/dist/cjs/src/helpers/cli/ASTSchemaBuilder.js +17 -1
  13. package/dist/cjs/src/helpers/cli/generateFactoryContent.js +16 -0
  14. package/dist/cjs/src/helpers/cloneDeepSafe.js +21 -10
  15. package/dist/cjs/src/helpers/customPgParsers.js +18 -1
  16. package/dist/cjs/src/helpers/db/normalizeDataForDb.js +81 -0
  17. package/dist/cjs/src/helpers/db/types/helpers.js +5 -0
  18. package/dist/cjs/src/helpers/db/types/isDatetimeOrDatetimeArrayColumn.js +2 -1
  19. package/dist/cjs/src/helpers/sortBy.js +7 -5
  20. package/dist/cjs/src/helpers/sqlAttributes.js +4 -53
  21. package/dist/cjs/src/helpers/stringCasing.js +6 -5
  22. package/dist/cjs/src/helpers/toSafeObject.js +17 -0
  23. package/dist/cjs/src/package-exports/errors.js +2 -0
  24. package/dist/cjs/src/package-exports/index.js +2 -0
  25. package/dist/cjs/src/serializer/SerializerRenderer.js +11 -11
  26. package/dist/cjs/src/types/clocktime.js +1 -0
  27. package/dist/cjs/src/utils/datetime/BaseClockTime.js +363 -0
  28. package/dist/cjs/src/utils/datetime/CalendarDate.js +110 -119
  29. package/dist/cjs/src/utils/datetime/ClockTime.js +173 -0
  30. package/dist/cjs/src/utils/datetime/ClockTimeTz.js +232 -0
  31. package/dist/cjs/src/utils/datetime/DateTime.js +327 -221
  32. package/dist/cjs/src/utils/datetime/helpers/isoTimeDecimalString.js +8 -4
  33. package/dist/cjs/src/utils/datetime/helpers/replaceISOMicroseconds.js +4 -1
  34. package/dist/esm/src/Dream.js +2 -40
  35. package/dist/esm/src/cli/index.js +4 -0
  36. package/dist/esm/src/dream/Query.js +14 -11
  37. package/dist/esm/src/dream/QueryDriver/Base.js +0 -14
  38. package/dist/esm/src/dream/QueryDriver/Kysely.js +86 -74
  39. package/dist/esm/src/dream/QueryDriver/Postgres.js +10 -2
  40. package/dist/esm/src/dream/internal/extractAssignableAssociationAttributes.js +9 -0
  41. package/dist/esm/src/errors/db/DataIncompatibleWithDatabaseField.js +1 -3
  42. package/dist/esm/src/helpers/areEqual.js +5 -0
  43. package/dist/esm/src/helpers/cli/ASTBuilder.js +54 -2
  44. package/dist/esm/src/helpers/cli/ASTKyselyCodegenEnhancer.js +84 -0
  45. package/dist/esm/src/helpers/cli/ASTSchemaBuilder.js +17 -1
  46. package/dist/esm/src/helpers/cli/generateFactoryContent.js +16 -0
  47. package/dist/esm/src/helpers/cloneDeepSafe.js +21 -10
  48. package/dist/esm/src/helpers/customPgParsers.js +18 -1
  49. package/dist/esm/src/helpers/db/normalizeDataForDb.js +81 -0
  50. package/dist/esm/src/helpers/db/types/helpers.js +5 -0
  51. package/dist/esm/src/helpers/db/types/isDatetimeOrDatetimeArrayColumn.js +2 -1
  52. package/dist/esm/src/helpers/sortBy.js +7 -5
  53. package/dist/esm/src/helpers/sqlAttributes.js +4 -53
  54. package/dist/esm/src/helpers/stringCasing.js +6 -5
  55. package/dist/esm/src/helpers/toSafeObject.js +17 -0
  56. package/dist/esm/src/package-exports/errors.js +2 -0
  57. package/dist/esm/src/package-exports/index.js +2 -0
  58. package/dist/esm/src/serializer/SerializerRenderer.js +11 -11
  59. package/dist/esm/src/types/clocktime.js +1 -0
  60. package/dist/esm/src/utils/datetime/BaseClockTime.js +363 -0
  61. package/dist/esm/src/utils/datetime/CalendarDate.js +110 -119
  62. package/dist/esm/src/utils/datetime/ClockTime.js +173 -0
  63. package/dist/esm/src/utils/datetime/ClockTimeTz.js +232 -0
  64. package/dist/esm/src/utils/datetime/DateTime.js +327 -221
  65. package/dist/esm/src/utils/datetime/helpers/isoTimeDecimalString.js +8 -4
  66. package/dist/esm/src/utils/datetime/helpers/replaceISOMicroseconds.js +4 -1
  67. package/dist/types/src/Dream.d.ts +29 -33
  68. package/dist/types/src/dream/DreamClassTransactionBuilder.d.ts +9 -10
  69. package/dist/types/src/dream/DreamInstanceTransactionBuilder.d.ts +16 -16
  70. package/dist/types/src/dream/LeftJoinLoadBuilder.d.ts +1 -1
  71. package/dist/types/src/dream/LoadBuilder.d.ts +1 -1
  72. package/dist/types/src/dream/Query.d.ts +16 -16
  73. package/dist/types/src/dream/QueryDriver/Base.d.ts +0 -1
  74. package/dist/types/src/dream/QueryDriver/Kysely.d.ts +1 -0
  75. package/dist/types/src/dream/internal/associations/associationQuery.d.ts +1 -1
  76. package/dist/types/src/dream/internal/associations/associationUpdateQuery.d.ts +1 -1
  77. package/dist/types/src/dream/internal/associations/destroyAssociation.d.ts +1 -1
  78. package/dist/types/src/dream/internal/associations/throughAssociationHasOptionsBesidesThroughAndSource.d.ts +4 -2
  79. package/dist/types/src/dream/internal/associations/undestroyAssociation.d.ts +1 -1
  80. package/dist/types/src/dream/internal/extractAssignableAssociationAttributes.d.ts +3 -0
  81. package/dist/types/src/dream/internal/similarity/SimilarityBuilder.d.ts +7 -7
  82. package/dist/types/src/errors/db/DataIncompatibleWithDatabaseField.d.ts +2 -7
  83. package/dist/types/src/helpers/cli/ASTBuilder.d.ts +31 -0
  84. package/dist/types/src/helpers/cli/ASTKyselyCodegenEnhancer.d.ts +13 -0
  85. package/dist/types/src/helpers/customPgParsers.d.ts +5 -0
  86. package/dist/types/src/helpers/db/normalizeDataForDb.d.ts +6 -0
  87. package/dist/types/src/helpers/db/types/helpers.d.ts +5 -0
  88. package/dist/types/src/helpers/sort.d.ts +2 -1
  89. package/dist/types/src/helpers/sortBy.d.ts +3 -0
  90. package/dist/types/src/helpers/toSafeObject.d.ts +8 -0
  91. package/dist/types/src/package-exports/errors.d.ts +2 -0
  92. package/dist/types/src/package-exports/index.d.ts +2 -0
  93. package/dist/types/src/package-exports/types.d.ts +2 -1
  94. package/dist/types/src/types/associations/shared.d.ts +15 -13
  95. package/dist/types/src/types/associations/shared.ts +81 -41
  96. package/dist/types/src/types/calendardate.d.ts +22 -1
  97. package/dist/types/src/types/calendardate.ts +33 -1
  98. package/dist/types/src/types/clocktime.d.ts +22 -0
  99. package/dist/types/src/types/clocktime.ts +59 -0
  100. package/dist/types/src/types/datetime.d.ts +13 -18
  101. package/dist/types/src/types/datetime.ts +18 -21
  102. package/dist/types/src/types/dream.d.ts +27 -13
  103. package/dist/types/src/types/dream.ts +40 -14
  104. package/dist/types/src/types/variadic.d.ts +10 -9
  105. package/dist/types/src/types/variadic.ts +30 -5
  106. package/dist/types/src/utils/datetime/BaseClockTime.d.ts +287 -0
  107. package/dist/types/src/utils/datetime/CalendarDate.d.ts +65 -47
  108. package/dist/types/src/utils/datetime/ClockTime.d.ts +138 -0
  109. package/dist/types/src/utils/datetime/ClockTimeTz.d.ts +194 -0
  110. package/dist/types/src/utils/datetime/DateTime.d.ts +169 -66
  111. package/dist/types/src/utils/datetime/helpers/isoTimeDecimalString.d.ts +3 -2
  112. package/dist/types/src/utils/datetime/helpers/replaceISOMicroseconds.d.ts +4 -2
  113. package/docs/assets/navigation.js +1 -1
  114. package/docs/assets/search.js +1 -1
  115. package/docs/classes/db.DreamMigrationHelpers.html +9 -9
  116. package/docs/classes/db.KyselyQueryDriver.html +33 -34
  117. package/docs/classes/db.PostgresQueryDriver.html +34 -35
  118. package/docs/classes/db.QueryDriverBase.html +32 -33
  119. package/docs/classes/errors.CheckConstraintViolation.html +4 -6
  120. package/docs/classes/errors.ColumnOverflow.html +4 -6
  121. package/docs/classes/errors.CreateOrFindByFailedToCreateAndFind.html +3 -3
  122. package/docs/classes/errors.DataIncompatibleWithDatabaseField.html +4 -6
  123. package/docs/classes/errors.DataTypeColumnTypeMismatch.html +4 -6
  124. package/docs/classes/errors.GlobalNameNotSet.html +3 -3
  125. package/docs/classes/errors.InvalidCalendarDate.html +2 -2
  126. package/docs/classes/errors.InvalidClockTime.html +17 -0
  127. package/docs/classes/errors.InvalidClockTimeTz.html +17 -0
  128. package/docs/classes/errors.InvalidDateTime.html +2 -2
  129. package/docs/classes/errors.MissingSerializersDefinition.html +3 -3
  130. package/docs/classes/errors.NonLoadedAssociation.html +3 -3
  131. package/docs/classes/errors.NotNullViolation.html +4 -6
  132. package/docs/classes/errors.RecordNotFound.html +3 -3
  133. package/docs/classes/errors.ValidationError.html +3 -3
  134. package/docs/classes/index.CalendarDate.html +80 -92
  135. package/docs/classes/index.ClockTime.html +232 -0
  136. package/docs/classes/index.ClockTimeTz.html +253 -0
  137. package/docs/classes/index.DateTime.html +176 -173
  138. package/docs/classes/index.Decorators.html +19 -19
  139. package/docs/classes/index.Dream.html +127 -127
  140. package/docs/classes/index.DreamApp.html +5 -5
  141. package/docs/classes/index.DreamTransaction.html +2 -2
  142. package/docs/classes/index.Env.html +2 -2
  143. package/docs/classes/index.Query.html +71 -71
  144. package/docs/classes/system.CliFileWriter.html +2 -2
  145. package/docs/classes/system.DreamBin.html +2 -2
  146. package/docs/classes/system.DreamCLI.html +5 -5
  147. package/docs/classes/system.DreamImporter.html +2 -2
  148. package/docs/classes/system.DreamLogos.html +2 -2
  149. package/docs/classes/system.DreamSerializerBuilder.html +8 -8
  150. package/docs/classes/system.ObjectSerializerBuilder.html +8 -8
  151. package/docs/classes/system.PathHelpers.html +3 -3
  152. package/docs/classes/utils.Encrypt.html +2 -2
  153. package/docs/classes/utils.Range.html +2 -2
  154. package/docs/functions/db.closeAllDbConnections.html +1 -1
  155. package/docs/functions/db.dreamDbConnections.html +1 -1
  156. package/docs/functions/db.untypedDb.html +1 -1
  157. package/docs/functions/db.validateColumn.html +1 -1
  158. package/docs/functions/db.validateTable.html +1 -1
  159. package/docs/functions/errors.pgErrorType.html +1 -1
  160. package/docs/functions/index.DreamSerializer.html +1 -1
  161. package/docs/functions/index.ObjectSerializer.html +1 -1
  162. package/docs/functions/index.ReplicaSafe.html +1 -1
  163. package/docs/functions/index.STI.html +1 -1
  164. package/docs/functions/index.SoftDelete.html +1 -1
  165. package/docs/functions/utils.camelize.html +1 -1
  166. package/docs/functions/utils.capitalize.html +1 -1
  167. package/docs/functions/utils.cloneDeepSafe.html +1 -1
  168. package/docs/functions/utils.compact.html +1 -1
  169. package/docs/functions/utils.groupBy.html +1 -1
  170. package/docs/functions/utils.hyphenize.html +1 -1
  171. package/docs/functions/utils.intersection.html +1 -1
  172. package/docs/functions/utils.isEmpty.html +1 -1
  173. package/docs/functions/utils.normalizeUnicode.html +1 -1
  174. package/docs/functions/utils.pascalize.html +1 -1
  175. package/docs/functions/utils.percent.html +1 -1
  176. package/docs/functions/utils.range-1.html +1 -1
  177. package/docs/functions/utils.round.html +1 -1
  178. package/docs/functions/utils.sanitizeString.html +1 -1
  179. package/docs/functions/utils.snakeify.html +1 -1
  180. package/docs/functions/utils.sort.html +1 -1
  181. package/docs/functions/utils.sortBy.html +1 -1
  182. package/docs/functions/utils.sortObjectByKey.html +1 -1
  183. package/docs/functions/utils.sortObjectByValue.html +1 -1
  184. package/docs/functions/utils.uncapitalize.html +1 -1
  185. package/docs/functions/utils.uniq.html +1 -1
  186. package/docs/interfaces/openapi.OpenapiDescription.html +2 -2
  187. package/docs/interfaces/openapi.OpenapiSchemaProperties.html +1 -1
  188. package/docs/interfaces/openapi.OpenapiSchemaPropertiesShorthand.html +1 -1
  189. package/docs/interfaces/openapi.OpenapiTypeFieldObject.html +1 -1
  190. package/docs/interfaces/types.BelongsToStatement.html +2 -2
  191. package/docs/interfaces/types.DecoratorContext.html +2 -2
  192. package/docs/interfaces/types.DreamAppInitOptions.html +2 -2
  193. package/docs/interfaces/types.DreamAppOpts.html +2 -2
  194. package/docs/interfaces/types.DurationObject.html +5 -5
  195. package/docs/interfaces/types.EncryptOptions.html +2 -2
  196. package/docs/interfaces/types.InternalAnyTypedSerializerRendersMany.html +2 -2
  197. package/docs/interfaces/types.InternalAnyTypedSerializerRendersOne.html +2 -2
  198. package/docs/interfaces/types.SerializerRendererOpts.html +2 -2
  199. package/docs/modules/db.html +1 -1
  200. package/docs/modules/errors.html +3 -1
  201. package/docs/modules/index.html +3 -1
  202. package/docs/modules/openapi.html +1 -1
  203. package/docs/modules/system.html +1 -1
  204. package/docs/modules/types.html +3 -1
  205. package/docs/modules/utils.html +1 -1
  206. package/docs/types/openapi.CommonOpenapiSchemaObjectFields.html +1 -1
  207. package/docs/types/openapi.OpenapiAllTypes.html +1 -1
  208. package/docs/types/openapi.OpenapiFormats.html +1 -1
  209. package/docs/types/openapi.OpenapiNumberFormats.html +1 -1
  210. package/docs/types/openapi.OpenapiPrimitiveBaseTypes.html +1 -1
  211. package/docs/types/openapi.OpenapiPrimitiveTypes.html +1 -1
  212. package/docs/types/openapi.OpenapiSchemaArray.html +1 -1
  213. package/docs/types/openapi.OpenapiSchemaArrayShorthand.html +1 -1
  214. package/docs/types/openapi.OpenapiSchemaBase.html +1 -1
  215. package/docs/types/openapi.OpenapiSchemaBody.html +1 -1
  216. package/docs/types/openapi.OpenapiSchemaBodyShorthand.html +1 -1
  217. package/docs/types/openapi.OpenapiSchemaCommonFields.html +1 -1
  218. package/docs/types/openapi.OpenapiSchemaExpressionAllOf.html +1 -1
  219. package/docs/types/openapi.OpenapiSchemaExpressionAnyOf.html +1 -1
  220. package/docs/types/openapi.OpenapiSchemaExpressionOneOf.html +1 -1
  221. package/docs/types/openapi.OpenapiSchemaExpressionRef.html +1 -1
  222. package/docs/types/openapi.OpenapiSchemaExpressionRefSchemaShorthand.html +1 -1
  223. package/docs/types/openapi.OpenapiSchemaInteger.html +1 -1
  224. package/docs/types/openapi.OpenapiSchemaNull.html +1 -1
  225. package/docs/types/openapi.OpenapiSchemaNumber.html +1 -1
  226. package/docs/types/openapi.OpenapiSchemaObject.html +1 -1
  227. package/docs/types/openapi.OpenapiSchemaObjectAllOf.html +1 -1
  228. package/docs/types/openapi.OpenapiSchemaObjectAllOfShorthand.html +1 -1
  229. package/docs/types/openapi.OpenapiSchemaObjectAnyOf.html +1 -1
  230. package/docs/types/openapi.OpenapiSchemaObjectAnyOfShorthand.html +1 -1
  231. package/docs/types/openapi.OpenapiSchemaObjectBase.html +1 -1
  232. package/docs/types/openapi.OpenapiSchemaObjectBaseShorthand.html +1 -1
  233. package/docs/types/openapi.OpenapiSchemaObjectOneOf.html +1 -1
  234. package/docs/types/openapi.OpenapiSchemaObjectOneOfShorthand.html +1 -1
  235. package/docs/types/openapi.OpenapiSchemaObjectShorthand.html +1 -1
  236. package/docs/types/openapi.OpenapiSchemaPrimitiveGeneric.html +1 -1
  237. package/docs/types/openapi.OpenapiSchemaShorthandExpressionAllOf.html +1 -1
  238. package/docs/types/openapi.OpenapiSchemaShorthandExpressionAnyOf.html +1 -1
  239. package/docs/types/openapi.OpenapiSchemaShorthandExpressionOneOf.html +1 -1
  240. package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializableRef.html +1 -1
  241. package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializerRef.html +1 -1
  242. package/docs/types/openapi.OpenapiSchemaShorthandPrimitiveGeneric.html +1 -1
  243. package/docs/types/openapi.OpenapiSchemaString.html +1 -1
  244. package/docs/types/openapi.OpenapiShorthandAllTypes.html +1 -1
  245. package/docs/types/openapi.OpenapiShorthandPrimitiveBaseTypes.html +1 -1
  246. package/docs/types/openapi.OpenapiShorthandPrimitiveTypes.html +1 -1
  247. package/docs/types/openapi.OpenapiTypeField.html +1 -1
  248. package/docs/types/system.DreamAppAllowedPackageManagersEnum.html +1 -1
  249. package/docs/types/types.CalendarDateDurationUnit.html +1 -1
  250. package/docs/types/types.CalendarDateObject.html +2 -0
  251. package/docs/types/types.Camelized.html +1 -1
  252. package/docs/types/types.ClockTimeObject.html +2 -0
  253. package/docs/types/types.DbConnectionType.html +1 -1
  254. package/docs/types/types.DbTypes.html +1 -1
  255. package/docs/types/types.DreamAssociationMetadata.html +1 -1
  256. package/docs/types/types.DreamAttributes.html +1 -1
  257. package/docs/types/types.DreamClassAssociationAndStatement.html +1 -1
  258. package/docs/types/types.DreamClassColumn.html +1 -1
  259. package/docs/types/types.DreamColumn.html +1 -1
  260. package/docs/types/types.DreamColumnNames.html +1 -1
  261. package/docs/types/types.DreamLogLevel.html +1 -1
  262. package/docs/types/types.DreamLogger.html +1 -1
  263. package/docs/types/types.DreamModelSerializerType.html +1 -1
  264. package/docs/types/types.DreamOrViewModelClassSerializerKey.html +1 -1
  265. package/docs/types/types.DreamOrViewModelSerializerKey.html +1 -1
  266. package/docs/types/types.DreamParamSafeAttributes.html +1 -1
  267. package/docs/types/types.DreamParamSafeColumnNames.html +1 -1
  268. package/docs/types/types.DreamSerializable.html +1 -1
  269. package/docs/types/types.DreamSerializableArray.html +1 -1
  270. package/docs/types/types.DreamSerializerKey.html +1 -1
  271. package/docs/types/types.DreamSerializers.html +1 -1
  272. package/docs/types/types.DreamVirtualColumns.html +1 -1
  273. package/docs/types/types.DurationUnit.html +2 -4
  274. package/docs/types/types.EncryptAlgorithm.html +1 -1
  275. package/docs/types/types.HasManyStatement.html +1 -1
  276. package/docs/types/types.HasOneStatement.html +1 -1
  277. package/docs/types/types.Hyphenized.html +1 -1
  278. package/docs/types/types.Pascalized.html +1 -1
  279. package/docs/types/types.PrimaryKeyType.html +1 -1
  280. package/docs/types/types.RoundingPrecision.html +1 -1
  281. package/docs/types/types.SerializerCasing.html +1 -1
  282. package/docs/types/types.SimpleObjectSerializerType.html +1 -1
  283. package/docs/types/types.Snakeified.html +1 -1
  284. package/docs/types/types.StrictInterface.html +1 -1
  285. package/docs/types/types.UpdateableAssociationProperties.html +1 -1
  286. package/docs/types/types.UpdateableProperties.html +1 -1
  287. package/docs/types/types.ValidationType.html +1 -1
  288. package/docs/types/types.ViewModel.html +1 -1
  289. package/docs/types/types.ViewModelClass.html +1 -1
  290. package/docs/types/types.WeekdayName.html +1 -1
  291. package/docs/types/types.WhereStatementForDream.html +1 -1
  292. package/docs/types/types.WhereStatementForDreamClass.html +1 -1
  293. package/docs/variables/index.DreamConst.html +1 -1
  294. package/docs/variables/index.ops.html +1 -1
  295. package/docs/variables/openapi.openapiPrimitiveTypes-1.html +1 -1
  296. package/docs/variables/openapi.openapiShorthandPrimitiveTypes-1.html +1 -1
  297. package/docs/variables/system.DreamAppAllowedPackageManagersEnumValues.html +1 -1
  298. package/docs/variables/system.primaryKeyTypes.html +1 -1
  299. package/package.json +2 -2
  300. package/dist/cjs/src/helpers/db/types/isDateOrDateArrayColumn.js +0 -3
  301. package/dist/cjs/src/helpers/db/types/isTextOrTextArrayColumn.js +0 -3
  302. package/dist/esm/src/helpers/db/types/isDateOrDateArrayColumn.js +0 -3
  303. package/dist/esm/src/helpers/db/types/isTextOrTextArrayColumn.js +0 -3
  304. package/dist/types/src/helpers/db/types/isDateOrDateArrayColumn.d.ts +0 -2
  305. package/dist/types/src/helpers/db/types/isTextOrTextArrayColumn.d.ts +0 -2
@@ -4,9 +4,17 @@ import { microsecondParts } from './helpers/microsecondParts.js';
4
4
  import replaceISOMicroseconds from './helpers/replaceISOMicroseconds.js';
5
5
  export const Settings = LuxonSettings;
6
6
  Settings.throwOnInvalid = true;
7
+ export const BASE_DATE_OBJECT = {
8
+ year: 2000,
9
+ month: 1,
10
+ day: 1,
11
+ };
7
12
  /**
8
13
  * DateTime wraps Luxon DateTime with microsecond precision (0-999).
9
14
  * The decimal part in ISO/SQL is 6 digits: first 3 = milliseconds, next 3 = microseconds.
15
+ *
16
+ * Full datetime output (toISO, toSQL) is normalized to UTC.
17
+ * Time-only output (toISOTime, toSQLTime) omits timezone offset by default.
10
18
  */
11
19
  export class DateTime {
12
20
  luxonDatetime;
@@ -87,9 +95,6 @@ export class DateTime {
87
95
  get offset() {
88
96
  return this.luxonDatetime.offset;
89
97
  }
90
- get isValid() {
91
- return this.luxonDatetime.isValid;
92
- }
93
98
  get invalidReason() {
94
99
  return this.luxonDatetime.invalidReason;
95
100
  }
@@ -102,6 +107,9 @@ export class DateTime {
102
107
  get zone() {
103
108
  return this.luxonDatetime.zone;
104
109
  }
110
+ /**
111
+ * @internal
112
+ */
105
113
  constructor(luxonDatetime, microseconds = 0) {
106
114
  this.luxonDatetime = luxonDatetime;
107
115
  this._microseconds = microseconds;
@@ -127,8 +135,8 @@ export class DateTime {
127
135
  * const now = DateTime.now()
128
136
  * ```
129
137
  */
130
- static now() {
131
- return new DateTime(LuxonDateTime.now(), 0);
138
+ static now({ zone = 'UTC' } = {}) {
139
+ return new DateTime(LuxonDateTime.now().setZone(zone), 0);
132
140
  }
133
141
  // Format presets for toLocaleString()
134
142
  /**
@@ -353,18 +361,22 @@ export class DateTime {
353
361
  }
354
362
  static local(yearOrOpts, month, day, hour, minute, second, millisecond, microsecondOrOpts, opts) {
355
363
  const isOpts = (v) => typeof v === 'object' && v !== null;
356
- const { luxonDatetime, microseconds } = buildLocalOrUtcDateTime(yearOrOpts, month, day, hour, minute, second, millisecond, microsecondOrOpts, opts, isOpts, (y, m, d, h, mi, s, ms, options) => LuxonDateTime.local(y, m, d, h, mi, s, ms, options));
364
+ const { luxonDatetime, microseconds } = buildLocalOrUtcDateTime(yearOrOpts, month, day, hour, minute, second, millisecond, microsecondOrOpts, opts, isOpts, (y, m, d, h, mi, s, ms, opts) => LuxonDateTime.local(y, m, d, h, mi, s, ms, opts));
357
365
  return new DateTime(luxonDatetime, microseconds);
358
366
  }
359
- static utc(yearOrOpts, month, day, hour, minute, second, millisecond, microsecondOrOpts, options) {
367
+ static utc(yearOrOpts, month, day, hour, minute, second, millisecond, microsecondOrOpts, opts) {
360
368
  const isOpts = (v) => typeof v === 'object' && v !== null;
361
- const { luxonDatetime, microseconds } = buildLocalOrUtcDateTime(yearOrOpts, month, day, hour, minute, second, millisecond, microsecondOrOpts, options, isOpts, (y, m, d, h, mi, s, ms, opts) => LuxonDateTime.utc(y, m, d, h, mi, s, ms, opts));
369
+ const { luxonDatetime, microseconds } = buildLocalOrUtcDateTime(yearOrOpts, month, day, hour, minute, second, millisecond, microsecondOrOpts, opts, isOpts, (y, m, d, h, mi, s, ms, opts) => LuxonDateTime.utc(y, m, d, h, mi, s, ms, opts));
362
370
  return new DateTime(luxonDatetime, microseconds);
363
371
  }
364
372
  /**
365
373
  * Create a DateTime from a JavaScript Date.
366
374
  * @param date - A JavaScript Date instance
367
- * @param options - Optional zone for the result
375
+ * @param opts - Optional configuration
376
+ * @param opts.zone - Timezone for the result (IANA timezone name or Zone object)
377
+ * @param opts.locale - Locale string (e.g., 'en-US', 'fr-FR')
378
+ * @param opts.numberingSystem - Numbering system (e.g., 'arab', 'beng')
379
+ * @param opts.outputCalendar - Calendar system (e.g., 'islamic', 'hebrew')
368
380
  * @returns A DateTime representing the same instant
369
381
  * @example
370
382
  * ```ts
@@ -372,66 +384,94 @@ export class DateTime {
372
384
  * DateTime.fromJSDate(new Date(), { zone: 'America/New_York' })
373
385
  * ```
374
386
  */
375
- static fromJSDate(date, options) {
376
- const luxonDatetime = options
387
+ static fromJSDate(date, opts) {
388
+ const luxonDatetime = opts
377
389
  ? // @ts-expect-error - exactOptionalPropertyTypes incompatibility with Luxon types
378
- LuxonDateTime.fromJSDate(date, options)
390
+ LuxonDateTime.fromJSDate(date, opts)
379
391
  : LuxonDateTime.fromJSDate(date);
380
392
  return new DateTime(luxonDatetime, 0);
381
393
  }
382
394
  /**
383
395
  * Create a DateTime from epoch milliseconds.
384
- * @param milliseconds - Unix timestamp in milliseconds
385
- * @param options - Optional zone/locale options
396
+ * @param millisecondInput - Unix timestamp in milliseconds (fractional part becomes microseconds)
397
+ * @param opts - Optional configuration
398
+ * @param opts.zone - Timezone for the result (IANA timezone name or Zone object)
399
+ * @param opts.locale - Locale string (e.g., 'en-US', 'fr-FR')
400
+ * @param opts.numberingSystem - Numbering system (e.g., 'arab', 'beng')
401
+ * @param opts.outputCalendar - Calendar system (e.g., 'islamic', 'hebrew')
386
402
  * @returns A DateTime for the given instant
387
403
  * @example
388
404
  * ```ts
389
405
  * DateTime.fromMillis(1707234567890)
406
+ * DateTime.fromMillis(1707234567890.123) // .123 ms = 123 microseconds
407
+ * DateTime.fromMillis(1707234567890, { zone: 'America/New_York' })
390
408
  * ```
391
409
  */
392
- static fromMillis(milliseconds, options) {
393
- const luxonDatetime = LuxonDateTime.fromMillis(milliseconds, options);
394
- return new DateTime(luxonDatetime, 0);
410
+ static fromMillis(millisecondInput, opts) {
411
+ const { milliseconds, microseconds } = microsecondParts(millisecondInput * 1000, {
412
+ errorIfNegative: false,
413
+ });
414
+ return new DateTime(LuxonDateTime.fromMillis(milliseconds, opts), microseconds);
395
415
  }
396
416
  /**
397
417
  * Create a DateTime from epoch microseconds.
398
418
  * @param microseconds - Unix timestamp in microseconds (milliseconds from quotient, microsecond from remainder)
399
- * @param options - Optional zone/locale options
419
+ * @param opts - Optional configuration
420
+ * @param opts.zone - Timezone for the result (IANA timezone name or Zone object)
421
+ * @param opts.locale - Locale string (e.g., 'en-US', 'fr-FR')
422
+ * @param opts.numberingSystem - Numbering system (e.g., 'arab', 'beng')
423
+ * @param opts.outputCalendar - Calendar system (e.g., 'islamic', 'hebrew')
400
424
  * @returns A DateTime for the given instant
401
425
  * @example
402
426
  * ```ts
403
427
  * DateTime.fromMicroseconds(1707234567890123)
428
+ * DateTime.fromMicroseconds(1707234567890123, { zone: 'America/New_York' })
404
429
  * ```
405
430
  */
406
- static fromMicroseconds(microsecondsInput, options) {
431
+ static fromMicroseconds(microsecondsInput, opts) {
407
432
  const { milliseconds, microseconds } = microsecondParts(microsecondsInput);
408
- const luxonDatetime = LuxonDateTime.fromMillis(milliseconds, options);
433
+ const luxonDatetime = LuxonDateTime.fromMillis(milliseconds, opts);
409
434
  return new DateTime(luxonDatetime, microseconds);
410
435
  }
411
436
  /**
412
437
  * Create a DateTime from epoch seconds.
413
- * @param seconds - Unix timestamp in seconds
414
- * @param options - Optional zone/locale options
438
+ * Fractional seconds are converted to milliseconds and microseconds.
439
+ * @param seconds - Unix timestamp in seconds (fractional part becomes ms + µs)
440
+ * @param opts - Optional configuration
441
+ * @param opts.zone - Timezone for the result (IANA timezone name or Zone object)
442
+ * @param opts.locale - Locale string (e.g., 'en-US', 'fr-FR')
443
+ * @param opts.numberingSystem - Numbering system (e.g., 'arab', 'beng')
444
+ * @param opts.outputCalendar - Calendar system (e.g., 'islamic', 'hebrew')
415
445
  * @returns A DateTime for the given instant
416
446
  * @example
417
447
  * ```ts
418
448
  * DateTime.fromSeconds(1707234567)
449
+ * DateTime.fromSeconds(1707234567.123456) // .123456 seconds = 123ms + 456µs
450
+ * DateTime.fromSeconds(1707234567, { zone: 'America/New_York' })
419
451
  * ```
420
452
  */
421
- static fromSeconds(seconds, options) {
422
- const luxonDatetime = LuxonDateTime.fromSeconds(seconds, options);
423
- return new DateTime(luxonDatetime, 0);
453
+ static fromSeconds(seconds, opts) {
454
+ // Convert seconds to microseconds to preserve full precision
455
+ const totalMicroseconds = seconds * 1_000_000;
456
+ const { milliseconds, microseconds } = microsecondParts(totalMicroseconds, { errorIfNegative: false });
457
+ const luxonDatetime = LuxonDateTime.fromMillis(milliseconds, opts);
458
+ return new DateTime(luxonDatetime, microseconds);
424
459
  }
425
460
  /**
426
461
  * Create a DateTime from an object with date/time units.
427
462
  * Fractional milliseconds are converted to microseconds (e.g., 1.5 ms = 1 ms + 500 µs).
428
- * @param obj - Object with year, month, day, etc.; supports optional microsecond
429
- * @param opts - Optional zone/locale options
463
+ * @param obj - Object with year, month, day, hour, minute, second, millisecond, microsecond
464
+ * @param opts - Optional configuration
465
+ * @param opts.zone - Timezone for the datetime (IANA timezone name or Zone object)
466
+ * @param opts.locale - Locale string (e.g., 'en-US', 'fr-FR')
467
+ * @param opts.numberingSystem - Numbering system (e.g., 'arab', 'beng')
468
+ * @param opts.outputCalendar - Calendar system (e.g., 'islamic', 'hebrew')
430
469
  * @returns A DateTime for the given components
431
470
  * @example
432
471
  * ```ts
433
472
  * DateTime.fromObject({ year: 2017, month: 3, day: 12, hour: 5, minute: 45, microsecond: 123 })
434
473
  * DateTime.fromObject({ year: 2017, month: 3, day: 12, millisecond: 1.5 }) // 1ms + 500µs
474
+ * DateTime.fromObject({ year: 2017, month: 3, day: 12 }, { zone: 'America/New_York' })
435
475
  * ```
436
476
  */
437
477
  static fromObject(obj, opts) {
@@ -446,39 +486,56 @@ export class DateTime {
446
486
  adjustedMillisecond = wholeMilli;
447
487
  }
448
488
  const { milliseconds, microseconds } = microsecondParts(microsecondsTotal);
449
- const luxonDatetime = wrapLuxonError(() => LuxonDateTime.fromObject({ ...rest, millisecond: adjustedMillisecond }, opts));
489
+ const luxonDatetime = wrapLuxonError(() => LuxonDateTime.fromObject({ ...rest, millisecond: adjustedMillisecond }, (opts?.zone ? opts : { zone: 'UTC', ...opts })));
450
490
  return new DateTime(milliseconds > 0 ? luxonDatetime.plus({ milliseconds }) : luxonDatetime, microseconds);
451
491
  }
452
492
  /**
453
493
  * Create a DateTime from an ISO 8601 string.
454
494
  * @param text - ISO string (e.g. "2024-03-15T10:30:45.123456-05:00"); parses up to 6 fractional second digits
455
- * @param opts - Optional parsing options
495
+ * @param opts - Optional configuration
496
+ * @param opts.zone - Timezone to interpret/convert the datetime in (defaults to UTC)
497
+ * @param opts.locale - Locale string (e.g., 'en-US', 'fr-FR')
498
+ * @param opts.numberingSystem - Numbering system (e.g., 'arab', 'beng')
499
+ * @param opts.outputCalendar - Calendar system (e.g., 'islamic', 'hebrew')
456
500
  * @returns A DateTime for the parsed instant
457
501
  * @example
458
502
  * ```ts
459
503
  * DateTime.fromISO('2024-03-15T10:30:45.123456-05:00')
504
+ * DateTime.fromISO('2024-03-15T10:30:45Z', { zone: 'America/New_York' })
460
505
  * ```
461
506
  */
462
507
  static fromISO(text, opts) {
463
508
  const { microsecond } = parseFractionalPart(text);
464
509
  const textForLuxon = toThreeDecimalFraction(text);
465
- const luxonDatetime = wrapLuxonError(() => LuxonDateTime.fromISO(textForLuxon, opts));
510
+ const hasTimezoneInString = hasIsoTimezoneInformation(textForLuxon);
511
+ const luxonOpts = opts?.zone
512
+ ? opts
513
+ : hasTimezoneInString
514
+ ? { setZone: true, ...opts }
515
+ : { zone: 'UTC', ...opts };
516
+ const luxonDatetime = wrapLuxonError(() => LuxonDateTime.fromISO(textForLuxon, luxonOpts));
466
517
  return new DateTime(luxonDatetime, microsecond);
467
518
  }
468
519
  /**
469
520
  * Create a DateTime from an SQL datetime string.
470
521
  * @param text - SQL string (e.g. "2024-03-15 10:30:45.123456"); parses up to 6 fractional second digits
471
- * @param opts - Optional parsing options
522
+ * @param opts - Optional configuration
523
+ * @param opts.zone - Timezone to interpret the datetime in (overrides timezone in string)
524
+ * @param opts.locale - Locale string (e.g., 'en-US', 'fr-FR')
525
+ * @param opts.numberingSystem - Numbering system (e.g., 'arab', 'beng')
526
+ * @param opts.outputCalendar - Calendar system (e.g., 'islamic', 'hebrew')
472
527
  * @returns A DateTime for the parsed instant
473
528
  * @example
474
529
  * ```ts
475
530
  * DateTime.fromSQL('2024-03-15 10:30:45.123456')
531
+ * DateTime.fromSQL('2024-03-15 10:30:45', { zone: 'America/New_York' })
476
532
  * ```
477
533
  */
478
534
  static fromSQL(text, opts) {
479
535
  const { microsecond } = parseFractionalPart(text);
480
536
  const textForLuxon = toThreeDecimalFraction(text);
481
- const luxonDatetime = wrapLuxonError(() => LuxonDateTime.fromSQL(textForLuxon, opts));
537
+ const luxonOpts = opts?.zone ? opts : { zone: 'UTC', ...opts };
538
+ const luxonDatetime = wrapLuxonError(() => LuxonDateTime.fromSQL(textForLuxon, luxonOpts));
482
539
  return new DateTime(luxonDatetime, microsecond);
483
540
  }
484
541
  /**
@@ -499,39 +556,34 @@ export class DateTime {
499
556
  * ```
500
557
  */
501
558
  static fromFormat(text, format, opts) {
502
- // Check if format includes microsecond token ('u' or 'SSSSSS')
503
559
  const hasMicrosecondToken = format.includes('.u') || format.includes('.SSSSSS');
504
- let microsecond = 0;
505
- if (hasMicrosecondToken) {
506
- // Extract microseconds from the text
507
- const { microsecond: extractedMicrosecond } = parseFractionalPart(text);
508
- microsecond = extractedMicrosecond;
509
- // Replace microsecond token with millisecond token for Luxon
510
- // 'u' -> 'SSS' (Luxon only supports milliseconds)
511
- // 'SSSSSS' -> 'SSS'
512
- const formatForLuxon = format.replace(/\.u\b/, '.SSS').replace(/\.SSSSSS\b/, '.SSS');
513
- // Truncate fractional part to 3 digits for Luxon
514
- const textForLuxon = toThreeDecimalFraction(text);
515
- const luxonDatetime = wrapLuxonError(() => LuxonDateTime.fromFormat(textForLuxon, formatForLuxon, opts));
516
- return new DateTime(luxonDatetime, microsecond);
517
- }
518
- else {
519
- // No microsecond token, use Luxon directly
520
- const luxonDatetime = wrapLuxonError(() => LuxonDateTime.fromFormat(text, format, opts));
521
- return new DateTime(luxonDatetime, 0);
522
- }
560
+ const microsecond = hasMicrosecondToken ? parseFractionalPart(text).microsecond : 0;
561
+ const textForLuxon = hasMicrosecondToken ? toThreeDecimalFraction(text) : text;
562
+ const formatForLuxon = hasMicrosecondToken
563
+ ? format.replace(/\.u\b/, '.SSS').replace(/\.SSSSSS\b/, '.SSS')
564
+ : format;
565
+ const luxonDatetime = wrapLuxonError(() => LuxonDateTime.fromFormat(textForLuxon, formatForLuxon, opts));
566
+ return new DateTime(luxonDatetime, microsecond);
523
567
  }
524
568
  /**
525
569
  * Returns an ISO 8601 string with 6 fractional second digits (milliseconds + microseconds).
526
- * @param opts - Optional format options (includeOffset, suppressMilliseconds, etc.)
527
- * @returns ISO string (e.g. "2024-03-15T10:30:45.123456-05:00")
570
+ *
571
+ * Always converts to UTC before formatting (e.g., '2024-03-15T15:30:45.123456Z').
572
+ *
573
+ * @param opts - Optional format options
574
+ * @param opts.suppressMilliseconds - If true, omits fractional seconds when they are zero
575
+ * @param opts.suppressSeconds - If true, omits seconds when they are zero
576
+ * @param opts.includeOffset - If true, includes timezone offset
577
+ * @param opts.format - Format variant: 'basic' (compact) or 'extended' (default, with separators)
578
+ * @returns ISO string (e.g. "2024-03-15T10:30:45.123456-05:00" or "2024-03-15T10:30:45.123456Z")
528
579
  * @example
529
580
  * ```ts
530
- * DateTime.fromISO('2024-03-15T10:30:45.123456').toISO()
581
+ * DateTime.fromISO('2024-03-15T10:30:45.123456').toISO() // Converts to UTC
531
582
  * ```
532
583
  */
533
584
  toISO(opts) {
534
- return replaceISOMicroseconds(this, this.luxonDatetime.toISO(opts), opts);
585
+ const dt = this.toUTC();
586
+ return replaceISOMicroseconds(dt, dt.luxonDatetime.toISO(opts), opts);
535
587
  }
536
588
  /**
537
589
  * Returns an ISO date string (date only, no time).
@@ -546,27 +598,44 @@ export class DateTime {
546
598
  }
547
599
  /**
548
600
  * Returns the time portion in ISO format with 6 fractional second digits.
601
+ *
602
+ * Omits timezone offset by default (e.g., '10:30:45.123456').
603
+ *
549
604
  * @param opts - Optional format options
550
- * @returns Time string (e.g. "10:30:45.123456-05:00")
605
+ * @param opts.suppressMilliseconds - If true, omits fractional seconds when they are zero
606
+ * @param opts.suppressSeconds - If true, omits seconds when they are zero
607
+ * @param opts.includeOffset - If true, includes timezone offset
608
+ * @param opts.format - Format variant: 'basic' (compact) or 'extended' (default, with colons)
609
+ * @returns Time string (e.g. "10:30:45.123456" or "10:30:45.123456-05:00")
551
610
  * @example
552
611
  * ```ts
553
- * DateTime.local(2017, 3, 12, 10, 30, 45, 123, 456).toISOTime()
612
+ * DateTime.local(2017, 3, 12, 10, 30, 45, 123, 456).toISOTime() // '10:30:45.123456'
554
613
  * ```
555
614
  */
556
615
  toISOTime(opts) {
557
- return replaceISOMicroseconds(this, this.luxonDatetime.toISOTime(opts), opts);
616
+ return replaceISOMicroseconds(this, this.luxonDatetime.toISOTime({
617
+ includeOffset: false,
618
+ ...opts,
619
+ }), opts);
558
620
  }
559
621
  /**
560
622
  * Returns an SQL datetime string with 6 fractional second digits.
561
- * @param opts - Optional format options
623
+ *
624
+ * Always converts to UTC before formatting (e.g., '2024-03-15 15:30:45.123456').
625
+ *
562
626
  * @returns SQL string (e.g. "2024-03-15 10:30:45.123456")
563
627
  * @example
564
628
  * ```ts
565
- * DateTime.local(2017, 3, 12, 10, 30, 45, 123, 456).toSQL()
629
+ * DateTime.local(2017, 3, 12, 10, 30, 45, 123, 456).toSQL() // Converts to UTC
566
630
  * ```
567
631
  */
568
- toSQL(opts) {
569
- return replaceISOMicroseconds(this, this.luxonDatetime.toSQL(opts), opts);
632
+ _toSQL;
633
+ toSQL() {
634
+ if (this._toSQL)
635
+ return this._toSQL;
636
+ const dt = this.toUTC();
637
+ this._toSQL = replaceISOMicroseconds(dt, dt.luxonDatetime.toSQL(), {});
638
+ return this._toSQL;
570
639
  }
571
640
  /**
572
641
  * Returns an SQL date string (date only, no time).
@@ -581,15 +650,22 @@ export class DateTime {
581
650
  }
582
651
  /**
583
652
  * Returns an SQL time string with 6 fractional second digits.
584
- * @param opts - Optional format options
653
+ *
654
+ * Omits timezone offset by default.
655
+ *
656
+ * @param opts - Optional SQL time format options
657
+ * @param opts.includeOffset - If true, includes timezone offset
585
658
  * @returns SQL time string (e.g. "10:30:45.123456")
586
659
  * @example
587
660
  * ```ts
588
- * DateTime.local(2017, 3, 12, 10, 30, 45, 123, 456).toSQLTime()
661
+ * DateTime.local(2017, 3, 12, 10, 30, 45, 123, 456).toSQLTime() // '10:30:45.123456'
662
+ * DateTime.local(2017, 3, 12, 10, 30, 45, 123, 456).toSQLTime({ includeOffset: true }) // '10:30:45.123456 -04:00'
589
663
  * ```
590
664
  */
591
- toSQLTime(opts) {
592
- return replaceISOMicroseconds(this, this.luxonDatetime.toSQLTime(opts), opts);
665
+ toSQLTime(opts = {}) {
666
+ return replaceISOMicroseconds(this, this.luxonDatetime.toSQLTime({
667
+ includeOffset: opts.includeOffset ?? false,
668
+ }), {});
593
669
  }
594
670
  /**
595
671
  * Returns a JavaScript Date object.
@@ -603,21 +679,30 @@ export class DateTime {
603
679
  return this.luxonDatetime.toJSDate();
604
680
  }
605
681
  /**
606
- * Returns the epoch time in microseconds (for valueOf() operations).
607
- * Includes full microsecond precision.
608
- * @returns Unix timestamp in microseconds
682
+ * Returns an ISO 8601 string representation (for valueOf() operations).
683
+ *
684
+ * Converts to UTC before formatting.
685
+ *
686
+ * @returns ISO datetime string with microsecond precision
609
687
  * @example
610
688
  * ```ts
611
- * DateTime.local(2017, 3, 12).valueOf()
612
- * DateTime.fromISO('2026-02-07T09:03:44.123456Z').valueOf() // includes microseconds
689
+ * DateTime.local(2017, 3, 12).valueOf() // Converts to UTC
690
+ * DateTime.fromISO('2026-02-07T09:03:44.123456Z').valueOf() // '2026-02-07T09:03:44.123456Z'
613
691
  * ```
614
692
  */
693
+ _valueOf;
615
694
  valueOf() {
616
- return this.toMicroseconds();
695
+ if (this._valueOf)
696
+ return this._valueOf;
697
+ this._valueOf = this.toISO();
698
+ return this._valueOf;
617
699
  }
618
700
  /**
619
701
  * Returns an ISO 8601 formatted string for JSON serialization.
620
702
  * This ensures DateTime objects are properly serialized to ISO format.
703
+ *
704
+ * Converts to UTC before formatting.
705
+ *
621
706
  * @returns ISO datetime string with microsecond precision
622
707
  * @example
623
708
  * ```ts
@@ -631,6 +716,9 @@ export class DateTime {
631
716
  /**
632
717
  * Returns an ISO 8601 formatted string representation.
633
718
  * Alias for toISO().
719
+ *
720
+ * Converts to UTC before formatting.
721
+ *
634
722
  * @returns ISO datetime string with microsecond precision
635
723
  * @example
636
724
  * ```ts
@@ -644,12 +732,17 @@ export class DateTime {
644
732
  }
645
733
  /**
646
734
  * Returns a localized string representation.
647
- * @param formatOpts - Optional format options
648
- * @param opts - Optional locale options
735
+ * @param formatOpts - Intl.DateTimeFormat options for formatting
736
+ * @param opts - Optional locale configuration
737
+ * @param opts.locale - Locale string (e.g., 'en-US', 'fr-FR')
738
+ * @param opts.numberingSystem - Numbering system (e.g., 'arab', 'beng')
739
+ * @param opts.outputCalendar - Calendar system (e.g., 'islamic', 'hebrew')
649
740
  * @returns Localized string
650
741
  * @example
651
742
  * ```ts
652
743
  * DateTime.local(2017, 3, 12).toLocaleString()
744
+ * DateTime.local(2017, 3, 12).toLocaleString(DateTime.DATE_FULL)
745
+ * DateTime.local(2017, 3, 12).toLocaleString({ weekday: 'long' }, { locale: 'fr-FR' })
653
746
  * ```
654
747
  */
655
748
  toLocaleString(formatOpts, opts) {
@@ -673,7 +766,6 @@ export class DateTime {
673
766
  // Check if format contains fractional second tokens (S)
674
767
  const fractionalMatch = fmt.match(/S+/);
675
768
  if (!fractionalMatch) {
676
- // No fractional seconds, just use Luxon's toFormat
677
769
  return this.luxonDatetime.toFormat(fmt, opts);
678
770
  }
679
771
  const tokenLength = fractionalMatch[0].length;
@@ -699,35 +791,10 @@ export class DateTime {
699
791
  * ```
700
792
  */
701
793
  plus(duration) {
702
- const durationObj = typeof duration === 'number' ? { milliseconds: duration } : duration;
703
- const { microseconds: microsecondsToAdd = 0, millisecond, milliseconds, ...rest } = durationObj;
704
- // Handle both millisecond and milliseconds (prefer milliseconds if both are present)
705
- const millisecondsToAdd = milliseconds ?? millisecond;
706
- let microsecondsAdjusted = microsecondsToAdd;
707
- // Build the object to pass to Luxon
708
- const luxonDuration = { ...rest };
709
- // Always handle fractional milliseconds by converting to microseconds
710
- if (millisecondsToAdd !== undefined) {
711
- const wholeMilli = Math.floor(millisecondsToAdd);
712
- const fractionalMilli = millisecondsToAdd - wholeMilli;
713
- microsecondsAdjusted += Math.round(fractionalMilli * 1000);
714
- if (milliseconds !== undefined) {
715
- luxonDuration.milliseconds = wholeMilli;
716
- }
717
- else {
718
- luxonDuration.millisecond = wholeMilli;
719
- }
720
- }
794
+ const { luxonDuration, microsecondDelta } = splitDurationAndMicroseconds(duration);
721
795
  const luxonDatetime = this.luxonDatetime.plus(luxonDuration);
722
- // Calculate total microseconds as: (current microseconds) + (microseconds to add)
723
- // This works with both positive and negative values
724
- const totalMicroseconds = this.microsecond + microsecondsAdjusted;
725
- // Normalize the microseconds
726
- const millisecondAdjustment = Math.floor(totalMicroseconds / 1000);
727
- const finalMicroseconds = ((totalMicroseconds % 1000) + 1000) % 1000; // Ensure 0-999 range
728
- return new DateTime(millisecondAdjustment !== 0
729
- ? luxonDatetime.plus({ milliseconds: millisecondAdjustment })
730
- : luxonDatetime, finalMicroseconds);
796
+ const normalized = normalizeMicrosecondTotal(this.microsecond + microsecondDelta);
797
+ return new DateTime(applyMillisecondAdjustment(luxonDatetime, normalized.millisecondAdjustment), normalized.microseconds);
731
798
  }
732
799
  /**
733
800
  * Subtracts a duration from this DateTime. Supports microsecond via DurationLikeObject.
@@ -741,34 +808,10 @@ export class DateTime {
741
808
  * ```
742
809
  */
743
810
  minus(duration) {
744
- const durationObj = typeof duration === 'number' ? { milliseconds: duration } : duration;
745
- const { microseconds: microsecondsToSubtract = 0, millisecond, milliseconds, ...rest } = durationObj;
746
- // Handle both millisecond and milliseconds (prefer milliseconds if both are present)
747
- const millisecondsToSubtract = milliseconds ?? millisecond;
748
- let microsecondsAdjusted = microsecondsToSubtract;
749
- // Build the object to pass to Luxon
750
- const luxonDuration = { ...rest };
751
- // Always handle fractional milliseconds by converting to microseconds
752
- if (millisecondsToSubtract !== undefined) {
753
- const wholeMilli = Math.floor(millisecondsToSubtract);
754
- const fractionalMilli = millisecondsToSubtract - wholeMilli;
755
- microsecondsAdjusted += Math.round(fractionalMilli * 1000);
756
- if (milliseconds !== undefined) {
757
- luxonDuration.milliseconds = wholeMilli;
758
- }
759
- else {
760
- luxonDuration.millisecond = wholeMilli;
761
- }
762
- }
811
+ const { luxonDuration, microsecondDelta } = splitDurationAndMicroseconds(duration);
763
812
  const luxonDatetime = this.luxonDatetime.minus(luxonDuration);
764
- // Calculate total microseconds as: (current microseconds) - (microseconds to subtract)
765
- const totalMicroseconds = this.microsecond - microsecondsAdjusted;
766
- // Normalize the microseconds
767
- const millisecondAdjustment = Math.floor(totalMicroseconds / 1000);
768
- const finalMicroseconds = ((totalMicroseconds % 1000) + 1000) % 1000; // Ensure 0-999 range
769
- return new DateTime(millisecondAdjustment !== 0
770
- ? luxonDatetime.plus({ milliseconds: millisecondAdjustment })
771
- : luxonDatetime, finalMicroseconds);
813
+ const normalized = normalizeMicrosecondTotal(this.microsecond - microsecondDelta);
814
+ return new DateTime(applyMillisecondAdjustment(luxonDatetime, normalized.millisecondAdjustment), normalized.microseconds);
772
815
  }
773
816
  /**
774
817
  * Returns a new DateTime with the given units set.
@@ -787,16 +830,24 @@ export class DateTime {
787
830
  }
788
831
  /**
789
832
  * Returns an object with date/time components including microsecond.
790
- * @param opts - Optional options (includeConfig for Luxon config)
791
833
  * @returns Object with year, month, day, hour, minute, second, millisecond, microsecond
792
834
  * @example
793
835
  * ```ts
794
836
  * DateTime.local(2017, 3, 12, 5, 45, 10, 123, 456).toObject()
795
837
  * ```
796
838
  */
797
- toObject(opts) {
798
- const obj = this.luxonDatetime.toObject(opts);
799
- return { ...obj, microsecond: this.microsecond };
839
+ toObject() {
840
+ const obj = this.luxonDatetime.toObject();
841
+ return {
842
+ year: obj.year,
843
+ month: obj.month,
844
+ day: obj.day,
845
+ hour: obj.hour,
846
+ minute: obj.minute,
847
+ second: obj.second,
848
+ millisecond: obj.millisecond,
849
+ microsecond: this.microsecond,
850
+ };
800
851
  }
801
852
  /**
802
853
  * Returns true if this and other represent the same instant and microsecond.
@@ -808,9 +859,7 @@ export class DateTime {
808
859
  * ```
809
860
  */
810
861
  equals(other) {
811
- if (!this.luxonDatetime.equals(other.luxonDatetime))
812
- return false;
813
- return this.microsecond === other.microsecond;
862
+ return this.valueOf() === other.valueOf();
814
863
  }
815
864
  /**
816
865
  * Returns the epoch time in milliseconds (toMillis * 1000 + millisecond).
@@ -853,20 +902,38 @@ export class DateTime {
853
902
  return luxonSeconds + additionalMicroseconds;
854
903
  }
855
904
  /**
856
- * Returns the epoch time in seconds as an integer (floor of toSeconds).
857
- * Truncates any fractional seconds, returning only the whole seconds portion.
858
- * Equivalent to Math.floor(toSeconds()).
859
- * @returns Unix timestamp in seconds (integer only, no fractional part)
905
+ * Returns the epoch time in seconds as an integer, truncating any fractional part.
860
906
  * @example
861
907
  * ```ts
862
- * DateTime.fromSeconds(1707234567).toUnixInteger() // 1707234567
863
- * DateTime.fromISO('2026-02-07T09:03:44.123456Z').toUnixInteger() // 1770455024 (fractional part truncated)
864
- * DateTime.fromISO('2026-02-07T09:03:44.999999Z').toUnixInteger() // 1770455024 (not rounded up)
908
+ * DateTime.fromISO('2026-02-07T09:03:44.123456Z').unixIntegerSeconds // 1770455024
909
+ * DateTime.fromISO('2026-02-07T09:03:44.999999Z').unixIntegerSeconds // 1770455024 (not rounded up)
865
910
  * ```
866
911
  */
867
- toUnixInteger() {
912
+ get unixIntegerSeconds() {
868
913
  return Math.floor(this.toSeconds());
869
914
  }
915
+ /**
916
+ * Returns the epoch time in milliseconds as an integer, truncating any fractional part.
917
+ * @example
918
+ * ```ts
919
+ * DateTime.fromISO('2026-02-07T09:03:44.123456Z').unixIntegerMilliseconds // 1770455024123
920
+ * DateTime.fromISO('2026-02-07T09:03:44.123999Z').unixIntegerMilliseconds // 1770455024123 (not rounded up)
921
+ * ```
922
+ */
923
+ get unixIntegerMilliseconds() {
924
+ return Math.floor(this.toMillis());
925
+ }
926
+ /**
927
+ * Returns the epoch time in microseconds as an integer.
928
+ * Equivalent to `toMicroseconds()` since microseconds are always whole numbers.
929
+ * @example
930
+ * ```ts
931
+ * DateTime.fromISO('2026-02-07T09:03:44.123456Z').unixIntegerMicroseconds // 1770455024123456
932
+ * ```
933
+ */
934
+ get unixIntegerMicroseconds() {
935
+ return this.toMicroseconds();
936
+ }
870
937
  /**
871
938
  * Returns the earliest DateTime from the given arguments.
872
939
  * @param dateTimes - DateTimes to compare
@@ -879,7 +946,7 @@ export class DateTime {
879
946
  static min(...dateTimes) {
880
947
  if (dateTimes.length === 0)
881
948
  return null;
882
- return dateTimes.reduce((best, dt) => (dt.toMicroseconds() < best.toMicroseconds() ? dt : best), dateTimes[0]);
949
+ return dateTimes.reduce((min, datetime) => (datetime.valueOf() < min.valueOf() ? datetime : min), dateTimes[0]);
883
950
  }
884
951
  /**
885
952
  * Returns the latest DateTime from the given arguments.
@@ -893,7 +960,7 @@ export class DateTime {
893
960
  static max(...dateTimes) {
894
961
  if (dateTimes.length === 0)
895
962
  return null;
896
- return dateTimes.reduce((best, dt) => (dt.toMicroseconds() > best.toMicroseconds() ? dt : best), dateTimes[0]);
963
+ return dateTimes.reduce((max, datetime) => (datetime.valueOf() > max.valueOf() ? datetime : max), dateTimes[0]);
897
964
  }
898
965
  /**
899
966
  * Returns a new DateTime in the given zone. Microsecond is preserved.
@@ -932,8 +999,7 @@ export class DateTime {
932
999
  * ```
933
1000
  */
934
1001
  toLocal() {
935
- const luxonDatetime = this.luxonDatetime.toLocal();
936
- return new DateTime(luxonDatetime, this.microsecond);
1002
+ return new DateTime(this.luxonDatetime.toLocal(), this.microsecond);
937
1003
  }
938
1004
  /**
939
1005
  * Returns a new DateTime at the start of the given unit.
@@ -1021,62 +1087,47 @@ export class DateTime {
1021
1087
  * dt1.diff(dt2, 'days') // { days: 5 }
1022
1088
  * dt1.diff(dt2, ['days', 'hours']) // { days: 5, hours: 3 }
1023
1089
  * dt1.diff(dt2, ['milliseconds', 'microseconds']) // { milliseconds: 123, microseconds: 456 }
1024
- * dt1.diff(dt2) // { years: 0, months: 0, ..., milliseconds: 123 }
1090
+ * dt1.diff(dt2) // { years: 0, months: 0, days: 0, hours: 0, minutes: 0, seconds: 1, milliseconds: 500, microseconds: 0 }
1025
1091
  * ```
1026
1092
  */
1027
1093
  diff(other, unit) {
1028
- // Check if we need to calculate microseconds
1029
- const needsMicroseconds = unit === 'microseconds' ||
1030
- (Array.isArray(unit) && unit.includes('microseconds'));
1031
- // Check if we also need milliseconds
1032
- const needsMilliseconds = unit === 'milliseconds' ||
1033
- (Array.isArray(unit) && unit.includes('milliseconds'));
1034
- // Filter out 'microseconds' from the unit array since Luxon doesn't support it
1035
- let luxonUnits = unit;
1036
- if (Array.isArray(unit)) {
1037
- const filtered = unit.filter(u => u !== 'microseconds');
1038
- luxonUnits = filtered.length > 0 ? filtered : undefined;
1094
+ const unitArray = normalizeDiffUnitArray(unit);
1095
+ let result = {};
1096
+ const onlyMicroseconds = unitArray.length === 1 && unitArray[0] === 'microseconds';
1097
+ if (!onlyMicroseconds) {
1098
+ const luxonUnitArray = normalizeDiffUnitArray(unitArray.filter(u => u !== 'microseconds'));
1099
+ const luxonDuration = this.luxonDatetime.diff(other.toLuxon(), luxonUnitArray);
1100
+ result = luxonDuration.toObject();
1039
1101
  }
1040
- else if (unit === 'microseconds') {
1041
- luxonUnits = undefined;
1042
- }
1043
- // Get Luxon's diff (which handles all units except microseconds)
1044
- const luxonDuration = this.luxonDatetime.diff(other.toLuxon(), luxonUnits);
1045
- const fullObject = luxonDuration.toObject();
1046
- // Calculate microsecond difference if needed
1047
- if (needsMicroseconds || unit === undefined) {
1048
- // When microseconds is the ONLY unit requested, return total microseconds
1049
- if (unit === 'microseconds') {
1050
- const thisTotalMicroseconds = this.toMicroseconds();
1051
- const otherTotalMicroseconds = other.toMicroseconds();
1052
- fullObject.microseconds = thisTotalMicroseconds - otherTotalMicroseconds;
1102
+ const microsecondFieldDiff = this.microsecond - other.microsecond;
1103
+ if (unitArray.includes('microseconds')) {
1104
+ if (onlyMicroseconds) {
1105
+ const millisecondDiff = this.luxonDatetime.diff(other.toLuxon(), 'milliseconds').milliseconds ?? 0;
1106
+ result.microseconds = Math.round(millisecondDiff * 1000) + microsecondFieldDiff;
1053
1107
  }
1054
1108
  else {
1055
- // When microseconds is requested with other units, return only the fractional part (0-999)
1056
- const thisMicrosecond = this.microsecond;
1057
- const otherMicrosecond = other.microsecond;
1058
- fullObject.microseconds = thisMicrosecond - otherMicrosecond;
1059
- }
1060
- // If milliseconds are also requested/present, truncate them to whole numbers
1061
- // since the fractional part is now represented in microseconds
1062
- if ((needsMilliseconds || unit === undefined) && fullObject.milliseconds !== undefined) {
1063
- fullObject.milliseconds = Math.trunc(fullObject.milliseconds);
1109
+ // Find the bottom (smallest) Luxon unit in the result and convert its
1110
+ // entire value to microseconds, add the microsecond field diff, then
1111
+ // split back. This handles borrowing/carrying automatically.
1112
+ const bottomUnit = bottomLuxonUnit(result);
1113
+ if (bottomUnit !== undefined) {
1114
+ const microsPerUnit = MICROSECONDS_PER_UNIT[bottomUnit];
1115
+ const totalMicros = Math.round(result[bottomUnit] * microsPerUnit) + microsecondFieldDiff;
1116
+ result[bottomUnit] = Math.trunc(totalMicros / microsPerUnit) || 0;
1117
+ result.microseconds = totalMicros - result[bottomUnit] * microsPerUnit;
1118
+ }
1119
+ else {
1120
+ result.microseconds = microsecondFieldDiff;
1121
+ }
1064
1122
  }
1065
1123
  }
1066
- // If no unit specified, return all units (including microseconds)
1067
- if (unit === undefined) {
1068
- return fullObject;
1069
- }
1070
- // If unit is a single string, return only that unit
1071
- if (typeof unit === 'string') {
1072
- return { [unit]: fullObject[unit] ?? 0 };
1124
+ else if (unitArray.includes('milliseconds')) {
1125
+ result.milliseconds = (result.milliseconds ?? 0) + microsecondFieldDiff / 1000;
1073
1126
  }
1074
- // If unit is an array, return only those units
1075
- const result = {};
1076
- for (const u of unit) {
1077
- result[u] = fullObject[u] ?? 0;
1078
- }
1079
- return result;
1127
+ const filtered = {};
1128
+ for (const requestedUnit of unitArray)
1129
+ filtered[requestedUnit] = result[requestedUnit] ?? 0;
1130
+ return filtered;
1080
1131
  }
1081
1132
  /**
1082
1133
  * Returns the difference between this DateTime and now.
@@ -1112,33 +1163,84 @@ function wrapLuxonError(fn) {
1112
1163
  * @internal
1113
1164
  */
1114
1165
  function buildLocalOrUtcDateTime(yearOrOpts, month, day, hour, minute, second, millisecond, microsecondOrOpts, opts, isOpts, factory) {
1115
- const options = isOpts(opts)
1116
- ? opts
1117
- : isOpts(microsecondOrOpts)
1118
- ? microsecondOrOpts
1119
- : isOpts(millisecond)
1120
- ? millisecond
1121
- : isOpts(second)
1122
- ? second
1123
- : isOpts(minute)
1124
- ? minute
1125
- : isOpts(hour)
1126
- ? hour
1127
- : isOpts(day)
1128
- ? day
1129
- : isOpts(month)
1130
- ? month
1131
- : isOpts(yearOrOpts)
1132
- ? yearOrOpts
1133
- : undefined;
1166
+ const options = firstOptionArg(isOpts, opts, microsecondOrOpts, millisecond, second, minute, hour, day, month, yearOrOpts);
1134
1167
  const { milliseconds: millisecondPartOfMicroseconds, microseconds } = microsecondParts(typeof microsecondOrOpts === 'number' ? microsecondOrOpts : 0);
1135
- const y = typeof yearOrOpts === 'number' ? yearOrOpts : 0;
1136
- const m = typeof month === 'number' ? month : 1;
1137
- const d = typeof day === 'number' ? day : 1;
1138
- const ms = (typeof millisecond === 'number' ? millisecond : 0) + millisecondPartOfMicroseconds;
1139
- const luxonDatetime = factory(y, m, d, typeof hour === 'number' ? hour : 0, typeof minute === 'number' ? minute : 0, typeof second === 'number' ? second : 0, ms, options);
1168
+ const y = numericOrDefault(yearOrOpts, 0);
1169
+ const m = numericOrDefault(month, 1);
1170
+ const d = numericOrDefault(day, 1);
1171
+ const ms = numericOrDefault(millisecond, 0) + millisecondPartOfMicroseconds;
1172
+ const luxonDatetime = factory(y, m, d, numericOrDefault(hour, 0), numericOrDefault(minute, 0), numericOrDefault(second, 0), ms, options);
1140
1173
  return { luxonDatetime, microseconds };
1141
1174
  }
1175
+ function splitDurationAndMicroseconds(duration) {
1176
+ const durationObj = typeof duration === 'number' ? { milliseconds: duration } : duration;
1177
+ const { microsecond = 0, microseconds = 0, millisecond, milliseconds, ...rest } = durationObj;
1178
+ const luxonDuration = { ...rest };
1179
+ const millisecondInput = milliseconds ?? millisecond;
1180
+ let microsecondDelta = microsecond + microseconds;
1181
+ if (millisecondInput !== undefined) {
1182
+ const wholeMilliseconds = Math.floor(millisecondInput);
1183
+ const fractionalMilliseconds = millisecondInput - wholeMilliseconds;
1184
+ microsecondDelta += Math.round(fractionalMilliseconds * 1000);
1185
+ if (milliseconds !== undefined) {
1186
+ luxonDuration.milliseconds = wholeMilliseconds;
1187
+ }
1188
+ else {
1189
+ luxonDuration.millisecond = wholeMilliseconds;
1190
+ }
1191
+ }
1192
+ return { luxonDuration: luxonDuration, microsecondDelta };
1193
+ }
1194
+ function normalizeMicrosecondTotal(totalMicroseconds) {
1195
+ const millisecondAdjustment = Math.floor(totalMicroseconds / 1000);
1196
+ const microseconds = ((totalMicroseconds % 1000) + 1000) % 1000;
1197
+ return { millisecondAdjustment, microseconds };
1198
+ }
1199
+ function applyMillisecondAdjustment(datetime, milliseconds) {
1200
+ return milliseconds !== 0 ? datetime.plus({ milliseconds }) : datetime;
1201
+ }
1202
+ function normalizeDiffUnitArray(unit) {
1203
+ if (unit === undefined)
1204
+ return DEFAULT_DIFF_UNITS_WITH_MICROSECONDS;
1205
+ return typeof unit === 'string' ? [unit] : unit.length === 0 ? DEFAULT_DIFF_UNITS_WITH_MICROSECONDS : unit;
1206
+ }
1207
+ const DEFAULT_DIFF_UNITS_WITH_MICROSECONDS = [
1208
+ 'years',
1209
+ 'months',
1210
+ 'days',
1211
+ 'hours',
1212
+ 'minutes',
1213
+ 'seconds',
1214
+ 'milliseconds',
1215
+ 'microseconds',
1216
+ ];
1217
+ /** Microseconds per Luxon unit, ordered from smallest to largest unit. */
1218
+ const MICROSECONDS_PER_UNIT = {
1219
+ milliseconds: 1_000,
1220
+ seconds: 1_000_000,
1221
+ minutes: 60_000_000,
1222
+ hours: 3_600_000_000,
1223
+ days: 86_400_000_000,
1224
+ weeks: 604_800_000_000,
1225
+ };
1226
+ /** Returns the smallest Luxon unit present in the result object (excluding microseconds). */
1227
+ function bottomLuxonUnit(result) {
1228
+ for (const unit of Object.keys(MICROSECONDS_PER_UNIT)) {
1229
+ if (result[unit] !== undefined)
1230
+ return unit;
1231
+ }
1232
+ return undefined;
1233
+ }
1234
+ function numericOrDefault(value, fallback) {
1235
+ return typeof value === 'number' ? value : fallback;
1236
+ }
1237
+ function firstOptionArg(isOpts, ...values) {
1238
+ for (const value of values) {
1239
+ if (isOpts(value))
1240
+ return value;
1241
+ }
1242
+ return undefined;
1243
+ }
1142
1244
  /** Parse fractional part from ISO/SQL string: first 3 digits = ms, next 3 = µs */
1143
1245
  function parseFractionalPart(str) {
1144
1246
  const match = str.match(/\.(\d+)/);
@@ -1158,6 +1260,10 @@ function toThreeDecimalFraction(str) {
1158
1260
  const frac = (match[1] ?? '').padEnd(3, '0').slice(0, 3);
1159
1261
  return str.replace(/\.\d+/, '.' + frac);
1160
1262
  }
1263
+ const ISO_TIMEZONE_SUFFIX_REGEX = /(Z|[+-]\d{2}(?::?\d{2})?)$/i;
1264
+ function hasIsoTimezoneInformation(str) {
1265
+ return ISO_TIMEZONE_SUFFIX_REGEX.test(str);
1266
+ }
1161
1267
  /**
1162
1268
  * Thrown when a DateTime is invalid (e.g. invalid input or Luxon error).
1163
1269
  * @param error - The original error (available as cause)