How to get the hour from an NSDate in Swift 2? - nsdate

Before Swift 2 I used this extension to get the hour from a NSDate:
func hour() -> Int{
let calendar = NSCalendar.currentCalendar()
let components = calendar.components(.CalendarUnitHour, fromDate: self)
let hour = components.hour
return hour
}
but now with Swift 2 I get the error for the let-components-line
Type of expression is ambiguous without more context
Is there a better known way now? Thnx!

Use .Hour instead of .CalendarUnitHour.

Related

How to Convert excel Date into Year, month and date component or NaiveDate?

I'm using rust and chrono::NaiveDate to read an Excel file with date column type in it.
The date itself is formatted with "dd-mm-yyyy"
I can read the excel file and found out that the reader I use (https://docs.rs/calamine/latest/calamine/) returns float value for the date
A documentation in Microsoft site states that the date starts from January 1st, 1900
The float value in it corresponds to this dates:
date_value (FLOAT)
real value (in dd-mm-yyyy)
44198
02-01-2021
44199
03-01-2021
44200
04-01-2021
etc...
Basically I need a function or crate that can calculate month, date, and years from the float value I get. I have no clue on how to do this.
Below is my code
let data_type = calamine::DataType::deserialize(deserializer);
match data_type {
Ok(DataType::Error(_)) => {
Ok(None)
}
Ok(DataType::String(date_str)) => {
let msg = "Failed to convert Date. Wrong format or empty.";
let val = NaiveDate::parse_from_str(&date_str, DATE_FORMAT)
.map_err(|_| Error::custom(msg))?;
Ok(Some(val))
}
Ok(DataType::Float(dt)) => {
println!("this is float!!!");
println!("dt: {}", dt); // dt is a float number that count the number of days from January 1st 1900
let year = ? // what should I do here ?
let month = ?
let day = ?
let val = NaiveDate::from_ymd_opt(year, month, day)
Ok(None)
}
_ => {
Ok(None)
}
}
calamine has a dates feature that adds a DataType.as_date() method returning an Option<chrono::NaiveDate>.
There are also DataType.as_datetime() and DataType.as_time().
I don't know why it isn't documented, so use carefully.
At least, the method code could be a starting point for your own implementation.
A possible solution regarding my comment would be:
use chrono::{Duration, NaiveDate};
fn main() {
let start = NaiveDate::from_ymd_opt(1900, 1, 1).expect("DATE");
let date = start.checked_add_signed(Duration::days(44198)); // date_value
println!("{}", date.unwrap());
}
Playground

How to get the start and the end of date for each month with NaiveDate (Rust)?

How to get the last date of a month with chrono::NaiveDate. I try to search the manual but can't found anything useful.
use chrono::{NaiveDate, Datelike};
// from January to December m = 1 - 12
for m in 1..=12 {
let end = ..... ??
let start_date = NaiveDate::from_ymd(year, m, 1); /// each month starts with 1
let end_date = NaiveDate::from_ymd(year, m, end); /// here's the problem
container.push((start_date, end_date));
}
the closest would be to use Datelike::day https://docs.rs/chrono/0.4.6/chrono/trait.Datelike.html#tymethod.day0
and subtract 1 day from it, but I don't know how to do that with NaiveDate. I'm open to any suggestion. Thanks in advance.
Proposal for requested function
The function last_day_of_month already was proposed to be added to the chrono library on GitHub, but was not accepted.
The Code
The following solution works exactly like #Zeppi's solution, but uses a more rusty approach.
An Option<NativeDate> returned from NativeDate::from_ymd_opt is used to check whether the month is the 12. month to the switch to the edge-case, where the day is the last day of the month.
fn last_day_of_month(year: i32, month: u32) -> NaiveDate {
NaiveDate::from_ymd_opt(year, month + 1, 1)
.unwrap_or(NaiveDate::from_ymd(year + 1, 1, 1))
.pred()
}
Credits to #lifthrasiir on GitHub for the code
Sources
Chrono library Issue 69: Provide days_in_month()
Chrono library Issue 29: Leap year and last day of month
You can use pred()or pre_opt().
A very dirty solution that works like this
for m in 1..=12 {
let start_date = NaiveDate::from_ymd(2022, m, 1);
let mut end_date;
if m < 12 {
end_date = NaiveDate::from_ymd(2022, m + 1, 1).pred();
} else {
end_date = NaiveDate::from_ymd(2023, 1, 1).pred();
}
println!("{:?}", end_date);
}

Convert String.Index to Int or Range<String.Index> to NSRange

So I've found issues relating to the case of converting NSRange to Range<String.Index>, but I've actually run into the opposite problem.
Quite simply, I have a String and a Range<String.Index> and need to convert the latter into an NSRange for use with an older function.
So far my only workaround has been to grab a substring instead like so:
func foo(theString: String, inRange: Range<String.Index>?) -> Bool {
let theSubString = (nil == inRange) ? theString : theString.substringWithRange(inRange!)
return olderFunction(theSubString, NSMakeRange(0, countElements(theSubString)))
}
This works of course, but it isn't very pretty, I'd much rather avoid having to grab a sub-string and just use the range itself somehow, is this possible?
If you look into the definition of String.Index you find:
struct Index : BidirectionalIndexType, Comparable, Reflectable {
/// Returns the next consecutive value after `self`.
///
/// Requires: the next value is representable.
func successor() -> String.Index
/// Returns the previous consecutive value before `self`.
///
/// Requires: the previous value is representable.
func predecessor() -> String.Index
/// Returns a mirror that reflects `self`.
func getMirror() -> MirrorType
}
So actually there is no way to convert it to Int and that for good reason. Depending on the encoding of the string the single characters occupy a different number of bytes. The only way would be to count how many successor operations are needed to reach the desired String.Index.
Edit The definition of String has changed over the various Swift versions but it's basically the same answer. To see the very current definition just CMD-click on a String definition in XCode to get to the root (works for other types as well).
The distanceTo is an extension which goes to a variety of protocols. Just look for it in the String source after the CMD-click.
let index: Int = string.startIndex.distanceTo(range.startIndex)
I don't know which version introduced it, but in Swift 4.2 you can easily convert between the two.
To convert Range<String.Index> to NSRange:
let range = s[s.startIndex..<s.endIndex]
let nsRange = NSRange(range, in: s)
To convert NSRange to Range<String.Index>:
let nsRange = NSMakeRange(0, 4)
let range = Range(nsRange, in: s)
Keep in mind that NSRange is UTF-16 based, while Range<String.Index> is Character based.
Hence you can't just use counts and positions to convert between the two!
In Swift 4, distanceTo() is deprecated. You may have to convert String to NSString to take advantage of its -[NSString rangeOfString:] method, which returns an NSRange.
Swift 4 Complete Solution:
OffsetIndexableCollection (String using Int Index)
https://github.com/frogcjn/OffsetIndexableCollection-String-Int-Indexable-
let a = "01234"
print(a[0]) // 0
print(a[0...4]) // 01234
print(a[...]) // 01234
print(a[..<2]) // 01
print(a[...2]) // 012
print(a[2...]) // 234
print(a[2...3]) // 23
print(a[2...2]) // 2
if let number = a.index(of: "1") {
print(number) // 1
print(a[number...]) // 1234
}
if let number = a.index(where: { $0 > "1" }) {
print(number) // 2
}
You can use this function and call it when ever you need convertion
extension String
{
func CnvIdxTooIntFnc(IdxPsgVal: Index) -> Int
{
return startIndex.distanceTo(IdxPsgVal)
}
}

Array of NSManagedObjects and reduce function compile but fail at runtime

Using playground to simulate a problem with a core data based app turned up an issue I can't seem to understand.
class Pole {
var length: NSNumber!
}
var poles: [Pole] = []
let pole1 = Pole()
pole1.length = 1
poles.append(pole1)
let pole2 = Pole()
pole2.length = 2
poles.append(pole2)
var sum = poles.reduce(0) { $0 + $1.length } // error Could not find member 'length'
The property (attribute) named length is NSNumber as it is in a NSManagedObject class (entity).
Changing the Type from NSNumber! to Int! allows the line to compile and run correctly in playground.
Leaving the the Type as NSNumber! and changing the the offending line as follows:
var sum = poles.reduce(0) { $0 + Int($1.length) }
also compiles and run correctly in playground. The next step, taking this to the app, using actual NSManagedObject entity and attribute compiles but fails at runtime. The failure is 'unwrapping a nil'.
So bottom line I can't figure out how to use the reduce function when the attribute is a NSNumber and casting it to an Int doesn't seem to be acceptable.
NSNumber is a Foundation class that is used to wrap numbers that would otherwise be represented in a value type, so they can be used wherever a reference type is expected. Swift bridges integer literals to NSNumber, but not the other way around, so the original reduce closure was trying to add an integer literal to an NSNumber, getting confused, and giving you a weird error message.
Creating a new Int is one way to handle the problem:
var sum = poles.reduce(0) { $0 + Int($1.length) }
Or you can use NSNumber's integerValue property, which returns the instance's value as an Int:
var sum = poles.reduce(0) { $0 + $1.length.integerValue }

String interpolation in Swift

A function in swift takes any numeric type in Swift (Int, Double, Float, UInt, etc).
the function converts the number to a string
the function signature is as follows :
func swiftNumbers <T : NumericType> (number : T) -> String {
//body
}
NumericType is a custom protocol that has been added to numeric types in Swift.
inside the body of the function, the number should be converted to a string:
I use the following
var stringFromNumber = "\(number)"
which is not so elegant, PLUS : if the absolute value of the number is strictly inferior to 0.0001 it gives this:
"\(0.000099)" //"9.9e-05"
or if the number is a big number :
"\(999999999999999999.9999)" //"1e+18"
is there a way to work around this string interpolation limitation? (without using Objective-C)
P.S :
NumberFormater doesn't work either
import Foundation
let number : NSNumber = 9_999_999_999_999_997
let formatter = NumberFormatter()
formatter.minimumFractionDigits = 20
formatter.minimumIntegerDigits = 20
formatter.minimumSignificantDigits = 40
formatter.string(from: number) // "9999999999999996.000000000000000000000000"
let stringFromNumber = String(format: "%20.20f", number) // "0.00000000000000000000"
Swift String Interpolation
1) Adding different types to a string
2) Means the string is created from a mix of constants, variables, literals or expressions.
Example:
let length:Float = 3.14
var breadth = 10
var myString = "Area of a rectangle is length*breadth"
myString = "\(myString) i.e. = \(length)*\(breadth)"
Output:
3.14
10
Area of a rectangle is length*breadth
Area of a rectangle is length*breadth i.e. = 3.14*10
Use the Swift String initializer: String(format: <#String#>, arguments: <#[CVarArgType]#>)
For example:
let stringFromNumber = String(format: "%.2f", number)
String and Characters conforms to StringInterpolationProtocol protocol which provide more power to the strings.
StringInterpolationProtocol - "Represents the contents of a string literal with interpolations while it’s being built up."
String interpolation has been around since the earliest days of Swift, but in Swift 5.0 it’s getting a massive overhaul to make it faster and more powerful.
let name = "Ashwinee Dhakde"
print("Hello, I'm \(name)")
Using the new string interpolation system in Swift 5.0 we can extend String.StringInterpolation to add our own custom interpolations, like this:
extension String.StringInterpolation {
mutating func appendInterpolation(_ value: Date) {
let formatter = DateFormatter()
formatter.dateStyle = .full
let dateString = formatter.string(from: value)
appendLiteral(dateString)
}
}
Usage: print("Today's date is \(Date()).")
We can even provide user-defined names to use String-Interpolation, let's understand with an example.
extension String.StringInterpolation {
mutating func appendInterpolation(JSON JSONData: Data) {
guard
let JSONObject = try? JSONSerialization.jsonObject(with: JSONData, options: []),
let jsonData = try? JSONSerialization.data(withJSONObject: JSONObject, options: .prettyPrinted) else {
appendInterpolation("Invalid JSON data")
return
}
appendInterpolation("\n\(String(decoding: jsonData, as: UTF8.self))")
}
}
print("The JSON is \(JSON: jsonData)")
Whenever we want to provide "JSON" in the string interpolation statement, it will print the .prettyPrinted
Isn't it cool!!

Resources