mirror of
https://github.com/swift-server/swift-aws-lambda-runtime.git
synced 2026-05-03 07:22:27 +00:00
447c1e4db1
Fix [#384](https://github.com/swift-server/swift-aws-lambda-runtime/issues/384) Note: this PR introduces an API change that will break Lambda functions using `LambdaContext`, we should integrate this change during the beta otherwise it will require a major version bump. ### Motivation: `DispatchWallTime` has no public API to extract the time in milliseconds, making it a dead end. Previous implementation used the internal representation of time inside `DispatchWallTime` to extract the value, creating a risk if its implementation will change in the future. Moreover, the use of `DispatchWallTime` obliges users to import the `Dispatch` library or `Foundation`. Old Code: ``` extension DispatchWallTime { @usableFromInline init(millisSinceEpoch: Int64) { let nanoSinceEpoch = UInt64(millisSinceEpoch) * 1_000_000 let seconds = UInt64(nanoSinceEpoch / 1_000_000_000) let nanoseconds = nanoSinceEpoch - (seconds * 1_000_000_000) self.init(timespec: timespec(tv_sec: Int(seconds), tv_nsec: Int(nanoseconds))) } var millisSinceEpoch: Int64 { Int64(bitPattern: self.rawValue) / -1_000_000 } } ``` Issue [#384](https://github.com/swift-server/swift-aws-lambda-runtime/issues/384) has a long discussion about possible replacements, including creating a brand new `UTCClock`, which I think is an overkill for this project. Instead, I propose this simple implementation, based on two assumptions: - AWS always sends the time in milliseconds since Unix Epoch (1st Jan 1970) ([Lambda Runtime API documentation](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-api.html#runtimes-api-next)) - AWS always uses UTC time (not only for Lambda, this is a general rule for all AWS APIs) ([TZ=UTC on Lambda](https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html)) Therefore, this library just needs to store and make math on milliseconds since epoch, without having to care about timezone. I had two possibilities to implement the storage and the math on milliseconds since Unix Epoch: either I could use an `UInt64` (as does [the Rust implementation](https://github.com/awslabs/aws-lambda-rust-runtime/blob/aff8d883c62997ef2615714dce9f7ddfd557147d/lambda-runtime/src/types.rs#L70)) or I could use a standard Swift type, such as `Duration`. `Duration` is a good candidate for this because 1/ the time we receive from the Lambda Service API is indeed a duration between 1/1/1970 and the execution deadline for the Lambda function, expressed in milliseconds, 2/ it gives a strong type that can be verified by the compiler, and 3/ it is possible to do basic arithmetic operations and compare two values. As an additional benefit, it allows library users to not import `Dispatch` or `Foundation` ### Modifications: I made two changes: 1. I extend the `Duration` type to provide us with simple unix epoch time manipulation functions and values. ```swift extension Duration { /// Returns the time in milliseconds since the Unix epoch. @usableFromInline static var millisSinceEpoch: Duration { var ts = timespec() clock_gettime(CLOCK_REALTIME, &ts) return .milliseconds(Int64(ts.tv_sec) * 1000 + Int64(ts.tv_nsec) / 1_000_000) } /// Returns a Duration between Unix epoch and the distant future @usableFromInline static var distantFuture: Duration { // Use a very large value to represent the distant future millisSinceEpoch + Duration.seconds(.greatestFiniteMagnitude) } /// Returns the Duration in milliseconds @usableFromInline func milliseconds() -> Int64 { Int64(self / .milliseconds(1)) } /// Create a Duration from milliseconds since Unix Epoch @usableFromInline init(millisSinceEpoch: Int64) { self = .milliseconds(millisSinceEpoch) } } ``` 3. I replaced all references to `DispatchWallTime` by `Duration` ### Result: No more `DispatchWallTime` No dependencies on Foundation, as I use `clock_gettime()` to get the epoch from the system clock.