@rvoh/dream 2.3.0-alpha.6 → 2.3.0-alpha.8

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 (302) 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 +288 -193
  32. package/dist/cjs/src/utils/datetime/helpers/isoTimeDecimalString.js +1 -1
  33. package/dist/esm/src/Dream.js +2 -40
  34. package/dist/esm/src/cli/index.js +4 -0
  35. package/dist/esm/src/dream/Query.js +14 -11
  36. package/dist/esm/src/dream/QueryDriver/Base.js +0 -14
  37. package/dist/esm/src/dream/QueryDriver/Kysely.js +86 -74
  38. package/dist/esm/src/dream/QueryDriver/Postgres.js +10 -2
  39. package/dist/esm/src/dream/internal/extractAssignableAssociationAttributes.js +9 -0
  40. package/dist/esm/src/errors/db/DataIncompatibleWithDatabaseField.js +1 -3
  41. package/dist/esm/src/helpers/areEqual.js +5 -0
  42. package/dist/esm/src/helpers/cli/ASTBuilder.js +54 -2
  43. package/dist/esm/src/helpers/cli/ASTKyselyCodegenEnhancer.js +84 -0
  44. package/dist/esm/src/helpers/cli/ASTSchemaBuilder.js +17 -1
  45. package/dist/esm/src/helpers/cli/generateFactoryContent.js +16 -0
  46. package/dist/esm/src/helpers/cloneDeepSafe.js +21 -10
  47. package/dist/esm/src/helpers/customPgParsers.js +18 -1
  48. package/dist/esm/src/helpers/db/normalizeDataForDb.js +81 -0
  49. package/dist/esm/src/helpers/db/types/helpers.js +5 -0
  50. package/dist/esm/src/helpers/db/types/isDatetimeOrDatetimeArrayColumn.js +2 -1
  51. package/dist/esm/src/helpers/sortBy.js +7 -5
  52. package/dist/esm/src/helpers/sqlAttributes.js +4 -53
  53. package/dist/esm/src/helpers/stringCasing.js +6 -5
  54. package/dist/esm/src/helpers/toSafeObject.js +17 -0
  55. package/dist/esm/src/package-exports/errors.js +2 -0
  56. package/dist/esm/src/package-exports/index.js +2 -0
  57. package/dist/esm/src/serializer/SerializerRenderer.js +11 -11
  58. package/dist/esm/src/types/clocktime.js +1 -0
  59. package/dist/esm/src/utils/datetime/BaseClockTime.js +363 -0
  60. package/dist/esm/src/utils/datetime/CalendarDate.js +110 -119
  61. package/dist/esm/src/utils/datetime/ClockTime.js +173 -0
  62. package/dist/esm/src/utils/datetime/ClockTimeTz.js +232 -0
  63. package/dist/esm/src/utils/datetime/DateTime.js +288 -193
  64. package/dist/esm/src/utils/datetime/helpers/isoTimeDecimalString.js +1 -1
  65. package/dist/types/src/Dream.d.ts +29 -33
  66. package/dist/types/src/dream/DreamClassTransactionBuilder.d.ts +9 -10
  67. package/dist/types/src/dream/DreamInstanceTransactionBuilder.d.ts +16 -16
  68. package/dist/types/src/dream/LeftJoinLoadBuilder.d.ts +1 -1
  69. package/dist/types/src/dream/LoadBuilder.d.ts +1 -1
  70. package/dist/types/src/dream/Query.d.ts +16 -16
  71. package/dist/types/src/dream/QueryDriver/Base.d.ts +0 -1
  72. package/dist/types/src/dream/QueryDriver/Kysely.d.ts +1 -0
  73. package/dist/types/src/dream/internal/associations/associationQuery.d.ts +1 -1
  74. package/dist/types/src/dream/internal/associations/associationUpdateQuery.d.ts +1 -1
  75. package/dist/types/src/dream/internal/associations/destroyAssociation.d.ts +1 -1
  76. package/dist/types/src/dream/internal/associations/throughAssociationHasOptionsBesidesThroughAndSource.d.ts +4 -2
  77. package/dist/types/src/dream/internal/associations/undestroyAssociation.d.ts +1 -1
  78. package/dist/types/src/dream/internal/extractAssignableAssociationAttributes.d.ts +3 -0
  79. package/dist/types/src/dream/internal/similarity/SimilarityBuilder.d.ts +7 -7
  80. package/dist/types/src/errors/db/DataIncompatibleWithDatabaseField.d.ts +2 -7
  81. package/dist/types/src/helpers/cli/ASTBuilder.d.ts +31 -0
  82. package/dist/types/src/helpers/cli/ASTKyselyCodegenEnhancer.d.ts +13 -0
  83. package/dist/types/src/helpers/customPgParsers.d.ts +5 -0
  84. package/dist/types/src/helpers/db/normalizeDataForDb.d.ts +6 -0
  85. package/dist/types/src/helpers/db/types/helpers.d.ts +5 -0
  86. package/dist/types/src/helpers/sort.d.ts +2 -1
  87. package/dist/types/src/helpers/sortBy.d.ts +3 -0
  88. package/dist/types/src/helpers/toSafeObject.d.ts +8 -0
  89. package/dist/types/src/package-exports/errors.d.ts +2 -0
  90. package/dist/types/src/package-exports/index.d.ts +2 -0
  91. package/dist/types/src/package-exports/types.d.ts +2 -1
  92. package/dist/types/src/types/associations/shared.d.ts +15 -13
  93. package/dist/types/src/types/associations/shared.ts +81 -41
  94. package/dist/types/src/types/calendardate.d.ts +22 -1
  95. package/dist/types/src/types/calendardate.ts +33 -1
  96. package/dist/types/src/types/clocktime.d.ts +22 -0
  97. package/dist/types/src/types/clocktime.ts +59 -0
  98. package/dist/types/src/types/datetime.d.ts +11 -18
  99. package/dist/types/src/types/datetime.ts +16 -21
  100. package/dist/types/src/types/dream.d.ts +27 -13
  101. package/dist/types/src/types/dream.ts +40 -14
  102. package/dist/types/src/types/variadic.d.ts +10 -9
  103. package/dist/types/src/types/variadic.ts +30 -5
  104. package/dist/types/src/utils/datetime/BaseClockTime.d.ts +287 -0
  105. package/dist/types/src/utils/datetime/CalendarDate.d.ts +65 -47
  106. package/dist/types/src/utils/datetime/ClockTime.d.ts +138 -0
  107. package/dist/types/src/utils/datetime/ClockTimeTz.d.ts +194 -0
  108. package/dist/types/src/utils/datetime/DateTime.d.ts +142 -56
  109. package/dist/types/src/utils/datetime/helpers/isoTimeDecimalString.d.ts +1 -1
  110. package/docs/assets/navigation.js +1 -1
  111. package/docs/assets/search.js +1 -1
  112. package/docs/classes/db.DreamMigrationHelpers.html +9 -9
  113. package/docs/classes/db.KyselyQueryDriver.html +33 -34
  114. package/docs/classes/db.PostgresQueryDriver.html +34 -35
  115. package/docs/classes/db.QueryDriverBase.html +32 -33
  116. package/docs/classes/errors.CheckConstraintViolation.html +4 -6
  117. package/docs/classes/errors.ColumnOverflow.html +4 -6
  118. package/docs/classes/errors.CreateOrFindByFailedToCreateAndFind.html +3 -3
  119. package/docs/classes/errors.DataIncompatibleWithDatabaseField.html +4 -6
  120. package/docs/classes/errors.DataTypeColumnTypeMismatch.html +4 -6
  121. package/docs/classes/errors.GlobalNameNotSet.html +3 -3
  122. package/docs/classes/errors.InvalidCalendarDate.html +2 -2
  123. package/docs/classes/errors.InvalidClockTime.html +17 -0
  124. package/docs/classes/errors.InvalidClockTimeTz.html +17 -0
  125. package/docs/classes/errors.InvalidDateTime.html +2 -2
  126. package/docs/classes/errors.MissingSerializersDefinition.html +3 -3
  127. package/docs/classes/errors.NonLoadedAssociation.html +3 -3
  128. package/docs/classes/errors.NotNullViolation.html +4 -6
  129. package/docs/classes/errors.RecordNotFound.html +3 -3
  130. package/docs/classes/errors.ValidationError.html +3 -3
  131. package/docs/classes/index.CalendarDate.html +80 -92
  132. package/docs/classes/index.ClockTime.html +232 -0
  133. package/docs/classes/index.ClockTimeTz.html +253 -0
  134. package/docs/classes/index.DateTime.html +123 -129
  135. package/docs/classes/index.Decorators.html +19 -19
  136. package/docs/classes/index.Dream.html +127 -127
  137. package/docs/classes/index.DreamApp.html +5 -5
  138. package/docs/classes/index.DreamTransaction.html +2 -2
  139. package/docs/classes/index.Env.html +2 -2
  140. package/docs/classes/index.Query.html +71 -71
  141. package/docs/classes/system.CliFileWriter.html +2 -2
  142. package/docs/classes/system.DreamBin.html +2 -2
  143. package/docs/classes/system.DreamCLI.html +5 -5
  144. package/docs/classes/system.DreamImporter.html +2 -2
  145. package/docs/classes/system.DreamLogos.html +2 -2
  146. package/docs/classes/system.DreamSerializerBuilder.html +8 -8
  147. package/docs/classes/system.ObjectSerializerBuilder.html +8 -8
  148. package/docs/classes/system.PathHelpers.html +3 -3
  149. package/docs/classes/utils.Encrypt.html +2 -2
  150. package/docs/classes/utils.Range.html +2 -2
  151. package/docs/functions/db.closeAllDbConnections.html +1 -1
  152. package/docs/functions/db.dreamDbConnections.html +1 -1
  153. package/docs/functions/db.untypedDb.html +1 -1
  154. package/docs/functions/db.validateColumn.html +1 -1
  155. package/docs/functions/db.validateTable.html +1 -1
  156. package/docs/functions/errors.pgErrorType.html +1 -1
  157. package/docs/functions/index.DreamSerializer.html +1 -1
  158. package/docs/functions/index.ObjectSerializer.html +1 -1
  159. package/docs/functions/index.ReplicaSafe.html +1 -1
  160. package/docs/functions/index.STI.html +1 -1
  161. package/docs/functions/index.SoftDelete.html +1 -1
  162. package/docs/functions/utils.camelize.html +1 -1
  163. package/docs/functions/utils.capitalize.html +1 -1
  164. package/docs/functions/utils.cloneDeepSafe.html +1 -1
  165. package/docs/functions/utils.compact.html +1 -1
  166. package/docs/functions/utils.groupBy.html +1 -1
  167. package/docs/functions/utils.hyphenize.html +1 -1
  168. package/docs/functions/utils.intersection.html +1 -1
  169. package/docs/functions/utils.isEmpty.html +1 -1
  170. package/docs/functions/utils.normalizeUnicode.html +1 -1
  171. package/docs/functions/utils.pascalize.html +1 -1
  172. package/docs/functions/utils.percent.html +1 -1
  173. package/docs/functions/utils.range-1.html +1 -1
  174. package/docs/functions/utils.round.html +1 -1
  175. package/docs/functions/utils.sanitizeString.html +1 -1
  176. package/docs/functions/utils.snakeify.html +1 -1
  177. package/docs/functions/utils.sort.html +1 -1
  178. package/docs/functions/utils.sortBy.html +1 -1
  179. package/docs/functions/utils.sortObjectByKey.html +1 -1
  180. package/docs/functions/utils.sortObjectByValue.html +1 -1
  181. package/docs/functions/utils.uncapitalize.html +1 -1
  182. package/docs/functions/utils.uniq.html +1 -1
  183. package/docs/interfaces/openapi.OpenapiDescription.html +2 -2
  184. package/docs/interfaces/openapi.OpenapiSchemaProperties.html +1 -1
  185. package/docs/interfaces/openapi.OpenapiSchemaPropertiesShorthand.html +1 -1
  186. package/docs/interfaces/openapi.OpenapiTypeFieldObject.html +1 -1
  187. package/docs/interfaces/types.BelongsToStatement.html +2 -2
  188. package/docs/interfaces/types.DecoratorContext.html +2 -2
  189. package/docs/interfaces/types.DreamAppInitOptions.html +2 -2
  190. package/docs/interfaces/types.DreamAppOpts.html +2 -2
  191. package/docs/interfaces/types.DurationObject.html +5 -5
  192. package/docs/interfaces/types.EncryptOptions.html +2 -2
  193. package/docs/interfaces/types.InternalAnyTypedSerializerRendersMany.html +2 -2
  194. package/docs/interfaces/types.InternalAnyTypedSerializerRendersOne.html +2 -2
  195. package/docs/interfaces/types.SerializerRendererOpts.html +2 -2
  196. package/docs/modules/db.html +1 -1
  197. package/docs/modules/errors.html +3 -1
  198. package/docs/modules/index.html +3 -1
  199. package/docs/modules/openapi.html +1 -1
  200. package/docs/modules/system.html +1 -1
  201. package/docs/modules/types.html +3 -1
  202. package/docs/modules/utils.html +1 -1
  203. package/docs/types/openapi.CommonOpenapiSchemaObjectFields.html +1 -1
  204. package/docs/types/openapi.OpenapiAllTypes.html +1 -1
  205. package/docs/types/openapi.OpenapiFormats.html +1 -1
  206. package/docs/types/openapi.OpenapiNumberFormats.html +1 -1
  207. package/docs/types/openapi.OpenapiPrimitiveBaseTypes.html +1 -1
  208. package/docs/types/openapi.OpenapiPrimitiveTypes.html +1 -1
  209. package/docs/types/openapi.OpenapiSchemaArray.html +1 -1
  210. package/docs/types/openapi.OpenapiSchemaArrayShorthand.html +1 -1
  211. package/docs/types/openapi.OpenapiSchemaBase.html +1 -1
  212. package/docs/types/openapi.OpenapiSchemaBody.html +1 -1
  213. package/docs/types/openapi.OpenapiSchemaBodyShorthand.html +1 -1
  214. package/docs/types/openapi.OpenapiSchemaCommonFields.html +1 -1
  215. package/docs/types/openapi.OpenapiSchemaExpressionAllOf.html +1 -1
  216. package/docs/types/openapi.OpenapiSchemaExpressionAnyOf.html +1 -1
  217. package/docs/types/openapi.OpenapiSchemaExpressionOneOf.html +1 -1
  218. package/docs/types/openapi.OpenapiSchemaExpressionRef.html +1 -1
  219. package/docs/types/openapi.OpenapiSchemaExpressionRefSchemaShorthand.html +1 -1
  220. package/docs/types/openapi.OpenapiSchemaInteger.html +1 -1
  221. package/docs/types/openapi.OpenapiSchemaNull.html +1 -1
  222. package/docs/types/openapi.OpenapiSchemaNumber.html +1 -1
  223. package/docs/types/openapi.OpenapiSchemaObject.html +1 -1
  224. package/docs/types/openapi.OpenapiSchemaObjectAllOf.html +1 -1
  225. package/docs/types/openapi.OpenapiSchemaObjectAllOfShorthand.html +1 -1
  226. package/docs/types/openapi.OpenapiSchemaObjectAnyOf.html +1 -1
  227. package/docs/types/openapi.OpenapiSchemaObjectAnyOfShorthand.html +1 -1
  228. package/docs/types/openapi.OpenapiSchemaObjectBase.html +1 -1
  229. package/docs/types/openapi.OpenapiSchemaObjectBaseShorthand.html +1 -1
  230. package/docs/types/openapi.OpenapiSchemaObjectOneOf.html +1 -1
  231. package/docs/types/openapi.OpenapiSchemaObjectOneOfShorthand.html +1 -1
  232. package/docs/types/openapi.OpenapiSchemaObjectShorthand.html +1 -1
  233. package/docs/types/openapi.OpenapiSchemaPrimitiveGeneric.html +1 -1
  234. package/docs/types/openapi.OpenapiSchemaShorthandExpressionAllOf.html +1 -1
  235. package/docs/types/openapi.OpenapiSchemaShorthandExpressionAnyOf.html +1 -1
  236. package/docs/types/openapi.OpenapiSchemaShorthandExpressionOneOf.html +1 -1
  237. package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializableRef.html +1 -1
  238. package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializerRef.html +1 -1
  239. package/docs/types/openapi.OpenapiSchemaShorthandPrimitiveGeneric.html +1 -1
  240. package/docs/types/openapi.OpenapiSchemaString.html +1 -1
  241. package/docs/types/openapi.OpenapiShorthandAllTypes.html +1 -1
  242. package/docs/types/openapi.OpenapiShorthandPrimitiveBaseTypes.html +1 -1
  243. package/docs/types/openapi.OpenapiShorthandPrimitiveTypes.html +1 -1
  244. package/docs/types/openapi.OpenapiTypeField.html +1 -1
  245. package/docs/types/system.DreamAppAllowedPackageManagersEnum.html +1 -1
  246. package/docs/types/types.CalendarDateDurationUnit.html +1 -1
  247. package/docs/types/types.CalendarDateObject.html +2 -0
  248. package/docs/types/types.Camelized.html +1 -1
  249. package/docs/types/types.ClockTimeObject.html +2 -0
  250. package/docs/types/types.DbConnectionType.html +1 -1
  251. package/docs/types/types.DbTypes.html +1 -1
  252. package/docs/types/types.DreamAssociationMetadata.html +1 -1
  253. package/docs/types/types.DreamAttributes.html +1 -1
  254. package/docs/types/types.DreamClassAssociationAndStatement.html +1 -1
  255. package/docs/types/types.DreamClassColumn.html +1 -1
  256. package/docs/types/types.DreamColumn.html +1 -1
  257. package/docs/types/types.DreamColumnNames.html +1 -1
  258. package/docs/types/types.DreamLogLevel.html +1 -1
  259. package/docs/types/types.DreamLogger.html +1 -1
  260. package/docs/types/types.DreamModelSerializerType.html +1 -1
  261. package/docs/types/types.DreamOrViewModelClassSerializerKey.html +1 -1
  262. package/docs/types/types.DreamOrViewModelSerializerKey.html +1 -1
  263. package/docs/types/types.DreamParamSafeAttributes.html +1 -1
  264. package/docs/types/types.DreamParamSafeColumnNames.html +1 -1
  265. package/docs/types/types.DreamSerializable.html +1 -1
  266. package/docs/types/types.DreamSerializableArray.html +1 -1
  267. package/docs/types/types.DreamSerializerKey.html +1 -1
  268. package/docs/types/types.DreamSerializers.html +1 -1
  269. package/docs/types/types.DreamVirtualColumns.html +1 -1
  270. package/docs/types/types.DurationUnit.html +2 -4
  271. package/docs/types/types.EncryptAlgorithm.html +1 -1
  272. package/docs/types/types.HasManyStatement.html +1 -1
  273. package/docs/types/types.HasOneStatement.html +1 -1
  274. package/docs/types/types.Hyphenized.html +1 -1
  275. package/docs/types/types.Pascalized.html +1 -1
  276. package/docs/types/types.PrimaryKeyType.html +1 -1
  277. package/docs/types/types.RoundingPrecision.html +1 -1
  278. package/docs/types/types.SerializerCasing.html +1 -1
  279. package/docs/types/types.SimpleObjectSerializerType.html +1 -1
  280. package/docs/types/types.Snakeified.html +1 -1
  281. package/docs/types/types.StrictInterface.html +1 -1
  282. package/docs/types/types.UpdateableAssociationProperties.html +1 -1
  283. package/docs/types/types.UpdateableProperties.html +1 -1
  284. package/docs/types/types.ValidationType.html +1 -1
  285. package/docs/types/types.ViewModel.html +1 -1
  286. package/docs/types/types.ViewModelClass.html +1 -1
  287. package/docs/types/types.WeekdayName.html +1 -1
  288. package/docs/types/types.WhereStatementForDream.html +1 -1
  289. package/docs/types/types.WhereStatementForDreamClass.html +1 -1
  290. package/docs/variables/index.DreamConst.html +1 -1
  291. package/docs/variables/index.ops.html +1 -1
  292. package/docs/variables/openapi.openapiPrimitiveTypes-1.html +1 -1
  293. package/docs/variables/openapi.openapiShorthandPrimitiveTypes-1.html +1 -1
  294. package/docs/variables/system.DreamAppAllowedPackageManagersEnumValues.html +1 -1
  295. package/docs/variables/system.primaryKeyTypes.html +1 -1
  296. package/package.json +2 -2
  297. package/dist/cjs/src/helpers/db/types/isDateOrDateArrayColumn.js +0 -3
  298. package/dist/cjs/src/helpers/db/types/isTextOrTextArrayColumn.js +0 -3
  299. package/dist/esm/src/helpers/db/types/isDateOrDateArrayColumn.js +0 -3
  300. package/dist/esm/src/helpers/db/types/isTextOrTextArrayColumn.js +0 -3
  301. package/dist/types/src/helpers/db/types/isDateOrDateArrayColumn.d.ts +0 -2
  302. 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,74 +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
396
  * @param millisecondInput - Unix timestamp in milliseconds (fractional part becomes microseconds)
385
- * @param options - Optional zone/locale options
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)
390
406
  * DateTime.fromMillis(1707234567890.123) // .123 ms = 123 microseconds
407
+ * DateTime.fromMillis(1707234567890, { zone: 'America/New_York' })
391
408
  * ```
392
409
  */
393
- static fromMillis(millisecondInput, options) {
410
+ static fromMillis(millisecondInput, opts) {
394
411
  const { milliseconds, microseconds } = microsecondParts(millisecondInput * 1000, {
395
412
  errorIfNegative: false,
396
413
  });
397
- return new DateTime(LuxonDateTime.fromMillis(milliseconds, options), microseconds);
414
+ return new DateTime(LuxonDateTime.fromMillis(milliseconds, opts), microseconds);
398
415
  }
399
416
  /**
400
417
  * Create a DateTime from epoch microseconds.
401
418
  * @param microseconds - Unix timestamp in microseconds (milliseconds from quotient, microsecond from remainder)
402
- * @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')
403
424
  * @returns A DateTime for the given instant
404
425
  * @example
405
426
  * ```ts
406
427
  * DateTime.fromMicroseconds(1707234567890123)
428
+ * DateTime.fromMicroseconds(1707234567890123, { zone: 'America/New_York' })
407
429
  * ```
408
430
  */
409
- static fromMicroseconds(microsecondsInput, options) {
431
+ static fromMicroseconds(microsecondsInput, opts) {
410
432
  const { milliseconds, microseconds } = microsecondParts(microsecondsInput);
411
- const luxonDatetime = LuxonDateTime.fromMillis(milliseconds, options);
433
+ const luxonDatetime = LuxonDateTime.fromMillis(milliseconds, opts);
412
434
  return new DateTime(luxonDatetime, microseconds);
413
435
  }
414
436
  /**
415
437
  * Create a DateTime from epoch seconds.
416
438
  * Fractional seconds are converted to milliseconds and microseconds.
417
439
  * @param seconds - Unix timestamp in seconds (fractional part becomes ms + µs)
418
- * @param options - Optional zone/locale options
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')
419
445
  * @returns A DateTime for the given instant
420
446
  * @example
421
447
  * ```ts
422
448
  * DateTime.fromSeconds(1707234567)
423
449
  * DateTime.fromSeconds(1707234567.123456) // .123456 seconds = 123ms + 456µs
450
+ * DateTime.fromSeconds(1707234567, { zone: 'America/New_York' })
424
451
  * ```
425
452
  */
426
- static fromSeconds(seconds, options) {
453
+ static fromSeconds(seconds, opts) {
427
454
  // Convert seconds to microseconds to preserve full precision
428
455
  const totalMicroseconds = seconds * 1_000_000;
429
456
  const { milliseconds, microseconds } = microsecondParts(totalMicroseconds, { errorIfNegative: false });
430
- const luxonDatetime = LuxonDateTime.fromMillis(milliseconds, options);
457
+ const luxonDatetime = LuxonDateTime.fromMillis(milliseconds, opts);
431
458
  return new DateTime(luxonDatetime, microseconds);
432
459
  }
433
460
  /**
434
461
  * Create a DateTime from an object with date/time units.
435
462
  * Fractional milliseconds are converted to microseconds (e.g., 1.5 ms = 1 ms + 500 µs).
436
- * @param obj - Object with year, month, day, etc.; supports optional microsecond
437
- * @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')
438
469
  * @returns A DateTime for the given components
439
470
  * @example
440
471
  * ```ts
441
472
  * DateTime.fromObject({ year: 2017, month: 3, day: 12, hour: 5, minute: 45, microsecond: 123 })
442
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' })
443
475
  * ```
444
476
  */
445
477
  static fromObject(obj, opts) {
@@ -454,39 +486,56 @@ export class DateTime {
454
486
  adjustedMillisecond = wholeMilli;
455
487
  }
456
488
  const { milliseconds, microseconds } = microsecondParts(microsecondsTotal);
457
- const luxonDatetime = wrapLuxonError(() => LuxonDateTime.fromObject({ ...rest, millisecond: adjustedMillisecond }, opts));
489
+ const luxonDatetime = wrapLuxonError(() => LuxonDateTime.fromObject({ ...rest, millisecond: adjustedMillisecond }, (opts?.zone ? opts : { zone: 'UTC', ...opts })));
458
490
  return new DateTime(milliseconds > 0 ? luxonDatetime.plus({ milliseconds }) : luxonDatetime, microseconds);
459
491
  }
460
492
  /**
461
493
  * Create a DateTime from an ISO 8601 string.
462
494
  * @param text - ISO string (e.g. "2024-03-15T10:30:45.123456-05:00"); parses up to 6 fractional second digits
463
- * @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')
464
500
  * @returns A DateTime for the parsed instant
465
501
  * @example
466
502
  * ```ts
467
503
  * DateTime.fromISO('2024-03-15T10:30:45.123456-05:00')
504
+ * DateTime.fromISO('2024-03-15T10:30:45Z', { zone: 'America/New_York' })
468
505
  * ```
469
506
  */
470
507
  static fromISO(text, opts) {
471
508
  const { microsecond } = parseFractionalPart(text);
472
509
  const textForLuxon = toThreeDecimalFraction(text);
473
- 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));
474
517
  return new DateTime(luxonDatetime, microsecond);
475
518
  }
476
519
  /**
477
520
  * Create a DateTime from an SQL datetime string.
478
521
  * @param text - SQL string (e.g. "2024-03-15 10:30:45.123456"); parses up to 6 fractional second digits
479
- * @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')
480
527
  * @returns A DateTime for the parsed instant
481
528
  * @example
482
529
  * ```ts
483
530
  * DateTime.fromSQL('2024-03-15 10:30:45.123456')
531
+ * DateTime.fromSQL('2024-03-15 10:30:45', { zone: 'America/New_York' })
484
532
  * ```
485
533
  */
486
534
  static fromSQL(text, opts) {
487
535
  const { microsecond } = parseFractionalPart(text);
488
536
  const textForLuxon = toThreeDecimalFraction(text);
489
- const luxonDatetime = wrapLuxonError(() => LuxonDateTime.fromSQL(textForLuxon, opts));
537
+ const luxonOpts = opts?.zone ? opts : { zone: 'UTC', ...opts };
538
+ const luxonDatetime = wrapLuxonError(() => LuxonDateTime.fromSQL(textForLuxon, luxonOpts));
490
539
  return new DateTime(luxonDatetime, microsecond);
491
540
  }
492
541
  /**
@@ -507,39 +556,34 @@ export class DateTime {
507
556
  * ```
508
557
  */
509
558
  static fromFormat(text, format, opts) {
510
- // Check if format includes microsecond token ('u' or 'SSSSSS')
511
559
  const hasMicrosecondToken = format.includes('.u') || format.includes('.SSSSSS');
512
- let microsecond = 0;
513
- if (hasMicrosecondToken) {
514
- // Extract microseconds from the text
515
- const { microsecond: extractedMicrosecond } = parseFractionalPart(text);
516
- microsecond = extractedMicrosecond;
517
- // Replace microsecond token with millisecond token for Luxon
518
- // 'u' -> 'SSS' (Luxon only supports milliseconds)
519
- // 'SSSSSS' -> 'SSS'
520
- const formatForLuxon = format.replace(/\.u\b/, '.SSS').replace(/\.SSSSSS\b/, '.SSS');
521
- // Truncate fractional part to 3 digits for Luxon
522
- const textForLuxon = toThreeDecimalFraction(text);
523
- const luxonDatetime = wrapLuxonError(() => LuxonDateTime.fromFormat(textForLuxon, formatForLuxon, opts));
524
- return new DateTime(luxonDatetime, microsecond);
525
- }
526
- else {
527
- // No microsecond token, use Luxon directly
528
- const luxonDatetime = wrapLuxonError(() => LuxonDateTime.fromFormat(text, format, opts));
529
- return new DateTime(luxonDatetime, 0);
530
- }
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);
531
567
  }
532
568
  /**
533
569
  * Returns an ISO 8601 string with 6 fractional second digits (milliseconds + microseconds).
534
- * @param opts - Optional format options (includeOffset, suppressMilliseconds, etc.)
535
- * @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")
536
579
  * @example
537
580
  * ```ts
538
- * DateTime.fromISO('2024-03-15T10:30:45.123456').toISO()
581
+ * DateTime.fromISO('2024-03-15T10:30:45.123456').toISO() // Converts to UTC
539
582
  * ```
540
583
  */
541
584
  toISO(opts) {
542
- return replaceISOMicroseconds(this, this.luxonDatetime.toISO(opts), opts);
585
+ const dt = this.toUTC();
586
+ return replaceISOMicroseconds(dt, dt.luxonDatetime.toISO(opts), opts);
543
587
  }
544
588
  /**
545
589
  * Returns an ISO date string (date only, no time).
@@ -554,27 +598,44 @@ export class DateTime {
554
598
  }
555
599
  /**
556
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
+ *
557
604
  * @param opts - Optional format options
558
- * @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")
559
610
  * @example
560
611
  * ```ts
561
- * 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'
562
613
  * ```
563
614
  */
564
615
  toISOTime(opts) {
565
- return replaceISOMicroseconds(this, this.luxonDatetime.toISOTime(opts), opts);
616
+ return replaceISOMicroseconds(this, this.luxonDatetime.toISOTime({
617
+ includeOffset: false,
618
+ ...opts,
619
+ }), opts);
566
620
  }
567
621
  /**
568
622
  * Returns an SQL datetime string with 6 fractional second digits.
569
- * @param opts - Optional format options
623
+ *
624
+ * Always converts to UTC before formatting (e.g., '2024-03-15 15:30:45.123456').
625
+ *
570
626
  * @returns SQL string (e.g. "2024-03-15 10:30:45.123456")
571
627
  * @example
572
628
  * ```ts
573
- * 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
574
630
  * ```
575
631
  */
576
- toSQL(opts) {
577
- 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;
578
639
  }
579
640
  /**
580
641
  * Returns an SQL date string (date only, no time).
@@ -589,15 +650,22 @@ export class DateTime {
589
650
  }
590
651
  /**
591
652
  * Returns an SQL time string with 6 fractional second digits.
592
- * @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
593
658
  * @returns SQL time string (e.g. "10:30:45.123456")
594
659
  * @example
595
660
  * ```ts
596
- * 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'
597
663
  * ```
598
664
  */
599
- toSQLTime(opts) {
600
- return replaceISOMicroseconds(this, this.luxonDatetime.toSQLTime(opts), opts);
665
+ toSQLTime(opts = {}) {
666
+ return replaceISOMicroseconds(this, this.luxonDatetime.toSQLTime({
667
+ includeOffset: opts.includeOffset ?? false,
668
+ }), {});
601
669
  }
602
670
  /**
603
671
  * Returns a JavaScript Date object.
@@ -611,21 +679,30 @@ export class DateTime {
611
679
  return this.luxonDatetime.toJSDate();
612
680
  }
613
681
  /**
614
- * Returns the epoch time in microseconds (for valueOf() operations).
615
- * Includes full microsecond precision.
616
- * @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
617
687
  * @example
618
688
  * ```ts
619
- * DateTime.local(2017, 3, 12).valueOf()
620
- * 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'
621
691
  * ```
622
692
  */
693
+ _valueOf;
623
694
  valueOf() {
624
- return this.toMicroseconds();
695
+ if (this._valueOf)
696
+ return this._valueOf;
697
+ this._valueOf = this.toISO();
698
+ return this._valueOf;
625
699
  }
626
700
  /**
627
701
  * Returns an ISO 8601 formatted string for JSON serialization.
628
702
  * This ensures DateTime objects are properly serialized to ISO format.
703
+ *
704
+ * Converts to UTC before formatting.
705
+ *
629
706
  * @returns ISO datetime string with microsecond precision
630
707
  * @example
631
708
  * ```ts
@@ -639,6 +716,9 @@ export class DateTime {
639
716
  /**
640
717
  * Returns an ISO 8601 formatted string representation.
641
718
  * Alias for toISO().
719
+ *
720
+ * Converts to UTC before formatting.
721
+ *
642
722
  * @returns ISO datetime string with microsecond precision
643
723
  * @example
644
724
  * ```ts
@@ -652,12 +732,17 @@ export class DateTime {
652
732
  }
653
733
  /**
654
734
  * Returns a localized string representation.
655
- * @param formatOpts - Optional format options
656
- * @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')
657
740
  * @returns Localized string
658
741
  * @example
659
742
  * ```ts
660
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' })
661
746
  * ```
662
747
  */
663
748
  toLocaleString(formatOpts, opts) {
@@ -681,7 +766,6 @@ export class DateTime {
681
766
  // Check if format contains fractional second tokens (S)
682
767
  const fractionalMatch = fmt.match(/S+/);
683
768
  if (!fractionalMatch) {
684
- // No fractional seconds, just use Luxon's toFormat
685
769
  return this.luxonDatetime.toFormat(fmt, opts);
686
770
  }
687
771
  const tokenLength = fractionalMatch[0].length;
@@ -707,35 +791,10 @@ export class DateTime {
707
791
  * ```
708
792
  */
709
793
  plus(duration) {
710
- const durationObj = typeof duration === 'number' ? { milliseconds: duration } : duration;
711
- const { microseconds: microsecondsToAdd = 0, millisecond, milliseconds, ...rest } = durationObj;
712
- // Handle both millisecond and milliseconds (prefer milliseconds if both are present)
713
- const millisecondsToAdd = milliseconds ?? millisecond;
714
- let microsecondsAdjusted = microsecondsToAdd;
715
- // Build the object to pass to Luxon
716
- const luxonDuration = { ...rest };
717
- // Always handle fractional milliseconds by converting to microseconds
718
- if (millisecondsToAdd !== undefined) {
719
- const wholeMilli = Math.floor(millisecondsToAdd);
720
- const fractionalMilli = millisecondsToAdd - wholeMilli;
721
- microsecondsAdjusted += Math.round(fractionalMilli * 1000);
722
- if (milliseconds !== undefined) {
723
- luxonDuration.milliseconds = wholeMilli;
724
- }
725
- else {
726
- luxonDuration.millisecond = wholeMilli;
727
- }
728
- }
794
+ const { luxonDuration, microsecondDelta } = splitDurationAndMicroseconds(duration);
729
795
  const luxonDatetime = this.luxonDatetime.plus(luxonDuration);
730
- // Calculate total microseconds as: (current microseconds) + (microseconds to add)
731
- // This works with both positive and negative values
732
- const totalMicroseconds = this.microsecond + microsecondsAdjusted;
733
- // Normalize the microseconds
734
- const millisecondAdjustment = Math.floor(totalMicroseconds / 1000);
735
- const finalMicroseconds = ((totalMicroseconds % 1000) + 1000) % 1000; // Ensure 0-999 range
736
- return new DateTime(millisecondAdjustment !== 0
737
- ? luxonDatetime.plus({ milliseconds: millisecondAdjustment })
738
- : luxonDatetime, finalMicroseconds);
796
+ const normalized = normalizeMicrosecondTotal(this.microsecond + microsecondDelta);
797
+ return new DateTime(applyMillisecondAdjustment(luxonDatetime, normalized.millisecondAdjustment), normalized.microseconds);
739
798
  }
740
799
  /**
741
800
  * Subtracts a duration from this DateTime. Supports microsecond via DurationLikeObject.
@@ -749,34 +808,10 @@ export class DateTime {
749
808
  * ```
750
809
  */
751
810
  minus(duration) {
752
- const durationObj = typeof duration === 'number' ? { milliseconds: duration } : duration;
753
- const { microseconds: microsecondsToSubtract = 0, millisecond, milliseconds, ...rest } = durationObj;
754
- // Handle both millisecond and milliseconds (prefer milliseconds if both are present)
755
- const millisecondsToSubtract = milliseconds ?? millisecond;
756
- let microsecondsAdjusted = microsecondsToSubtract;
757
- // Build the object to pass to Luxon
758
- const luxonDuration = { ...rest };
759
- // Always handle fractional milliseconds by converting to microseconds
760
- if (millisecondsToSubtract !== undefined) {
761
- const wholeMilli = Math.floor(millisecondsToSubtract);
762
- const fractionalMilli = millisecondsToSubtract - wholeMilli;
763
- microsecondsAdjusted += Math.round(fractionalMilli * 1000);
764
- if (milliseconds !== undefined) {
765
- luxonDuration.milliseconds = wholeMilli;
766
- }
767
- else {
768
- luxonDuration.millisecond = wholeMilli;
769
- }
770
- }
811
+ const { luxonDuration, microsecondDelta } = splitDurationAndMicroseconds(duration);
771
812
  const luxonDatetime = this.luxonDatetime.minus(luxonDuration);
772
- // Calculate total microseconds as: (current microseconds) - (microseconds to subtract)
773
- const totalMicroseconds = this.microsecond - microsecondsAdjusted;
774
- // Normalize the microseconds
775
- const millisecondAdjustment = Math.floor(totalMicroseconds / 1000);
776
- const finalMicroseconds = ((totalMicroseconds % 1000) + 1000) % 1000; // Ensure 0-999 range
777
- return new DateTime(millisecondAdjustment !== 0
778
- ? luxonDatetime.plus({ milliseconds: millisecondAdjustment })
779
- : luxonDatetime, finalMicroseconds);
813
+ const normalized = normalizeMicrosecondTotal(this.microsecond - microsecondDelta);
814
+ return new DateTime(applyMillisecondAdjustment(luxonDatetime, normalized.millisecondAdjustment), normalized.microseconds);
780
815
  }
781
816
  /**
782
817
  * Returns a new DateTime with the given units set.
@@ -795,16 +830,24 @@ export class DateTime {
795
830
  }
796
831
  /**
797
832
  * Returns an object with date/time components including microsecond.
798
- * @param opts - Optional options (includeConfig for Luxon config)
799
833
  * @returns Object with year, month, day, hour, minute, second, millisecond, microsecond
800
834
  * @example
801
835
  * ```ts
802
836
  * DateTime.local(2017, 3, 12, 5, 45, 10, 123, 456).toObject()
803
837
  * ```
804
838
  */
805
- toObject(opts) {
806
- const obj = this.luxonDatetime.toObject(opts);
807
- 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
+ };
808
851
  }
809
852
  /**
810
853
  * Returns true if this and other represent the same instant and microsecond.
@@ -816,9 +859,7 @@ export class DateTime {
816
859
  * ```
817
860
  */
818
861
  equals(other) {
819
- if (!this.luxonDatetime.equals(other.luxonDatetime))
820
- return false;
821
- return this.microsecond === other.microsecond;
862
+ return this.valueOf() === other.valueOf();
822
863
  }
823
864
  /**
824
865
  * Returns the epoch time in milliseconds (toMillis * 1000 + millisecond).
@@ -905,7 +946,7 @@ export class DateTime {
905
946
  static min(...dateTimes) {
906
947
  if (dateTimes.length === 0)
907
948
  return null;
908
- 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]);
909
950
  }
910
951
  /**
911
952
  * Returns the latest DateTime from the given arguments.
@@ -919,7 +960,7 @@ export class DateTime {
919
960
  static max(...dateTimes) {
920
961
  if (dateTimes.length === 0)
921
962
  return null;
922
- 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]);
923
964
  }
924
965
  /**
925
966
  * Returns a new DateTime in the given zone. Microsecond is preserved.
@@ -958,8 +999,7 @@ export class DateTime {
958
999
  * ```
959
1000
  */
960
1001
  toLocal() {
961
- const luxonDatetime = this.luxonDatetime.toLocal();
962
- return new DateTime(luxonDatetime, this.microsecond);
1002
+ return new DateTime(this.luxonDatetime.toLocal(), this.microsecond);
963
1003
  }
964
1004
  /**
965
1005
  * Returns a new DateTime at the start of the given unit.
@@ -1047,46 +1087,46 @@ export class DateTime {
1047
1087
  * dt1.diff(dt2, 'days') // { days: 5 }
1048
1088
  * dt1.diff(dt2, ['days', 'hours']) // { days: 5, hours: 3 }
1049
1089
  * dt1.diff(dt2, ['milliseconds', 'microseconds']) // { milliseconds: 123, microseconds: 456 }
1050
- * 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 }
1051
1091
  * ```
1052
1092
  */
1053
1093
  diff(other, unit) {
1054
- const unitArray = unit === undefined ? undefined : typeof unit === 'string' ? [unit] : unit;
1055
- const needsMicroseconds = !unitArray || unitArray.includes('microseconds');
1056
- const needsMilliseconds = !unitArray || unitArray.includes('milliseconds');
1057
- // Strip 'microseconds' before passing to Luxon (it doesn't support it)
1058
- const luxonUnits = unitArray?.filter(u => u !== 'microseconds');
1059
- const luxonDuration = this.luxonDatetime.diff(other.toLuxon(), luxonUnits && luxonUnits.length > 0 ? luxonUnits : undefined);
1060
- const result = luxonDuration.toObject();
1061
- if (needsMicroseconds) {
1062
- if (unit === 'microseconds') {
1063
- // Only microseconds requested: return total difference
1064
- result.microseconds = this.toMicroseconds() - other.toMicroseconds();
1065
- }
1066
- else if (needsMilliseconds && result.milliseconds !== undefined) {
1067
- // Both ms + µs requested: combine Luxon's ms with µs field diff, then re-split.
1068
- // Example: Luxon says -1ms, µs fields are 999-0=+999 → total=-1µs → 0ms, -1µs
1069
- const totalMicroDiff = Math.round(result.milliseconds * 1000) + (this.microsecond - other.microsecond);
1070
- result.milliseconds = Math.trunc(totalMicroDiff / 1000) || 0; // || 0 normalizes -0
1071
- result.microseconds = totalMicroDiff - result.milliseconds * 1000;
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();
1101
+ }
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;
1072
1107
  }
1073
1108
  else {
1074
- // µs without ms: just the field difference
1075
- result.microseconds = this.microsecond - other.microsecond;
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
+ }
1076
1122
  }
1077
1123
  }
1078
- else if (needsMilliseconds && result.milliseconds !== undefined) {
1079
- // ms without µs: truncate fractional part
1080
- result.milliseconds = Math.trunc(result.milliseconds);
1124
+ else if (unitArray.includes('milliseconds')) {
1125
+ result.milliseconds = (result.milliseconds ?? 0) + microsecondFieldDiff / 1000;
1081
1126
  }
1082
- // Return only the requested units
1083
- if (unit === undefined)
1084
- return result;
1085
- if (typeof unit === 'string')
1086
- return { [unit]: result[unit] ?? 0 };
1087
1127
  const filtered = {};
1088
- for (const u of unit)
1089
- filtered[u] = result[u] ?? 0;
1128
+ for (const requestedUnit of unitArray)
1129
+ filtered[requestedUnit] = result[requestedUnit] ?? 0;
1090
1130
  return filtered;
1091
1131
  }
1092
1132
  /**
@@ -1123,33 +1163,84 @@ function wrapLuxonError(fn) {
1123
1163
  * @internal
1124
1164
  */
1125
1165
  function buildLocalOrUtcDateTime(yearOrOpts, month, day, hour, minute, second, millisecond, microsecondOrOpts, opts, isOpts, factory) {
1126
- const options = isOpts(opts)
1127
- ? opts
1128
- : isOpts(microsecondOrOpts)
1129
- ? microsecondOrOpts
1130
- : isOpts(millisecond)
1131
- ? millisecond
1132
- : isOpts(second)
1133
- ? second
1134
- : isOpts(minute)
1135
- ? minute
1136
- : isOpts(hour)
1137
- ? hour
1138
- : isOpts(day)
1139
- ? day
1140
- : isOpts(month)
1141
- ? month
1142
- : isOpts(yearOrOpts)
1143
- ? yearOrOpts
1144
- : undefined;
1166
+ const options = firstOptionArg(isOpts, opts, microsecondOrOpts, millisecond, second, minute, hour, day, month, yearOrOpts);
1145
1167
  const { milliseconds: millisecondPartOfMicroseconds, microseconds } = microsecondParts(typeof microsecondOrOpts === 'number' ? microsecondOrOpts : 0);
1146
- const y = typeof yearOrOpts === 'number' ? yearOrOpts : 0;
1147
- const m = typeof month === 'number' ? month : 1;
1148
- const d = typeof day === 'number' ? day : 1;
1149
- const ms = (typeof millisecond === 'number' ? millisecond : 0) + millisecondPartOfMicroseconds;
1150
- 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);
1151
1173
  return { luxonDatetime, microseconds };
1152
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
+ }
1153
1244
  /** Parse fractional part from ISO/SQL string: first 3 digits = ms, next 3 = µs */
1154
1245
  function parseFractionalPart(str) {
1155
1246
  const match = str.match(/\.(\d+)/);
@@ -1169,6 +1260,10 @@ function toThreeDecimalFraction(str) {
1169
1260
  const frac = (match[1] ?? '').padEnd(3, '0').slice(0, 3);
1170
1261
  return str.replace(/\.\d+/, '.' + frac);
1171
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
+ }
1172
1267
  /**
1173
1268
  * Thrown when a DateTime is invalid (e.g. invalid input or Luxon error).
1174
1269
  * @param error - The original error (available as cause)